To: linux-m68k@lists.linux-m68k.org
Subject: L68K: [tytso@mit.edu] TTY changes to 2.1.65
From: Jes Degn Soerensen <jds@kom.auc.dk>
Date: 24 Nov 1997 18:04:48 +0100
Sender: owner-linux-m68k@phil.uni-sb.de

Hi

I thought you might find this interesting.

Do we have a volunteer?

Jes

PS: and yes Linus has said that these these changes will be in .66.
------- Start of forwarded message -------
Date: 	Mon, 24 Nov 1997 11:33:18 -0500
Message-Id: <199711241633.LAA01943@rsts-11.mit.edu>
To: torvalds@transmeta.com
cc: linux-kernel@vger.rutgers.edu, linux-serial@vger.rutgers.edu
Subject: TTY changes to 2.1.65
From: tytso@mit.edu

Hi Linus,

	The following patches fix a few bugs in the serial driver, as
well as starts a process of taking common functionality which is being
done in each tty low-level driver, and moves it up to the high-level tty
driver.  The strategy which is followed is much like the one followed by
the SCSI layer --- there are high-level functions which handle the
common case which are available to the low-level driver, but each
low-level driver is always free to do its own thing if it needs to do so
for some reason.

	This set of changes moves the SOFTCAR ioctl's, break handling,
and the tty baud rate determination to the high-level code.  Future
changes which I plan to make will include the modem control line
ioctl's, the icount functions, and the throttle/unthrottle functions.
(They are all somewhat inter-related.)

	The changes are all backwards compatible, so old drivers will
continue to function with the new tty driver changes, although they
won't take advantage of the new high-level functionality.  I've made
changes to the esp, serial, and rocketport drivers mainly as a proof of
concept to make sure the new high-level abstractions make sense.  

	Linus, if you could apply these patches into the 2.1 tree, I'd
greatly appreciate it.  Thanks!!

						- Ted


Patch generated: on Mon Nov 24 11:26:35 EST 1997 by tytso@rsts-11
against Linux version 2.1.65
 
===================================================================
RCS file: drivers/char/RCS/ChangeLog,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/ChangeLog
--- drivers/char/ChangeLog	1997/11/24 16:21:16	1.1
+++ drivers/char/ChangeLog	1997/11/24 16:20:23
@@ -1,3 +1,47 @@
+Mon Nov 24 10:37:49 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* serial.c, esp.c, rocket.c: Change drivers to take advantage of
+	 	tty_get_baud_rate().
+	
+	* tty_io.c (tty_get_baud_rate): New function which computes the
+		correct baud rate for the tty.  More factoring out of
+		common code out of the serial driver to the high-level tty
+		functions....
+
+Sat Nov 22 07:53:36 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* serial.c, esp.c, rocket.c: Add tty->driver.break() routine, and
+ 		allow high-level tty code to handle the break and soft
+ 		carrier ioctls.
+	
+	* tty_ioctl.c (n_tty_ioctl): Support TIOCGSOFTCAR and
+ 		TIOCSSOFTCAR, so that device drivers don't have to support
+ 		it.
+
+	* serial.c (autoconfig): Change 16750 test to hopefully eliminate
+		false results by people with strange 16550A's being
+		detected as 16750's.  Hopefully 16750's will still be
+		detected as 16750, and other wierd UART's won't get poorly
+		autodetected.  If this doesn't work, I'll have to disable
+		the auto identification for the 16750....
+
+	* tty_io.c (tty_hangup): Now do actually do the tty hangup
+		processing during the timer processing, and disable
+		interrupts while doing the hangup processing.  This avoids
+		several nasty race conditions which happened when the
+		hangup processing was done asynchronously.
+		(tty_ioctl): Do break handling in the tty driver if
+		driver's break function is supported.  
+		(tty_flip_buffer_push): New exported function which should
+		be used by drivers to push characters in the flip buffer
+		to the tty handler.  This may either be done using a task
+		queue function for better CPU efficiency, or directly for
+		low latency operation.
+
+	* serial.c (rs_set_termios): Fix bug rs_set_termios when
+		transitioning away from B0, submitted by Stanislav
+		Voronyi. 
+
 Thu Jun 19 20:05:58 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
 
 	* serial.c (begin_break, end_break, rs_ioctl): Applied patch
===================================================================
RCS file: drivers/char/RCS/serial.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/serial.c
--- drivers/char/serial.c	1997/11/19 06:49:34	1.1
+++ drivers/char/serial.c	1997/11/24 16:26:27
@@ -22,6 +22,9 @@
  *  1/97:  Extended dumb serial ports are a config option now.  
  *         Saves 4k.   Michael A. Griffith <grif@acm.org>
  * 
+ *  8/97: Fix bug in rs_set_termios with RTS
+ *        Stanislav V. Voronyi <stas@uanet.kharkov.ua>
+ *
  * This module exports the following rs232 io functions:
  *
  *	int rs_init(void);
@@ -319,13 +322,6 @@
 	return 0;
 }
 
-/*
- * This is used to figure out the divisor speeds and the timeouts
- */
-static int baud_table[] = {
-	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-	9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
-
 static inline unsigned int serial_in(struct async_struct *info, int offset)
 {
 #ifdef CONFIG_HUB6
@@ -541,7 +537,7 @@
 	ignore_char:
 		*status = serial_inp(info, UART_LSR);
 	} while (*status & UART_LSR_DR);
-	queue_task(&tty->flip.tqueue, &tq_timer);
+	tty_flip_buffer_push(tty);
 }
 
 static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
@@ -624,10 +620,10 @@
 		else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
 			   (info->flags & ASYNC_CALLOUT_NOHUP))) {
 #ifdef SERIAL_DEBUG_OPEN
-			printk("scheduling hangup...");
+			printk("doing serial hangup...");
 #endif
-			queue_task(&info->tqueue_hangup,
-					   &tq_scheduler);
+			if (info->tty)
+				tty_hangup(info->tty);
 		}
 	}
 	if (info->flags & ASYNC_CTS_FLOW) {
@@ -908,28 +904,6 @@
 }
 
 /*
- * This routine is called from the scheduler tqueue when the interrupt
- * routine has signalled that a hangup has occurred.  The path of
- * hangup processing is:
- *
- * 	serial interrupt routine -> (scheduler tqueue) ->
- * 	do_serial_hangup() -> tty->hangup() -> rs_hangup()
- * 
- */
-static void do_serial_hangup(void *private_)
-{
-	struct async_struct	*info = (struct async_struct *) private_;
-	struct tty_struct	*tty;
-	
-	tty = info->tty;
-	if (!tty)
-		return;
-
-	tty_hangup(tty);
-}
-
-
-/*
  * This subroutine is called when the RS_TIMER goes off.  It is used
  * by the serial driver to handle ports that do not have an interrupt
  * (irq=0).  This doesn't work very well for 16450's, but gives barely
@@ -1062,7 +1036,6 @@
 	unsigned short ICP;
 #endif
 
-
 	page = get_free_page(GFP_KERNEL);
 	if (!page)
 		return -ENOMEM;
@@ -1365,9 +1338,9 @@
 static void change_speed(struct async_struct *info)
 {
 	unsigned short port;
-	int	quot = 0, baud_base;
+	int	quot = 0, baud_base, baud;
 	unsigned cflag, cval, fcr = 0;
-	int	i, bits;
+	int	bits;
 	unsigned long	flags;
 
 	if (!info->tty || !info->tty->termios)
@@ -1401,37 +1374,20 @@
 #endif
 
 	/* Determine divisor based on baud rate */
-	i = cflag & CBAUD;
-	if (i & CBAUDEX) {
-		i &= ~CBAUDEX;
-		if (i < 1 || i > 4) 
-			info->tty->termios->c_cflag &= ~CBAUDEX;
-		else
-			i += 15;
-	}
-	if (i == 15) {
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-			i += 1;
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-			i += 2;
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-			i += 3;
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-			i += 4;
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
-			quot = info->state->custom_divisor;
-	}
+	baud = tty_get_baud_rate(info->tty);
 	baud_base = info->state->baud_base;
-	if (!quot) {
-		if (baud_table[i] == 134)
+	if (baud == 38400)
+		quot = info->state->custom_divisor;
+	else {
+		if (baud == 134)
 			/* Special case since 134 is really 134.5 */
 			quot = (2*baud_base / 269);
-		else if (baud_table[i])
-			quot = baud_base / baud_table[i];
-		/* If the quotient is ever zero, default to 9600 bps */
-		if (!quot)
-			quot = baud_base / 9600;
+		else if (baud)
+			quot = baud_base / baud;
 	}
+	/* If the quotient is ever zero, default to 9600 bps */
+	if (!quot)
+		quot = baud_base / 9600;
 	info->quot = quot;
 	info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
 	info->timeout += HZ/50;		/* Add .02 seconds of slop */
@@ -1843,8 +1799,17 @@
 	if (state->flags & ASYNC_INITIALIZED) {
 		if (((old_state.flags & ASYNC_SPD_MASK) !=
 		     (state->flags & ASYNC_SPD_MASK)) ||
-		    (old_state.custom_divisor != state->custom_divisor))
+		    (old_state.custom_divisor != state->custom_divisor)) {
+			if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+				info->tty->alt_speed = 57600;
+			if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+				info->tty->alt_speed = 115200;
+			if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+				info->tty->alt_speed = 230400;
+			if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+				info->tty->alt_speed = 460800;
 			change_speed(info);
+		}
 	} else
 		retval = startup(info);
 	return retval;
@@ -1975,51 +1940,27 @@
 	return 0;
 }
 
-
 /*
- * This routine sends a break character out the serial port.
+ * rs_break() --- routine which turns the break handling on or off
  */
-static void send_break(	struct async_struct * info, int duration)
+static void rs_break(struct tty_struct *tty, int break_state)
 {
-	if (!info->port)
-		return;
-	current->state = TASK_INTERRUPTIBLE;
-	current->timeout = jiffies + duration;
-#ifdef SERIAL_DEBUG_SEND_BREAK
-	printk("rs_send_break(%d) jiff=%lu...", duration, jiffies);
-#endif
-	cli();
-	serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
-	schedule();
-	serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
-	sti();
-#ifdef SERIAL_DEBUG_SEND_BREAK
-	printk("done jiffies=%lu\n", jiffies);
-#endif
-}
-
-/*
- * This routine sets the break condition on the serial port.
- */
-static void begin_break(struct async_struct * info)
-{
-	if (!info->port)
+	struct async_struct * info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if (serial_paranoia_check(info, tty->device, "rs_break"))
 		return;
-	cli();
-	serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
-	sti();
-}
 
