diff -Nurp linux.old/drivers/char/n_tty.c linux.new/drivers/char/n_tty.c
--- linux.old/drivers/char/n_tty.c	2008-08-14 09:38:48.618009570 -0600
+++ linux.new/drivers/char/n_tty.c	2008-08-14 09:46:33.468010034 -0600
@@ -152,6 +152,23 @@ static void check_unthrottle(struct tty_
 }
 
 /**
+ *	clear_echo_buffer	-	clear out the echo buffer
+ *	@tty: terminal to reset
+ *
+ *	Clear all pending characters and operations from the echo buffer.
+ *	Called from reset_buffer_flags() and when receiving a signal char.
+ */
+
+static void clear_echo_buffer(struct tty_struct *tty)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&tty->echo_lock, flags);
+	tty->echo_pos = tty->echo_cnt = tty->echo_overrun = 0;
+	spin_unlock_irqrestore(&tty->echo_lock, flags);
+}
+
+/**
  *	reset_buffer_flags	-	reset buffer state
  *	@tty: terminal to reset
  *
@@ -159,6 +176,7 @@ static void check_unthrottle(struct tty_
  *	and make sure the driver is unthrottled. Called
  *	from n_tty_open() and n_tty_flush_buffer().
  */
+
 static void reset_buffer_flags(struct tty_struct *tty)
 {
 	unsigned long flags;
@@ -166,6 +184,7 @@ static void reset_buffer_flags(struct tt
 	spin_lock_irqsave(&tty->read_lock, flags);
 	tty->read_head = tty->read_tail = tty->read_cnt = 0;
 	spin_unlock_irqrestore(&tty->read_lock, flags);
+	clear_echo_buffer(tty);
 	tty->canon_head = tty->canon_data = tty->erasing = 0;
 	memset(&tty->read_flags, 0, sizeof tty->read_flags);
 	n_tty_set_room(tty);
@@ -254,89 +273,107 @@ static inline int is_continuation(unsign
 }
 
 /**
- *	opost			-	output post processor
+ *	output_char			-	output one character
+ *	@space: space available in write buffer
  *	@c: character (or partial unicode symbol)
  *	@tty: terminal device
  *
- *	Perform OPOST processing.  Returns -1 when the output device is
- *	full and the character must be retried. Note that Linux currently
- *	ignores TABDLY, CRDLY, VTDLY, FFDLY and NLDLY. They simply aren't
- *	relevant in the world today. If you ever need them, add them here.
+ *	Puts one character in the write buffer (in the tty driver).
+ *	Returns the number of bytes of buffer space used or -1 if
+ *	no space left.
  *
- *	Called from both the receive and transmit sides and can be called
- *	re-entrantly. Relies on lock_kernel() for tty->column state.
+ *	Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY
+ *	and NLDLY.  They simply aren't relevant in the world today.
+ *	If you ever need them, add them here.
  */
 
-static int opost(unsigned char c, struct tty_struct *tty)
+static int output_char(int space, unsigned char c, struct tty_struct *tty)
 {
-	int	space, spaces;
+	int	spaces;
 
-	space = tty_write_room(tty);
 	if (!space)
 		return -1;
-
-	lock_kernel();
-	if (O_OPOST(tty)) {
-		switch (c) {
-		case '\n':
+	
+	switch (c) {
+	case '\n':
+		if (O_ONLRET(tty))
+			tty->column = 0;
+		if (O_ONLCR(tty)) {
+			if (space < 2)
+				return -1;
+			tty_put_char(tty, '\r');
+			tty->column = 0;
+		}
+		tty->canon_column = tty->column;
+		break;
+	case '\r':
+		if (O_ONOCR(tty) && tty->column == 0)
+			return 0;
+		if (O_OCRNL(tty)) {
+			c = '\n';
 			if (O_ONLRET(tty))
-				tty->column = 0;
-			if (O_ONLCR(tty)) {
-				if (space < 2) {
-					unlock_kernel();
-					return -1;
-				}
-				tty_put_char(tty, '\r');
-				tty->column = 0;
-			}
-			tty->canon_column = tty->column;
+				tty->canon_column = tty->column = 0;
 			break;
-		case '\r':
-			if (O_ONOCR(tty) && tty->column == 0) {
-				unlock_kernel();
-				return 0;
-			}
-			if (O_OCRNL(tty)) {
-				c = '\n';
-				if (O_ONLRET(tty))
-					tty->canon_column = tty->column = 0;
-				break;
-			}
-			tty->canon_column = tty->column = 0;
-			break;
-		case '\t':
-			spaces = 8 - (tty->column & 7);
-			if (O_TABDLY(tty) == XTABS) {
-				if (space < spaces) {
-					unlock_kernel();
-					return -1;
-				}
-				tty->column += spaces;
-				tty->ops->write(tty, "        ", spaces);
-				unlock_kernel();
-				return 0;
-			}
+		}
+		tty->canon_column = tty->column = 0;
+		break;
+	case '\t':
+		spaces = 8 - (tty->column & 7);
+		if (O_TABDLY(tty) == XTABS) {
+			if (space < spaces)
+				return -1;
 			tty->column += spaces;
-			break;
-		case '\b':
-			if (tty->column > 0)
-				tty->column--;
-			break;
-		default:
-			if (O_OLCUC(tty))
-				c = toupper(c);
-			if (!iscntrl(c) && !is_continuation(c, tty))
-				tty->column++;
-			break;
+			tty->ops->write(tty, "        ", spaces);
+			return spaces;
 		}
+		tty->column += spaces;
+		break;
+	case '\b':
+		if (tty->column > 0)
+			tty->column--;
+		break;
+	default:
+		if (O_OLCUC(tty))
+			c = toupper(c);
+		if (!iscntrl(c) && !is_continuation(c, tty))
+			tty->column++;
+		break;
 	}
+
 	tty_put_char(tty, c);
+	return 1;
+}
+
+/**
+ *	process_output			-	output post processor
+ *	@c: character (or partial unicode symbol)
+ *	@tty: terminal device
+ *
+ *	Perform OPOST processing.  Returns -1 when the output device is
+ *	full and the character must be retried.
+ *
+ *	Called from both the receive and transmit sides and can be called
+ *	re-entrantly. Relies on lock_kernel for tty->column state.
+ */
+
+static int process_output(unsigned char c, struct tty_struct *tty)
+{
+	int	space, retval;
+
+	lock_kernel();
+
+	space = tty_write_room(tty);
+	retval = output_char(space, c, tty);
+
 	unlock_kernel();
-	return 0;
+	if (retval < 0)
+		return -1;
+	else
+		return 0;
 }
 
 /**
- *	opost_block		-	block postprocess
+ *	process_output_block		-	block post processor
  *	@tty: terminal device
  *	@inbuf: user buffer
  *	@nr: number of bytes
@@ -350,20 +387,24 @@ static int opost(unsigned char c, struct
  *	on lock_kernel for the tty->column state.
  */
 
-static ssize_t opost_block(struct tty_struct *tty,
-		       const unsigned char *buf, unsigned int nr)
+static ssize_t process_output_block(struct tty_struct *tty,
+				    const unsigned char *buf, unsigned int nr)
 {
 	int	space;
 	int 	i;
 	const unsigned char *cp;
 
+	lock_kernel();
+
 	space = tty_write_room(tty);
 	if (!space)
+	{
+		unlock_kernel();
 		return 0;
+	}
 	if (nr > space)
 		nr = space;
 
-	lock_kernel();
 	for (i = 0, cp = buf; i < nr; i++, cp++) {
 		switch (*cp) {
 		case '\n':
@@ -395,38 +436,369 @@ static ssize_t opost_block(struct tty_st
 		}
 	}
 break_out:
-	if (tty->ops->flush_chars)
-		tty->ops->flush_chars(tty);
+	//if (tty->ops->flush_chars)
+	//	tty->ops->flush_chars(tty);
 	i = tty->ops->write(tty, buf, i);
+
 	unlock_kernel();
 	return i;
 }
 
+#define ECHO_OP_START 0xff
+#define ECHO_OP_MOVE_BACK_COL 0x80
+#define ECHO_OP_SET_CANON_COL 0x81
+#define ECHO_OP_TAB_ERASE 0x82
+
+/**
+ *	process_echo_chars	-	write pending echoed characters
+ *	@tty: terminal device
+ *
+ *	Write buffered echoed (and other ldisc-generated) characters
+ *	to the tty.
+ *
+ *	Characters generated by the ldisc (including echoes) need to
+ *	be buffered because the driver's write buffer can fill during
+ *	heavy program output.  Echoing straight to the driver will
+ *	often fail the first time under these conditions.
+ *
+ *	In addition to buffering characters, operations like certain
+ *	changes in column state are also saved in the buffer to be
+ *	replayed here.
+ *
+ *	A circular fifo buffer is used so that the most recent characters
+ *	are prioritized.  Also, it is ensured that when control characters
+ *	are echoed with a prefixed "^", the pair is treated atomically
+ *	and thus not separated.
+ *
+ *	Like the process_output functions, this uses lock_kernel.
+ *	If this is ever removed, we should use the echo_lock.
+ */
+
+static void process_echo_chars(struct tty_struct *tty)
+{
+	int	space, num, nr;
+	unsigned char c;
+	unsigned char *cp, *buf_end;
+
+	if (!(num = tty->echo_cnt))
+		return;
+
+	lock_kernel();
+
+	space = tty_write_room(tty);
+	/*
+	if (!space)
+	{
+		unlock_kernel();
+		return;
+	}
+	*/
+
+	/*
+	if (tty->echo_overrun) {
+		if (space < 4) {
+			unlock_kernel();
+			return;
+		}
+		tty->ops->write(tty, "...", 3);
+		tty->column += 3;
+	}
+	*/
+
+	buf_end = tty->echo_buf + N_TTY_BUF_SIZE;
+	cp = tty->echo_buf + tty->echo_pos;
+	nr = num;
+	while (nr > 0) {
+		c = *cp;
+		//if (c > 31 && c < 0x7f)
+		//	printk("echo: processing %c\n", c);
+		//else
+		//	printk("echo: processing 0x%02x\n", c);
+		if (c == ECHO_OP_START) {
+			unsigned char op;
+			unsigned char *opp;
+			int no_space_left = 0;
+
+			/*
+			 * If the buffer byte is the start of a multi-byte
+			 * operation, get the next byte, which is either the
+			 * op code or a control character value.
+			 */
+			opp = cp + 1;
+			if (opp == buf_end)
+				opp -= N_TTY_BUF_SIZE;
+			op = *opp;
+			
+			switch (op) {
+				unsigned char lbyte, hbyte;
+				unsigned short rdiff;
+				int num_bs;
+				unsigned int col;
+
+			case ECHO_OP_TAB_ERASE:
+				/* Extract rdiff value from next two bytes */
+				if (++opp == buf_end)
+					opp -= N_TTY_BUF_SIZE;
+				lbyte = *opp;
+				if (++opp == buf_end)
+					opp -= N_TTY_BUF_SIZE;
+				hbyte = *opp;
+				rdiff = (hbyte << 8) | lbyte;
+				
+				col = tty->canon_column + rdiff;
+
+				/* should never happen */
+				if (tty->column > 0x80000000)
+					tty->column = 0;
+
+				num_bs = tty->column - col;
+				if (num_bs < 0)
+					num_bs = 0;
+				if (num_bs > space) {
+					no_space_left = 1;
+					break;
+				}
+
+				/* Now backup to that column. */
+				while (tty->column > col) {
+					tty_put_char(tty, '\b');
+					if (tty->column > 0)
+						tty->column--;
+				}
+				space -= num_bs;
+				cp += 4;
+				nr -= 4;
+				break;
+
+			case ECHO_OP_SET_CANON_COL:
+				tty->canon_column = tty->column;
+				cp += 2;
+				nr -= 2;
+				break;
+
+			case ECHO_OP_MOVE_BACK_COL:
+				if (tty->column > 0)
+					tty->column--;
+				cp += 2;
+				nr -= 2;
+				break;
+
+			case ECHO_OP_START:
+				/* This is an escaped echo op start code */
+				if (!space) {
+					no_space_left = 1;
+					break;
+				}
+				tty_put_char(tty, ECHO_OP_START);
+				tty->column++;
+				space--;
+				cp += 2;
+				nr -= 2;
+				break;
+
+			default:
+				if (iscntrl(op)) {
+					if (L_ECHOCTL(tty)) {
+						/*
+						 * Ensure there is enough space
+						 * for the whole ctrl pair.
+						 */
+						if (space < 2) {
+							no_space_left = 1;
+							break;
+						}
+						tty_put_char(tty, '^');
+						tty_put_char(tty, op ^ 0100);
+						tty->column += 2;
+						space -= 2;
+					} else {
+						if (!space) {
+							no_space_left = 1;
+							break;
+						}
+						tty_put_char(tty, op);
+						space--;
+					}
+				}
+				/*
+				 * If above falls through, this was an
+				 * undefined op.
+				 */
+				cp += 2;
+				nr -= 2;
+			}
+
+			if (no_space_left)
+				break;
+		} else {
+			int retval;
+
+			if ((retval = output_char(space, c, tty)) < 0)
+				break;
+			space -= retval;
+			cp += 1;
+			nr -= 1;
+		}
+
+		/* When end of circular buffer reached, wrap around */
+		if (cp >= buf_end)
+			cp -= N_TTY_BUF_SIZE;
+	}
+
+	tty->echo_cnt = nr;
+	if (tty->echo_cnt == 0) {
+		tty->echo_pos = 0;
+		tty->echo_overrun = 0;
+	} else {
+		int num_processed = (num - nr);
+		tty->echo_pos += num_processed;
+		tty->echo_pos &= (N_TTY_BUF_SIZE - 1);
+		if (num_processed > 0)
+			tty->echo_overrun = 0;
+	}
+
+	unlock_kernel();
+
+	if (tty->ops->flush_chars)
+		tty->ops->flush_chars(tty);
+}
+
+/**
+ *	add_echo_byte	-	add a byte to the echo buffer
+ *	@c: unicode byte to echo
+ *	@tty: terminal device
+ *
+ *	Add a character or operation byte to the echo buffer.
+ */
+
+static void add_echo_byte(unsigned char c, struct tty_struct *tty)
+{
+	int 	add_char_pos;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tty->echo_lock, flags);
+
+	add_char_pos = tty->echo_pos + tty->echo_cnt;
+	if (add_char_pos >= N_TTY_BUF_SIZE)
+		add_char_pos -= N_TTY_BUF_SIZE;
+
+	/* Detect overrun */
+	if (tty->echo_cnt == N_TTY_BUF_SIZE) {
+		/*
+		 * If the start position pointer needs to be advanced,
+		 * be sure it is done in such a way as to remove whole
+		 * operation byte groups.
+		 */
+		if (*(tty->echo_buf +
+		      (tty->echo_pos & (N_TTY_BUF_SIZE - 1))) == ECHO_OP_START)
+		{
+			if (*(tty->echo_buf +
+			      ((tty->echo_pos + 1) & (N_TTY_BUF_SIZE - 1))) == ECHO_OP_TAB_ERASE) {
+				tty->echo_pos += 4;
+				tty->echo_cnt -= 3;
+			} else {
+				tty->echo_pos += 2;
+				tty->echo_cnt -= 1;
+			}
+		} else
+			tty->echo_pos++;
+		tty->echo_pos &= (N_TTY_BUF_SIZE - 1);
+		tty->echo_overrun = 1;
+	} else
+		tty->echo_cnt++;
+
+	tty->echo_buf[add_char_pos] = c;
+
+	spin_unlock_irqrestore(&tty->echo_lock, flags);
+}
+
+/**
+ *	echo_move_back_col	-	add instruction to move back a column
+ *	@tty: terminal device
+ *
+ *	Add an instruction to the echo buffer to move back one column.
+ */
+
+static void echo_move_back_col(struct tty_struct *tty)
+{
+	add_echo_byte(ECHO_OP_START, tty);
+	add_echo_byte(ECHO_OP_MOVE_BACK_COL, tty);
+}
+
+/**
+ *	echo_set_canon_col	-	add instruction to set the canon column
+ *	@tty: terminal device
+ *
+ *	Add an instruction to the echo buffer to set the canon column
+ *	to the current column.
+ */
+
+static void echo_set_canon_col(struct tty_struct *tty)
+{
+	add_echo_byte(ECHO_OP_START, tty);
+	add_echo_byte(ECHO_OP_SET_CANON_COL, tty);
+}
+
+/**
+ *	echo_tab_erase	-	add instruction to erase tabs
+ *	@tty: terminal device
+ *
+ *	Add an instruction to the echo buffer to set the canon column
+ *	to the current column.
+ */
+
+static void echo_tab_erase(unsigned short rdiff, struct tty_struct *tty)
+{
+	add_echo_byte(ECHO_OP_START, tty);
+	add_echo_byte(ECHO_OP_TAB_ERASE, tty);
+	add_echo_byte(rdiff & 0xff, tty);
+	add_echo_byte(rdiff >> 8, tty);
+}
 
 /**
- *	echo_char	-	echo characters
+ *	echo_char_raw	-	echo a character raw
  *	@c: unicode byte to echo
  *	@tty: terminal device
  *
  *	Echo user input back onto the screen. This must be called only when
  *	L_ECHO(tty) is true. Called from the driver receive_buf path.
+ *
+ *	This variant does not convert treat control characters specially.
  */
 
-static void echo_char(unsigned char c, struct tty_struct *tty)
+static void echo_char_raw(unsigned char c, struct tty_struct *tty)
 {
-	if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') {
-		tty_put_char(tty, '^');
-		tty_put_char(tty, c ^ 0100);
-		tty->column += 2;
+	if (c == ECHO_OP_START) {
+		add_echo_byte(ECHO_OP_START, tty);
+		add_echo_byte(ECHO_OP_START, tty);
 	} else
-		opost(c, tty);
+		add_echo_byte(c, tty);
+}
+
+/**
+ *	echo_char	-	echo a character
+ *	@c: unicode byte to echo
+ *	@tty: terminal device
+ *
+ *	Echo user input back onto the screen. This must be called only when
+ *	L_ECHO(tty) is true. Called from the driver receive_buf path.
+ *
+ *	This variant tags control characters to be possibly echoed as
+ *	as "^X" (where X is the letter representing the control char).
+ */
+
+static void echo_char(unsigned char c, struct tty_struct *tty)
+{
+	if (iscntrl(c) && c != '\t')
+		add_echo_byte(ECHO_OP_START, tty);
+	echo_char_raw(c, tty);
+	//process_echo_chars(tty);
 }
 
 static inline void finish_erasing(struct tty_struct *tty)
 {
 	if (tty->erasing) {
-		tty_put_char(tty, '/');
-		tty->column++;
+		echo_char_raw('/', tty);
 		tty->erasing = 0;
 	}
 }
@@ -448,7 +820,7 @@ static void eraser(unsigned char c, stru
 	unsigned long flags;
 
 	if (tty->read_head == tty->canon_head) {
-		/* opost('\a', tty); */		/* what do you think? */
+		/* echo_char_raw('\a', tty); */ /* what do you think? */
 		return;
 	}
 	if (c == ERASE_CHAR(tty))
@@ -474,7 +846,7 @@ static void eraser(unsigned char c, stru
 			echo_char(KILL_CHAR(tty), tty);
 			/* Add a newline if ECHOK is on and ECHOKE is off. */
 			if (L_ECHOK(tty))
-				opost('\n', tty);
+				echo_char_raw('\n', tty);
 			return;
 		}
 		kill_type = KILL;
@@ -509,67 +881,58 @@ static void eraser(unsigned char c, stru
 		if (L_ECHO(tty)) {
 			if (L_ECHOPRT(tty)) {
 				if (!tty->erasing) {
-					tty_put_char(tty, '\\');
-					tty->column++;
+					echo_char_raw('\\', tty);
 					tty->erasing = 1;
 				}
 				/* if cnt > 1, output a multi-byte character */
 				echo_char(c, tty);
 				while (--cnt > 0) {
 					head = (head+1) & (N_TTY_BUF_SIZE-1);
-					tty_put_char(tty, tty->read_buf[head]);
+					echo_char_raw(tty->read_buf[head], tty);
+					echo_move_back_col(tty);
 				}
 			} else if (kill_type == ERASE && !L_ECHOE(tty)) {
 				echo_char(ERASE_CHAR(tty), tty);
 			} else if (c == '\t') {
-				unsigned int col = tty->canon_column;
+				unsigned short rdiff = 0;
 				unsigned long tail = tty->canon_head;
 
-				/* Find the column of the last char. */
+				/*
+				 * Find number of columns from canon_head
+				 * to read_head.  This will later be
+				 * added to the canon_column to determine
+				 * how far to erase up to the cur column.
+				 */
 				while (tail != tty->read_head) {
 					c = tty->read_buf[tail];
 					if (c == '\t')
-						col = (col | 7) + 1;
+						rdiff = (rdiff | 7) + 1;
 					else if (iscntrl(c)) {
 						if (L_ECHOCTL(tty))
-							col += 2;
+							rdiff += 2;
 					} else if (!is_continuation(c, tty))
-						col++;
+						rdiff++;
 					tail = (tail+1) & (N_TTY_BUF_SIZE-1);
 				}
 
-				/* should never happen */
-				if (tty->column > 0x80000000)
-					tty->column = 0;
-
-				/* Now backup to that column. */
-				while (tty->column > col) {
-					/* Can't use opost here. */
-					tty_put_char(tty, '\b');
-					if (tty->column > 0)
-						tty->column--;
-				}
+				echo_tab_erase(rdiff, tty);
 			} else {
 				if (iscntrl(c) && L_ECHOCTL(tty)) {
-					tty_put_char(tty, '\b');
-					tty_put_char(tty, ' ');
-					tty_put_char(tty, '\b');
-					if (tty->column > 0)
-						tty->column--;
+					echo_char_raw('\b', tty);
+					echo_char_raw(' ', tty);
+					echo_char_raw('\b', tty);
 				}
 				if (!iscntrl(c) || L_ECHOCTL(tty)) {
-					tty_put_char(tty, '\b');
-					tty_put_char(tty, ' ');
-					tty_put_char(tty, '\b');
-					if (tty->column > 0)
-						tty->column--;
+					echo_char_raw('\b', tty);
+					echo_char_raw(' ', tty);
+					echo_char_raw('\b', tty);
 				}
 			}
 		}
 		if (kill_type == ERASE)
 			break;
 	}
-	if (tty->read_head == tty->canon_head)
+	if (tty->read_head == tty->canon_head && L_ECHO(tty))
 		finish_erasing(tty);
 }
 
@@ -698,14 +1061,18 @@ static inline void n_tty_receive_char(st
 		c=tolower(c);
 
 	if (tty->stopped && !tty->flow_stopped && I_IXON(tty) &&
-	    ((I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty)) ||
-	     c == INTR_CHAR(tty) || c == QUIT_CHAR(tty) || c == SUSP_CHAR(tty)))
+	    I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) &&
+	    c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) {
 		start_tty(tty);
+		process_echo_chars(tty);
+	}
 
 	if (tty->closing) {
 		if (I_IXON(tty)) {
-			if (c == START_CHAR(tty))
+			if (c == START_CHAR(tty)) {
 				start_tty(tty);
+				process_echo_chars(tty);
+			}
 			else if (c == STOP_CHAR(tty))
 				stop_tty(tty);
 		}
@@ -719,17 +1086,19 @@ static inline void n_tty_receive_char(st
 	 * up.
 	 */
 	if (!test_bit(c, tty->process_char_map) || tty->lnext) {
-		finish_erasing(tty);
 		tty->lnext = 0;
 		if (L_ECHO(tty)) {
+			finish_erasing(tty);
 			if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
-				tty_put_char(tty, '\a'); /* beep if no space */
+				echo_char_raw('\a', tty); /* beep if no space */
+				process_echo_chars(tty);
 				return;
 			}
 			/* Record the column of first canon char. */
 			if (tty->canon_head == tty->read_head)
-				tty->canon_column = tty->column;
+				echo_set_canon_col(tty);
 			echo_char(c, tty);
+			process_echo_chars(tty);
 		}
 		if (I_PARMRK(tty) && c == (unsigned char) '\377')
 			put_tty_queue(c, tty);
@@ -740,6 +1109,7 @@ static inline void n_tty_receive_char(st
 	if (I_IXON(tty)) {
 		if (c == START_CHAR(tty)) {
 			start_tty(tty);
+			process_echo_chars(tty);
 			return;
 		}
 		if (c == STOP_CHAR(tty)) {
@@ -769,8 +1139,13 @@ send_signal:
 				n_tty_flush_buffer(tty);
 				tty_driver_flush_buffer(tty);
 			}
-			if (L_ECHO(tty))
+			clear_echo_buffer(tty);
+			if (I_IXON(tty))
+				start_tty(tty);
+			if (L_ECHO(tty)) {
 				echo_char(c, tty);
+				process_echo_chars(tty);
+			}
 			if (tty->pgrp)
 				kill_pgrp(tty->pgrp, signal, 1);
 			return;
@@ -789,6 +1164,7 @@ send_signal:
 		if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
 		    (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
 			eraser(c, tty);
+			process_echo_chars(tty);
 			return;
 		}
 		if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
@@ -796,8 +1172,9 @@ send_signal:
 			if (L_ECHO(tty)) {
 				finish_erasing(tty);
 				if (L_ECHOCTL(tty)) {
-					tty_put_char(tty, '^');
-					tty_put_char(tty, '\b');
+					echo_char_raw('^', tty);
+					echo_char_raw('\b', tty);
+					process_echo_chars(tty);
 				}
 			}
 			return;
@@ -808,18 +1185,20 @@ send_signal:
 
 			finish_erasing(tty);
 			echo_char(c, tty);
-			opost('\n', tty);
+			echo_char_raw('\n', tty);
 			while (tail != tty->read_head) {
 				echo_char(tty->read_buf[tail], tty);
 				tail = (tail+1) & (N_TTY_BUF_SIZE-1);
 			}
+			process_echo_chars(tty);
 			return;
 		}
 		if (c == '\n') {
 			if (L_ECHO(tty) || L_ECHONL(tty)) {
 				if (tty->read_cnt >= N_TTY_BUF_SIZE-1)
-					tty_put_char(tty, '\a');
-				opost('\n', tty);
+					echo_char_raw('\a', tty);
+				echo_char_raw('\n', tty);
+				process_echo_chars(tty);
 			}
 			goto handle_newline;
 		}
@@ -836,11 +1215,12 @@ send_signal:
 			 */
 			if (L_ECHO(tty)) {
 				if (tty->read_cnt >= N_TTY_BUF_SIZE-1)
-					tty_put_char(tty, '\a');
+					echo_char_raw('\a', tty);
 				/* Record the column of first canon char. */
 				if (tty->canon_head == tty->read_head)
-					tty->canon_column = tty->column;
+					echo_set_canon_col(tty);
 				echo_char(c, tty);
+				process_echo_chars(tty);
 			}
 			/*
 			 * XXX does PARMRK doubling happen for
@@ -863,20 +1243,21 @@ handle_newline:
 		}
 	}
 
-	finish_erasing(tty);
 	if (L_ECHO(tty)) {
+		finish_erasing(tty);
 		if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
-			tty_put_char(tty, '\a'); /* beep if no space */
+			echo_char_raw('\a', tty); /* beep if no space */
 			return;
 		}
 		if (c == '\n')
-			opost('\n', tty);
+			echo_char_raw('\n', tty);
 		else {
 			/* Record the column of first canon char. */
 			if (tty->canon_head == tty->read_head)
-				tty->canon_column = tty->column;
+				echo_set_canon_col(tty);
 			echo_char(c, tty);
 		}
+		process_echo_chars(tty);
 	}
 
 	if (I_PARMRK(tty) && c == (unsigned char) '\377')
@@ -897,6 +1278,9 @@ handle_newline:
 
 static void n_tty_write_wakeup(struct tty_struct *tty)
 {
+	/* Write out any echoed characters that are still pending */
+	process_echo_chars(tty);
+	
 	if (tty->fasync) {
 		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
 		kill_fasync(&tty->fasync, SIGIO, POLL_OUT);
@@ -1094,6 +1478,10 @@ static void n_tty_close(struct tty_struc
 		free_buf(tty->read_buf);
 		tty->read_buf = NULL;
 	}
+	if (tty->echo_buf) {
+		free_buf(tty->echo_buf);
+		tty->echo_buf = NULL;
+	}
 }
 
 /**
@@ -1111,13 +1499,19 @@ static int n_tty_open(struct tty_struct 
 	if (!tty)
 		return -EINVAL;
 
-	/* This one is ugly. Currently a malloc failure here can panic */
+	/* These are ugly. Currently a malloc failure here can panic */
 	if (!tty->read_buf) {
 		tty->read_buf = alloc_buf();
 		if (!tty->read_buf)
 			return -ENOMEM;
 	}
+	if (!tty->echo_buf) {
+		tty->echo_buf = alloc_buf();
+		if (!tty->echo_buf)
+			return -ENOMEM;
+	}
 	memset(tty->read_buf, 0, N_TTY_BUF_SIZE);
+	memset(tty->echo_buf, 0, N_TTY_BUF_SIZE);
 	reset_buffer_flags(tty);
 	tty->column = 0;
 	n_tty_set_termios(tty, NULL);
@@ -1473,6 +1867,9 @@ static ssize_t write_chan(struct tty_str
 			return retval;
 	}
 
+	/* Write out any echoed characters that are still pending */
+	process_echo_chars(tty);
+	
 	add_wait_queue(&tty->write_wait, &wait);
 	while (1) {
 		set_current_state(TASK_INTERRUPTIBLE);
@@ -1486,7 +1883,7 @@ static ssize_t write_chan(struct tty_str
 		}
 		if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
 			while (nr > 0) {
-				ssize_t num = opost_block(tty, b, nr);
+				ssize_t num = process_output_block(tty, b, nr);
 				if (num < 0) {
 					if (num == -EAGAIN)
 						break;
@@ -1498,7 +1895,7 @@ static ssize_t write_chan(struct tty_str
 				if (nr == 0)
 					break;
 				c = *b;
-				if (opost(c, tty) < 0)
+				if (process_output(c, tty) < 0)
 					break;
 				b++; nr--;
 			}
diff -Nurp linux.old/drivers/char/tty_io.c linux.new/drivers/char/tty_io.c
--- linux.old/drivers/char/tty_io.c	2008-08-14 09:38:59.948014281 -0600
+++ linux.new/drivers/char/tty_io.c	2008-08-14 09:46:32.818009428 -0600
@@ -3832,6 +3832,7 @@ static void initialize_tty_struct(struct
 	mutex_init(&tty->atomic_read_lock);
 	mutex_init(&tty->atomic_write_lock);
 	spin_lock_init(&tty->read_lock);
+	spin_lock_init(&tty->echo_lock);
 	spin_lock_init(&tty->ctrl_lock);
 	INIT_LIST_HEAD(&tty->tty_files);
 	INIT_WORK(&tty->SAK_work, do_SAK_work);
diff -Nurp linux.old/include/linux/tty.h linux.new/include/linux/tty.h
--- linux.old/include/linux/tty.h	2008-08-14 09:41:08.518009633 -0600
+++ linux.new/include/linux/tty.h	2008-08-14 09:46:47.928011645 -0600
@@ -222,6 +222,7 @@ struct tty_struct {
 	unsigned int column;
 	unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
 	unsigned char closing:1;
+	unsigned char echo_overrun:1;
 	unsigned short minimum_to_wake;
 	unsigned long overrun_time;
 	int num_overrun;
@@ -231,6 +232,9 @@ struct tty_struct {
 	int read_tail;
 	int read_cnt;
 	unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];
+	unsigned char *echo_buf;
+	unsigned int echo_pos;
+	unsigned int echo_cnt;
 	int canon_data;
 	unsigned long canon_head;
 	unsigned int canon_column;
@@ -239,6 +243,7 @@ struct tty_struct {
 	unsigned char *write_buf;
 	int write_cnt;
 	spinlock_t read_lock;
+	spinlock_t echo_lock;
 	/* If the tty has a pending do_SAK, queue it here - akpm */
 	struct work_struct SAK_work;
 };