patch-2.1.8 linux/drivers/char/serial.c

Next file: linux/drivers/char/tty_ioctl.c
Previous file: linux/drivers/char/n_tty.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.7/linux/drivers/char/serial.c linux/drivers/char/serial.c
@@ -42,6 +42,7 @@
 #include <linux/ptrace.h>
 #include <linux/ioport.h>
 #include <linux/mm.h>
+#include <linux/malloc.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -49,7 +50,7 @@
 #include <asm/bitops.h>
 
 static char *serial_name = "Serial driver";
-static char *serial_version = "4.13";
+static char *serial_version = "4.20";
 
 DECLARE_TASK_QUEUE(tq_serial);
 
@@ -109,8 +110,27 @@
 static volatile int rs_triggered;
 static int rs_wild_int_mask;
 
-static void autoconfig(struct async_struct * info);
+static void autoconfig(struct serial_state * info);
 static void change_speed(struct async_struct *info);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+
+/*
+ * Here we define the default xmit fifo size used for each type of
+ * UART
+ */
+static struct serial_uart_config uart_config[] = {
+	{ "unknown", 1, 0 }, 
+	{ "8250", 1, 0 }, 
+	{ "16450", 1, 0 }, 
+	{ "16550", 1, 0 }, 
+	{ "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, 
+	{ "cirrus", 1, 0 }, 
+	{ "ST16650", 1, UART_CLEAR_FIFO |UART_STARTECH }, 
+	{ "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO |
+		  UART_STARTECH }, 
+	{ "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
+	{ 0, 0}
+};
 	
 /*
  * This assumes you have a 1.8432 MHz clock for your UART.
@@ -146,7 +166,7 @@
 
 #define C_P(card,port) (((card)<<6|(port)<<3) + 1)
 
-struct async_struct rs_table[] = {
+struct serial_state rs_table[] = {
 	/* UART CLK   PORT IRQ     FLAGS        */
 	{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS },	/* ttyS0 */
 	{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS },	/* ttyS1 */
@@ -204,7 +224,7 @@
 #endif
 };
 
-#define NR_PORTS	(sizeof(rs_table)/sizeof(struct async_struct))
+#define NR_PORTS	(sizeof(rs_table)/sizeof(struct serial_state))
 
 static struct tty_struct *serial_table[NR_PORTS];
 static struct termios *serial_termios[NR_PORTS];
@@ -252,7 +272,7 @@
  */
 static int baud_table[] = {
 	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-	9600, 19200, 38400, 57600, 115200, 0 };
+	9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
 
 static inline unsigned int serial_in(struct async_struct *info, int offset)
 {
@@ -400,11 +420,14 @@
 
 	do {
 		ch = serial_inp(info, UART_RX);
+		if (*status & UART_LSR_BI)
+			*status &= ~(UART_LSR_FE | UART_LSR_PE);
 		if (*status & info->ignore_status_mask) {
 			if (++ignored > 100)
 				break;
 			goto ignore_char;
 		}
+		*status &= info->read_status_mask;
 		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
 			break;
 		tty->flip.count++;
@@ -425,7 +448,7 @@
 			*tty->flip.flag_buf_ptr++ = 0;
 		*tty->flip.char_buf_ptr++ = ch;
 	ignore_char:
-		*status = serial_inp(info, UART_LSR) & info->read_status_mask;
+		*status = serial_inp(info, UART_LSR);
 	} while (*status & UART_LSR_DR);
 	queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
 #ifdef SERIAL_DEBUG_INTR
@@ -477,19 +500,27 @@
 static _INLINE_ void check_modem_status(struct async_struct *info)
 {
 	int	status;
+	struct	async_icount *icount;
 	
 	status = serial_in(info, UART_MSR);
 
 	if (status & UART_MSR_ANY_DELTA) {
+		icount = &info->state->icount;
 		/* update input line counters */
 		if (status & UART_MSR_TERI)
-			info->icount.rng++;
+			icount->rng++;
 		if (status & UART_MSR_DDSR)
-			info->icount.dsr++;
-		if (status & UART_MSR_DDCD)
-			info->icount.dcd++;
+			icount->dsr++;
+		if (status & UART_MSR_DDCD) {
+			icount->dcd++;
+#ifdef CONFIG_HARD_PPS
+			if ((info->flags & ASYNC_HARDPPS_CD) &&
+			    (status & UART_MSR_DCD))
+				hardpps();
+#endif
+		}
 		if (status & UART_MSR_DCTS)
-			info->icount.cts++;
+			icount->cts++;
 		wake_up_interruptible(&info->delta_msr_wait);
 	}
 
@@ -569,7 +600,7 @@
 
 		info->last_active = jiffies;
 
-		status = serial_inp(info, UART_LSR) & info->read_status_mask;
+		status = serial_inp(info, UART_LSR);
 #ifdef SERIAL_DEBUG_INTR
 		printk("status = %x...", status);
 #endif
@@ -594,7 +625,8 @@
 	} while (end_mark != info);
 	if (multi->port_monitor)
 		printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n",
-		       info->irq, first_multi, inb(multi->port_monitor));
+		       info->state->irq, first_multi,
+		       inb(multi->port_monitor));
 #ifdef SERIAL_DEBUG_INTR
 	printk("end.\n");
 #endif
@@ -624,7 +656,7 @@
 		first_multi = inb(multi->port_monitor);
 
 	do {
-		status = serial_inp(info, UART_LSR) & info->read_status_mask;
+		status = serial_inp(info, UART_LSR);
 #ifdef SERIAL_DEBUG_INTR
 		printk("status = %x...", status);
 #endif
@@ -643,7 +675,8 @@
 	info->last_active = jiffies;
 	if (multi->port_monitor)
 		printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",
-		       info->irq, first_multi, inb(multi->port_monitor));
+		       info->state->irq, first_multi,
+		       inb(multi->port_monitor));
 #ifdef SERIAL_DEBUG_INTR
 	printk("end.\n");
 #endif
@@ -683,7 +716,7 @@
 
 		info->last_active = jiffies;
 
-		status = serial_inp(info, UART_LSR) & info->read_status_mask;
+		status = serial_inp(info, UART_LSR);
 #ifdef SERIAL_DEBUG_INTR
 		printk("status = %x...", status);
 #endif
