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

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

diff -u --recursive --new-file v2.3.28/linux/drivers/char/serial.c linux/drivers/char/serial.c
@@ -44,6 +44,9 @@
  *	int rs_init(void);
  */
 
+static char *serial_version = "4.91";
+static char *serial_revdate = "1999-11-17";
+
 /*
  * Serial driver configuration section.  Here are the various options:
  *
@@ -86,10 +89,15 @@
 #define CONFIG_HUB6
 #endif
 
-#if (defined(CONFIG_PCI) && (LINUX_VERSION_CODE >= 131072))
+#ifdef CONFIG_PCI
 #define ENABLE_SERIAL_PCI
+#ifndef CONFIG_SERIAL_SHARE_IRQ
 #define CONFIG_SERIAL_SHARE_IRQ
 #endif
+#ifndef CONFIG_SERIAL_MANY_PORTS
+#define CONFIG_SERIAL_MANY_PORTS
+#endif
+#endif
 
 /* Set of debugging defines */
 
@@ -98,6 +106,7 @@
 #undef SERIAL_DEBUG_FLOW
 #undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
 #undef SERIAL_DEBUG_PCI
+#undef SERIAL_DEBUG_AUTOCONF
 
 /* Sanity checks */
 
@@ -119,10 +128,9 @@
 #define RS_STROBE_TIME (10*HZ)
 #define RS_ISR_PASS_LIMIT 256
 
-#define IRQ_T(state) \
-	((state->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
-
+#if (defined(__i386__) && (CPU==386 || CPU==486))
 #define SERIAL_INLINE
+#endif
   
 #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
 #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
@@ -135,12 +143,10 @@
  * End of serial driver configuration section.
  */
 
-#if (LINUX_VERSION_CODE > 66304)
 #define NEW_MODULES
 #ifdef LOCAL_HEADERS		/* We're building standalone */
 #define MODULE
 #endif
-#endif
 
 #ifdef NEW_MODULES
 #ifdef MODVERSIONS
@@ -152,6 +158,7 @@
 #endif
 #endif /* NEW_MODULES */
 #include <linux/module.h>
+
 #include <linux/types.h>
 #ifdef LOCAL_HEADERS
 #include "serial_local.h"
@@ -160,7 +167,7 @@
 #include <linux/serialP.h>
 #include <linux/serial_reg.h>
 #include <asm/serial.h>
-static char *serial_version = "4.30";
+#define LOCAL_VERSTRING ""
 #endif
 
 #include <linux/errno.h>
@@ -177,10 +184,11 @@
 #include <linux/ioport.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
-#if (LINUX_VERSION_CODE >= 131343) /* 2.1.15 -- XX get correct version */
+#if (LINUX_VERSION_CODE >= 131343)
 #include <linux/init.h>
-#else
-#define __initfunc(x)	x
+#endif
+#if (LINUX_VERSION_CODE >= 131336)
+#include <asm/uaccess.h>
 #endif
 #include <linux/delay.h>
 #ifdef CONFIG_SERIAL_CONSOLE
@@ -190,6 +198,14 @@
 #include <linux/pci.h>
 #endif
 
+/*
+ * All of the compatibilty code so we can compile serial.c against
+ * older kernels is hidden in serial_compat.h
+ */
+#if (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */
+#include "serial_compat.h"	
+#endif
+
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -203,6 +219,8 @@
 
 #ifdef SERIAL_INLINE
 #define _INLINE_ inline
+#else
+#define _INLINE_
 #endif
 	
 static char *serial_name = "Serial driver";
@@ -233,6 +251,7 @@
 static int IRQ_timeout[NR_IRQS];
 #ifdef CONFIG_SERIAL_CONSOLE
 static struct console sercons;
+static unsigned long break_pressed; /* break, really ... */
 #endif
 
 static unsigned detect_uart_irq (struct serial_state * state);
@@ -274,7 +293,11 @@
 #define NR_PCI_BOARDS	8
 static struct pci_board_inst	serial_pci_board[NR_PCI_BOARDS];
 static int serial_pci_board_idx = 0;
+#ifdef PCI_NUM_RESOURCES
 #define PCI_BASE_ADDRESS(dev, r) ((dev)->resource[r].start)
+#else
+#define PCI_BASE_ADDRESS(dev, r) ((dev)->base_address[r])
+#endif
 #endif	/* ENABLE_SERIAL_PCI  */
 
 static struct tty_struct *serial_table[NR_PORTS];
@@ -301,87 +324,6 @@
 static struct semaphore tmp_buf_sem = MUTEX;
 #endif
 
-/*
- * Provide backwards compatibility for kernels prior to 2.1.XX.
- */
-#if (LINUX_VERSION_CODE < 0x20000)
-typedef dev_t kdev_t;
-#endif
-
-#if (LINUX_VERSION_CODE < 0x02017E)
-static signed long schedule_timeout(signed long timeout)
-{
-	unsigned long expire;
-
-	expire = timeout + jiffies;
-
-	current->timeout = jiffies + timeout;
-	schedule();
-
-	timeout = expire - jiffies;
-	return timeout < 0 ? 0 : timeout;
-}
-#endif
-
-#ifndef time_after
-#define time_after(a,b)		((long)(b) - (long)(a) < 0)
-#endif
-
-#if (LINUX_VERSION_CODE < 0x020100)
-static inline int irq_cannonicalize(int irq)
-{
-	return ((irq == 2) ? 9 : irq);
-}
-#endif
-
-#if (LINUX_VERSION_CODE < 131336)
-static int copy_from_user(void *to, const void *from_user, unsigned long len)
-{
-	int	error;
-
-	error = verify_area(VERIFY_READ, from_user, len);
-	if (error)
-		return len;
-	memcpy_fromfs(to, from_user, len);
-	return 0;
-}
-
-static int copy_to_user(void *to_user, const void *from, unsigned long len)
-{
-	int	error;
-	
-	error = verify_area(VERIFY_WRITE, to_user, len);
-	if (error)
-		return len;
-	memcpy_tofs(to_user, from, len);
-	return 0;
-}
-
-static inline int signal_pending(struct task_struct *p)
-{
-	return (p->signal & (~p->blocked != 0));
-}
-
-#else
-#include <asm/uaccess.h>
-#endif
-
-#ifdef CAP_SYS_ADMIN
-#define serial_isroot()	(capable(CAP_SYS_ADMIN))
-#else
-#define serial_isroot() (suser())
-#endif
-
-#if (LINUX_VERSION_CODE < 131394) /* 2.1.66 */
-#define test_and_clear_bit(x,y)		clear_bit(x,y)
-
-static inline void remove_bh(int nr)
-{
-	bh_base[nr] = NULL;
-	bh_mask &= ~(1 << nr);
-}
-#endif
-
 
 static inline int serial_paranoia_check(struct async_struct *info,
 					kdev_t device, const char *routine)
@@ -404,78 +346,63 @@
 	return 0;
 }
 
-static inline unsigned int serial_in(struct async_struct *info, int offset)
+static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset)
 {
+	switch (info->io_type) {
 #ifdef CONFIG_HUB6
-    if (info->hub6) {
-	outb(info->hub6 - 1 + offset, info->port);
-	return inb(info->port+1);
-    } else
+	case SERIAL_IO_HUB6:
+		outb(info->hub6 - 1 + offset, info->port);
+		return inb(info->port+1);
 #endif
 #ifdef CONFIG_SERIAL_PCI_MEMMAPPED
-    if (info->iomem_base)
-	return readb(info->iomem_base + (offset<<info->iomem_reg_shift));
-    else
+	case SERIAL_IO_MEM:
+		return readb(info->iomem_base +
+			     (offset<<info->iomem_reg_shift));
+#endif
+#ifdef CONFIG_SERIAL_GSC
+	case SERIAL_IO_GSC:
+		return gsc_readb(info->iomem_base + offset);
 #endif
-	return inb(info->port + offset);
+	default:
+		return inb(info->port + offset);
+	}
 }
 
-static inline unsigned int serial_inp(struct async_struct *info, int offset)
+static _INLINE_ void serial_out(struct async_struct *info, int offset,
+				int value)
 {
+	switch (info->io_type) {
 #ifdef CONFIG_HUB6
-    if (info->hub6) {
-	outb(info->hub6 - 1 + offset, info->port);
-	return inb_p(info->port+1);
-    } else
+	case SERIAL_IO_HUB6:
+		outb(info->hub6 - 1 + offset, info->port);
+		outb(value, info->port+1);
+		break;
 #endif
 #ifdef CONFIG_SERIAL_PCI_MEMMAPPED
-    if (info->iomem_base)
-	return readb(info->iomem_base + (offset<<info->iomem_reg_shift));
-    else
+	case SERIAL_IO_MEM:
+		writeb(value, info->iomem_base +
+			      (offset<<info->iomem_reg_shift));
+		break;
 #endif
-#ifdef CONFIG_SERIAL_NOPAUSE_IO
-	return inb(info->port + offset);
-#else
-	return inb_p(info->port + offset);
+#ifdef CONFIG_SERIAL_GSC
+	case SERIAL_IO_GSC:
+		gsc_writeb(value, info->iomem_base + offset);
+		break;
 #endif
+	default:
+		outb(value, info->port+offset);
+	}
 }
 
-static inline void serial_out(struct async_struct *info, int offset, int value)
-{
-#ifdef CONFIG_HUB6
-    if (info->hub6) {
-	outb(info->hub6 - 1 + offset, info->port);
-	outb(value, info->port+1);
-    } else
-#endif
-#ifdef CONFIG_SERIAL_PCI_MEMMAPPED
-    if (info->iomem_base)
-	writeb(value, info->iomem_base + (offset<<info->iomem_reg_shift));
-    else
-#endif
-	outb(value, info->port+offset);
-}
+/*
+ * We used to support using pause I/O for certain machines.  We
+ * haven't supported this for a while, but just in case it's badly
+ * needed for certain old 386 machines, I've left these #define's
+ * in....
+ */
+#define serial_inp(info, offset)		serial_in(info, offset)
+#define serial_outp(info, offset, value)	serial_out(info, offset, value)
 
-static inline void serial_outp(struct async_struct *info, int offset,
-			       int value)
-{
-#ifdef CONFIG_HUB6
-    if (info->hub6) {
-	outb(info->hub6 - 1 + offset, info->port);
-	outb_p(value, info->port+1);
-    } else
-#endif
-#ifdef CONFIG_SERIAL_PCI_MEMMAPPED
-    if (info->iomem_base)
-	writeb(value, info->iomem_base + (offset<<info->iomem_reg_shift));
-    else
-#endif
-#ifdef CONFIG_SERIAL_NOPAUSE_IO
-	outb(value, info->port+offset);
-#else
-    	outb_p(value, info->port+offset);
-#endif
-}
 
 /*
  * For the 16C950
@@ -579,7 +506,7 @@
 }
 
 static _INLINE_ void receive_chars(struct async_struct *info,
-				 int *status)
+				 int *status, struct pt_regs * regs)
 {
 	struct tty_struct *tty = info->tty;
 	unsigned char ch;
@@ -590,7 +517,7 @@
 	do {
 		ch = serial_inp(info, UART_RX);
 		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-			break;
+			goto ignore_char;
 		*tty->flip.char_buf_ptr = ch;
 		icount->rx++;
 		
@@ -629,6 +556,15 @@
 #ifdef SERIAL_DEBUG_INTR
 				printk("handling break....");
 #endif
+#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && !defined(MODULE)
+				if (info->line == sercons.index) {
+					if (!break_pressed) {
+						break_pressed = jiffies;
+						goto ignore_char;
+					}
+					break_pressed = 0;
+				}
+#endif
 				*tty->flip.flag_buf_ptr = TTY_BREAK;
 				if (info->flags & ASYNC_SAK)
 					do_SAK(tty);
@@ -642,14 +578,25 @@
 				 * reported immediately, and doesn't
 				 * affect the current character
 				 */
-				if (tty->flip.count < TTY_FLIPBUF_SIZE) {
-					tty->flip.count++;
-					tty->flip.flag_buf_ptr++;
-					tty->flip.char_buf_ptr++;
-					*tty->flip.flag_buf_ptr = TTY_OVERRUN;
-				}
+				tty->flip.count++;
+				tty->flip.flag_buf_ptr++;
+				tty->flip.char_buf_ptr++;
+				*tty->flip.flag_buf_ptr = TTY_OVERRUN;
+				if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+					goto ignore_char;
 			}
 		}
+#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && !defined(MODULE)
+		if (break_pressed && info->line == sercons.index) {
+			if (ch != 0 &&
+			    time_before(jiffies, break_pressed + HZ*5)) {
+				handle_sysrq(ch, regs, NULL, NULL);
+				break_pressed = 0;
+				goto ignore_char;
+			}
+			break_pressed = 0;
+		}
+#endif
 		tty->flip.flag_buf_ptr++;
 		tty->flip.char_buf_ptr++;
 		tty->flip.count++;
@@ -819,7 +766,7 @@
 		printk("status = %x...", status);
 #endif
 		if (status & UART_LSR_DR)
-			receive_chars(info, &status);
+			receive_chars(info, &status, regs);
 		check_modem_status(info);
 		if (status & UART_LSR_THRE)
 			transmit_chars(info, 0);
@@ -883,7 +830,7 @@
 		printk("status = %x...", status);
 #endif
 		if (status & UART_LSR_DR)
-			receive_chars(info, &status);
+			receive_chars(info, &status, regs);
 		check_modem_status(info);
 		if (status & UART_LSR_THRE)
 			transmit_chars(info, 0);
@@ -946,7 +893,7 @@
 		printk("status = %x...", status);
 #endif
 		if (status & UART_LSR_DR)
-			receive_chars(info, &status);
+			receive_chars(info, &status, regs);
 		check_modem_status(info);
 		if (status & UART_LSR_THRE)
 			transmit_chars(info, 0);
@@ -1041,7 +988,7 @@
 	unsigned long flags;
 
 	if ((jiffies - last_strobe) >= RS_STROBE_TIME) {
-		for (i=1; i < NR_IRQS; i++) {
+		for (i=0; i < NR_IRQS; i++) {
 			info = IRQ_ports[i];
 			if (!info)
 				continue;
@@ -1228,7 +1175,7 @@
 	 */
 	if (serial_inp(info, UART_LSR) == 0xff) {
 		printk("LSR safety check engaged!\n");
-		if (serial_isroot()) {
+		if (capable(CAP_SYS_ADMIN)) {
 			if (info->tty)
 				set_bit(TTY_IO_ERROR, &info->tty->flags);
 		} else
@@ -1243,7 +1190,7 @@
 			  !IRQ_ports[state->irq]->next_port)) {
 		if (IRQ_ports[state->irq]) {
 #ifdef CONFIG_SERIAL_SHARE_IRQ
-			free_irq(state->irq, NULL);
+			free_irq(state->irq, &IRQ_ports[state->irq]);
 #ifdef CONFIG_SERIAL_MULTIPORT				
 			if (rs_multiport[state->irq].port1)
 				handler = rs_interrupt_multi;
@@ -1257,10 +1204,10 @@
 		} else 
 			handler = rs_interrupt_single;
 
-		retval = request_irq(state->irq, handler, IRQ_T(state),
-				     "serial", NULL);
+		retval = request_irq(state->irq, handler, SA_SHIRQ,
+				     "serial", &IRQ_ports[state->irq]);
 		if (retval) {
-			if (serial_isroot()) {
+			if (capable(CAP_SYS_ADMIN)) {
 				if (info->tty)
 					set_bit(TTY_IO_ERROR,
 						&info->tty->flags);
@@ -1409,15 +1356,16 @@
 	if (state->irq && (!IRQ_ports[state->irq] ||
 			  !IRQ_ports[state->irq]->next_port)) {
 		if (IRQ_ports[state->irq]) {
-			free_irq(state->irq, NULL);
+			free_irq(state->irq, &IRQ_ports[state->irq]);
 			retval = request_irq(state->irq, rs_interrupt_single,
-					     IRQ_T(state), "serial", NULL);
+					     SA_SHIRQ, "serial",
+					     &IRQ_ports[state->irq]);
 			
 			if (retval)
 				printk("serial shutdown: request_irq: error %d"
 				       "  Couldn't reacquire IRQ.\n", retval);
 		} else
-			free_irq(state->irq, NULL);
+			free_irq(state->irq, &IRQ_ports[state->irq]);
 	}
 
 	if (info->xmit_buf) {
@@ -1445,8 +1393,11 @@
 	serial_outp(info, UART_MCR, info->MCR);
 
 	/* disable FIFO's */	
-	serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+	serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO |
+				     UART_FCR_CLEAR_RCVR |
 				     UART_FCR_CLEAR_XMIT));
+	serial_outp(info, UART_FCR, 0);
+
 	(void)serial_in(info, UART_RX);    /* read data port to reset things */
 	
 	if (info->tty)
@@ -1922,6 +1873,7 @@
 	tmp.closing_wait = state->closing_wait;
 	tmp.custom_divisor = state->custom_divisor;
 	tmp.hub6 = state->hub6;
+	tmp.io_type = state->io_type;
 	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
 		return -EFAULT;
 	return 0;
@@ -1944,7 +1896,7 @@
 	change_port = (new_serial.port != state->port) ||
 		(new_serial.hub6 != state->hub6);
   
-	if (!serial_isroot()) {
+	if (!capable(CAP_SYS_ADMIN)) {
 		if (change_irq || change_port ||
 		    (new_serial.baud_base != state->baud_base) ||
 		    (new_serial.type != state->type) ||
@@ -1963,7 +1915,7 @@
 
 	new_serial.irq = irq_cannonicalize(new_serial.irq);
 
-	if ((new_serial.irq >= NR_IRQS) || (new_serial.port > 0xffff) ||
+	if ((new_serial.irq >= NR_IRQS) || 
 	    (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) ||
 	    (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) ||
 	    (new_serial.type == PORT_STARTECH)) {
@@ -1973,7 +1925,7 @@
 	if ((new_serial.type != state->type) ||
 	    (new_serial.xmit_fifo_size <= 0))
 		new_serial.xmit_fifo_size =
-			uart_config[state->type].dfl_xmit_fifo_size;
+			uart_config[new_serial.type].dfl_xmit_fifo_size;
 
 	/* Make sure address is not already in use */
 	if (new_serial.type) {
@@ -1998,17 +1950,17 @@
 	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;
-#if (LINUX_VERSION_CODE > 0x200100)
+#if (LINUX_VERSION_CODE > 0x20100)
 	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 #endif
 	info->xmit_fifo_size = state->xmit_fifo_size =
 		new_serial.xmit_fifo_size;
 
-	if (state->type != PORT_UNKNOWN && state->port)
+	if ((state->type != PORT_UNKNOWN) && state->port)
 		release_region(state->port,8);
+	state->type = new_serial.type;
 	if (change_port || change_irq) {
 		/*
 		 * We need to shutdown the serial port at the old
@@ -2018,6 +1970,10 @@
 		state->irq = new_serial.irq;
 		info->port = state->port = new_serial.port;
 		info->hub6 = state->hub6 = new_serial.hub6;
+		if (info->hub6)
+			info->io_type = state->io_type = SERIAL_IO_HUB6;
+		else if (info->io_type == SERIAL_IO_HUB6)
+			info->io_type = state->io_type = SERIAL_IO_PORT;
 	}
 	if ((state->type != PORT_UNKNOWN) && state->port)
 		request_region(state->port,8,"serial(set)");
@@ -2068,6 +2024,18 @@
 	status = serial_in(info, UART_LSR);
 	restore_flags(flags);
 	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
+
+	/*
+	 * If we're about to load something into the transmit
+	 * register, we'll pretend the transmitter isn't empty to
+	 * avoid a race condition (depending on when the transmit
+	 * interrupt happens).
+	 */
+	if (info->x_char || 
+	    ((info->xmit_cnt > 0) && !info->tty->stopped &&
+	     !info->tty->hw_stopped))
+		result &= TIOCSER_TEMT;
+
 	if (copy_to_user(value, &result, sizeof(int)))
 		return -EFAULT;
 	return 0;
@@ -2162,7 +2130,7 @@
 {
 	int			retval;
 	
-	if (!serial_isroot())
+	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 	
 	if (info->state->count > 1)
@@ -2265,7 +2233,7 @@
 	int	retval;
 	void (*handler)(int, void *, struct pt_regs *);
 
-	if (!serial_isroot())
+	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 	state = info->state;
 	
@@ -2318,14 +2286,14 @@
 	
 	if (IRQ_ports[state->irq]->next_port &&
 	    (was_multi != now_multi)) {
-		free_irq(state->irq, NULL);
+		free_irq(state->irq, &IRQ_ports[state->irq]);
 		if (now_multi)
 			handler = rs_interrupt_multi;
 		else
 			handler = rs_interrupt;
 
-		retval = request_irq(state->irq, handler, IRQ_T(state),
-				     "serial", NULL);
+		retval = request_irq(state->irq, handler, SA_SHIRQ,
+				     "serial", &IRQ_ports[state->irq]);
 		if (retval) {
 			printk("Couldn't reallocate serial interrupt "
 			       "driver!!\n");
@@ -2652,7 +2620,7 @@
 	info->tty = 0;
 	if (info->blocked_open) {
 		if (info->close_delay) {
-			current->state = TASK_INTERRUPTIBLE;
+			set_current_state(TASK_INTERRUPTIBLE);
 			schedule_timeout(info->close_delay);
 		}
 		wake_up_interruptible(&info->open_wait);
@@ -2716,14 +2684,14 @@
 #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
 		printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
 #endif
-		current->state = TASK_INTERRUPTIBLE;
+		set_current_state(TASK_INTERRUPTIBLE);
 		schedule_timeout(char_time);
 		if (signal_pending(current))
 			break;
 		if (timeout && time_after(jiffies, orig_jiffies + timeout))
 			break;
 	}
-	current->state = TASK_RUNNING;
+	set_current_state(TASK_RUNNING);
 #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
 	printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
 #endif
@@ -2759,11 +2727,7 @@
 static int block_til_ready(struct tty_struct *tty, struct file * filp,
 			   struct async_struct *info)
 {
-#ifdef DECLARE_WAITQUEUE
 	DECLARE_WAITQUEUE(wait, current);
-#else
-	struct wait_queue wait = { current, NULL };
-#endif
 	struct serial_state *state = info->state;
 	int		retval;
 	int		do_clocal = 0, extra_count = 0;
@@ -2880,7 +2844,7 @@
 #endif
 		schedule();
 	}
-	current->state = TASK_RUNNING;
+	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&info->open_wait, &wait);
 	if (extra_count)
 		state->count++;
@@ -2912,14 +2876,13 @@
 		return -ENOMEM;
 	}
 	memset(info, 0, sizeof(struct async_struct));
-#ifdef DECLARE_WAITQUEUE
 	init_waitqueue_head(&info->open_wait);
 	init_waitqueue_head(&info->close_wait);
 	init_waitqueue_head(&info->delta_msr_wait);
-#endif
 	info->magic = SERIAL_MAGIC;
 	info->port = sstate->port;
 	info->flags = sstate->flags;
+	info->io_type = sstate->io_type;
 #ifdef CONFIG_SERIAL_PCI_MEMMAPPED
 	info->iomem_base = sstate->iomem_base;
 	info->iomem_reg_shift = sstate->iomem_reg_shift;
@@ -3126,7 +3089,8 @@
 	int i, len = 0, l;
 	off_t	begin = 0;
 
-	len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
+	len += sprintf(page, "serinfo:1.0 driver:%s%s revision:%s\n",
+		       serial_version, LOCAL_VERSTRING, serial_revdate);
 	for (i = 0; i < NR_PORTS && len < 4000; i++) {
 		l = line_info(page + len, &rs_table[i]);
 		len += l;
@@ -3160,7 +3124,8 @@
  */
 static _INLINE_ void show_serial_version(void)
 {
- 	printk(KERN_INFO "%s version %s with", serial_name, serial_version);
+ 	printk(KERN_INFO "%s version %s%s (%s) with", serial_name,
+	       serial_version, LOCAL_VERSTRING, serial_revdate);
 #ifdef CONFIG_HUB6
 	printk(" HUB-6");
 #define SERIAL_OPT
@@ -3298,11 +3263,11 @@
 }
 
 /*
- * This is a helper routine to autodetect StarTech/Exar UART's.  When
- * this function is called we know it is at least a StarTech 16650 V2,
- * but it might be one of several StarTech UARTs, or one of its
- * clones.  (We treat the broken original StarTech 16650 V1 as a
- * 16550A, and why not?  Startech doesn't seem to even acknowledge its
+ * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's.
+ * When this function is called we know it is at least a StarTech
+ * 16650 V2, but it might be one of several StarTech UARTs, or one of
+ * its clones.  (We treat the broken original StarTech 16650 V1 as a
+ * 16550, and why not?  Startech doesn't seem to even acknowledge its
  * existence.)
  * 
  * What evil have men's minds wrought...
@@ -3311,39 +3276,48 @@
 				      struct serial_state *state,
 				      unsigned long flags)
 {
-	unsigned char scratch, status1, status2, old_fctr, old_emsr;
+	unsigned char scratch, scratch2, scratch3;
 
 	/*
-	 * Here we check for the XR16C85x family.  We do this by
-	 * checking for to see if we can replace the scratch register
-	 * with the receive FIFO count register.
+	 * First we check to see if it's an Oxford Semiconductor UART.
 	 *
-	 * XXX  I don't have one of these chips, but it should also be
-	 * possible to check for them by setting DLL and DLM to 0, and
-	 * then reading back DLL and DLM.  If the DLM reads back as
-	 * 0x10, then the UART is a XR16C850 and the DLL contains the
-	 * chip revision.
-	 */
-	old_fctr = serial_inp(info, UART_FCTR);
-	serial_outp(info, UART_FCTR, old_fctr | UART_FCTR_SCR_SWAP);
-	old_emsr = serial_inp(info, UART_EMSR);
-	serial_outp(info, UART_EMSR, 0x00);
-	serial_outp(info, UART_LCR, 0x00);
-	scratch = serial_in(info, UART_SCR);
-	serial_outp(info, UART_SCR, 0xa5);
-	status1 = serial_in(info, UART_SCR);
-	serial_outp(info, UART_SCR, 0x5a);
-	status2 = serial_in(info, UART_SCR);
-	serial_outp(info, UART_SCR, scratch);
-	if ((status1 != 0xa5) || (status2 != 0x5a)) {
-		serial_outp(info, UART_LCR, 0xBF);
-		serial_outp(info, UART_FCTR, old_fctr | UART_FCTR_SCR_SWAP);
-		serial_outp(info, UART_EMSR, old_emsr);
-		serial_outp(info, UART_FCTR, old_fctr);
+	 * If we have to do this here because some non-National
+	 * Semiconductor clone chips lock up if you try writing to the
+	 * LSR register (which serial_icr_read does)
+	 */
+	if (state->type == PORT_16550A) {
+		/* Check for Oxford Semiconductor 16C950 */
+		scratch = serial_icr_read(info, UART_ID1);
+		scratch2 = serial_icr_read(info, UART_ID2);
+		scratch3 = serial_icr_read(info, UART_ID3);
+		
+		if (scratch == 0x16 && scratch2 == 0xC9 &&
+		    (scratch3 == 0x50 || scratch3 == 0x52 ||
+		     scratch3 == 0x54)) {
+			state->type = PORT_16C950;
+			state->revision = serial_icr_read(info, UART_REV);
+			return;
+		}
+	}
+	
+	/*
+	 * We check for a XR16C850 by setting DLL and DLM to 0, and
+	 * then reading back DLL and DLM.  If DLM reads back 0x10,
+	 * then the UART is a XR16C850 and the DLL contains the chip
+	 * revision.  If DLM reads back 0x14, then the UART is a
+	 * XR16C854.
+	 * 
+	 */
+	serial_outp(info, UART_LCR, UART_LCR_DLAB);
+	serial_outp(info, UART_DLL, 0);
+	serial_outp(info, UART_DLM, 0);
+	state->revision = serial_inp(info, UART_DLL);
+	scratch = serial_inp(info, UART_DLM);
+	serial_outp(info, UART_LCR, 0);
+	if (scratch == 0x10 || scratch == 0x14) {
 		state->type = PORT_16850;
 		return;
 	}
-	serial_outp(info, UART_IER, old_fctr);
 
 	/*
 	 * We distinguish between the '654 and the '650 by counting
@@ -3368,10 +3342,16 @@
 static void autoconfig(struct serial_state * state)
 {
 	unsigned char status1, status2, scratch, scratch2, scratch3;
+	unsigned char save_lcr, save_mcr;
 	struct async_struct *info, scr_info;
 	unsigned long flags;
 
 	state->type = PORT_UNKNOWN;
+
+#ifdef SERIAL_DEBUG_AUTOCONF
+	printk("Testing ttyS%d (0x%04x, 0x%04x)...\n", state->line,
+	       state->port, (unsigned) state->iomem_base);
+#endif
 	
 	if (!CONFIGURED_SERIAL_PORT(state))
 		return;
@@ -3385,6 +3365,7 @@
 #ifdef CONFIG_HUB6
 	info->hub6 = state->hub6;
 #endif
+	info->io_type = state->io_type;
 #ifdef CONFIG_SERIAL_PCI_MEMMAPPED
 	info->iomem_base = state->iomem_base;
 	info->iomem_reg_shift = state->iomem_reg_shift;
@@ -3405,15 +3386,29 @@
 		 */
 		scratch = serial_inp(info, UART_IER);
 		serial_outp(info, UART_IER, 0);
+#ifdef __i386__
 		outb(0xff, 0x080);
+#endif
 		scratch2 = serial_inp(info, UART_IER);
+		serial_outp(info, UART_IER, 0x0F);
+#ifdef __i386__
+		outb(0, 0x080);
+#endif
+		scratch3 = serial_inp(info, UART_IER);
 		serial_outp(info, UART_IER, scratch);
-		if (scratch2) {
+		if (scratch2 || scratch3 != 0x0F) {
+#ifdef SERIAL_DEBUG_AUTOCONF
+			printk("serial: ttyS%d: simple autoconfig failed\n",
+			       state->line);
+#endif
 			restore_flags(flags);
 			return;		/* We failed; there's nothing here */
 		}
 	}
 
+	save_mcr = serial_in(info, UART_MCR);
+	save_lcr = serial_in(info, UART_LCR);
+
 	/* 
 	 * Check to see if a UART is really there.  Certain broken
 	 * internal modems based on the Rockwell chipset fail this
@@ -3424,18 +3419,18 @@
 	 * that conflicts with COM 1-4 --- we hope!
 	 */
 	if (!(state->flags & ASYNC_SKIP_TEST)) {
-		scratch = serial_inp(info, UART_MCR);
-		serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch);
 		serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A);
 		status1 = serial_inp(info, UART_MSR) & 0xF0;
-		serial_outp(info, UART_MCR, scratch);
+		serial_outp(info, UART_MCR, save_mcr);
 		if (status1 != 0x90) {
+#ifdef SERIAL_DEBUG_AUTOCONF
+			printk("serial: ttyS%d: no UART loopback failed\n",
+			       state->line);
+#endif
 			restore_flags(flags);
 			return;
 		}
-	} 
-
-	scratch2 = serial_in(info, UART_LCR);
+	}
 	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, 0);
@@ -3456,21 +3451,6 @@
 			break;
 	}
 	if (state->type == PORT_16550A) {
-		/* Check for Oxford Semiconductor 16C950 */
-		unsigned char scratch4;
-
-		scratch = serial_icr_read(info, UART_ID1);
-		scratch4 = serial_icr_read(info, UART_ID2);
-		scratch3 = serial_icr_read(info, UART_ID3);
-		
-		if (scratch == 0x16 && scratch4 == 0xC9 &&
-		    (scratch3 == 0x50 || scratch3 == 0x52 ||
-		     scratch3 == 0x54)) {
-			state->type = PORT_16C950;
-			state->revision = serial_icr_read(info, UART_REV);
-		}
-	}
-	if (state->type == PORT_16550A) {
 		/* Check for Startech UART's */
 		serial_outp(info, UART_LCR, UART_LCR_DLAB);
 		if (serial_in(info, UART_EFR) == 0) {
@@ -3483,7 +3463,7 @@
 	}
 	if (state->type == PORT_16550A) {
 		/* Check for TI 16750 */
-		serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+		serial_outp(info, UART_LCR, save_lcr | UART_LCR_DLAB);
 		serial_outp(info, UART_FCR,
 			    UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
 		scratch = serial_in(info, UART_IIR) >> 5;
@@ -3504,7 +3484,7 @@
 		}
 		serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
 	}
-	serial_outp(info, UART_LCR, scratch2);
+	serial_outp(info, UART_LCR, save_lcr);
 	if (state->type == PORT_16450) {
 		scratch = serial_in(info, UART_SCR);
 		serial_outp(info, UART_SCR, 0xa5);
@@ -3529,9 +3509,11 @@
 	/*
 	 * Reset the UART.
 	 */
-	serial_outp(info, UART_MCR, 0x00 | ALPHA_KLUDGE_MCR); /* Don't ask */
-	serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+	serial_outp(info, UART_MCR, save_mcr);
+	serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO |
+				     UART_FCR_CLEAR_RCVR |
 				     UART_FCR_CLEAR_XMIT));
+	serial_outp(info, UART_FCR, 0);
 	(void)serial_in(info, UART_RX);
 	serial_outp(info, UART_IER, 0);
 	
@@ -3562,11 +3544,11 @@
  * 
  * Note that __init is a no-op if MODULE is defined; we depend on this.
  */
-static void __init pci_plx9050_fn(struct pci_dev *dev,
+static int __init pci_plx9050_fn(struct pci_dev *dev,
 				  struct pci_board *board,
 				  int enable)
 {
-	u8 data, *p;
+	u8 data, *p, scratch;
 
 	pci_read_config_byte(dev, PCI_COMMAND, &data);
 
@@ -3576,12 +3558,94 @@
 	
 	/* enable/disable interrupts */
 	p = ioremap(PCI_BASE_ADDRESS(dev, 0), 0x80);
-	writel(enable ? 0x41 : 0x00, p + 0x4c);
+	if (board->vendor == PCI_VENDOR_ID_PANACOM) {
+		scratch = readl(p + 0x4c);
+		if (enable)
+			scratch |= 0x40;
+		else
+			scratch &= ~0x40;
+		writel(scratch, p + 0x4c);
+	} else
+		writel(enable ? 0x41 : 0x00, p + 0x4c);
 	iounmap(p);
 
 	if (!enable)
 		pci_write_config_byte(dev, PCI_COMMAND,
 				      data & ~PCI_COMMAND_MEMORY);
+	return 0;
+}
+
+/*
+ * SIIG serial cards have an PCI interface chip which also controls
+ * the UART clocking frequency. Each UART can be clocked independently
+ * (except cards equiped with 4 UARTs) and initial clocking settings
+ * are stored in the EEPROM chip. It can cause problems because this
+ * version of serial driver doesn't support differently clocked UART's
+ * on single PCI card. To prevent this, initialization functions set
+ * high frequency clocking for all UART's on given card. It is safe (I
+ * hope) because it doesn't touch EEPROM settings to prevent conflicts
+ * with other OSes (like M$ DOS).
+ *
+ *  SIIG support added by Andrey Panin <pazke@mail.tp.ru>, 10/1999
+ * 
+ * There is two family of SIIG serial cards with different PCI
+ * interface chip and different configuration methods:
+ *     - 10x cards have control registers in IO and/or memory space;
+ *     - 20x cards have control registers in standard PCI configuration space.
+ */
+
+#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc)
+#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8)
+
+static int __init pci_siig10x_fn(struct pci_dev *dev,
+                                 struct pci_board *board,
+                                 int enable)
+{
+       u16 data, *p;
+
+       if (!enable) return 0;
+
+       p = ioremap(PCI_BASE_ADDRESS(dev, 0), 0x80);
+
+       switch (dev->device & 0xfff8) {
+               case PCI_DEVICE_ID_SIIG_1S_10x:         /* 1S */
+                       data = 0xffdf;
+                       break;
+               case PCI_DEVICE_ID_SIIG_2S_10x:         /* 2S, 2S1P */
+                       data = 0xf7ff;
+                       break;
+               default:                                /* 1S1P, 4S */
+                       data = 0xfffb;
+                       break;
+       }
+
+       writew(readw(p + 0x28) & data, p + 0x28);
+       iounmap(p);
+       return 0;
+}
+
+#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc)
+#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc)
+
+static int __init pci_siig20x_fn(struct pci_dev *dev,
+                                 struct pci_board *board,
+                                 int enable)
+{
+       u8 data;
+
+       if (!enable) return 0;
+
+       /* Change clock frequency for the first UART. */
+       pci_read_config_byte(dev, 0x6f, &data);
+       pci_write_config_byte(dev, 0x6f, data & 0xef);
+
+       /* If this card has 2 UART, we have to do the same with second UART. */
+       if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) ||
+           ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) {
+               pci_read_config_byte(dev, 0x73, &data);
+               pci_write_config_byte(dev, 0x73, data & 0xef);
+       }
+       return 0;
 }
 
 /*
@@ -3592,9 +3656,9 @@
 	/*
 	 * Vendor ID, 	Device ID,
 	 * Subvendor ID,	Subdevice ID,
-	 * Number of Ports, Base (Maximum) Baud Rate,
-	 * Offset of register holding Uart register offset
-	 * Mask to apply to above register's value
+	 * PCI Flags, Number of Ports, Base (Maximum) Baud Rate,
+	 * Offset to get to next UART's registers
+	 * Register shift to use for memory-mapped I/O
 	 */
 	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
 		PCI_SUBVENDOR_ID_CONNECT_TECH,
@@ -3677,7 +3741,7 @@
 		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 },
 	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8,
 		PCI_ANY_ID, PCI_ANY_ID,
-		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 8, 115200 },
+		SPCI_FL_BASE2, 8, 115200 },
 	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2,
 		PCI_ANY_ID, PCI_ANY_ID,
 		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 },
@@ -3691,12 +3755,226 @@
 		0x400, 7, pci_plx9050_fn },
 	{	PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM,
 		PCI_ANY_ID, PCI_ANY_ID,
-		SPCI_FL_BASE2 | SPCI_FL_IOMEM, 4, 921600,
+		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600,
 		0x400, 7, pci_plx9050_fn },
 	{	PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM,
 		PCI_ANY_ID, PCI_ANY_ID,
-		SPCI_FL_BASE2 | SPCI_FL_IOMEM, 2, 921600,
+		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600,
 		0x400, 7, pci_plx9050_fn },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_CHASE_PCIFAST,
+		PCI_SUBDEVICE_ID_CHASE_PCIFAST4,
+		SPCI_FL_BASE2, 4, 460800 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_CHASE_PCIFAST,
+		PCI_SUBDEVICE_ID_CHASE_PCIFAST8,
+		SPCI_FL_BASE2, 8, 460800 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_CHASE_PCIFAST,
+		PCI_SUBDEVICE_ID_CHASE_PCIFAST16,
+		SPCI_FL_BASE2, 16, 460800 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_CHASE_PCIFAST,
+		PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC,
+		SPCI_FL_BASE2, 16, 460800 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_CHASE_PCIRAS,
+		PCI_SUBDEVICE_ID_CHASE_PCIRAS4,
+		SPCI_FL_BASE2, 4, 460800 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_CHASE_PCIRAS,
+		PCI_SUBDEVICE_ID_CHASE_PCIRAS8,
+		SPCI_FL_BASE2, 8, 460800 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE1, 4, 115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE1, 2, 115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE1, 8, 115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE1, 8, 115200 },
+	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
+		PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4,
+		SPCI_FL_BASE0 , 4, 115200 },
+	{	PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954,
+		PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4,
+		SPCI_FL_BASE0 , 4, 921600 },
+	{	PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0 , 2, 921600 },
+	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_SERIAL,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2, 1, 460800,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2, 1, 460800,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2, 1, 460800,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2, 1, 921600,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2, 1, 921600,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2, 1, 921600,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600,
+		0, 0, pci_siig10x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0, 1, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0, 1, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0, 1, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0, 1, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0, 1, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0, 1, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0, 1, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0, 1, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0, 1, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600,
+		0, 0, pci_siig20x_fn },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600,
+		0, 0, pci_siig20x_fn },
+	/* Computone devices submitted by Doug McNash dmcnash@computone.com */
+	{	PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
+		PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4,
+		SPCI_FL_IOMEM | SPCI_FL_BASE0, 4, 921600,
+		0x40, 2, NULL, 0x200 },
+	{	PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
+		PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8,
+		SPCI_FL_IOMEM | SPCI_FL_BASE0, 8, 921600,
+		0x40, 2, NULL, 0x200 },
+	{	PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
+		PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6,
+		SPCI_FL_IOMEM | SPCI_FL_BASE0, 6, 921600,
+		0x40, 2, NULL, 0x200 },
+	/*
+	 * Untested PCI modems, sent in from various folks...
+	 */
+	/* at+t zoom 56K faxmodem, from dougd@shieldsbag.com */	
+	{	PCI_VENDOR_ID_ATT, 0x442,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE1, 1, 115200 },
+	/* at&t unknown modem, from jimd@esoft.com */
+	{	PCI_VENDOR_ID_ATT, 0x480,
+		PCI_ANY_ID, PCI_ANY_ID,
+		SPCI_FL_BASE1, 1, 115200 },
+	/* Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de> */
+	{	PCI_VENDOR_ID_ROCKWELL, 0x1004,
+		0x1048, 0x1500, 
+		SPCI_FL_BASE1, 1, 115200 },
+	/* 3Com US Robotics 56k* Voice Internal PCI, model# 2884 */
+	/* from evidal@iti.upv.es */
+	/* XXX complete guess this may not work!!! */
+	{	PCI_VENDOR_ID_USR, 0x1006,
+		0x12b9, 0x0060,
+		SPCI_FL_BASE1 | SPCI_FL_IOMEM, 1, 115200 },
 	{	0, }
 };
 
@@ -3767,18 +4045,22 @@
 		 * Run the initialization function, if any
 		 */
 		if (board->init_fn)
-			(board->init_fn)(dev, board, 1);
+			if ((board->init_fn)(dev, board, 1) != 0)
+				continue;
 
 		/*
 		 * Register the serial board in the array so we can
 		 * shutdown the board later, if necessary.
 		 */
+		if (serial_pci_board_idx >= NR_PCI_BOARDS)
+			continue;
 		serial_pci_board[serial_pci_board_idx].board = board;
 		serial_pci_board[serial_pci_board_idx].dev = dev;
 		serial_pci_board_idx++;
 
 		base_idx = board->flags & SPCI_FL_BASE_MASK;
-		port = PCI_BASE_ADDRESS(dev, base_idx);
+		port = PCI_BASE_ADDRESS(dev, base_idx) +
+			board->first_uart_offset;
 		if (board->flags & SPCI_FL_IOMEM)
 			port &= PCI_BASE_ADDRESS_MEM_MASK;
 		else
@@ -3829,6 +4111,7 @@
 			fake_state.port = port;
 #ifdef CONFIG_SERIAL_PCI_MEMMAPPED
 			if (board->flags & SPCI_FL_IOMEM) {
+				fake_state.io_type = SERIAL_IO_MEM;
 				fake_state.iomem_base =
 					ioremap(port, board->uart_offset);
 				fake_state.iomem_reg_shift = board->reg_shift;
@@ -3869,6 +4152,16 @@
 	dualsp_serial_init ();
 #endif
 
+	if (timer_table[RS_TIMER].fn) {
+		printk("RS_TIMER already set, another serial driver "
+		       "already loaded?\n");
+#ifdef MODULE
+		printk("Can't load serial driver module over built-in "
+		       "serial driver\n");
+#endif
+		return -EBUSY;
+	}
+
 	init_bh(SERIAL_BH, do_serial_bh);
 	timer_table[RS_TIMER].fn = rs_timer;
 	timer_table[RS_TIMER].expires = 0;
@@ -3974,6 +4267,8 @@
 		state->icount.frame = state->icount.parity = 0;
 		state->icount.overrun = state->icount.brk = 0;
 		state->irq = irq_cannonicalize(state->irq);
+		if (state->hub6)
+			state->io_type = SERIAL_IO_HUB6;
 		if (state->port && check_region(state->port,8))
 			continue;
 		if (state->flags & ASYNC_BOOT_AUTOCONF)
@@ -4035,10 +4330,13 @@
 	state->irq = req->irq;
 	state->port = req->port;
 	state->flags = req->flags;
+	state->io_type = req->io_type;
 #ifdef CONFIG_SERIAL_PCI_MEMMAPPED
 	state->iomem_base = req->iomem_base;
 	state->iomem_reg_shift = req->iomem_reg_shift;
 #endif
+	if (req->baud_base)
+		state->baud_base = req->baud_base;
 
 	autoconfig(state);
 	if (state->type == PORT_UNKNOWN) {
@@ -4103,13 +4401,12 @@
 	restore_flags(flags);
 
 	for (i = 0; i < NR_PORTS; i++) {
+		if ((info = rs_table[i].info)) {
+			rs_table[i].info = NULL;
+			kfree_s(info, sizeof(struct async_struct));
+		}
 		if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port)
 			release_region(rs_table[i].port, 8);
- 		info = rs_table[i].info;
- 		if (info) {
- 			rs_table[i].info = NULL;
- 			kfree_s(info, sizeof(struct async_struct));
- 		}
 #if defined(ENABLE_SERIAL_PCI) && defined (CONFIG_SERIAL_PCI_MEMMAPPED)
 		if (rs_table[i].iomem_base)
 			iounmap(rs_table[i].iomem_base);
@@ -4140,20 +4437,20 @@
 
 #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
 
+static struct async_struct async_sercons;
+
 /*
  *	Wait for transmitter & holding register to empty
  */
-static inline void wait_for_xmitr(struct serial_state *ser)
+static inline void wait_for_xmitr(struct async_struct *info)
 {
-	int lsr;
 	unsigned int tmout = 1000000;
 
-	do {
-		lsr = inb(ser->port + UART_LSR);
-		if (--tmout == 0) break;
-	} while ((lsr & BOTH_EMPTY) != BOTH_EMPTY);
+	while (--tmout &&
+	       ((serial_in(info, UART_LSR) & BOTH_EMPTY) != BOTH_EMPTY));
 }
 
+
 /*
  *	Print a string to the serial port trying not to disturb
  *	any possible real use of the port...
@@ -4161,31 +4458,30 @@
 static void serial_console_write(struct console *co, const char *s,
 				unsigned count)
 {
-	struct serial_state *ser;
+	static struct async_struct *info = &async_sercons;
 	int ier;
 	unsigned i;
 
-	ser = rs_table + co->index;
 	/*
 	 *	First save the IER then disable the interrupts
 	 */
-	ier = inb(ser->port + UART_IER);
-	outb(0x00, ser->port + UART_IER);
+	ier = serial_in(info, UART_IER);
+	serial_out(info, UART_IER, 0x00);
 
 	/*
 	 *	Now, do each character
 	 */
 	for (i = 0; i < count; i++, s++) {
-		wait_for_xmitr(ser);
+		wait_for_xmitr(info);
 
 		/*
 		 *	Send the character out.
 		 *	If a LF, also do CR...
 		 */
-		outb(*s, ser->port + UART_TX);
+		serial_out(info, UART_TX, *s);
 		if (*s == 10) {
-			wait_for_xmitr(ser);
-			outb(13, ser->port + UART_TX);
+			wait_for_xmitr(info);
+			serial_out(info, UART_TX, 13);
 		}
 	}
 
@@ -4193,8 +4489,8 @@
 	 *	Finally, Wait for transmitter & holding register to empty
 	 * 	and restore the IER
 	 */
-	wait_for_xmitr(ser);
-	outb(ier, ser->port + UART_IER);
+	wait_for_xmitr(info);
+	serial_out(info, UART_IER, ier);
 }
 
 /*
@@ -4202,30 +4498,26 @@
  */
 static int serial_console_wait_key(struct console *co)
 {
-	struct serial_state *ser;
-	int ier;
-	int lsr;
-	int c;
+	static struct async_struct *info;
+	int ier, c;
 
-	ser = rs_table + co->index;
+	info = &async_sercons;
 
 	/*
 	 *	First save the IER then disable the interrupts so
 	 *	that the real driver for the port does not get the
 	 *	character.
 	 */
-	ier = inb(ser->port + UART_IER);
-	outb(0x00, ser->port + UART_IER);
-
-	do {
-		lsr = inb(ser->port + UART_LSR);
-	} while (!(lsr & UART_LSR_DR));
-	c = inb(ser->port + UART_RX);
+	ier = serial_in(info, UART_IER);
+	serial_out(info, UART_IER, 0x00);
+ 
+	while ((serial_in(info, UART_LSR) & UART_LSR_DR) == 0);
+	c = serial_in(info, UART_RX);
 
 	/*
 	 *	Restore the interrupts
 	 */
-	outb(ier, ser->port + UART_IER);
+	serial_out(info, UART_IER, ier);
 
 	return c;
 }
@@ -4243,7 +4535,8 @@
  */
 static int __init serial_console_setup(struct console *co, char *options)
 {
-	struct serial_state *ser;
+	static struct async_struct *info;
+	struct serial_state *state;
 	unsigned cval;
 	int	baud = 9600;
 	int	bits = 8;
@@ -4251,6 +4544,9 @@
 	int	cflag = CREAD | HUPCL | CLOCAL;
 	int	quot = 0;
 	char	*s;
+#if defined(CONFIG_KDB)
+	extern int  kdb_port;
+#endif
 
 	if (options) {
 		baud = simple_strtoul(options, NULL, 10);
@@ -4313,8 +4609,21 @@
 	/*
 	 *	Divisor, bytesize and parity
 	 */
-	ser = rs_table + co->index;
-	quot = ser->baud_base / baud;
+	state = rs_table + co->index;
+	info = &async_sercons;
+	info->magic = SERIAL_MAGIC;
+	info->state = state;
+	info->port = state->port;
+	info->flags = state->flags;
+#ifdef CONFIG_HUB6
+	info->hub6 = state->hub6;
+#endif
+	info->io_type = state->io_type;
+#ifdef CONFIG_SERIAL_PCI_MEMMAPPED
+	info->iomem_base = state->iomem_base;
+	info->iomem_reg_shift = state->iomem_reg_shift;
+#endif
+	quot = state->baud_base / baud;
 	cval = cflag & (CSIZE | CSTOPB);
 #if defined(__powerpc__) || defined(__alpha__)
 	cval >>= 8;
@@ -4330,18 +4639,27 @@
 	 *	Disable UART interrupts, set DTR and RTS high
 	 *	and set speed.
 	 */
-	outb(cval | UART_LCR_DLAB, ser->port + UART_LCR);	/* set DLAB */
-	outb(quot & 0xff, ser->port + UART_DLL);	/* LS of divisor */
-	outb(quot >> 8, ser->port + UART_DLM);		/* MS of divisor */
-	outb(cval, ser->port + UART_LCR);		/* reset DLAB */
-	outb(0, ser->port + UART_IER);
-	outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR);
+	serial_out(info, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
+	serial_out(info, UART_DLL, quot & 0xff);	/* LS of divisor */
+	serial_out(info, UART_DLM, quot >> 8);		/* MS of divisor */
+	serial_out(info, UART_LCR, cval);		/* reset DLAB */
+	serial_out(info, UART_IER, 0);
+	serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
 
 	/*
 	 *	If we read 0xff from the LSR, there is no UART here.
 	 */
-	if (inb(ser->port + UART_LSR) == 0xff)
+	if (serial_in(info, UART_LSR) == 0xff)
 		return -1;
+
+#if defined(CONFIG_KDB)
+	/*
+	 * Remember I/O port for kdb
+	 */
+	if (kdb_port == 0 )
+		kdb_port = ser->port;
+#endif	/* CONFIG_KDB */
+	
 	return 0;
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)