-/*
- * This routine clears the break condition on the serial port.
- */
-static void end_break(struct async_struct * info)
-{
 	if (!info->port)
 		return;
-	cli();
-	serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
-	sti();
+	save_flags(flags); cli();
+	if (break_state == -1)
+		serial_out(info, UART_LCR,
+			   serial_inp(info, UART_LCR) | UART_LCR_SBC);
+	else
+		serial_out(info, UART_LCR,
+			   serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+	restore_flags(flags);
 }
 
 /*
@@ -2186,7 +2127,6 @@
 {
 	int error;
 	struct async_struct * info = (struct async_struct *)tty->driver_data;
-	int retval;
 	struct async_icount cprev, cnow;	/* kernel counter temps */
 	struct serial_icounter_struct *p_cuser;	/* user space */
 
@@ -2202,53 +2142,6 @@
 	}
 	
 	switch (cmd) {
-		case TCSBRK:	/* SVID version: non-zero arg --> no break */
-			retval = tty_check_change(tty);
-			if (retval)
-				return retval;
-			tty_wait_until_sent(tty, 0);
-			if (signal_pending(current))
-				return -EINTR;
-			if (!arg) {
-				send_break(info, HZ/4);	/* 1/4 second */
-				if (signal_pending(current))
-					return -EINTR;
-			}
-			return 0;
-		case TCSBRKP:	/* support for POSIX tcsendbreak() */
-			retval = tty_check_change(tty);
-			if (retval)
-				return retval;
-			tty_wait_until_sent(tty, 0);
-			if (signal_pending(current))
-				return -EINTR;
-			send_break(info, arg ? arg*(HZ/10) : HZ/4);
-			if (signal_pending(current))
-				return -EINTR;
-			return 0;
-		case TIOCSBRK:
-			retval = tty_check_change(tty);
-			if (retval)
-				return retval;
-			tty_wait_until_sent(tty, 0);
-			begin_break(info);
-			return 0;
-		case TIOCCBRK:
-			retval = tty_check_change(tty);
-			if (retval)
-				return retval;
-			end_break(info);
-			return 0;
-		case TIOCGSOFTCAR:
-			return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
-		case TIOCSSOFTCAR:
-			error = get_user(arg, (unsigned int *) arg);
-			if (error)
-				return error;
-			tty->termios->c_cflag =
-				((tty->termios->c_cflag & ~CLOCAL) |
-				 (arg ? CLOCAL : 0));
-			return 0;
 		case TIOCMGET:
 			return get_modem_info(info, (unsigned int *) arg);
 		case TIOCMBIS:
@@ -2379,8 +2272,8 @@
 	if (!(old_termios->c_cflag & CBAUD) &&
 	    (tty->termios->c_cflag & CBAUD)) {
 		info->MCR |= UART_MCR_DTR;
-		if (!tty->hw_stopped ||
-		    !(tty->termios->c_cflag & CRTSCTS)) {
+		if (!(tty->termios->c_cflag & CRTSCTS) || 
+		    !test_bit(TTY_THROTTLED, &tty->flags)) {
 			info->MCR |= UART_MCR_RTS;
 		}
 		cli();
@@ -2617,10 +2510,8 @@
 		if (info->flags & ASYNC_CLOSING)
 			interruptible_sleep_on(&info->close_wait);
 #ifdef SERIAL_DO_RESTART
-		if (info->flags & ASYNC_HUP_NOTIFY)
-			return -EAGAIN;
-		else
-			return -ERESTARTSYS;
+		return ((info->flags & ASYNC_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
 #else
 		return -EAGAIN;
 #endif
@@ -2758,8 +2649,6 @@
 	info->line = line;
 	info->tqueue.routine = do_softint;
 	info->tqueue.data = info;
-	info->tqueue_hangup.routine = do_serial_hangup;
-	info->tqueue_hangup.data = info;
 	info->state = sstate;
 	if (sstate->info) {
 		kfree_s(info, sizeof(struct async_struct));
@@ -2807,7 +2696,22 @@
 		else
 			tmp_buf = (unsigned char *) page;
 	}
-	
+
+	/*
+	 * If the port is the middle of closing, bail out now
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ASYNC_CLOSING)) {
+		if (info->flags & ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+		return ((info->flags & ASYNC_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+#else
+		return -EAGAIN;
+#endif
+	}
+
 	/*
 	 * Start up serial port
 	 */
@@ -3201,7 +3105,6 @@
 		scratch = serial_in(info, UART_IIR) >> 5;
 		if (scratch == 7) {
 			serial_outp(info, UART_LCR, 0);
-			serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
 			scratch = serial_in(info, UART_IIR) >> 5;
 			if (scratch == 6)
 				state->type = PORT_16750;
@@ -3316,6 +3219,7 @@
 	serial_driver.stop = rs_stop;
 	serial_driver.start = rs_start;
 	serial_driver.hangup = rs_hangup;
+	serial_driver.break_ctl = rs_break;
 	serial_driver.wait_until_sent = rs_wait_until_sent;
 	serial_driver.read_proc = rs_read_proc;
 	
===================================================================
RCS file: drivers/char/RCS/tty_io.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/tty_io.c
--- drivers/char/tty_io.c	1997/11/19 06:49:34	1.1
+++ drivers/char/tty_io.c	1997/11/24 01:28:38
@@ -366,14 +366,18 @@
 	NULL		/* hung_up_tty_fasync */
 };
 
-void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
+void do_tty_hangup(void *data)
 {
-
+	struct tty_struct *tty = (struct tty_struct *) data;
 	struct file * filp;
 	struct task_struct *p;
+	unsigned long	flags;
 
 	if (!tty)
 		return;
+	
+	save_flags(flags); cli();
+	
 	check_tty_count(tty, "do_tty_hangup");
 	for (filp = inuse_filps; filp; filp = filp->f_next) {
 		if (filp->private_data != tty)
@@ -387,7 +391,7 @@
 		if (filp->f_op != &tty_fops)
 			continue;
 		tty_fasync(filp, 0);
-		filp->f_op = fops;
+		filp->f_op = &hung_up_tty_fops;
 	}
 	
 	if (tty->ldisc.flush_buffer)
@@ -404,6 +408,8 @@
 	 * Shutdown the current line discipline, and reset it to
 	 * N_TTY.
 	 */
+	if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
+		*tty->termios = tty->driver.init_termios;
 	if (tty->ldisc.num != ldiscs[N_TTY].num) {
 		if (tty->ldisc.close)
 			(tty->ldisc.close)(tty);
@@ -435,10 +441,9 @@
 	tty->session = 0;
 	tty->pgrp = -1;
 	tty->ctrl_status = 0;
-	if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
-		*tty->termios = tty->driver.init_termios;
 	if (tty->driver.hangup)
 		(tty->driver.hangup)(tty);
+	restore_flags(flags);
 }
 
 void tty_hangup(struct tty_struct * tty)
@@ -446,7 +451,7 @@
 #ifdef TTY_DEBUG_HANGUP
 	printk("%s hangup...\n", tty_name(tty));
 #endif
-	do_tty_hangup(tty, &hung_up_tty_fops);
+	queue_task(&tty->tq_hangup, &tq_timer);
 }
 
 void tty_vhangup(struct tty_struct * tty)
@@ -454,7 +459,7 @@
 #ifdef TTY_DEBUG_HANGUP
 	printk("%s vhangup...\n", tty_name(tty));
 #endif
-	do_tty_hangup(tty, &hung_up_tty_fops);
+	do_tty_hangup((void *) tty);
 }
 
 int tty_hung_up_p(struct file * filp)
@@ -1490,15 +1495,26 @@
 {
 	int retval, ldisc;
 
-	retval = tty_check_change(tty);
-	if (retval)
-		return retval;
 	retval = get_user(ldisc, arg);
 	if (retval)
 		return retval;
 	return tty_set_ldisc(tty, ldisc);
 }
 
+static int send_break(struct tty_struct *tty, int duration)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + duration;
+
+	tty->driver.break_ctl(tty, -1);
+	if (!signal_pending(current))
+		schedule();
+	tty->driver.break_ctl(tty, 0);
+	if (signal_pending(current))
+		return -EINTR;
+	return 0;
+}
+
 /*
  * Split this up, as gcc can choke on it otherwise..
  */
@@ -1506,6 +1522,7 @@
 		     unsigned int cmd, unsigned long arg)
 {
 	struct tty_struct *tty, *real_tty;
+	int retval;
 	
 	tty = (struct tty_struct *)file->private_data;
 	if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
@@ -1516,6 +1533,46 @@
 	    tty->driver.subtype == PTY_TYPE_MASTER)
 		real_tty = tty->link;
 
+	/*
+	 * Break handling by driver
+	 */
+	if (!tty->driver.break_ctl) {
+		switch(cmd) {
+		case TIOCSBRK:
+		case TIOCCBRK:
+			return tty->driver.ioctl(tty, file, cmd, arg);
+			
+		/* These two ioctl's always return success; even if */
+		/* the driver doesn't support them. */
+		case TCSBRK:
+		case TCSBRKP:			
+			retval = tty->driver.ioctl(tty, file, cmd, arg);
+			if (retval == -ENOIOCTLCMD)
+				retval = 0;
+			return retval;
+		}
+	}
+
+	/*
+	 * Factor out some common prep work
+	 */
+	switch (cmd) {
+	case TIOCSETD:
+	case TIOCSBRK:
+	case TIOCCBRK:
+	case TCSBRK:
+	case TCSBRKP:			
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		if (cmd != TIOCCBRK) {
+			tty_wait_until_sent(tty, 0);
+			if (signal_pending(current))
+				return -EINTR;
+		}
+		break;
+	}
+
 	switch (cmd) {
 		case TIOCSTI:
 			return tiocsti(tty, (char *)arg);
@@ -1556,6 +1613,28 @@
 #endif
 		case TIOCTTYGSTRUCT:
 			return tiocttygstruct(tty, (struct tty_struct *) arg);
+
+		/*
+		 * Break handling
+		 */
+		case TIOCSBRK:	/* Turn break on, unconditionally */
+			tty->driver.break_ctl(tty, -1);
+			return 0;
+			
+		case TIOCCBRK:	/* Turn break off, unconditionally */
+			tty->driver.break_ctl(tty, 0);
+			return 0;
+		case TCSBRK:   /* SVID version: non-zero arg --> no break */
+			/*
+			 * XXX is the above comment correct, or the
+			 * code below correct?  Is this ioctl used at
+			 * all by anyone?
+			 */
+			if (!arg)
+				return send_break(tty, HZ/4);
+			return 0;
+		case TCSBRKP:	/* support for POSIX tcsendbreak() */	
+			return send_break(tty, arg ? arg*(HZ/10) : HZ/4);
 	}
 	if (tty->driver.ioctl) {
 		int retval = (tty->driver.ioctl)(tty, file, cmd, arg);
@@ -1630,13 +1709,14 @@
 	unsigned char	*cp;
 	char		*fp;
 	int		count;
+	unsigned long flags;
 
 	if (tty->flip.buf_num) {
 		cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
 		fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
 		tty->flip.buf_num = 0;
 
-		cli();
+		save_flags(flags); cli();
 		tty->flip.char_buf_ptr = tty->flip.char_buf;
 		tty->flip.flag_buf_ptr = tty->flip.flag_buf;
 	} else {
@@ -1644,22 +1724,57 @@
 		fp = tty->flip.flag_buf;
 		tty->flip.buf_num = 1;
 
-		cli();
+		save_flags(flags); cli();
 		tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
 		tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
 	}
 	count = tty->flip.count;
 	tty->flip.count = 0;
-	sti();
+	restore_flags(flags);
 	
-#if 0
-	if (count > tty->max_flip_cnt)
-		tty->max_flip_cnt = count;
-#endif
 	tty->ldisc.receive_buf(tty, cp, fp, count);
 }
 
 /*
+ * Routine which returns the baud rate of the tty
+ */
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+	9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
+
+int tty_get_baud_rate(struct tty_struct *tty)
+{
+	unsigned int cflag, i;
+
+	cflag = tty->termios->c_cflag;
+
+	i = cflag & CBAUD;
+	if (i & CBAUDEX) {
+		i &= ~CBAUDEX;
+		if (i < 1 || i > 4) 
+			tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			i += 15;
+	}
+	if (i==15 && tty->alt_speed)
+		return(tty->alt_speed);
+	
+	return baud_table[i];
+}
+
+void tty_flip_buffer_push(struct tty_struct *tty)
+{
+	if (tty->low_latency)
+		flush_to_ldisc((void *) tty);
+	else
+		queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+/*
  * This subroutine initializes a tty structure.
  */
 static void initialize_tty_struct(struct tty_struct *tty)
@@ -1673,6 +1788,8 @@
 	tty->flip.tqueue.routine = flush_to_ldisc;
 	tty->flip.tqueue.data = tty;
 	tty->flip.pty_sem = MUTEX;
+	tty->tq_hangup.routine = do_tty_hangup;
+	tty->tq_hangup.data = tty;
 }
 
 /*
===================================================================
RCS file: drivers/char/RCS/tty_ioctl.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/tty_ioctl.c
--- drivers/char/tty_ioctl.c	1997/11/19 07:36:37	1.1
+++ drivers/char/tty_ioctl.c	1997/11/19 12:36:23
@@ -509,18 +509,15 @@
 				tty->packet = 0;
 			return 0;
 		}
-		/* These two ioctl's always return success; even if */
-		/* the driver doesn't support them. */
-		case TCSBRK: case TCSBRKP:
-			retval = tty_check_change(tty);
+		case TIOCGSOFTCAR:
+			return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
+		case TIOCSSOFTCAR:
+			retval = get_user(arg, (unsigned int *) arg);
 			if (retval)
 				return retval;
-			tty_wait_until_sent(tty, 0);
-			if (signal_pending(current))
-				return -EINTR;
-			if (!tty->driver.ioctl)
-				return 0;
-			tty->driver.ioctl(tty, file, cmd, arg);
+			tty->termios->c_cflag =
+				((tty->termios->c_cflag & ~CLOCAL) |
+				 (arg ? CLOCAL : 0));
 			return 0;
 		default:
 			return -ENOIOCTLCMD;
===================================================================
RCS file: drivers/char/RCS/esp.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/esp.c
--- drivers/char/esp.c	1997/11/19 06:49:34	1.1
+++ drivers/char/esp.c	1997/11/24 16:19:04
@@ -47,6 +47,7 @@
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial.h>
+#include <linux/serialP.h>
 #include <linux/serial_reg.h>
 #include <linux/major.h>
 #include <linux/string.h>
@@ -140,11 +141,8 @@
 static void rs_wait_until_sent(struct tty_struct *, int);
 	
 /*
- * This assumes you have a 1.8432 MHz clock for your UART.
- *
- * It'd be nice if someone built a serial card with a 24.576 MHz
- * clock, since the 16550A is capable of handling a top speed of 1.5
- * megabits/second; but this requires the faster clock.
+ * The ESP card has a clock rate of 14.7456 MHz (that is, 2**ESPC_SCALE
+ * times the normal 1.8432 Mhz clock of most serial boards).
  */
 #define BASE_BAUD ((1843200 / 16) * (1 << ESPC_SCALE))
 
@@ -192,15 +190,6 @@
 	return 0;
 }
 
-/*
- * This is used to figure out the divisor speeds
- */
-static int quot_table[] = {
-/*      0,    50,    75,  110,  134,  150,  200,  300, 600, 1200, */
-	0, 18432, 12288, 8378, 6878, 6144, 4608, 3072, 1536, 768,
-/*     1800,2400,4800,9600,19200,38400,57600,115200,230400,460800 */
-	512, 384, 192,  96,   48,   24,   16,     8,     4,     2, 0 };
-
 static inline unsigned int serial_in(struct esp_struct *info, int offset)
 {
 	return inb(info->port + offset);
@@ -1096,7 +1085,7 @@
 	unsigned short port;
 	int	quot = 0;
 	unsigned cflag,cval;
-	int	i, bits;
+	int	baud, bits;
 	unsigned char flow1 = 0, flow2 = 0;
 	unsigned long flags;
 
@@ -1104,27 +1093,7 @@
 		return;
 	cflag = info->tty->termios->c_cflag;
 	port = info->port;
-	i = cflag & CBAUD;
-	if (i & CBAUDEX) {
-		i &= ~CBAUDEX;
-		if (i < 1 || i > 2) 
-			info->tty->termios->c_cflag &= ~CBAUDEX;
-		else
-			i += 15;
-	}
-	if (i == 15) {
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-			i += 1;
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-			i += 2;
-                if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-                        i += 3;
-                if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-                        i += 4;
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
-			quot = info->custom_divisor;
-	}
-
+	
 	/* byte size and parity */
 	switch (cflag & CSIZE) {
 	      case CS5: cval = 0x00; bits = 7; break;
@@ -1148,14 +1117,20 @@
 		cval |= UART_LCR_SPAR;
 #endif
 
-	if (!quot) {
-		quot = quot_table[i];
-
-		/* default to 9600 bps */
-		if (!quot)
-			quot = BASE_BAUD / 9600;
-	}
-
+	baud = tty_get_baud_rate(info->tty);
+	if (baud == 38400)
+		quot = info->custom_divisor;
+	else {
+		if (baud == 134)
+			/* Special case since 134 is really 134.5 */
+			quot = (2*BASE_BAUD / 269);
+		else if (baud)
+			quot = BASE_BAUD / baud;
+	}
+	/* If the quotient is ever zero, default to 9600 bps */
+	if (!quot)
+		quot = BASE_BAUD / 9600;
+	
 	info->timeout = ((1024 * HZ * bits * quot) / BASE_BAUD) + (HZ / 50);
 
 	/* CTS flow control flag and modem status interrupts */
@@ -1632,8 +1607,17 @@
 		if (((old_info.flags & ASYNC_SPD_MASK) !=
 		     (info->flags & ASYNC_SPD_MASK)) ||
 		    (old_info.custom_divisor != info->custom_divisor) ||
-		    change_flow)
+		    change_flow) {
+			if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+				info->tty->alt_speed = 57600;
+			if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+				info->tty->alt_speed = 115200;
+			if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+				info->tty->alt_speed = 230400;
+			if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+				info->tty->alt_speed = 460800;
 			change_speed(info);
+		}
 	} else
 		retval = startup(info);
 	return retval;
@@ -1723,30 +1707,27 @@
 }
 
 /*
- * This routine sends a break character out the serial port.
+ * rs_break() --- routine which turns the break handling on or off
  */
-static void send_break(	struct esp_struct * info, int duration)
+static void esp_break(struct tty_struct *tty, int break_state)
 {
-	cli();
-	serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
-	serial_out(info, UART_ESI_CMD2, 0x01);
+	struct esp_struct * info = (struct esp_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if (serial_paranoia_check(info, tty->device, "esp_break"))
+		return;
 
-	interruptible_sleep_on(&info->break_wait);
+	save_flags(flags); cli();
+	if (break_state == -1) {
+		serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
+		serial_out(info, UART_ESI_CMD2, 0x01);
 
-	if (signal_pending(current)) {
+		interruptible_sleep_on(&info->break_wait);
+	} else {
 		serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
 		serial_out(info, UART_ESI_CMD2, 0x00);
-		sti();
-		return;
 	}
-
-	current->state = TASK_INTERRUPTIBLE;
-	current->timeout = jiffies + duration;
-	schedule();
-
-	serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
-	serial_out(info, UART_ESI_CMD2, 0x00);
-	sti();
+	restore_flags(flags);
 }
 
 static int rs_ioctl(struct tty_struct *tty, struct file * file,
@@ -1754,7 +1735,6 @@
 {
 	int error;
 	struct esp_struct * info = (struct esp_struct *)tty->driver_data;
-	int retval;
 	struct async_icount cprev, cnow;	/* kernel counter temps */
 	struct serial_icounter_struct *p_cuser;	/* user space */
 
@@ -1770,41 +1750,6 @@
 	}
 	
 	switch (cmd) {
-		case TCSBRK:	/* SVID version: non-zero arg --> no break */
-			retval = tty_check_change(tty);
-			if (retval)
-				return retval;
-			tty_wait_until_sent(tty, 0);
-			if (signal_pending(current))
-				return -EINTR;
-			if (!arg) {
-				send_break(info, HZ/4);	/* 1/4 second */
-				if (signal_pending(current))
-					return -EINTR;
-			}
-			return 0;
-		case TCSBRKP:	/* support for POSIX tcsendbreak() */
-			retval = tty_check_change(tty);
-			if (retval)
-				return retval;
-			tty_wait_until_sent(tty, 0);
-			if (signal_pending(current))
-				return -EINTR;
-			send_break(info, arg ? arg*(HZ/10) : HZ/4);
-			if (signal_pending(current))
-				return -EINTR;
-			return 0;
-		case TIOCGSOFTCAR:
-			return put_user(C_CLOCAL(tty) ? 1 : 0,
-				    (int *) arg);
-		case TIOCSSOFTCAR:
-			error = get_user(arg, (unsigned int *)arg);
-			if (error)
-				return error;
-			tty->termios->c_cflag =
-				((tty->termios->c_cflag & ~CLOCAL) |
-				 (arg ? CLOCAL : 0));
-			return 0;
 		case TIOCMGET:
 			return get_modem_info(info, (unsigned int *) arg);
 		case TIOCMBIS:
@@ -2527,6 +2472,7 @@
 	esp_driver.stop = rs_stop;
 	esp_driver.start = rs_start;
 	esp_driver.hangup = esp_hangup;
+	esp_driver.break_ctl = esp_break;
 	esp_driver.wait_until_sent = rs_wait_until_sent;
 
 	/*
===================================================================
RCS file: drivers/char/RCS/rocket.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/rocket.c
--- drivers/char/rocket.c	1997/11/19 07:36:46	1.1
+++ drivers/char/rocket.c	1997/11/24 15:47:57
@@ -622,10 +622,12 @@
 	rp_table[line] = info;
 }
 
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
 static int baud_table[] = {
 	0, 50, 75, 110, 134, 150, 200, 300,
 	600, 1200, 1800, 2400, 4800, 9600, 19200,
 	38400, 57600, 115200, 230400, 460800, 0 };
+#endif
 
 /*
  * This routine configures a rocketport port so according to its
@@ -671,6 +673,7 @@
 	}
 	
 	/* baud rate */
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
 	i = cflag & CBAUD;
 	if (i & CBAUDEX) {
 		i &= ~CBAUDEX;
@@ -690,6 +693,11 @@
 			i += 4;
 	}
 	baud = baud_table[i] ? baud_table[i] : 9600;
+#else
+	baud = tty_get_baud_rate(info->tty);
+	if (!baud)
+		baud = 9600;
+#endif
 	info->cps = baud / bits;
 	sSetBaud(cp, (rp_baud_base/baud) - 1);
 	
@@ -1182,7 +1190,7 @@
 /*
  * Here are the routines used by rp_ioctl
  */
-
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
 static void send_break(	struct r_port * info, int duration)
 {
 	current->state = TASK_INTERRUPTIBLE;
@@ -1193,6 +1201,24 @@
 	sClrBreak(&info->channel);
 	sti();
 }
+#else
+static void rp_break(struct tty_struct *tty, int break_state)
+{
+	struct r_port * info = (struct r_port *)tty->driver_data;
+	unsigned long flags;
+	
+	if (rocket_paranoia_check(info, tty->device, "rp_break"))
+		return;
+
+	save_flags(flags); cli();
+	if (break_state == -1) {
+		sSendBreak(&info->channel);
+	} else {
+		sClrBreak(&info->channel);
+	}
+	restore_flags(flags);
+}
+#endif
 
 static int get_modem_info(struct r_port * info, unsigned int *value)
 {
@@ -1289,8 +1315,19 @@
 			(new_serial.flags & ROCKET_FLAGS));
 	info->close_delay = new_serial.close_delay;
 	info->closing_wait = new_serial.closing_wait;
-	configure_r_port(info);
+
+#if (LINUX_VERSION_CODE >= 131393) /* Linux 2.1.65 */
+	if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
+		info->tty->alt_speed = 57600;
+	if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
+		info->tty->alt_speed = 115200;
+	if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
+		info->tty->alt_speed = 230400;
+	if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
+		info->tty->alt_speed = 460800;
+#endif
 	
+	configure_r_port(info);
 	return 0;
 }
 
@@ -1319,15 +1356,17 @@
 static int rp_ioctl(struct tty_struct *tty, struct file * file,
 		    unsigned int cmd, unsigned long arg)
 {
-	int tmp;
 	struct r_port * info = (struct r_port *)tty->driver_data;
-	int retval;
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
+	int retval, tmp;
+#endif
 
 	if (cmd != RCKP_GET_PORTS &&
 	    rocket_paranoia_check(info, tty->device, "rp_ioctl"))
 		return -ENODEV;
 
 	switch (cmd) {
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
 		case TCSBRK:	/* SVID version: non-zero arg --> no break */
 			retval = tty_check_change(tty);
 			if (retval)
@@ -1365,6 +1404,7 @@
 				((tty->termios->c_cflag & ~CLOCAL) |
 				 (tmp ? CLOCAL : 0));
 			return 0;
+#endif
 		case TIOCMGET:
 			return get_modem_info(info, (unsigned int *) arg);
 		case TIOCMBIS:
@@ -2093,6 +2133,9 @@
 	rocket_driver.stop = rp_stop;
 	rocket_driver.start = rp_start;
 	rocket_driver.hangup = rp_hangup;
+#if (LINUX_VERSION_CODE >= 131393) /* Linux 2.1.65 */
+	rocket_driver.break_ctl = rp_break;
+#endif
 #if (LINUX_VERSION_CODE >= 131343)
 	rocket_driver.send_xchar = rp_send_xchar;
 	rocket_driver.wait_until_sent = rp_wait_until_sent;
===================================================================
RCS file: kernel/RCS/ksyms.c,v
retrieving revision 1.1
diff -u -r1.1 kernel/ksyms.c
--- kernel/ksyms.c	1997/11/19 06:49:34	1.1
+++ kernel/ksyms.c	1997/11/24 15:06:10
@@ -242,6 +242,8 @@
 EXPORT_SYMBOL(tty_wait_until_sent);
 EXPORT_SYMBOL(tty_check_change);
 EXPORT_SYMBOL(tty_hung_up_p);
+EXPORT_SYMBOL(tty_flip_buffer_push);
+EXPORT_SYMBOL(tty_get_baud_rate);
 EXPORT_SYMBOL(do_SAK);
 EXPORT_SYMBOL(console_print);
 
===================================================================
RCS file: include/linux/RCS/tty.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/tty.h
--- include/linux/tty.h	1997/11/19 06:49:34	1.1
+++ include/linux/tty.h	1997/11/24 01:26:09
@@ -21,6 +21,7 @@
 #include <linux/tqueue.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_ldisc.h>
+#include <linux/serialP.h>
 
 #include <asm/system.h>
 
@@ -223,14 +224,17 @@
 	int count;
 	struct winsize winsize;
 	unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;
+	unsigned char low_latency:1;
 	unsigned char ctrl_status;
 
 	struct tty_struct *link;
 	struct fasync_struct *fasync;
 	struct tty_flip_buffer flip;
 	int max_flip_cnt;
+	int alt_speed;		/* For magic substitution of 38400 bps */
 	struct wait_queue *write_wait;
 	struct wait_queue *read_wait;
+	struct tq_struct tq_hangup;
 	void *disc_data;
 	void *driver_data;
 
@@ -329,6 +333,8 @@
 extern int tty_hung_up_p(struct file * filp);
 extern void do_SAK(struct tty_struct *tty);
 extern void disassociate_ctty(int priv);
+extern void tty_flip_buffer_push(struct tty_struct *tty);
+extern int tty_get_baud_rate(struct tty_struct *tty);
 
 /* n_tty.c */
 extern struct tty_ldisc tty_ldisc_N_TTY;
===================================================================
RCS file: include/linux/RCS/tty_driver.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/tty_driver.h
--- include/linux/tty_driver.h	1997/11/19 07:38:05	1.1
+++ include/linux/tty_driver.h	1997/11/24 01:26:09
@@ -92,6 +92,18 @@
  * 	This routine notifies the tty driver that it should hangup the
  * 	tty device.
  *
+ * void (*break_ctl)(struct tty_stuct *tty, int state);
+ *
+ * 	This optional routine requests the tty driver to turn on or
+ * 	off BREAK status on the RS-232 port.  If state is -1,
+ * 	then the BREAK status should be turned on; if state is 0, then
+ * 	BREAK should be turned off.
+ *
+ * 	If this routine is implemented, the high-level tty driver will
+ * 	handle the following ioctls: TCSBRK, TCSBRKP, TIOCSBRK,
+ * 	TIOCCBRK.  Otherwise, these ioctls will be passed down to the
+ * 	driver to handle.
+ *
  * void (*wait_until_sent)(struct tty_struct *tty, int timeout);
  * 
  * 	This routine waits until the device has written out all of the
@@ -148,6 +160,7 @@
 	void (*stop)(struct tty_struct *tty);
 	void (*start)(struct tty_struct *tty);
 	void (*hangup)(struct tty_struct *tty);
+	void (*break_ctl)(struct tty_struct *tty, int state);
 	void (*flush_buffer)(struct tty_struct *tty);
 	void (*set_ldisc)(struct tty_struct *tty);
 	void (*wait_until_sent)(struct tty_struct *tty, int timeout);
===================================================================
RCS file: include/linux/RCS/serial.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/serial.h
--- include/linux/serial.h	1997/11/19 06:49:34	1.1
+++ include/linux/serial.h	1997/11/19 06:49:48
@@ -132,113 +132,6 @@
 
 
 #ifdef __KERNEL__
-/*
- * This is our internal structure for each serial port's state.
- * 
- * Many fields are paralleled by the structure used by the serial_struct
- * structure.
- *
- * For definitions of the flags field, see tty.h
- */
-
-#include <linux/termios.h>
-#include <linux/tqueue.h>
-
-/*
- * Counters of the input lines (CTS, DSR, RI, CD) interrupts
- */
-struct async_icount {
-	__u32	cts, dsr, rng, dcd, tx, rx;
-	__u32	frame, parity, overrun, brk;
-	__u32	buf_overrun;
-};
-
-struct serial_state {
-	int	magic;
-	int	baud_base;
-	int	port;
-	int	irq;
-	int	flags;
-	int	hub6;
-	int	type;
-	int	line;
-	int	xmit_fifo_size;
-	int	custom_divisor;
-	int	count;
-	unsigned short	close_delay;
-	unsigned short	closing_wait; /* time to wait before closing */
-	struct async_icount	icount;	
-	struct termios		normal_termios;
-	struct termios		callout_termios;
-	struct async_struct *info;
-};
-
-struct async_struct {
-	int			magic;
-	int			port;
-	int			hub6;
-	int			flags;
-	int			xmit_fifo_size;
-	struct serial_state	*state;
-	struct tty_struct 	*tty;
-	int			read_status_mask;
-	int			ignore_status_mask;
-	int			timeout;
-	int			quot;
-	int			x_char;	/* xon/xoff character */
-	int			close_delay;
-	unsigned short		closing_wait;
-	unsigned short		closing_wait2;
-	int			IER; 	/* Interrupt Enable Register */
-	int			MCR; 	/* Modem control register */
-	unsigned long		event;
-	unsigned long		last_active;
-	int			line;
-	int			blocked_open; /* # of blocked opens */
-	long			session; /* Session of opening process */
-	long			pgrp; /* pgrp of opening process */
-	unsigned char 		*xmit_buf;
-	int			xmit_head;
-	int			xmit_tail;
-	int			xmit_cnt;
-	struct tq_struct	tqueue;
-	struct tq_struct	tqueue_hangup;
-	struct wait_queue	*open_wait;
-	struct wait_queue	*close_wait;
-	struct wait_queue	*delta_msr_wait;
-	struct async_struct	*next_port; /* For the linked list */
-	struct async_struct	*prev_port;
-};
-
-#define SERIAL_MAGIC 0x5301
-#define SSTATE_MAGIC 0x5302
-
-/*
- * The size of the serial xmit buffer is 1 page, or 4096 bytes
- */
-#define SERIAL_XMIT_SIZE 4096
-
-/*
- * Events are used to schedule things to happen at timer-interrupt
- * time, instead of at rs interrupt time.
- */
-#define RS_EVENT_WRITE_WAKEUP	0
-
-/*
- * Multiport serial configuration structure --- internal structure
- */
-struct rs_multiport_struct {
-	int		port1;
-	unsigned char	mask1, match1;
-	int		port2;
-	unsigned char	mask2, match2;
-	int		port3;
-	unsigned char	mask3, match3;
-	int		port4;
-	unsigned char	mask4, match4;
-	int		port_monitor;
-};
-
 /* Export to allow PCMCIA to use this - Dave Hinds */
 extern int register_serial(struct serial_struct *req);
 extern void unregister_serial(int line);
--- /dev/null	Mon Dec 31 23:00:00 1979
+++ include/linux/serialP.h	Wed Nov 19 02:29:16 1997
@@ -0,0 +1,119 @@
+/*
+ * Private header file for the (dumb) serial driver
+ *
+ * Copyright (C) 1997 by Theodore Ts'o.
+ * 
+ * Redistribution of this file is permitted under the terms of the GNU 
+ * Public License (GPL)
+ */
+
+#ifndef _LINUX_SERIALP_H
+#define _LINUX_SERIALP_H
+
+/*
+ * This is our internal structure for each serial port's state.
+ * 
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+
+/*
+ * Counters of the input lines (CTS, DSR, RI, CD) interrupts
+ */
+struct async_icount {
+	__u32	cts, dsr, rng, dcd, tx, rx;
+	__u32	frame, parity, overrun, brk;
+	__u32	buf_overrun;
+};
+
+struct serial_state {
+	int	magic;
+	int	baud_base;
+	int	port;
+	int	irq;
+	int	flags;
+	int	hub6;
+	int	type;
+	int	line;
+	int	xmit_fifo_size;
+	int	custom_divisor;
+	int	count;
+	unsigned short	close_delay;
+	unsigned short	closing_wait; /* time to wait before closing */
+	struct async_icount	icount;	
+	struct termios		normal_termios;
+	struct termios		callout_termios;
+	struct async_struct *info;
+};
+
+struct async_struct {
+	int			magic;
+	int			port;
+	int			hub6;
+	int			flags;
+	int			xmit_fifo_size;
+	struct serial_state	*state;
+	struct tty_struct 	*tty;
+	int			read_status_mask;
+	int			ignore_status_mask;
+	int			timeout;
+	int			quot;
+	int			x_char;	/* xon/xoff character */
+	int			close_delay;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
+	int			IER; 	/* Interrupt Enable Register */
+	int			MCR; 	/* Modem control register */
+	unsigned long		event;
+	unsigned long		last_active;
+	int			line;
+	int			blocked_open; /* # of blocked opens */
+	long			session; /* Session of opening process */
+	long			pgrp; /* pgrp of opening process */
+	unsigned char 		*xmit_buf;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct tq_struct	tqueue;
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+	struct wait_queue	*delta_msr_wait;
+	struct async_struct	*next_port; /* For the linked list */
+	struct async_struct	*prev_port;
+};
+
+#define SERIAL_MAGIC 0x5301
+#define SSTATE_MAGIC 0x5302
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP	0
+
+/*
+ * Multiport serial configuration structure --- internal structure
+ */
+struct rs_multiport_struct {
+	int		port1;
+	unsigned char	mask1, match1;
+	int		port2;
+	unsigned char	mask2, match2;
+	int		port3;
+	unsigned char	mask3, match3;
+	int		port4;
+	unsigned char	mask4, match4;
+	int		port_monitor;
+};
+
+#endif /* _LINUX_SERIAL_H */

------- End of forwarded message -------