@@ -707,7 +740,7 @@
 		}
 		if (multi->port_monitor)
 			printk("rs port monitor irq %d: 0x%x, 0x%x\n",
-			       info->irq, first_multi,
+			       info->state->irq, first_multi,
 			       inb(multi->port_monitor));
 		if ((inb(multi->port1) & multi->mask1) != multi->match1)
 			continue;
@@ -910,13 +943,13 @@
 	unsigned long flags;
 	int	retval;
 	void (*handler)(int, void *, struct pt_regs *);
+	struct serial_state *state= info->state;
 	unsigned long page;
 
 	page = get_free_page(GFP_KERNEL);
 	if (!page)
 		return -ENOMEM;
 
-	
 	save_flags(flags); cli();
 
 	if (info->flags & ASYNC_INITIALIZED) {
@@ -925,7 +958,7 @@
 		return 0;
 	}
 
-	if (!info->port || !info->type) {
+	if (!state->port || !state->type) {
 		if (info->tty)
 			set_bit(TTY_IO_ERROR, &info->tty->flags);
 		free_page(page);
@@ -938,23 +971,30 @@
 		info->xmit_buf = (unsigned char *) page;
 
 #ifdef SERIAL_DEBUG_OPEN
-	printk("starting up ttys%d (irq %d)...", info->line, info->irq);
+	printk("starting up ttys%d (irq %d)...", info->line, state->irq);
 #endif
 
+	if (uart_config[info->state->type].flags & UART_STARTECH) {
+		/* Wake up UART */
+		serial_outp(info, UART_LCR, 0xBF);
+		serial_outp(info, UART_EFR, UART_EFR_ECB);
+		serial_outp(info, UART_IER, 0);
+		serial_outp(info, UART_EFR, 0);
+		serial_outp(info, UART_LCR, 0);
+	}
+
+	if (info->state->type == PORT_16750) {
+		/* Wake up UART */
+		serial_outp(info, UART_IER, 0);
+	}
+
 	/*
 	 * Clear the FIFO buffers and disable them
 	 * (they will be reenabled in change_speed())
 	 */
-	if (info->type == PORT_16650) {
-		serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
-					     UART_FCR_CLEAR_XMIT));
-		info->xmit_fifo_size = 1; /* disabled for now */
-	} else if (info->type == PORT_16550A) {
+	if (uart_config[state->type].flags & UART_CLEAR_FIFO)
 		serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
 					     UART_FCR_CLEAR_XMIT));
-		info->xmit_fifo_size = 16;
-	} else
-		info->xmit_fifo_size = 1;
 
 	/*
 	 * At this point there's no way the LSR could still be 0xFF;
@@ -974,18 +1014,18 @@
 	/*
 	 * Allocate the IRQ if necessary
 	 */
-	if (info->irq && (!IRQ_ports[info->irq] ||
-			  !IRQ_ports[info->irq]->next_port)) {
-		if (IRQ_ports[info->irq]) {
-			free_irq(info->irq, NULL);
-			if (rs_multiport[info->irq].port1)
+	if (state->irq && (!IRQ_ports[state->irq] ||
+			  !IRQ_ports[state->irq]->next_port)) {
+		if (IRQ_ports[state->irq]) {
+			free_irq(state->irq, NULL);
+			if (rs_multiport[state->irq].port1)
 				handler = rs_interrupt_multi;
 			else
 				handler = rs_interrupt;
 		} else 
 			handler = rs_interrupt_single;
 
-		retval = request_irq(info->irq, handler, IRQ_T(info),
+		retval = request_irq(state->irq, handler, IRQ_T(info),
 				     "serial", NULL);
 		if (retval) {
 			restore_flags(flags);
@@ -1022,7 +1062,7 @@
 	info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
 	info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2;
 #endif
-	if (info->irq == 0)
+	if (state->irq == 0)
 		info->MCR = info->MCR_noint;
 	serial_outp(info, UART_MCR, info->MCR);
 	
@@ -1055,11 +1095,11 @@
 	 * Insert serial port into IRQ chain.
 	 */
 	info->prev_port = 0;
-	info->next_port = IRQ_ports[info->irq];
+	info->next_port = IRQ_ports[state->irq];
 	if (info->next_port)
 		info->next_port->prev_port = info;
-	IRQ_ports[info->irq] = info;
-	figure_IRQ_timeout(info->irq);
+	IRQ_ports[state->irq] = info;
+	figure_IRQ_timeout(state->irq);
 
 	/*
 	 * Set up serial timers...
@@ -1084,14 +1124,17 @@
 static void shutdown(struct async_struct * info)
 {
 	unsigned long	flags;
+	struct serial_state *state;
 	int		retval;
 
 	if (!(info->flags & ASYNC_INITIALIZED))
 		return;
 
+	state = info->state;
+
 #ifdef SERIAL_DEBUG_OPEN
 	printk("Shutting down serial port %d (irq %d)....", info->line,
-	       info->irq);
+	       state->irq);
 #endif
 	
 	save_flags(flags); cli(); /* Disable interrupts */
@@ -1110,24 +1153,24 @@
 	if (info->prev_port)
 		info->prev_port->next_port = info->next_port;
 	else
-		IRQ_ports[info->irq] = info->next_port;
-	figure_IRQ_timeout(info->irq);
+		IRQ_ports[state->irq] = info->next_port;
+	figure_IRQ_timeout(state->irq);
 	
 	/*
 	 * Free the IRQ, if necessary
 	 */
-	if (info->irq && (!IRQ_ports[info->irq] ||
-			  !IRQ_ports[info->irq]->next_port)) {
-		if (IRQ_ports[info->irq]) {
-			free_irq(info->irq, NULL);
-			retval = request_irq(info->irq, rs_interrupt_single,
+	if (state->irq && (!IRQ_ports[state->irq] ||
+			  !IRQ_ports[state->irq]->next_port)) {
+		if (IRQ_ports[state->irq]) {
+			free_irq(state->irq, NULL);
+			retval = request_irq(state->irq, rs_interrupt_single,
 					     IRQ_T(info), "serial", NULL);
 			
 			if (retval)
 				printk("serial shutdown: request_irq: error %d"
 				       "  Couldn't reacquire IRQ.\n", retval);
 		} else
-			free_irq(info->irq, NULL);
+			free_irq(state->irq, NULL);
 	}
 
 	if (info->xmit_buf) {
@@ -1155,7 +1198,18 @@
 	
 	if (info->tty)
 		set_bit(TTY_IO_ERROR, &info->tty->flags);
-	
+
+	if (uart_config[info->state->type].flags & UART_STARTECH) {
+		/* Arrange to enter sleep mode */
+		serial_outp(info, UART_LCR, 0xBF);
+		serial_outp(info, UART_EFR, UART_EFR_ECB);
+		serial_outp(info, UART_IER, UART_IERX_SLEEP);
+		serial_outp(info, UART_LCR, 0);
+	}
+	if (info->state->type == PORT_16750) {
+		/* Arrange to enter sleep mode */
+		serial_outp(info, UART_IER, UART_IERX_SLEEP);
+	}
 	info->flags &= ~ASYNC_INITIALIZED;
 	restore_flags(flags);
 }
@@ -1167,8 +1221,8 @@
 static void change_speed(struct async_struct *info)
 {
 	unsigned short port;
-	int	quot = 0;
-	unsigned cflag,cval,fcr;
+	int	quot = 0, baud_base;
+	unsigned cflag, cval, fcr = 0;
 	int	i;
 
 	if (!info->tty || !info->tty->termios)
@@ -1179,7 +1233,7 @@
 	i = cflag & CBAUD;
 	if (i & CBAUDEX) {
 		i &= ~CBAUDEX;
-		if (i < 1 || i > 2) 
+		if (i < 1 || i > 4) 
 			info->tty->termios->c_cflag &= ~CBAUDEX;
 		else
 			i += 15;
@@ -1189,29 +1243,28 @@
 			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;
+			quot = info->state->custom_divisor;
 	}
+	baud_base = info->state->baud_base;
 	if (quot) {
 		info->timeout = ((info->xmit_fifo_size*HZ*15*quot) /
-				 info->baud_base) + 2;
+				 baud_base) + 2;
 	} else if (baud_table[i] == 134) {
-		quot = (2*info->baud_base / 269);
+		quot = (2*baud_base / 269);
 		info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
 	} else if (baud_table[i]) {
-		quot = info->baud_base / baud_table[i];
+		quot = baud_base / baud_table[i];
 		info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
 	} else {
 		quot = 0;
 		info->timeout = 0;
 	}
-	if (quot) {
-		info->MCR |= UART_MCR_DTR;
-		info->MCR_noint |= UART_MCR_DTR;
-		cli();
-		serial_out(info, UART_MCR, info->MCR);
-		sti();
-	} else {
+	if (!quot) {
 		info->MCR &= ~UART_MCR_DTR;
 		info->MCR_noint &= ~UART_MCR_DTR;
 		cli();
@@ -1234,28 +1287,19 @@
 		cval |= UART_LCR_PARITY;
 	if (!(cflag & PARODD))
 		cval |= UART_LCR_EPAR;
-	if (info->type == PORT_16550A) {
-		if ((info->baud_base / quot) < 2400)
+	if (uart_config[info->state->type].flags & UART_USE_FIFO) {
+		if ((info->state->baud_base / quot) < 2400)
 			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
 		else
 			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
-	} else if (info->type == PORT_16650) {
-		/*
-		 * On the 16650, we disable the FIFOs altogether
-		 * because of a design bug in how the implement
-		 * things.  We could support it by completely changing
-		 * how we handle the interrupt driver, but not today....
-		 *
-		 * N.B.  Because there's no way to set a FIFO trigger
-		 * at 1 char, we'd probably disable at speed below
-		 * 2400 baud anyway...
-		 */
-		fcr = 0;
-	} else
-		fcr = 0;
+	}
+	if (info->state->type == PORT_16750)
+		fcr |= UART_FCR7_64BYTE;
 	
 	/* CTS flow control flag and modem status interrupts */
 	info->IER &= ~UART_IER_MSI;
+	if (info->flags & ASYNC_HARDPPS_CD)
+		info->IER |= UART_IER_MSI;
 	if (cflag & CRTSCTS) {
 		info->flags |= ASYNC_CTS_FLOW;
 		info->IER |= UART_IER_MSI;
@@ -1280,32 +1324,37 @@
 	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
 		info->read_status_mask |= UART_LSR_BI;
 	
+	/*
+	 * Characters to ignore
+	 */
 	info->ignore_status_mask = 0;
-#if 0
-	/* This should be safe, but for some broken bits of hardware... */
-	if (I_IGNPAR(info->tty)) {
+	if (I_IGNPAR(info->tty))
 		info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
-		info->read_status_mask |= UART_LSR_PE | UART_LSR_FE;
-	}
-#endif
 	if (I_IGNBRK(info->tty)) {
 		info->ignore_status_mask |= UART_LSR_BI;
-		info->read_status_mask |= UART_LSR_BI;
 		/*
 		 * If we're ignore parity and break indicators, ignore 
 		 * overruns too.  (For real raw support).
 		 */
-		if (I_IGNPAR(info->tty)) {
-			info->ignore_status_mask |= UART_LSR_OE | \
-				UART_LSR_PE | UART_LSR_FE;
-			info->read_status_mask |= UART_LSR_OE | \
-				UART_LSR_PE | UART_LSR_FE;
-		}
+		if (I_IGNPAR(info->tty))
+			info->ignore_status_mask |= UART_LSR_OE;
 	}
+	/*
+	 * !!! ignore all characters if CREAD is not set
+	 */
+	if ((cflag & CREAD) == 0)
+		info->ignore_status_mask |= UART_LSR_DR;
 	cli();
+	if (uart_config[info->state->type].flags & UART_STARTECH) {
+		serial_outp(info, UART_LCR, 0xBF);
+		serial_outp(info, UART_EFR,
+			    (cflag & CRTSCTS) ? UART_EFR_CTS : 0);
+	}
 	serial_outp(info, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
 	serial_outp(info, UART_DLL, quot & 0xff);	/* LS of divisor */
 	serial_outp(info, UART_DLM, quot >> 8);		/* MS of divisor */
+	if (info->state->type == PORT_16750)
+		serial_outp(info, UART_FCR, fcr); 	/* set fcr */
 	serial_outp(info, UART_LCR, cval);		/* reset DLAB */
 	serial_outp(info, UART_FCR, fcr); 	/* set fcr */
 	sti();
@@ -1438,6 +1487,25 @@
 }
 
 /*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+void rs_send_xchar(struct tty_struct *tty, char ch)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->device, "rs_send_char"))
+		return;
+
+	info->x_char = ch;
+	if (ch) {
+		/* Make sure transmit interrupts are on */
+		info->IER |= UART_IER_THRI;
+		serial_out(info, UART_IER, info->IER);
+	}
+}
+
+/*
  * ------------------------------------------------------------
  * rs_throttle()
  * 
@@ -1459,10 +1527,12 @@
 		return;
 	
 	if (I_IXOFF(tty))
-		info->x_char = STOP_CHAR(tty);
+		rs_send_xchar(tty, STOP_CHAR(tty));
 
-	info->MCR &= ~UART_MCR_RTS;
-	info->MCR_noint &= ~UART_MCR_RTS;
+	if (tty->termios->c_cflag & CRTSCTS) {
+		info->MCR &= ~UART_MCR_RTS;
+		info->MCR_noint &= ~UART_MCR_RTS;
+	}
 	cli();
 	serial_out(info, UART_MCR, info->MCR);
 	sti();
@@ -1485,10 +1555,12 @@
 		if (info->x_char)
 			info->x_char = 0;
 		else
-			info->x_char = START_CHAR(tty);
+			rs_send_xchar(tty, START_CHAR(tty));
+	}
+	if (tty->termios->c_cflag & CRTSCTS) {
+		info->MCR |= UART_MCR_RTS;
+		info->MCR_noint |= UART_MCR_RTS;
 	}
-	info->MCR |= UART_MCR_RTS;
-	info->MCR_noint |= UART_MCR_RTS;
 	cli();
 	serial_out(info, UART_MCR, info->MCR);
 	sti();
@@ -1504,20 +1576,22 @@
 			   struct serial_struct * retinfo)
 {
 	struct serial_struct tmp;
-  
+	struct serial_state *state = info->state;
+   
 	if (!retinfo)
 		return -EFAULT;
 	memset(&tmp, 0, sizeof(tmp));
-	tmp.type = info->type;
-	tmp.line = info->line;
-	tmp.port = info->port;
-	tmp.irq = info->irq;
-	tmp.flags = info->flags;
-	tmp.baud_base = info->baud_base;
-	tmp.close_delay = info->close_delay;
-	tmp.closing_wait = info->closing_wait;
-	tmp.custom_divisor = info->custom_divisor;
-	tmp.hub6 = info->hub6;
+	tmp.type = state->type;
+	tmp.line = state->line;
+	tmp.port = state->port;
+	tmp.irq = state->irq;
+	tmp.flags = state->flags;
+	tmp.xmit_fifo_size = state->xmit_fifo_size;
+	tmp.baud_base = state->baud_base;
+	tmp.close_delay = state->close_delay;
+	tmp.closing_wait = state->closing_wait;
+	tmp.custom_divisor = state->custom_divisor;
+	tmp.hub6 = state->hub6;
 	copy_to_user(retinfo,&tmp,sizeof(*retinfo));
 	return 0;
 }
@@ -1526,29 +1600,32 @@
 			   struct serial_struct * new_info)
 {
 	struct serial_struct new_serial;
-	struct async_struct old_info;
+ 	struct serial_state old_state, *state;
 	unsigned int		i,change_irq,change_port;
 	int 			retval = 0;
 
 	if (!new_info)
 		return -EFAULT;
 	copy_from_user(&new_serial,new_info,sizeof(new_serial));
-	old_info = *info;
-
-	change_irq = new_serial.irq != info->irq;
-	change_port = (new_serial.port != info->port) || (new_serial.hub6 != info->hub6);
-
+	state = info->state;
+	old_state = *state;
+  
+	change_irq = new_serial.irq != state->irq;
+	change_port = (new_serial.port != state->port) ||
+		(new_serial.hub6 != state->hub6);
+  
 	if (!suser()) {
 		if (change_irq || change_port ||
-		    (new_serial.baud_base != info->baud_base) ||
-		    (new_serial.type != info->type) ||
-		    (new_serial.close_delay != info->close_delay) ||
+		    (new_serial.baud_base != state->baud_base) ||
+		    (new_serial.type != state->type) ||
+		    (new_serial.close_delay != state->close_delay) ||
+		    (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
 		    ((new_serial.flags & ~ASYNC_USR_MASK) !=
-		     (info->flags & ~ASYNC_USR_MASK)))
+		     (state->flags & ~ASYNC_USR_MASK)))
 			return -EPERM;
-		info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+		state->flags = ((state->flags & ~ASYNC_USR_MASK) |
 			       (new_serial.flags & ASYNC_USR_MASK));
-		info->custom_divisor = new_serial.custom_divisor;
+		state->custom_divisor = new_serial.custom_divisor;
 		goto check_and_exit;
 	}
 
@@ -1563,13 +1640,13 @@
 	/* Make sure address is not already in use */
 	if (new_serial.type) {
 		for (i = 0 ; i < NR_PORTS; i++)
-			if ((info != &rs_table[i]) &&
+			if ((state != &rs_table[i]) &&
 			    (rs_table[i].port == new_serial.port) &&
 			    rs_table[i].type)
 				return -EADDRINUSE;
 	}
 
-	if ((change_port || change_irq) && (info->count > 1))
+	if ((change_port || change_irq) && (state->count > 1))
 		return -EBUSY;
 
 	/*
@@ -1577,36 +1654,43 @@
 	 * At this point, we start making changes.....
 	 */
 
-	info->baud_base = new_serial.baud_base;
-	info->flags = ((info->flags & ~ASYNC_FLAGS) |
+	state->baud_base = new_serial.baud_base;
+	state->flags = ((state->flags & ~ASYNC_FLAGS) |
 			(new_serial.flags & ASYNC_FLAGS));
-	info->custom_divisor = new_serial.custom_divisor;
-	info->type = new_serial.type;
-	info->close_delay = new_serial.close_delay * HZ/100;
-	info->closing_wait = new_serial.closing_wait * HZ/100;
+	info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) |
+		       (info->flags & ASYNC_INTERNAL_FLAGS));
+	state->custom_divisor = new_serial.custom_divisor;
+	state->type = new_serial.type;
+	state->close_delay = new_serial.close_delay * HZ/100;
+	state->closing_wait = new_serial.closing_wait * HZ/100;
+	info->xmit_fifo_size = state->xmit_fifo_size =
+		new_serial.xmit_fifo_size;
 
-	release_region(info->port,8);
+	release_region(state->port,8);
 	if (change_port || change_irq) {
 		/*
 		 * We need to shutdown the serial port at the old
 		 * port/irq combination.
 		 */
 		shutdown(info);
-		info->irq = new_serial.irq;
-		info->port = new_serial.port;
-		info->hub6 = new_serial.hub6;
+		state->irq = new_serial.irq;
+		info->port = state->port = new_serial.port;
+		info->hub6 = state->hub6 = new_serial.hub6;
 	}
-	if(info->type != PORT_UNKNOWN)
-		request_region(info->port,8,"serial(set)");
+	if (state->type != PORT_UNKNOWN)
+		request_region(state->port,8,"serial(set)");
 
 	
 check_and_exit:
-	if (!info->port || !info->type)
+	if (!state->port || !state->type)
 		return 0;
-	if (info->flags & ASYNC_INITIALIZED) {
-		if (((old_info.flags & ASYNC_SPD_MASK) !=
-		     (info->flags & ASYNC_SPD_MASK)) ||
-		    (old_info.custom_divisor != info->custom_divisor))
+	if (state->type != old_state.type)
+		state->xmit_fifo_size =
+			uart_config[state->type].dfl_xmit_fifo_size;
+	if (state->flags & ASYNC_INITIALIZED) {
+		if (((old_state.flags & ASYNC_SPD_MASK) !=
+		     (state->flags & ASYNC_SPD_MASK)) ||
+		    (old_state.custom_divisor != state->custom_divisor))
 			change_speed(info);
 	} else
 		retval = startup(info);
@@ -1713,13 +1797,13 @@
 	if (!suser())
 		return -EPERM;
 	
-	if (info->count > 1)
+	if (info->state->count > 1)
 		return -EBUSY;
 	
 	shutdown(info);
 
 	cli();
-	autoconfig(info);
+	autoconfig(info->state);
 	sti();
 
 	retval = startup(info);
@@ -1795,7 +1879,7 @@
 	struct serial_multiport_struct ret;
 	struct rs_multiport_struct *multi;
 	
-	multi = &rs_multiport[info->irq];
+	multi = &rs_multiport[info->state->irq];
 
 	ret.port_monitor = multi->port_monitor;
 	
@@ -1815,7 +1899,7 @@
 	ret.mask4 = multi->mask4;
 	ret.match4 = multi->match4;
 
-	ret.irq = info->irq;
+	ret.irq = info->state->irq;
 
 	copy_to_user(retinfo,&ret,sizeof(*retinfo));
 	return 0;
@@ -1827,6 +1911,7 @@
 {
 	struct serial_multiport_struct new_multi;
 	struct rs_multiport_struct *multi;
+	struct serial_state *state;
 	int	was_multi, now_multi;
 	int	retval;
 	void (*handler)(int, void *, struct pt_regs *);
@@ -1835,14 +1920,16 @@
 		return -EPERM;
 	if (!in_multi)
 		return -EFAULT;
+	state = info->state;
+	
 	copy_from_user(&new_multi, in_multi,
 		      sizeof(struct serial_multiport_struct));
 
-	if (new_multi.irq != info->irq || info->irq == 0 ||
-	    !IRQ_ports[info->irq])
+	if (new_multi.irq != state->irq || state->irq == 0 ||
+	    !IRQ_ports[state->irq])
 		return -EINVAL;
 
-	multi = &rs_multiport[info->irq];
+	multi = &rs_multiport[state->irq];
 	was_multi = (multi->port1 != 0);
 	
 	multi->port_monitor = new_multi.port_monitor;
@@ -1881,15 +1968,15 @@
 
 	now_multi = (multi->port1 != 0);
 	
-	if (IRQ_ports[info->irq]->next_port &&
+	if (IRQ_ports[state->irq]->next_port &&
 	    (was_multi != now_multi)) {
-		free_irq(info->irq, NULL);
+		free_irq(state->irq, NULL);
 		if (now_multi)
 			handler = rs_interrupt_multi;
 		else
 			handler = rs_interrupt;
 
-		retval = request_irq(info->irq, handler, IRQ_T(info),
+		retval = request_irq(state->irq, handler, IRQ_T(info),
 				     "serial", NULL);
 		if (retval) {
 			printk("Couldn't reallocate serial interrupt "
@@ -2036,7 +2123,8 @@
 		 */
 		 case TIOCMIWAIT:
 			cli();
-			cprev = info->icount;	/* note the counters on entry */
+			/* note the counters on entry */
+			cprev = info->state->icount;
 			sti();
 			while (1) {
 				interruptible_sleep_on(&info->delta_msr_wait);
@@ -2044,7 +2132,7 @@
 				if (current->signal & ~current->blocked)
 					return -ERESTARTSYS;
 				cli();
-				cnow = info->icount;	/* atomic copy */
+				cnow = info->state->icount; /* atomic copy */
 				sti();
 				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
 				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
@@ -2071,7 +2159,7 @@
 			if (error)
 				return error;
 			cli();
-			cnow = info->icount;
+			cnow = info->state->icount;
 			sti();
 			p_cuser = (struct serial_icounter_struct *) arg;
 			put_user(cnow.cts, &p_cuser->cts);
@@ -2097,6 +2185,14 @@
 
 	change_speed(info);
 
+	if (!(old_termios->c_cflag & CBAUD) &&
+	    (tty->termios->c_cflag & CBAUD)) {
+		info->MCR |= UART_MCR_DTR;
+		info->MCR_noint |= UART_MCR_DTR;
+		cli();
+		serial_out(info, UART_MCR, info->MCR);
+		sti();
+	}
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	    !(tty->termios->c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
@@ -2129,11 +2225,13 @@
 static void rs_close(struct tty_struct *tty, struct file * filp)
 {
 	struct async_struct * info = (struct async_struct *)tty->driver_data;
+	struct serial_state *state;
 	unsigned long flags;
-	unsigned long timeout;
 
 	if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
 		return;
+
+	state = info->state;
 	
 	save_flags(flags); cli();
 	
@@ -2145,26 +2243,26 @@
 	}
 	
 #ifdef SERIAL_DEBUG_OPEN
-	printk("rs_close ttys%d, count = %d\n", info->line, info->count);
+	printk("rs_close ttys%d, count = %d\n", info->line, state->count);
 #endif
-	if ((tty->count == 1) && (info->count != 1)) {
+	if ((tty->count == 1) && (state->count != 1)) {
 		/*
 		 * Uh, oh.  tty->count is 1, which means that the tty
-		 * structure will be freed.  Info->count should always
+		 * structure will be freed.  state->count should always
 		 * be one in these conditions.  If it's greater than
 		 * one, we've got real problems, since it means the
 		 * serial port won't be shutdown.
 		 */
 		printk("rs_close: bad serial port count; tty->count is 1, "
-		       "info->count is %d\n", info->count);
-		info->count = 1;
+		       "state->count is %d\n", state->count);
+		state->count = 1;
 	}
-	if (--info->count < 0) {
+	if (--state->count < 0) {
 		printk("rs_close: bad serial port count for ttys%d: %d\n",
-		       info->line, info->count);
-		info->count = 0;
+		       info->line, state->count);
+		state->count = 0;
 	}
-	if (info->count) {
+	if (state->count) {
 		DBG_CNT("before DEC-2");
 		MOD_DEC_USE_COUNT;
 		restore_flags(flags);
@@ -2176,9 +2274,9 @@
 	 * separate termios for callout and dialin.
 	 */
 	if (info->flags & ASYNC_NORMAL_ACTIVE)
-		info->normal_termios = *tty->termios;
+		info->state->normal_termios = *tty->termios;
 	if (info->flags & ASYNC_CALLOUT_ACTIVE)
-		info->callout_termios = *tty->termios;
+		info->state->callout_termios = *tty->termios;
 	/*
 	 * Now we wait for the transmit buffer to clear; and we notify 
 	 * the line discipline to only process XON/XOFF characters.
@@ -2201,14 +2299,7 @@
 		 * has completely drained; this is especially
 		 * important if there is a transmit FIFO!
 		 */
-		timeout = jiffies+HZ;
-		while (!(serial_inp(info, UART_LSR) & UART_LSR_TEMT)) {
-			current->state = TASK_INTERRUPTIBLE;
-			current->timeout = jiffies + info->timeout;
-			schedule();
-			if (jiffies > timeout)
-				break;
-		}
+		rs_wait_until_sent(tty, HZ);
 	}
 	shutdown(info);
 	if (tty->driver.flush_buffer)
@@ -2234,19 +2325,49 @@
 }
 
 /*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	struct async_struct * info = (struct async_struct *)tty->driver_data;
+	unsigned long orig_jiffies;
+	
+	if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent"))
+		return;
+
+	orig_jiffies = jiffies;
+	current->state = TASK_INTERRUPTIBLE;
+	current->counter = 0;	/* make us low-priority */
+	while (!(serial_inp(info, UART_LSR) & UART_LSR_TEMT)) {
+		current->timeout = jiffies + info->timeout;
+		schedule();
+		if (current->signal & ~current->blocked)
+			break;
+		if (timeout && ((orig_jiffies + timeout) > jiffies))
+			break;
+		if (jiffies > timeout)
+			break;
+	}
+	current->state = TASK_RUNNING;
+}
+
+/*
  * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
  */
 void rs_hangup(struct tty_struct *tty)
 {
 	struct async_struct * info = (struct async_struct *)tty->driver_data;
+	struct serial_state *state = info->state;
 	
 	if (serial_paranoia_check(info, tty->device, "rs_hangup"))
 		return;
+
+	state = info->state;
 	
 	rs_flush_buffer(tty);
 	shutdown(info);
 	info->event = 0;
-	info->count = 0;
+	state->count = 0;
 	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
 	info->tty = 0;
 	wake_up_interruptible(&info->open_wait);
@@ -2261,6 +2382,7 @@
 			   struct async_struct *info)
 {
 	struct wait_queue wait = { current, NULL };
+	struct serial_state *state = info->state;
 	int		retval;
 	int		do_clocal = 0;
 
@@ -2314,7 +2436,7 @@
 	}
 
 	if (info->flags & ASYNC_CALLOUT_ACTIVE) {
-		if (info->normal_termios.c_cflag & CLOCAL)
+		if (state->normal_termios.c_cflag & CLOCAL)
 			do_clocal = 1;
 	} else {
 		if (tty->termios->c_cflag & CLOCAL)
@@ -2324,7 +2446,7 @@
 	/*
 	 * Block waiting for the carrier detect and the line to become
 	 * free (i.e., not in use by the callout).  While we are in
-	 * this loop, info->count is dropped by one, so that
+	 * this loop, state->count is dropped by one, so that
 	 * rs_close() knows when to free things.  We restore it upon
 	 * exit, either normal or abnormal.
 	 */
@@ -2332,11 +2454,11 @@
 	add_wait_queue(&info->open_wait, &wait);
 #ifdef SERIAL_DEBUG_OPEN
 	printk("block_til_ready before block: ttys%d, count = %d\n",
-	       info->line, info->count);
+	       state->line, state->count);
 #endif
 	cli();
 	if (!tty_hung_up_p(filp)) 
-		info->count--;
+		state->count--;
 	sti();
 	info->blocked_open++;
 	while (1) {
@@ -2370,24 +2492,60 @@
 		}
 #ifdef SERIAL_DEBUG_OPEN
 		printk("block_til_ready blocking: ttys%d, count = %d\n",
-		       info->line, info->count);
+		       info->line, state->count);
 #endif
 		schedule();
 	}
 	current->state = TASK_RUNNING;
 	remove_wait_queue(&info->open_wait, &wait);
 	if (!tty_hung_up_p(filp))
-		info->count++;
+		state->count++;
 	info->blocked_open--;
 #ifdef SERIAL_DEBUG_OPEN
 	printk("block_til_ready after blocking: ttys%d, count = %d\n",
-	       info->line, info->count);
+	       info->line, state->count);
 #endif
 	if (retval)
 		return retval;
 	info->flags |= ASYNC_NORMAL_ACTIVE;
 	return 0;
-}	
+}
+
+int get_async_struct(int line, struct async_struct **ret_info)
+{
+	struct async_struct *info;
+	struct serial_state *sstate;
+
+	sstate = rs_table + line;
+	sstate->count++;
+	if (sstate->info) {
+		*ret_info = sstate->info;
+		return 0;
+	}
+	info = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
+	if (!info) {
+		sstate->count--;
+		return -ENOMEM;
+	}
+	memset(info, 0, sizeof(struct async_struct));
+	info->magic = SERIAL_MAGIC;
+	info->port = sstate->port;
+	info->flags = sstate->flags;
+	info->xmit_fifo_size = sstate->xmit_fifo_size;
+	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));
+		*ret_info = sstate->info;
+		return 0;
+	}
+	*ret_info = sstate->info = info;
+	return 0;
+}
 
 /*
  * This routine is called whenever a serial port is opened.  It
@@ -2404,15 +2562,16 @@
 	line = MINOR(tty->device) - tty->driver.minor_start;
 	if ((line < 0) || (line >= NR_PORTS))
 		return -ENODEV;
-	info = rs_table + line;
+	retval = get_async_struct(line, &info);
+	if (retval)
+		return retval;
 	if (serial_paranoia_check(info, tty->device, "rs_open"))
 		return -ENODEV;
 
 #ifdef SERIAL_DEBUG_OPEN
 	printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
-	       info->count);
+	       info->state->count);
 #endif
-	info->count++;
 	tty->driver_data = info;
 	info->tty = tty;
 
@@ -2443,11 +2602,12 @@
 		return retval;
 	}
 
-	if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+	if ((info->state->count == 1) &&
+	    (info->flags & ASYNC_SPLIT_TERMIOS)) {
 		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
-			*tty->termios = info->normal_termios;
+			*tty->termios = info->state->normal_termios;
 		else 
-			*tty->termios = info->callout_termios;
+			*tty->termios = info->state->callout_termios;
 		change_speed(info);
 	}
 
@@ -2587,16 +2747,18 @@
  * whether or not this UART is a 16550A or not, since this will
  * determine whether or not we can use its FIFO features or not.
  */
-static void autoconfig(struct async_struct * info)
+static void autoconfig(struct serial_state * state)
 {
 	unsigned char status1, status2, scratch, scratch2;
-	unsigned port = info->port;
+	struct async_struct *info, scr_info;
 	unsigned long flags;
 
-	info->type = PORT_UNKNOWN;
+	state->type = PORT_UNKNOWN;
 	
-	if (!port)
+	if (!state->port)
 		return;
+	info = &scr_info;	/* This is just for serial_{in,out} */
+	info->port = state->port; 
 
 	save_flags(flags); cli();
 	
@@ -2628,7 +2790,7 @@
 	 * manufacturer would be stupid enough to design a board
 	 * that conflicts with COM 1-4 --- we hope!
 	 */
-	if (!(info->flags & ASYNC_SKIP_TEST)) {
+	if (!(state->flags & ASYNC_SKIP_TEST)) {
 		scratch = serial_inp(info, UART_MCR);
 		serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch);
 		scratch2 = serial_inp(info, UART_MSR);
@@ -2646,39 +2808,52 @@
 	 * If the AUTO_IRQ flag is set, try to do the automatic IRQ
 	 * detection.
 	 */
-	if (info->flags & ASYNC_AUTO_IRQ)
-		info->irq = do_auto_irq(info);
+	if (state->flags & ASYNC_AUTO_IRQ)
+		state->irq = do_auto_irq(info);
 		
 	scratch2 = serial_in(info, UART_LCR);
-	serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+	serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */
 	serial_outp(info, UART_EFR, 0);	/* EFR is the same as FCR */
-	serial_outp(info, UART_LCR, scratch2);
+	serial_outp(info, UART_LCR, 0);
 	serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
 	scratch = serial_in(info, UART_IIR) >> 6;
-	info->xmit_fifo_size = 1;
 	switch (scratch) {
 		case 0:
-			info->type = PORT_16450;
+			state->type = PORT_16450;
 			break;
 		case 1:
-			info->type = PORT_UNKNOWN;
+			state->type = PORT_UNKNOWN;
 			break;
 		case 2:
-			info->type = PORT_16550;
+			state->type = PORT_16550;
 			break;
 		case 3:
-			serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
-			if (serial_in(info, UART_EFR) == 0) {
-				info->type = PORT_16650;
-				info->xmit_fifo_size = 32;
-			} else {
-				info->type = PORT_16550A;
-				info->xmit_fifo_size = 16;
-			}
-			serial_outp(info, UART_LCR, scratch2);
+			state->type = PORT_16550A;
 			break;
 	}
-	if (info->type == PORT_16450) {
+	if (state->type == PORT_16550A) {
+		/* Check for Startech UART's */
+		serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+		if (serial_in(info, UART_EFR) == 0) {
+			state->type = PORT_16650;
+		} else {
+			serial_outp(info, UART_LCR, 0xBF);
+			if (serial_in(info, UART_EFR) == 0)
+				state->type = PORT_16650V2;
+		}
+	}
+	if (state->type == PORT_16550A) {
+		/* Check for TI 16750 */
+		serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+		serial_outp(info, UART_FCR,
+			    UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+		scratch = serial_in(info, UART_IIR) >> 5;
+		if (scratch == 7)
+			state->type = PORT_16750;
+		serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+	}
+	serial_outp(info, UART_LCR, scratch2);
+	if (state->type == PORT_16450) {
 		scratch = serial_in(info, UART_SCR);
 		serial_outp(info, UART_SCR, 0xa5);
 		status1 = serial_in(info, UART_SCR);
@@ -2687,8 +2862,10 @@
 		serial_outp(info, UART_SCR, scratch);
 
 		if ((status1 != 0xa5) || (status2 != 0x5a))
-			info->type = PORT_8250;
+			state->type = PORT_8250;
 	}
+	state->xmit_fifo_size =	uart_config[state->type].dfl_xmit_fifo_size;
+
 	request_region(info->port,8,"serial(auto)");
 
 	/*
@@ -2726,7 +2903,7 @@
 int rs_init(void)
 {
 	int i;
-	struct async_struct * info;
+	struct serial_state * state;
 	
 	init_bh(SERIAL_BH, do_serial_bh);
 	timer_table[RS_TIMER].fn = rs_timer;
@@ -2773,10 +2950,12 @@
 	serial_driver.ioctl = rs_ioctl;
 	serial_driver.throttle = rs_throttle;
 	serial_driver.unthrottle = rs_unthrottle;
+	serial_driver.send_xchar = rs_send_xchar;
 	serial_driver.set_termios = rs_set_termios;
 	serial_driver.stop = rs_stop;
 	serial_driver.start = rs_start;
 	serial_driver.hangup = rs_hangup;
+	serial_driver.wait_until_sent = rs_wait_until_sent;
 
 	/*
 	 * The callout device is just like normal device except for
@@ -2792,63 +2971,31 @@
 	if (tty_register_driver(&callout_driver))
 		panic("Couldn't register callout driver\n");
 	
-	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
-		info->magic = SERIAL_MAGIC;
-		info->line = i;
-		info->tty = 0;
-		info->type = PORT_UNKNOWN;
-		info->custom_divisor = 0;
-		info->close_delay = 5*HZ/10;
-		info->closing_wait = 30*HZ;
-		info->x_char = 0;
-		info->event = 0;
-		info->count = 0;
-		info->blocked_open = 0;
-		info->tqueue.routine = do_softint;
-		info->tqueue.data = info;
-		info->tqueue_hangup.routine = do_serial_hangup;
-		info->tqueue_hangup.data = info;
-		info->callout_termios =callout_driver.init_termios;
-		info->normal_termios = serial_driver.init_termios;
-		info->open_wait = 0;
-		info->close_wait = 0;
-		info->delta_msr_wait = 0;
-		info->icount.cts = info->icount.dsr = 
-			info->icount.rng = info->icount.dcd = 0;
-		info->next_port = 0;
-		info->prev_port = 0;
-		if (info->irq == 2)
-			info->irq = 9;
-		if (info->type == PORT_UNKNOWN) {
-			if (!(info->flags & ASYNC_BOOT_AUTOCONF))
+	for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
+		state->magic = SSTATE_MAGIC;
+		state->line = i;
+		state->type = PORT_UNKNOWN;
+		state->custom_divisor = 0;
+		state->close_delay = 5*HZ/10;
+		state->closing_wait = 30*HZ;
+		state->callout_termios = callout_driver.init_termios;
+		state->normal_termios = serial_driver.init_termios;
+		state->icount.cts = state->icount.dsr = 
+			state->icount.rng = state->icount.dcd = 0;
+		if (state->irq == 2)
+			state->irq = 9;
+		if (state->type == PORT_UNKNOWN) {
+			if (!(state->flags & ASYNC_BOOT_AUTOCONF))
 				continue;
-			autoconfig(info);
-			if (info->type == PORT_UNKNOWN)
+			autoconfig(state);
+			if (state->type == PORT_UNKNOWN)
 				continue;
 		}
-		printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d)", info->line, 
-		       (info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
-		       info->port, info->irq);
-		switch (info->type) {
-			case PORT_8250:
-				printk(" is a 8250\n");
-				break;
-			case PORT_16450:
-				printk(" is a 16450\n");
-				break;
-			case PORT_16550:
-				printk(" is a 16550\n");
-				break;
-			case PORT_16550A:
-				printk(" is a 16550A\n");
-				break;
-			case PORT_16650:
-				printk(" is a 16650\n");
-				break;
-			default:
-				printk("\n");
-				break;
-		}
+		printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d) is a %s\n",
+		       state->line,
+		       (state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
+		       state->port, state->irq,
+		       uart_config[state->type].name);
 	}
 	register_symtab(&serial_syms);
 	return 0;
@@ -2862,7 +3009,7 @@
 {
 	int i;
 	unsigned long flags;
-	struct async_struct *info;
+	struct serial_state *state;
 
 	save_flags(flags);
 	cli();
@@ -2880,51 +3027,40 @@
 		restore_flags(flags);
 		return -1;
 	}
-	info = &rs_table[i];
+	state = &rs_table[i];
 	if (rs_table[i].count) {
 		restore_flags(flags);
 		printk("Couldn't configure serial #%d (port=%d,irq=%d): "
 		       "device already open\n", i, req->port, req->irq);
 		return -1;
 	}
-	info->irq = req->irq;
-	info->port = req->port;
-	info->flags = req->flags;
-	autoconfig(info);
-	if (info->type == PORT_UNKNOWN) {
+	state->irq = req->irq;
+	state->port = req->port;
+	state->flags = req->flags;
+	autoconfig(state);
+	if (state->type == PORT_UNKNOWN) {
 		restore_flags(flags);
 		printk("register_serial(): autoconfig failed\n");
 		return -1;
 	}
-	printk(KERN_INFO "tty%02d at 0x%04x (irq = %d)", info->line, 
-	       info->port, info->irq);
-	switch (info->type) {
-	case PORT_8250:
-		printk(" is a 8250\n"); break;
-	case PORT_16450:
-		printk(" is a 16450\n"); break;
-	case PORT_16550:
-		printk(" is a 16550\n"); break;
-	case PORT_16550A:
-		printk(" is a 16550A\n"); break;
-	default:
-		printk("\n"); break;
-	}
+	printk(KERN_INFO "tty%02d at 0x%04x (irq = %d) is a %s\n",
+	       state->line, state->port, state->irq,
+	       uart_config[state->type].name);
 	restore_flags(flags);
-	return info->line;
+	return state->line;
 }
 
 void unregister_serial(int line)
 {
 	unsigned long flags;
-	struct async_struct *info = &rs_table[line];
+	struct serial_state *state = &rs_table[line];
 
 	save_flags(flags);
 	cli();
-	if (info->tty)
-		tty_hangup(info->tty);
-	info->type = PORT_UNKNOWN;
-	printk(KERN_INFO "tty%02d unloaded\n", info->line);
+	if (state->info && state->info->tty)
+		tty_hangup(state->info->tty);
+	state->type = PORT_UNKNOWN;
+	printk(KERN_INFO "tty%02d unloaded\n", state->line);
 	restore_flags(flags);
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov