patch-2.4.19 linux-2.4.19/drivers/net/wan/8253x/8253xtty.c

Next file: linux-2.4.19/drivers/net/wan/8253x/8253xutl.c
Previous file: linux-2.4.19/drivers/net/wan/8253x/8253xsyn.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/drivers/net/wan/8253x/8253xtty.c linux-2.4.19/drivers/net/wan/8253x/8253xtty.c
@@ -0,0 +1,2925 @@
+/* -*- linux-c -*- */
+/* $Id: 8253xtty.c,v 1.23 2002/02/10 22:17:25 martillo Exp $
+ * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ *
+ * Modified by Francois Wautier 2000 (fw@auroratech.com)
+ *
+ * Extended extensively by Joachim Martillo 2001 (Telford002@aol.com)
+ * 	to provide synchronous/asynchronous TTY/Callout/character/network device
+ * 	capabilities.
+ *
+ * Modifications and extensions
+ * Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+/* Standard in kernel modules */
+#define DEFINE_VARIABLE
+#include <linux/module.h>   /* Specifically, a module */
+#include <asm/io.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/mm.h>
+#include <linux/version.h>
+#include <asm/uaccess.h>
+#include <linux/pci.h>
+#include "8253xctl.h"
+#include "sp502.h"
+
+DECLARE_TASK_QUEUE(tq_8253x_serial); /* this just initializes a list head called */
+				     /* tq_8253x_serial*/
+
+struct tty_driver sab8253x_serial_driver, sab8253x_callout_driver, sync_sab8253x_serial_driver;
+int sab8253x_refcount;
+
+/* Trace things on serial device, useful for console debugging: */
+#undef SERIAL_LOG_DEVICE
+
+#ifdef SERIAL_LOG_DEVICE
+static void dprint_init(int tty);
+#endif
+
+static void sab8253x_change_speed(struct sab_port *port);
+
+static struct tty_struct **sab8253x_tableASY = 0;	/* make dynamic */
+static struct tty_struct **sab8253x_tableCUA = 0;	/* make dynamic */
+static struct tty_struct **sab8253x_tableSYN = 0;	/* make dynamic */
+static struct termios **sab8253x_termios = 0 ;
+static struct termios **sab8253x_termios_locked = 0;
+
+#ifdef MODULE
+#undef XCONFIG_SERIAL_CONSOLE	/* leaving out CONFIG_SERIAL_CONSOLE for now */
+#endif
+
+#ifdef XCONFIG_SERIAL_CONSOLE	/* not really implemented yet */
+extern int serial_console;
+struct console sab8253x_console;
+int sab8253x_console_init(void);
+#endif
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+char sab8253x_serial_version[16];
+
+static void sab8253x_flush_to_ldisc(void *private_)
+{
+	struct tty_struct *tty = (struct tty_struct *) private_;
+	unsigned char	*cp;
+	char		*fp;
+	int		count;
+	struct sab_port *port;
+	struct sk_buff *skb;  
+	
+	if(tty)
+	{
+		port = (struct sab_port *)tty->driver_data; /* probably a silly check */
+	}
+	else
+	{
+		return;
+	}
+	
+	if(!port)
+	{
+		return;
+	}
+	
+	if (test_bit(TTY_DONT_FLIP, &tty->flags)) 
+	{
+		queue_task(&tty->flip.tqueue, &tq_timer);
+		return;
+	}
+	/* note that a hangup may have occurred -- perhaps should check for that */
+	port->DoingInterrupt = 1;
+	while(port->sab8253xc_rcvbuflist && (skb_queue_len(port->sab8253xc_rcvbuflist) > 0))
+	{
+		skb = skb_dequeue(port->sab8253xc_rcvbuflist);
+		count = skb->data_len;
+		cp = skb->data;
+		fp = skb->data + (count/2);
+		(*tty->ldisc.receive_buf)(tty, cp, fp, count/2);
+		dev_kfree_skb_any(skb);
+	}
+	port->DoingInterrupt = 0;
+}
+
+/* only used asynchronously */
+static void inline sab8253x_tec_wait(struct sab_port *port)
+{
+	int count = port->tec_timeout;
+	
+	while((READB(port, star) & SAB82532_STAR_TEC) && --count)
+	{
+		udelay(1);
+	}
+}
+
+void sab8253x_start_tx(struct sab_port *port)
+{
+	unsigned long flags;
+	register int count;
+	register int total;
+	register int offset;
+	char temporary[32];
+	register unsigned int slopspace;
+	register int sendsize;
+	unsigned int totaltransmit;
+	unsigned fifospace;
+	unsigned loadedcount;
+	struct tty_struct *tty = port->tty;
+	
+	fifospace = port->xmit_fifo_size;
+	loadedcount = 0;
+	
+	if(port->sabnext2.transmit == NULL)
+	{
+		return;
+	}
+	
+	save_flags(flags); 
+	cli();			
+	
+	
+	while(count = port->sabnext2.transmit->Count, (count & OWNER) == OWN_SAB)
+	{
+		count &= ~OWN_SAB; /* OWN_SAB is really 0 but cannot guarantee in the future */
+		
+		if(port->sabnext2.transmit->HostVaddr)
+		{
+			total = (port->sabnext2.transmit->HostVaddr->tail - 
+				 port->sabnext2.transmit->HostVaddr->data); /* packet size */
+		}
+		else
+		{
+			total = 0;		/* the data is only in the crc/trailer */
+		}
+		
+		if(tty && (tty->stopped || tty->hw_stopped))
+		{			/* works for frame that only has a trailer (crc) */
+			port->interrupt_mask1 |= SAB82532_IMR1_XPR;
+			WRITEB(port, imr1, port->interrupt_mask1);
+			restore_flags(flags);	/* can't send */
+			return;
+		}
+		
+		offset = (total - count);	/* offset to data still to send */
+		
+		port->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
+		WRITEB(port, imr1, port->interrupt_mask1);
+		port->all_sent = 0;
+		
+		
+		if(READB(port,star) & SAB82532_STAR_XFW)
+		{
+			if(count <= fifospace)
+			{
+				port->xmit_cnt = count;
+				slopspace = 0;
+				sendsize = 0;
+				if(port->sabnext2.transmit->sendcrc) 
+				/* obviously should not happen for async but might use for
+				   priority transmission */
+				{
+					slopspace = fifospace - count;
+				}
+				if(slopspace)
+				{
+					if(count)
+					{
+						memcpy(temporary, &port->sabnext2.transmit->HostVaddr->data[offset], 
+						       count);
+					}
+					sendsize = MIN(slopspace, (4 - port->sabnext2.transmit->crcindex)); 
+				/* how many bytes to send */
+					memcpy(&temporary[count], 
+					       &((unsigned char*)(&port->sabnext2.transmit->crc))
+					       [port->sabnext2.transmit->crcindex], 
+					       sendsize);
+					port->sabnext2.transmit->crcindex += sendsize;
+					if(port->sabnext2.transmit->crcindex >= 4)
+					{
+						port->sabnext2.transmit->sendcrc = 0;
+					}
+					port->xmit_buf = temporary;
+				}
+				else
+				{
+					port->xmit_buf =	/* set up wrifefifo variables */
+						&port->sabnext2.transmit->HostVaddr->data[offset];
+				}
+				port->xmit_cnt += sendsize;
+				count = 0;
+			}
+			else
+			{
+				count -= fifospace;
+				port->xmit_cnt = fifospace;
+				port->xmit_buf =	/* set up wrifefifo variables */
+					&port->sabnext2.transmit->HostVaddr->data[offset];
+				
+			}
+			port->xmit_tail= 0;
+			loadedcount = port->xmit_cnt;
+			(*port->writefifo)(port);
+			totaltransmit = Sab8253xCountTransmitDescriptors(port);
+			if((sab8253xt_listsize - totaltransmit) > 2) 
+			{
+				sab8253x_sched_event(port, SAB8253X_EVENT_WRITE_WAKEUP);
+			}
+			
+			if((sab8253xt_listsize - totaltransmit) > (sab8253xt_listsize/2))
+			{
+				port->buffergreedy = 0;
+			}
+			else
+			{
+				port->buffergreedy = 1;
+			}
+			
+			port->xmit_buf = NULL; /* this var is used to indicate whether to call kfree */
+			
+			fifospace -= loadedcount;
+			
+			if ((count <= 0) && (port->sabnext2.transmit->sendcrc == 0))
+			{
+				port->sabnext2.transmit->Count = OWN_DRIVER;
+#ifdef FREEININTERRUPT		/* treat this routine as if taking place in interrupt */
+				if(port->sabnext2.transmit->HostVaddr)
+				{
+					skb_unlink(port->sabnext2.transmit->HostVaddr);
+					dev_kfree_skb_any(port->sabnext2.transmit->HostVaddr);
+					port->sabnext2.transmit->HostVaddr = 0; /* no skb */
+				}
+				port->sabnext2.transmit->crcindex = 0; /* no single byte */
+#endif
+				port->sabnext2.transmit = port->sabnext2.transmit->VNext;
+				if((port->sabnext2.transmit->Count & OWNER) == OWN_SAB)
+				{
+					if(fifospace > 0)
+					{
+						continue;	/* the only place where this code really loops */
+					}
+					if(fifospace < 0)
+					{
+						printk(KERN_ALERT "sab8253x:  bad math in interrupt handler.\n");
+					}
+					port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+					WRITEB(port, imr1, port->interrupt_mask1);
+				}
+				else
+				{
+					port->interrupt_mask1 |= SAB82532_IMR1_XPR;
+					WRITEB(port, imr1, port->interrupt_mask1);
+				}
+				sab8253x_cec_wait(port);
+				/* Issue a Transmit Frame command. */
+				WRITEB(port, cmdr, SAB82532_CMDR_XF); 
+				/* This could be optimized to load from next skbuff */
+				/* SAB82532_CMDR_XF is the same as SAB82532_CMDR_XTF */
+				restore_flags(flags);
+				return;
+			}
+			sab8253x_cec_wait(port);
+			/* Issue a Transmit Frame command. */
+			WRITEB(port, cmdr, SAB82532_CMDR_XF);	/* same as SAB82532_CMDR_XTF */
+			port->sabnext2.transmit->Count = (count|OWN_SAB);
+		}
+		port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+		WRITEB(port, imr1, port->interrupt_mask1);
+		restore_flags(flags);
+		return;
+	}
+	/*  The While loop only exits via return*/
+	/* we get here by skipping the loop  */
+	port->interrupt_mask1 |= SAB82532_IMR1_XPR;
+	WRITEB(port, imr1, port->interrupt_mask1);
+	restore_flags(flags);
+	return;
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab8253x_stop() and sab8253x_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+
+static void sab8253x_stop(struct tty_struct *tty)
+{
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	unsigned long flags;
+	
+	if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_stop"))
+	{
+		return;
+	}
+	
+	save_flags(flags); 
+	cli();	/* maybe should turn off ALLS as well
+		   but the stop flags are checked
+		   so ALLS is probably harmless
+		   and I have seen too much evil
+		   associated with that interrupt*/
+	port->interrupt_mask1 |= SAB82532_IMR1_XPR;
+	WRITEB(port, imr1, port->interrupt_mask1);
+	restore_flags(flags);
+}
+
+static void sab8253x_start(struct tty_struct *tty)
+{
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	
+	
+	if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_start"))
+	{
+		return;
+	}
+	
+	sab8253x_start_tx(port);
+}
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+/* no obvious changes for sync tty */
+
+static void sab8253x_receive_chars(struct sab_port *port,
+			    union sab8253x_irq_status *stat)
+{
+	struct tty_struct *tty = port->tty;
+	unsigned char buf[32];
+	unsigned char reordered[32];
+	unsigned char status;
+	int free_fifo = 0;
+	int i, count = 0;
+	struct sk_buff *skb;
+	
+	/* Read number of BYTES (Character + Status) available. */
+	if (stat->images[ISR0_IDX] & SAB82532_ISR0_RPF) 
+	{
+		count = port->recv_fifo_size;
+		free_fifo++;
+	}
+	
+	if (stat->images[ISR0_IDX] & SAB82532_ISR0_TCD) 
+	{
+		count = READB(port,rbcl) & (port->recv_fifo_size - 1);
+		free_fifo++;
+	}
+	
+	/* Issue a FIFO read command in case we where idle. */
+	if (stat->sreg.isr0 & SAB82532_ISR0_TIME) 
+	{
+		sab8253x_cec_wait(port);
+		WRITEB(port, cmdr, SAB82532_CMDR_RFRD);
+	}
+	
+	if (stat->images[ISR0_IDX] & SAB82532_ISR0_RFO) 
+	{				/* FIFO overflow */
+		free_fifo++;
+	}
+	
+	/* Read the FIFO. */
+	(*port->readfifo)(port, buf, count);
+	
+	/* Issue Receive Message Complete command. */
+	if (free_fifo) 
+	{
+		sab8253x_cec_wait(port);
+		WRITEB(port, cmdr, SAB82532_CMDR_RMC);
+	}
+	
+#ifdef CONSOLE_SUPPORT
+	if (port->is_console)
+	{
+		wake_up(&keypress_wait);
+	}
+#endif
+	if (!tty)
+	{
+		return;
+	}
+	
+	if(!count)
+	{
+		return;
+	}
+	
+	for(i = 0; i < count; i += 2)
+	{
+		reordered[i/2] = buf[i];
+		status = buf[i+1];
+		if (status & SAB82532_RSTAT_PE) 
+		{
+			status = TTY_PARITY;
+			port->icount.parity++;
+		} 
+		else if (status & SAB82532_RSTAT_FE) 
+		{
+			status = TTY_FRAME;
+			port->icount.frame++;
+		}
+		else
+		{
+			status = TTY_NORMAL;
+		}
+		reordered[(count+i)/2] = status;
+	}
+	
+	if(port->active2.receive == NULL)
+	{
+		return;
+	}
+	
+	memcpy(port->active2.receive->HostVaddr->tail, reordered, count);
+	port->active2.receive->HostVaddr->tail += count;
+	port->active2.receive->HostVaddr->data_len = count;
+	port->active2.receive->HostVaddr->len = count;
+	if(skb = dev_alloc_skb(port->recv_fifo_size), skb == NULL) /* use dev_alloc_skb because at int
+								      there is header space but so what*/
+	{
+		port->icount.buf_overrun++;
+		port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data; /* clear the buffer */
+		port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
+		port->active2.receive->HostVaddr->data_len = 0;
+		port->active2.receive->HostVaddr->len = 0;
+	}
+	else
+	{
+		skb_unlink(port->active2.receive->HostVaddr);
+		skb_queue_tail(port->sab8253xc_rcvbuflist, port->active2.receive->HostVaddr);
+		skb_queue_head(port->sab8253xbuflist, skb);
+		port->active2.receive->HostVaddr = skb;
+		port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
+	}
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static void sab8253x_transmit_chars(struct sab_port *port,
+				    union sab8253x_irq_status *stat)
+{
+	
+	if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) /* got an all sent int? */
+	{
+		port->interrupt_mask1 |= SAB82532_IMR1_ALLS;
+		WRITEB(port, imr1, port->interrupt_mask1);
+		port->all_sent = 1;	/* not much else to do */
+	} /* a very weird chip -- this int only indicates this int */
+	
+	sab8253x_start_tx(port);
+}
+
+static void sab8253x_check_status(struct sab_port *port,
+			   union sab8253x_irq_status *stat)
+{
+	struct tty_struct *tty = port->tty;
+	int modem_change = 0;
+	mctlsig_t         *sig;
+	struct sk_buff *skb;
+	
+	if (!tty)
+	{
+		return;
+	}
+	
+	if(port->active2.receive == NULL)
+	{
+		goto check_modem;
+	}
+	
+	if (stat->images[ISR1_IDX] & SAB82532_ISR1_BRK) 
+	{
+#ifdef XCONFIG_SERIAL_CONSOLE
+		if (port->is_console) 
+		{
+			batten_down_hatches(info); /* need to add this function */
+			return;
+		}
+#endif 
+		
+		port->active2.receive->HostVaddr->tail[0] = 0;
+		port->active2.receive->HostVaddr->tail[1] = TTY_PARITY;
+		port->active2.receive->HostVaddr->tail += 2;
+		port->active2.receive->HostVaddr->data_len = 2;
+		port->active2.receive->HostVaddr->len = 2;
+		
+		if(skb = dev_alloc_skb(port->recv_fifo_size), skb == NULL)
+		{
+			port->icount.buf_overrun++;
+			port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data; 
+				/* clear the buffer */
+			port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
+			port->active2.receive->HostVaddr->data_len = 0;
+			port->active2.receive->HostVaddr->len = 0;
+		}
+		else
+		{
+			skb_unlink(port->active2.receive->HostVaddr);
+			skb_queue_tail(port->sab8253xc_rcvbuflist, port->active2.receive->HostVaddr);
+			skb_queue_head(port->sab8253xbuflist, skb);
+			port->active2.receive->HostVaddr = skb;
+			port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
+		}
+		queue_task(&tty->flip.tqueue, &tq_timer);
+		port->icount.brk++;
+	}
+	
+	if (stat->images[ISR0_IDX] & SAB82532_ISR0_RFO) 
+	{
+		port->active2.receive->HostVaddr->tail[0] = 0;
+		port->active2.receive->HostVaddr->tail[1] = TTY_PARITY;
+		port->active2.receive->HostVaddr->tail += 2;
+		port->active2.receive->HostVaddr->data_len = 2;
+		port->active2.receive->HostVaddr->len = 2;
+		if(skb = dev_alloc_skb(port->recv_fifo_size), skb == NULL)
+		{
+			port->icount.buf_overrun++;
+			port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data; 
+				/* clear the buffer */
+			port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
+			port->active2.receive->HostVaddr->data_len = 0;
+			port->active2.receive->HostVaddr->len = 0;
+		}
+		else
+		{
+			skb_unlink(port->active2.receive->HostVaddr);
+			skb_queue_tail(port->sab8253xc_rcvbuflist, port->active2.receive->HostVaddr);
+			skb_queue_head(port->sab8253xbuflist, skb);
+			port->active2.receive->HostVaddr = skb;
+			port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
+		}
+		queue_task(&tty->flip.tqueue, &tq_timer);
+		port->icount.overrun++;
+	}
+	
+ check_modem:
+	/* Checking DCD */
+	sig = &port->dcd;
+	if (stat->images[sig->irq] & sig->irqmask) 
+	{
+		sig->val = ISON(port,dcd);
+		port->icount.dcd++;
+		modem_change++;
+	}
+	/* Checking CTS */
+	sig = &port->cts;
+	if (stat->images[sig->irq] & sig->irqmask) 
+	{
+		sig->val = ISON(port,cts);
+		port->icount.cts++;
+		modem_change++;
+	}
+	/* Checking DSR */
+	sig = &port->dsr;
+	if (stat->images[sig->irq] & sig->irqmask) 
+	{
+		sig->val = ISON(port,dsr);
+		port->icount.dsr++;
+		modem_change++;
+	}
+	if (modem_change)
+	{
+		wake_up_interruptible(&port->delta_msr_wait); /* incase kernel proc level was waiting on modem change */
+	}
+	
+	sig = &port->dcd;
+	if ((port->flags & FLAG8253X_CHECK_CD) &&
+	    (stat->images[sig->irq] & sig->irqmask)) 
+	{
+		
+		if (sig->val)
+		{
+			wake_up_interruptible(&port->open_wait); /* in case waiting in block_til_ready */
+		}
+		else if (!((port->flags & FLAG8253X_CALLOUT_ACTIVE) &&
+			   (port->flags & FLAG8253X_CALLOUT_NOHUP))) 
+		{
+			
+			MOD_INC_USE_COUNT;	/* in case a close is already in progress
+						   don't want structures to vanish during
+						   late processing of hangup */
+			if (schedule_task(&port->tqueue_hangup) == 0)
+			{
+				MOD_DEC_USE_COUNT; /* task schedule failed */
+			}
+		}
+	}
+	
+	sig = &port->cts;
+	if (port->flags & FLAG8253X_CTS_FLOW) 
+	{
+		if (port->tty->hw_stopped) 
+		{
+			if (sig->val) 
+			{
+				
+				port->tty->hw_stopped = 0;
+				sab8253x_sched_event(port, SAB8253X_EVENT_WRITE_WAKEUP);
+				sab8253x_start_tx(port);
+			}
+		} 
+		else 
+		{
+			if (!(sig->val)) 
+			{
+				port->tty->hw_stopped = 1;
+			}
+		}
+	}
+}
+
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * sab8253x_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using sab8253x_sched_event(), and they get done here.
+ */
+				/* The following routine is installed */
+				/* in the bottom half -- just search */
+				/* for the init_bh() call */
+				/* The logic: sab8253x_sched_event() */
+				/* enqueues the tqueue port entry on */
+				/* the tq_8253x_serial task list -- */
+				/* whenever the bottom half is run */
+				/* sab8253x_do_softint is invoked for */
+				/* every port that has invoked the bottom */
+				/* half via sab8253x_sched_event(). */
+				/* currently only a write wakeevent */
+				/* wakeup is scheduled -- to tell the */
+				/* tty driver to send more chars */
+				/* down to the serial driver.*/
+
+static void sab8253x_do_serial_bh(void)
+{
+	run_task_queue(&tq_8253x_serial);
+}
+
+				/* I believe the reason for the */
+				/* bottom half processing below is */
+				/* the length of time needed to transfer */
+				/* characters to the TTY driver. */
+
+static void sab8253x_do_softint(void *private_)
+{
+	struct sab_port	*port = (struct sab_port *)private_;
+	struct tty_struct *tty;
+	
+	tty = port->tty;
+	if (!tty)
+	{
+		return;
+	}
+	
+	port->DoingInterrupt = 1;
+	if (test_and_clear_bit(SAB8253X_EVENT_WRITE_WAKEUP, &port->event)) 
+	{
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait); /* in case tty driver waiting on write */
+	}
+	port->DoingInterrupt = 0;
+}
+
+/*
+ * 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() -> sab8253x_hangup()
+ * 
+ */
+/* This logic takes place at kernel */
+/* process context through the scheduler*/
+/* schedule_task(tqueue_hangup) */
+/* takes place in the interrupt handler*/
+static void sab8253x_do_serial_hangup(void *private_)
+{
+	struct sab_port *port = (struct sab_port *) private_;
+	struct tty_struct *tty;
+	
+	tty = port->tty;
+	if (tty)
+	{
+		tty_hangup(tty);
+	}
+	MOD_DEC_USE_COUNT;		/* in case busy waiting to unload module */
+}
+
+static void
+sab8253x_init_line(struct sab_port *port)
+{
+	unsigned char stat;
+
+	if(port->chip->c_cim)
+	{
+		if(port->chip->c_cim->ci_type == CIM_SP502)
+		{
+			aura_sp502_program(port, SP502_OFF_MODE);
+		}
+	}
+	
+	/*
+	 * Wait for any commands or immediate characters
+	 */
+	sab8253x_cec_wait(port);
+	sab8253x_tec_wait(port);
+	
+	/*
+	 * Clear the FIFO buffers.
+	 */
+	
+	WRITEB(port, cmdr, SAB82532_CMDR_RRES);
+	sab8253x_cec_wait(port);
+	WRITEB(port, cmdr, SAB82532_CMDR_XRES);
+	
+	
+	/*
+	 * Clear the interrupt registers.
+	 */
+	stat = READB(port, isr0);
+	stat = READB(port, isr1);
+	
+	/*
+	 * Now, initialize the UART 
+	 */
+	WRITEB(port, ccr0, 0);	  /* power-down */
+	WRITEB(port, ccr0,
+	       SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ | SAB82532_CCR0_SM_ASYNC);
+	WRITEB(port, ccr1,
+	       SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7);
+	WRITEB(port, ccr2,
+	       SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL | SAB82532_CCR2_TOE);
+	WRITEB(port, ccr3, 0);
+	WRITEB(port, ccr4,
+	       SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG);
+	WRITEB(port, mode,
+	       SAB82532_MODE_RTS | SAB82532_MODE_FCTS | SAB82532_MODE_RAC);
+	WRITEB(port, rfc,
+	       SAB82532_RFC_DPS | SAB82532_RFC_RFDF);
+	switch (port->recv_fifo_size) 
+	{
+	case 1:
+		SET_REG_BIT(port,rfc,SAB82532_RFC_RFTH_1);
+		break;
+	case 4:
+		SET_REG_BIT(port,rfc,SAB82532_RFC_RFTH_4);
+		break;
+	case 16:
+		SET_REG_BIT(port,rfc,SAB82532_RFC_RFTH_16);
+		break;
+	default:
+		port->recv_fifo_size = 32;
+	case 32:
+		SET_REG_BIT(port,rfc,SAB82532_RFC_RFTH_32);
+		break;
+	}
+	/* power-up */
+	SET_REG_BIT(port, ccr0, SAB82532_CCR0_PU);
+	if(port->chip->c_cim)
+	{
+		if(port->chip->c_cim->ci_type == CIM_SP502)
+		{
+			aura_sp502_program(port, port->sigmode);
+		}
+	}
+}
+
+
+static int sab8253x_startup(struct sab_port *port)
+{
+	unsigned long flags;
+	
+	int retval = 0;
+	
+	save_flags(flags); cli();
+	
+	if (port->flags & FLAG8253X_INITIALIZED) 
+	{
+		goto errout;
+	}
+	
+	port->msgbufindex = 0;
+	port->xmit_buf = NULL;
+	port->buffergreedy = 0;
+	
+	if (!port->regs) 
+	{
+		if (port->tty)
+		{
+			set_bit(TTY_IO_ERROR, &port->tty->flags);
+		}
+		retval = -ENODEV;
+		goto errout;
+	}
+	
+	/*
+	 * Initialize the Hardware
+	 */
+	sab8253x_init_line(port);
+	
+	if (port->tty->termios->c_cflag & CBAUD) 
+	{
+		/* Activate RTS */
+		RAISE(port,rts);
+		
+		/* Activate DTR */
+		RAISE(port,dtr);
+	}
+	
+	/*
+	 * Initialize the modem signals values
+	 */
+	port->dcd.val=ISON(port,dcd);
+	port->cts.val=ISON(port,cts);
+	port->dsr.val=ISON(port,dsr);
+	
+	/*
+	 * Finally, enable interrupts
+	 */
+	
+	port->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+		SAB82532_IMR0_PLLA;
+	WRITEB(port, imr0, port->interrupt_mask0);
+	port->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_XOFF |
+		SAB82532_IMR1_TIN | SAB82532_IMR1_XON |
+		SAB82532_IMR1_XPR;
+	WRITEB(port, imr1, port->interrupt_mask1);
+	port->all_sent = 1;
+	
+	if (port->tty)
+	{
+		clear_bit(TTY_IO_ERROR, &port->tty->flags);
+	}
+	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+	
+	/*
+	 * and set the speed of the serial port
+	 */
+	sab8253x_change_speed(port);
+	
+	port->flags |= FLAG8253X_INITIALIZED;
+	port->receive_chars = sab8253x_receive_chars;
+	port->transmit_chars = sab8253x_transmit_chars;
+	port->check_status = sab8253x_check_status;
+	port->receive_test = (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+			      SAB82532_ISR0_RFO | SAB82532_ISR0_RPF);
+	port->transmit_test = (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR);
+	port->check_status_test = SAB82532_ISR1_BRK;
+	
+	restore_flags(flags);
+	return 0;
+	
+ errout:
+	restore_flags(flags);
+	return retval;
+}
+
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void sab8253x_shutdown(struct sab_port *port)
+{
+	unsigned long flags;
+	
+	if (!(port->flags & FLAG8253X_INITIALIZED))
+	{
+		return;
+	}
+	
+	save_flags(flags); 
+	cli(); /* Disable interrupts */
+	
+	/*
+	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+	 * here so the queue might never be waken up
+	 */
+	wake_up_interruptible(&port->delta_msr_wait);	/* shutting down port modem status is pointless */
+	
+	if (port->xmit_buf) 
+	{
+		port->xmit_buf = NULL;
+	}
+	
+#ifdef XCONFIG_SERIAL_CONSOLE
+	if (port->is_console) 
+	{
+		port->interrupt_mask0 = 
+			SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+			/*SAB82532_IMR0_TIME |*/
+			SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
+		WRITEB(port,imr0,port->interrupt_mask0);
+		port->interrupt_mask1 = 
+			SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
+			SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
+			SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
+			SAB82532_IMR1_XPR;
+		WRITEB(port,imr1,port->interrupt_mask1);
+		if (port->tty)
+		{
+			set_bit(TTY_IO_ERROR, &port->tty->flags);
+		}
+		port->flags &= ~FLAG8253X_INITIALIZED;
+		restore_flags(flags);
+		return;
+	}
+#endif
+	
+	/* Disable Interrupts */
+	
+	port->interrupt_mask0 = 0xff;
+	WRITEB(port, imr0, port->interrupt_mask0);
+	port->interrupt_mask1 = 0xff;
+	WRITEB(port, imr1, port->interrupt_mask1);
+	
+	if (!port->tty || (port->tty->termios->c_cflag & HUPCL)) 
+	{
+		LOWER(port,rts);
+		LOWER(port,dtr);
+	}
+	
+	/* Disable break condition */
+	CLEAR_REG_BIT(port,dafo,SAB82532_DAFO_XBRK);
+	
+	/* Disable Receiver */	
+	CLEAR_REG_BIT(port,mode,SAB82532_MODE_RAC);
+	
+	/* Power Down */	
+	CLEAR_REG_BIT(port,ccr0,SAB82532_CCR0_PU);
+	
+	if (port->tty)
+	{
+		set_bit(TTY_IO_ERROR, &port->tty->flags);
+	}
+	
+	port->flags &= ~FLAG8253X_INITIALIZED;
+	restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void sab8253x_change_speed(struct sab_port *port)
+{
+	unsigned long	flags,baud;
+	tcflag_t	cflag;
+	u8	        dafo,ccr2=0,ccr4=0,ebrg=0,mode;
+	int		i, bits;
+#ifdef DEBUGGING
+	printk("Change speed!  ");
+#endif
+	if (!port->tty || !port->tty->termios) 
+	{
+#ifdef DEBUGGING
+		printk("NOT!\n");
+#endif
+		return;
+	}
+	
+#ifdef DEBUGGING
+	printk(" for real.\n");
+#endif
+	
+	cflag = port->tty->termios->c_cflag;
+	
+	/* Byte size and parity */
+	switch (cflag & CSIZE) 
+	{
+	case CS5: 
+		dafo = SAB82532_DAFO_CHL5; 
+		bits = 7; 
+		break;
+	case CS6: 
+		dafo = SAB82532_DAFO_CHL6; 
+		bits = 8; 
+		break;
+	case CS7: 
+		dafo = SAB82532_DAFO_CHL7; 
+		bits = 9; 
+		break;
+	default:
+	case CS8: 
+		dafo = SAB82532_DAFO_CHL8; 
+		bits = 10; 
+		break;
+	}
+	
+	if (cflag & CSTOPB) 
+	{
+		dafo |= SAB82532_DAFO_STOP;
+		bits++;
+	}
+	
+	if (cflag & PARENB) 
+	{
+		dafo |= SAB82532_DAFO_PARE;
+		bits++;
+	}
+	
+	if (cflag & PARODD) 
+	{
+#ifdef CMSPAR
+		if (cflag & CMSPAR)
+			dafo |= SAB82532_DAFO_PAR_MARK;
+		else
+#endif
+			dafo |= SAB82532_DAFO_PAR_ODD;
+	} 
+	else 
+	{
+#ifdef CMSPAR
+		if (cflag & CMSPAR)
+			dafo |= SAB82532_DAFO_PAR_SPACE;
+		else
+#endif
+			dafo |= SAB82532_DAFO_PAR_EVEN;
+	}
+	
+	/* Determine EBRG values based on the "encoded"baud rate */
+	i = cflag & CBAUD;
+	switch(i)
+	{
+	case B0:
+		baud=0;
+		break;
+	case  B50:
+		baud=100;
+		break;
+	case  B75:
+		baud=150;
+		break;
+	case  B110:
+		baud=220;
+		break;
+	case  B134:
+		baud=269;
+		break;
+	case  B150:
+		baud=300;
+		break;
+	case  B200:
+		baud=400;
+		break;
+	case B300:
+		baud=600;
+		break;
+	case B600:
+		baud=1200;
+		break;
+	case B1200:
+		baud=2400;
+		break;
+	case B1800:
+		baud=3600;
+		break;
+	case B2400:
+		baud=4800;
+		break;
+	case B4800:
+		baud=9600;
+		break;
+	case B9600:
+		baud=19200;
+		break;
+	case B19200:
+		baud=38400;
+		break;
+	case  B38400:
+		if(port->custspeed)
+		{
+			baud=port->custspeed<<1;
+		}
+		else
+		{
+			baud=76800;
+		}
+		break;
+	case B57600:
+		baud=115200;
+		break;
+#ifdef SKIPTHIS
+	case B76800:
+		baud=153600;
+		break;
+	case B153600:
+		baud=307200;
+		break;
+#endif
+	case B230400:
+		baud=460800;
+		break;
+	case  B460800:
+		baud=921600;
+		break;
+	case B115200:
+	default:
+		baud=230400;
+		break;
+	}
+	
+	if(!sab8253x_baud(port,baud,&ebrg,&ccr2,&ccr4,&(port->baud))) 
+	{
+		printk("Aurora Warning. baudrate %ld could not be set! Using 115200",baud);
+		baud=230400;
+		sab8253x_baud(port,baud,&ebrg,&ccr2,&ccr4,&(port->baud));
+	}
+	
+	if (port->baud)
+		port->timeout = (port->xmit_fifo_size * HZ * bits) / port->baud;
+	else
+		port->timeout = 0;
+	port->timeout += HZ / 50;		/* Add .02 seconds of slop */
+	
+	/* CTS flow control flags */
+	if (cflag & CRTSCTS)
+		port->flags |= FLAG8253X_CTS_FLOW;
+	else
+		port->flags &= ~(FLAG8253X_CTS_FLOW);
+	
+	if (cflag & CLOCAL)
+		port->flags &= ~(FLAG8253X_CHECK_CD);
+	else
+		port->flags |= FLAG8253X_CHECK_CD;
+	if (port->tty)
+		port->tty->hw_stopped = 0;
+	
+	/*
+	 * Set up parity check flag
+	 * XXX: not implemented, yet.
+	 */
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+	
+	/*
+	 * Characters to ignore
+	 * XXX: not implemented, yet.
+	 */
+	
+	/*
+	 * !!! ignore all characters if CREAD is not set
+	 * XXX: not implemented, yet.
+	 */
+	if ((cflag & CREAD) == 0)
+		port->ignore_status_mask |= SAB82532_ISR0_RPF |
+			/* SAB82532_ISR0_TIME |*/
+			SAB82532_ISR0_TCD ;
+	
+	save_flags(flags); 
+	cli();
+	sab8253x_cec_wait(port);
+	sab8253x_tec_wait(port);
+	WRITEB(port,dafo,dafo);
+	WRITEB(port,bgr,ebrg);
+	ccr2 |= READB(port,ccr2) & ~(0xc0);
+	WRITEB(port,ccr2,ccr2);
+	ccr4 |= READB(port,ccr4) & ~(SAB82532_CCR4_EBRG);
+	WRITEB(port,ccr4,ccr4);
+	
+	if (port->flags & FLAG8253X_CTS_FLOW) 
+	{
+		mode = READB(port,mode) & ~(SAB82532_MODE_RTS);
+		mode |= SAB82532_MODE_FRTS;
+		mode  &= ~(SAB82532_MODE_FCTS);
+	} 
+	else 
+	{
+		mode = READB(port,mode) & ~(SAB82532_MODE_FRTS);
+		mode |= SAB82532_MODE_RTS;
+		mode |= SAB82532_MODE_FCTS;
+	}
+	WRITEB(port,mode,mode);
+	mode |= SAB82532_MODE_RAC;
+	WRITEB(port,mode,mode);
+	restore_flags(flags);
+}
+
+static void sab8253x_flush_chars(struct tty_struct *tty)
+{
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	
+	if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_flush_chars"))
+	{
+		return;
+	}
+	
+	if ((Sab8253xCountTransmit(port) <= 0) || tty->stopped || tty->hw_stopped)
+	{
+		return;
+	}
+	
+	sab8253x_start_tx(port);
+}
+
+static int sab8253x_write(struct tty_struct * tty, int from_user,
+		   const unsigned char *buf, int count)
+{
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	struct sk_buff *skb = NULL;
+	int truelength = 0;
+	int do_queue = 1;
+	
+	if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_write"))
+	{
+		return 0;
+	}
+	
+	if(count == 0)
+	{
+		return 0;
+	}
+	
+	if(port->active2.transmit == NULL)
+	{
+		return 0;
+	}
+	
+	if((port->active2.transmit->Count & OWNER) == OWN_SAB)
+	{
+		sab8253x_start_tx(port);
+		return 0;
+	}
+	
+#ifndef FREEININTERRUPT
+	skb = port->active2.transmit->HostVaddr; /* current slot value */
+	
+	if(port->buffergreedy == 0)	/* are we avoiding buffer free's */
+	{				/* no */
+		if((skb != NULL) || /* not OWN_SAB from above */
+		   (port->active2.transmit->crcindex != 0)) 
+		{
+			register RING_DESCRIPTOR *freeme;
+			
+			freeme = port->active2.transmit;
+			do
+			{
+				if((freeme->crcindex == 0) && (freeme->HostVaddr == NULL))
+				{
+					break;
+				}
+				if(freeme->HostVaddr)
+				{
+					skb_unlink((struct sk_buff*)freeme->HostVaddr);
+					dev_kfree_skb_any((struct sk_buff*)freeme->HostVaddr);
+					freeme->HostVaddr = NULL;
+				}
+				freeme->sendcrc = 0;
+				freeme->crcindex = 0;
+				freeme = (RING_DESCRIPTOR*) freeme->VNext;
+			}
+			while((freeme->Count & OWNER) != OWN_SAB);
+		}
+		skb = NULL;		/* buffer was freed */
+	}
+	
+	if(skb != NULL)		/* potentially useful */
+	{
+		truelength = (skb->end - skb->head);
+		if(truelength >= count)
+		{
+			skb->data = skb->head; /* this buffer is already queued */
+			skb->tail = skb->head;
+			do_queue = 0;
+		}
+		else
+		{
+			skb_unlink(skb);
+			dev_kfree_skb_any(skb);
+			skb = NULL;
+			port->active2.transmit->HostVaddr = NULL;
+		}
+	}
+	/* in all cases the following is allowed */
+	port->active2.transmit->sendcrc = 0;
+	port->active2.transmit->crcindex = 0;
+#endif
+	
+	if(skb == NULL)
+	{
+		if(port->DoingInterrupt)
+		{
+			skb = alloc_skb(count, GFP_ATOMIC);
+		}
+		else
+		{
+			skb = alloc_skb(count, GFP_KERNEL);
+		}
+	}
+	
+	if(skb == NULL)
+	{
+		printk(KERN_ALERT "sab8253xt: no skbuffs available.\n");
+		return 0;
+	}
+	if(from_user)
+	{
+		copy_from_user(skb->data, buf, count);
+	}
+	else
+	{
+		memcpy(skb->data, buf, count);
+	}
+	skb->tail = (skb->data + count);
+	skb->data_len = count;
+	skb->len = count;
+	
+	if(do_queue)
+	{
+		skb_queue_head(port->sab8253xbuflist, skb);
+	}
+	
+	port->active2.transmit->HostVaddr = skb;
+	port->active2.transmit->sendcrc = 0;
+	port->active2.transmit->crcindex = 0;
+	port->active2.transmit->Count = (OWN_SAB|count);
+	port->active2.transmit = port->active2.transmit->VNext;
+	
+	sab8253x_start_tx(port);
+	return count;
+}
+
+static int sab8253x_write_room(struct tty_struct *tty)
+{
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	
+	if(sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_write_room"))
+	{
+		return 0;
+	}
+	
+	if(port->active2.transmit == NULL)
+	{
+		return 0;
+	}
+	
+	if((port->active2.transmit->Count & OWNER) == OWN_SAB)
+	{
+		return 0;
+	}
+	return ((sab8253xt_rbufsize) * /* really should not send buffs bigger than 32 I guess */
+		(sab8253xt_listsize - 
+		 Sab8253xCountTransmitDescriptors(port)));
+}
+
+static int sab8253x_chars_in_buffer(struct tty_struct *tty)
+{
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	
+	if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_chars_in_bufferS"))
+	{
+		return 0;
+	}
+	
+	return Sab8253xCountTransmit(port);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void sab8253x_send_xchar(struct tty_struct *tty, char ch)
+{
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	unsigned long flags;
+	
+	if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_send_xchar"))
+	{
+		return;
+	}
+	
+	save_flags(flags); 
+	cli();
+	sab8253x_tec_wait(port);
+	WRITEB(port, tic, ch);
+	restore_flags(flags);
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab8253x_throttle()
+ * 
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void sab8253x_throttle(struct tty_struct * tty)
+{
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	
+	if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_throttle"))
+	{
+		return;
+	}
+	
+	if (I_IXOFF(tty))
+	{
+		sab8253x_send_xchar(tty, STOP_CHAR(tty));
+	}
+}
+
+static void sab8253x_unthrottle(struct tty_struct * tty)
+{
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	
+	if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_unthrottle"))
+	{
+		return;
+	}
+	
+	if (I_IXOFF(tty)) 
+	{
+		if (port->x_char)
+		{
+			port->x_char = 0;
+		}
+		else
+		{
+			sab8253x_send_xchar(tty, START_CHAR(tty));
+		}
+	}
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab8253x_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int sab8253x_get_serial_info(struct sab_port *port,
+			     struct serial_struct *retinfo)
+{
+	struct serial_struct tmp;
+	
+	if (!retinfo)
+	{
+		return -EFAULT;
+	}
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = port->type;
+	tmp.line = port->line;
+	tmp.port = (unsigned long)port->regs;
+	tmp.irq = port->irq;
+	tmp.flags = port->flags;
+	tmp.xmit_fifo_size = port->xmit_fifo_size;
+	tmp.baud_base = 0;
+	tmp.close_delay = port->close_delay;
+	tmp.closing_wait = port->closing_wait;
+	tmp.custom_divisor = port->custom_divisor;
+	tmp.hub6 = 0;
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+	{
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static int sab8253x_set_serial_info(struct sab_port *port,
+			     struct serial_struct *new_info)
+{
+	return 0;
+}
+
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space. 
+ */
+static int sab8253x_get_lsr_info(struct sab_port * port, unsigned int *value)
+{
+	unsigned int result;
+	
+	result = (((Sab8253xCountTransmit(port) <= 0) && port->all_sent) ? TIOCSER_TEMT : 0);
+	return put_user(result, value);
+}
+
+
+static int sab8253x_get_modem_info(struct sab_port * port, unsigned int *value)
+{
+	unsigned int result;
+	
+	/* Using the cached values !! After all when changed int occurs
+	   and the cache is updated */
+	result=  
+		((port->dtr.val) ? TIOCM_DTR : 0)
+		| ((port->rts.val) ? TIOCM_RTS : 0)
+		| ((port->cts.val) ? TIOCM_CTS : 0)
+		| ((port->dsr.val) ? TIOCM_DSR : 0)
+		| ((port->dcd.val) ? TIOCM_CAR : 0);
+	
+	return put_user(result,value);
+}
+
+static int sab8253x_set_modem_info(struct sab_port * port, unsigned int cmd,
+				   unsigned int *value)
+{
+	int error;
+	unsigned int arg;
+	unsigned long flags;
+	
+	error = get_user(arg, value);
+	if (error)
+	{
+		return error;
+	}
+	
+	save_flags(flags);
+	cli();
+	switch (cmd) 
+	{
+	case TIOCMBIS: 
+		if (arg & TIOCM_RTS) 
+		{
+			RAISE(port, rts);
+		}
+		if (arg & TIOCM_DTR) 
+		{
+			RAISE(port, dtr);
+		}
+		break;
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS) 
+		{
+			LOWER(port,rts);
+		}
+		if (arg & TIOCM_DTR) 
+		{
+			LOWER(port,dtr);
+		}
+		break;
+	case TIOCMSET:
+		if (arg & TIOCM_RTS) 
+		{
+			RAISE(port, rts);
+		} 
+		else 
+		{
+			LOWER(port,rts);
+		}
+		if (arg & TIOCM_DTR) 
+		{
+			RAISE(port, dtr);
+		} 
+		else 
+		{
+			LOWER(port,dtr);
+		}
+		break;
+	default:
+		restore_flags(flags);
+		return -EINVAL;
+	}
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void sab8253x_break(struct tty_struct *tty, int break_state)
+{
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	unsigned long flags;
+	
+	if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_break"))
+	{
+		return;
+	}
+	
+	if (!port->regs)
+	{
+		return;
+	}
+	
+	save_flags(flags); 
+	cli();
+	if (break_state == -1) 
+	{
+		SET_REG_BIT(port,dafo,SAB82532_DAFO_XBRK);
+	} 
+	else 
+	{
+		CLEAR_REG_BIT(port,dafo,SAB82532_DAFO_XBRK);
+	}
+	restore_flags(flags);
+}
+
+static int sab8253x_ioctl(struct tty_struct *tty, struct file * file,
+			  unsigned int cmd, unsigned long arg)
+{
+	int error;
+	unsigned int wordindex;
+	unsigned short *wordptr;
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	struct async_icount cprev, cnow;	/* kernel counter temps */
+	struct serial_icounter_struct *p_cuser;	/* user space */
+	SAB_BOARD *bptr;
+	unsigned long flags;
+	
+	if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_ioctl"))
+	{
+		return -ENODEV;
+	}
+	
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
+	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
+	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) 
+	{
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		{
+			return -EIO;
+		}
+	}
+	
+	switch (cmd) 
+	{
+	case ATIS_IOCSPARAMS:
+		copy_from_user(&port->ccontrol, (struct channelcontrol*)arg , sizeof(struct channelcontrol));
+		break;
+	case ATIS_IOCGPARAMS:
+		copy_to_user((struct channelcontrol*) arg, &port->ccontrol, sizeof(struct channelcontrol));
+		break;
+		
+	case ATIS_IOCSSPEED:
+		copy_from_user(&port->custspeed, (unsigned long*)arg , sizeof(unsigned long));
+		break;
+	case ATIS_IOCGSPEED:
+		copy_to_user((unsigned long*) arg, &port->custspeed, sizeof(unsigned long));
+		break;
+		
+	case ATIS_IOCSSEP9050:
+		bptr = port->board;
+		if(bptr->b_type == BD_WANMCS)
+		{
+			return -EINVAL;
+		}
+		copy_from_user((unsigned char*) bptr->b_eprom, (unsigned char*) arg , sizeof(struct sep9050));
+		
+		wordptr = (unsigned short*) bptr->b_eprom;
+		plx9050_eprom_cmd(&((PLX9050*)(bptr->virtbaseaddress0))->ctrl, 
+				  NM93_WENCMD, NM93_WENADDR, 0);
+		for(wordindex = 0; wordindex < EPROM9050_SIZE; ++wordindex)
+		{
+			plx9050_eprom_cmd(&((PLX9050*)(bptr->virtbaseaddress0))->ctrl, 
+					  NM93_WRITECMD, 
+					  wordindex, wordptr[wordindex]);
+		}
+		plx9050_eprom_cmd(&((PLX9050*)(bptr->virtbaseaddress0))->ctrl, 
+				  NM93_WDSCMD, NM93_WDSADDR, 0);
+		break;
+	case ATIS_IOCGSEP9050:
+		bptr = port->board;
+		if(bptr->b_type == BD_WANMCS)
+		{
+			return -EINVAL;
+		}
+		if (!plx9050_eprom_read(&((PLX9050*)(bptr->virtbaseaddress0))->ctrl, 
+					(unsigned short*) bptr->b_eprom,
+					(unsigned char) 0, EPROM9050_SIZE))
+		{
+			printk(KERN_ALERT "auraXX20n: Could not read serial eprom.\n");
+			return -EIO;
+		}
+		copy_to_user((unsigned char*) arg, (unsigned char*) bptr->b_eprom, sizeof(struct sep9050));
+		break;
+		
+	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 sab8253x_get_modem_info(port, (unsigned int *) arg);
+	case TIOCMBIS:
+	case TIOCMBIC:
+	case TIOCMSET:
+		return sab8253x_set_modem_info(port, cmd, (unsigned int *) arg);
+	case TIOCGSERIAL:
+		return sab8253x_get_serial_info(port,
+						(struct serial_struct *) arg);
+	case TIOCSSERIAL:
+		return sab8253x_set_serial_info(port,
+						(struct serial_struct *) arg);
+		
+	case TIOCSERGETLSR: /* Get line status register */
+		return sab8253x_get_lsr_info(port, (unsigned int *) arg);
+		
+	case TIOCSERGSTRUCT:
+		if (copy_to_user((struct sab_port *) arg,
+				 port, sizeof(struct sab_port)))
+			return -EFAULT;
+		return 0;
+		
+		/*
+		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+		 * - mask passed in arg for lines of interest
+		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+		 * Caller should use TIOCGICOUNT to see which one it was
+		 */
+	case TIOCMIWAIT:
+		save_flags(flags);
+		cli();
+		/* note the counters on entry */
+		cprev = port->icount;
+		restore_flags(flags);
+		while (1) 
+		{
+			interruptible_sleep_on(&port->delta_msr_wait); /* waits for a modem signal change */
+			/* see if a signal did it */
+			if (signal_pending(current))
+			{
+				return -ERESTARTSYS;
+			}
+			save_flags(flags);
+			cli();
+			cnow = port->icount; /* atomic copy */
+			restore_flags(flags);
+			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
+			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+			{
+				return -EIO; /* no change => error */
+			}
+			if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+			     ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+			     ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+			     ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+				{
+					return 0;
+				}
+			}
+			cprev = cnow;
+		}
+		/* NOTREACHED */
+		break;
+		
+		/* 
+		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+		 * Return: write counters to the user passed counter struct
+		 * NB: both 1->0 and 0->1 transitions are counted except for
+		 *     RI where only 0->1 is counted.
+		 */
+	case TIOCGICOUNT:
+		save_flags(flags);
+		cli();
+		cnow = port->icount;
+		restore_flags(flags);
+		p_cuser = (struct serial_icounter_struct *) arg;
+		error = put_user(cnow.cts, &p_cuser->cts);
+		if (error) 
+		{
+			return error;
+		}
+		error = put_user(cnow.dsr, &p_cuser->dsr);
+		if (error) 
+		{
+			return error;
+		}
+		error = put_user(cnow.rng, &p_cuser->rng);
+		if (error) 
+		{
+			return error;
+		}
+		error = put_user(cnow.dcd, &p_cuser->dcd);
+		if (error) 
+		{
+			return error;
+		}
+		return 0;
+		
+	case ATIS_IOCSSIGMODE:
+		if(port->chip->c_cim)
+		{
+			if(port->chip->c_cim->ci_type == CIM_SP502)
+			{
+				copy_from_user(&port->sigmode, (unsigned int*)arg , sizeof(unsigned int));
+				return 0;
+			}
+		}
+		return -EINVAL;
+
+	case ATIS_IOCGSIGMODE:
+		if(port->chip->c_cim)
+		{
+			if(port->chip->c_cim->ci_type == CIM_SP502)
+			{
+				copy_to_user((unsigned int*) arg, &port->sigmode, sizeof(unsigned int));
+				return 0;
+			}
+		}
+		return -EINVAL;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static void sab8253x_set_termios(struct tty_struct *tty,
+				 struct termios *old_termios)
+{
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	
+	if((tty->termios->c_cflag == old_termios->c_cflag) && 
+	   (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag)))
+	{
+		return;
+	}
+	if(!port)
+	{
+		return;
+	}
+	sab8253x_change_speed(port);
+	
+	/* Handle transition to B0 status */
+	if ((old_termios->c_cflag & CBAUD) &&
+	    !(tty->termios->c_cflag & CBAUD)) 
+	{
+		LOWER(port,rts);
+		LOWER(port,dtr);
+	}
+	
+	/* Handle transition away from B0 status */
+	if (!(old_termios->c_cflag & CBAUD) &&
+	    (tty->termios->c_cflag & CBAUD)) 
+	{
+		RAISE(port, dtr);
+		if (!tty->hw_stopped ||
+		    !(tty->termios->c_cflag & CRTSCTS)) 
+		{
+			RAISE(port, rts);
+		}
+	}
+	
+	/* Handle turning off CRTSCTS */
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) 
+	{
+		tty->hw_stopped = 0;
+		sab8253x_start(tty);
+	}
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab8253x_close()
+ * 
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void sab8253x_close(struct tty_struct *tty, struct file * filp)
+{
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	unsigned long flags;
+	
+	MOD_DEC_USE_COUNT;	
+	if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_close"))
+	{
+		return;
+	}
+	if(port->open_type == OPEN_SYNC_NET)
+	{				/* port->tty field should already be NULL */
+		/* port count was not incremented */
+		return;
+	}
+	
+	--(port->count);		/* have a valid port */
+	if (tty_hung_up_p(filp)) 
+	{
+		
+		if(port->count == 0)	/* shutdown took place in hangup context */
+		{
+			port->open_type = OPEN_NOT;
+		}
+		else if(port->count < 0)
+		{
+			printk(KERN_ALERT "XX20: port->count went negative.\n");
+			port->count = 0;
+			port->open_type = OPEN_NOT;
+		}
+		return;
+	}
+	
+	if (port->count < 0) 
+	{
+		printk(KERN_ALERT "sab8253x_close: bad serial port count for ttys%d: %d\n",
+		       port->line, port->count);
+		port->count = 0;
+	}
+	
+	if (port->count) 
+	{
+		return;
+	}
+	
+	port->flags |= FLAG8253X_CLOSING;
+	
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (port->flags & FLAG8253X_NORMAL_ACTIVE)
+	{
+		port->normal_termios = *tty->termios;
+	}
+	if (port->flags & FLAG8253X_CALLOUT_ACTIVE)
+	{
+		port->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.
+	 */
+	tty->closing = 1;
+	if (port->closing_wait != SAB8253X_CLOSING_WAIT_NONE) 
+	{
+		tty_wait_until_sent(tty, port->closing_wait); /* wait for drain */
+	}
+	
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and turn off
+	 * the receiver.
+	 */
+	
+	save_flags(flags); 
+	cli();
+	port->interrupt_mask0 |= SAB82532_IMR0_TCD;
+	WRITEB(port,imr0,port->interrupt_mask0);
+	
+	CLEAR_REG_BIT(port,mode,SAB82532_MODE_RAC); /* ??????? */
+	restore_flags(flags);
+	
+	if (port->flags & FLAG8253X_INITIALIZED) 
+	{
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		sab8253x_wait_until_sent(tty, port->timeout);
+	}
+	sab8253x_shutdown(port);	/* no more ints on port */
+	Sab8253xCleanUpTransceiveN(port); /* should be okay */
+	if (tty->driver.flush_buffer)
+	{
+		tty->driver.flush_buffer(tty);
+	}
+	if (tty->ldisc.flush_buffer)
+	{
+		tty->ldisc.flush_buffer(tty);
+	}
+	tty->closing = 0;
+	port->event = 0;
+	port->tty = 0;
+	if (port->blocked_open) 
+	{
+		if (port->close_delay) 
+		{
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(port->close_delay);
+		}
+		wake_up_interruptible(&port->open_wait); /* deal with open blocks */
+	}
+	
+	if((port->flags & (FLAG8253X_CALLOUT_ACTIVE | FLAG8253X_NETWORK)) ==
+	   (FLAG8253X_CALLOUT_ACTIVE | FLAG8253X_NETWORK) &&
+	   port->dev)
+	{
+		port->flags &= ~(FLAG8253X_NORMAL_ACTIVE|FLAG8253X_CALLOUT_ACTIVE|
+				 FLAG8253X_CLOSING); /* leave network set */
+		netif_carrier_off(port->dev);
+		port->open_type = OPEN_SYNC_NET;
+		sab8253x_startupN(port);
+	}
+	else
+	{
+		port->flags &= ~(FLAG8253X_NORMAL_ACTIVE|FLAG8253X_CALLOUT_ACTIVE|
+				 FLAG8253X_CLOSING);
+		wake_up_interruptible(&port->close_wait);
+		port->open_type = OPEN_NOT;
+	}
+}
+
+/*
+ * sab8253x_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void sab8253x_hangup(struct tty_struct *tty)
+{
+	struct sab_port *port = (struct sab_port *)tty->driver_data;
+	
+	if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_hangup"))
+	{
+		return;
+	}
+	
+#ifdef XCONFIG_SERIAL_CONSOLE
+	if (port->is_console)
+	{
+		return;
+	}
+#endif
+	
+	sab8253x_flush_buffer(tty);
+	if(port)
+	{
+		sab8253x_shutdown(port);
+		Sab8253xCleanUpTransceiveN(port); /* this logic is a bit contorted
+						     Are we cleaning up the lists
+						     because we are waking up a
+						     blocked open?  There is possibly
+						     an order problem here perhaps the
+						     open count should have increased in the
+						     int handler so that it could decrease here*/
+		port->event = 0;
+		port->flags &= ~(FLAG8253X_NORMAL_ACTIVE|FLAG8253X_CALLOUT_ACTIVE);
+		port->tty = 0;
+		wake_up_interruptible(&port->open_wait); /* deal with blocking open */
+	}
+}
+/*
+ * ------------------------------------------------------------
+ * sab8253x_open() and friends
+ * ------------------------------------------------------------
+ */
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+
+static int sab8253x_open(struct tty_struct *tty, struct file * filp)
+{
+	struct sab_port	*port;
+	int retval, line;
+	int counter;
+	unsigned long flags;
+	
+	MOD_INC_USE_COUNT;
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	
+	for(counter = 0, port = AuraPortRoot; 
+	    (counter < line) && (port != NULL); 
+	    ++counter)
+	{
+		port = port->next;
+	}
+	
+	if (!port) 
+	{
+		printk(KERN_ALERT "sab8253x_open: can't find structure for line %d\n",
+		       line);
+		return -ENODEV;
+	}
+	
+	save_flags(flags);		/* Need to protect the port->tty field */
+	cli();
+	
+	if(port->tty == NULL)
+	{
+		port->tty = tty;		/* may be a standard tty waiting on a call out device */
+		tty->flip.tqueue.routine = sab8253x_flush_to_ldisc;
+	}
+	
+	tty->driver_data = port;	/* but the tty devices are unique for each type of open */
+	
+	if(port->function == FUNCTION_NA)
+	{				/* port 2 on 1020s and 1520s */
+		++(port->count);
+		restore_flags(flags);
+		return -ENODEV;
+	}
+	
+	/* Check whether or not the port is open in SYNC mode */
+	if(port->open_type == OPEN_SYNC_NET)
+	{
+		if(port->dev && netif_carrier_ok(port->dev));
+		{
+			port->tty= NULL;	/* Don't bother with open counting here
+						   but make sure the tty field is NULL*/
+			restore_flags(flags);
+			return -EBUSY;
+		}
+		sab8253x_flush_buffer(tty); /* don't restore flags here */
+		sab8253x_shutdownN(port);
+	}
+	else if (port->open_type > OPEN_ASYNC) /* can't have a callout or async line
+						* if already open in some sync mode */
+	{
+		++(port->count);
+		restore_flags(flags);
+		return -EBUSY;
+	}
+	restore_flags(flags);
+	
+	if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_open"))
+	{
+		++(port->count);
+		return -ENODEV;
+	}
+	
+#ifdef DEBUG_OPEN
+	printk("sab8253x_open %s%d, count = %d\n", tty->driver.name, port->line,
+	       port->count);
+#endif
+	
+	/*
+	 * If the port is in the middle of closing, bail out now.
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (port->flags & FLAG8253X_CLOSING)) 
+	{
+		
+		if (port->flags & FLAG8253X_CLOSING)
+		{
+			interruptible_sleep_on(&port->close_wait);
+		}
+#ifdef SERIAL_DO_RESTART
+		++(port->count);
+		return ((port->flags & FLAG8253X_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+#else
+		++(port->count);
+		return -EAGAIN;
+#endif
+	}
+	
+	if(Sab8253xSetUpLists(port))
+	{
+		++(port->count);
+		return -ENODEV;
+	}
+	if(Sab8253xInitDescriptors2(port, sab8253xt_listsize, sab8253xt_rbufsize))
+	{
+		++(port->count);
+		return -ENODEV;
+	}
+	
+	retval = sab8253x_startup(port);
+	if (retval)
+	{
+		++(port->count);
+		return retval;
+	}
+	
+	retval = sab8253x_block_til_ready(tty, filp, port);
+	++(port->count);  
+	if (retval) 
+	{
+		return retval;
+	}
+	
+	port->tty = tty;		/* may change here once through the block */
+	/* because now the port belongs to an new tty */
+	tty->flip.tqueue.routine = sab8253x_flush_to_ldisc; /* in case it was changed */
+	
+	if(Sab8253xSetUpLists(port))
+	{
+		return -ENODEV;
+	}
+	if(Sab8253xInitDescriptors2(port, sab8253xt_listsize, sab8253xt_rbufsize))
+	{
+		Sab8253xCleanUpTransceiveN(port);	/* the network functions should be okay -- only difference */
+		/* is the crc32 that is appended */
+		return -ENODEV;
+	}
+	
+	/*
+	 * Start up serial port
+	 */
+	retval = sab8253x_startup(port); /* just in case closing the cu dev
+					  * shutdown the port (but left CD) */
+	if (retval)
+	{
+		return retval;
+	}
+	
+	if ((port->count == 1) &&	/* first open */
+	    (port->flags & FLAG8253X_SPLIT_TERMIOS)) 
+	{
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+		{
+			*tty->termios = port->normal_termios;
+		}
+		else 
+		{
+			*tty->termios = port->callout_termios;
+		}
+		sab8253x_change_speed(port);
+	}
+	
+	
+#ifdef XCONFIG_SERIAL_CONSOLE
+	if (sab8253x_console.cflag && sab8253x_console.index == line) 
+	{
+		tty->termios->c_cflag = sab8253x_console.cflag;
+		sab8253x_console.cflag = 0;
+		sab8253x_change_speed(port);
+	}
+#endif
+	
+	port->session = current->session;
+	port->pgrp = current->pgrp;
+	port->open_type = OPEN_ASYNC;
+	return 0;
+}
+
+static char *signaling[] =
+{
+	"OFF  ",
+	"RS232",
+	"RS422",
+	"RS485",
+	"RS449",
+	"RS530",
+	"V.35 "
+};
+
+static int sab8253x_read_proc(char *page, char **start, off_t off, int count,
+			      int *eof, void *data)
+{
+	extern struct sab_port * AuraPortRoot;
+	struct sab_port *port = AuraPortRoot;
+	extern char *board_type[];
+	off_t begin = 0;
+	int len = 0;
+	int portno;
+	unsigned int typeno;
+	extern int sab8253x_rebootflag;
+	
+#ifdef FREEININTERRUPT
+	len += sprintf(page, "serinfo:2.01I driver:%s\n", sab8253x_serial_version);
+#else
+	len += sprintf(page, "serinfo:2.01N driver:%s\n", sab8253x_serial_version);
+#endif
+	if(sab8253x_rebootflag)
+	{
+		len += sprintf(page+len, 
+			       "WARNING:  Found %d cards that required reprogramming.  Reboot machine!.\n", 
+			       sab8253x_rebootflag);
+	}
+	len += sprintf(page+len, "TTY MAJOR = %d, CUA MAJOR = %d, STTY MAJOR = %d.\n", 
+		       sab8253x_serial_driver.major, sab8253x_callout_driver.major, 
+		       sync_sab8253x_serial_driver.major);
+	for (portno = 0; port != NULL; port = port->next, ++portno) 
+	{
+		typeno = port->board->b_type;
+		if(typeno > BD_8520P)
+		{
+			typeno = 0;
+		}
+		len += sprintf(page+len, 
+			       "%d: port %d: %s: v%d: chip %d: ATI %s: bus %d: slot %d: %s: ", 
+			       sab8253x_serial_driver.minor_start + portno,
+			       port->portno,
+			       (port->chip->chip_type == ESCC2) ? "sab82532" : "sab82538",
+			       port->type,
+			       port->chip->c_chipno,
+			       board_type[port->board->b_type],
+			       port->board->b_dev.bus->number,
+			       PCI_SLOT(port->board->b_dev.devfn),
+			       aura_functionality[((port->function > FUNCTION_UN) ? FUNCTION_UN : port->function)]);
+		switch(port->open_type)
+		{
+		case OPEN_ASYNC:
+			len += sprintf(page+len, "openA");
+			break;
+		case OPEN_SYNC:
+			len += sprintf(page+len, "openS");
+			break;
+		case OPEN_SYNC_NET:
+			len += sprintf(page+len, "openN");
+			break;	  
+		case OPEN_SYNC_CHAR:
+			len += sprintf(page+len, "openC");
+			break;
+		case OPEN_NOT:
+			len += sprintf(page+len, "close");
+			break;
+		default:
+			len += sprintf(page+len, "open?");
+			break;
+		}
+		if(port->chip->c_cim)
+		{
+			if(port->chip->c_cim->ci_type == CIM_SP502)
+			{
+				len += sprintf(page+len, ": %s\n", signaling[port->sigmode]);
+			}
+			else
+			{
+				len += sprintf(page+len, ": NOPRG\n");
+			}
+		}
+		else
+		{
+			len += sprintf(page+len, ": NOPRG\n");
+		}
+
+		if (len+begin > off+count)
+		{
+			goto done;
+		}
+		if (len+begin < off) 
+		{
+			begin += len;
+			len = 0;
+		}
+	}
+	*eof = 1;
+ done:
+	if (off >= len+begin)
+	{
+		return 0;
+	}
+	*start = page + (off-begin);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * sab8253x_init() and friends
+ *
+ * sab8253x_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+static void inline show_aurora_version(void)
+{
+	char *revision = "$Revision: 1.23 $";
+	char *version, *p;
+	
+	version = strchr(revision, ' ');
+	strcpy(sab8253x_serial_version, ++version);
+	p = strchr(sab8253x_serial_version, ' ');
+	*p = '\0';
+	printk("Aurora serial driver version %s\n", sab8253x_serial_version);
+}
+
+#ifndef MODULE
+static int GetMinorStart(void)
+{
+	struct tty_driver *ttydriver;
+	int minor_start = 0;
+	kdev_t device;
+	
+	device = MKDEV(TTY_MAJOR, minor_start);
+	while(ttydriver = get_tty_driver(device), ttydriver != NULL)
+	{
+		minor_start += ttydriver->num;
+		device = MKDEV(TTY_MAJOR, minor_start);
+	}
+	return minor_start;
+	
+}
+#endif
+
+int finish_sab8253x_setup_ttydriver(void) 
+{
+	extern unsigned int NumSab8253xPorts;
+	
+	sab8253x_tableASY = (struct tty_struct **) kmalloc(NumSab8253xPorts*sizeof(struct tty_struct *), GFP_KERNEL);
+	if(sab8253x_tableASY == NULL)
+	{
+		printk(KERN_ALERT "auraXX20:  Could not allocate memory for sab8253x_tableASY.\n");
+		return -1;
+	}
+	memset(sab8253x_tableASY, 0, NumSab8253xPorts*sizeof(struct tty_struct *));
+	sab8253x_tableCUA = (struct tty_struct **) kmalloc(NumSab8253xPorts*sizeof(struct tty_struct *), GFP_KERNEL);
+	if(sab8253x_tableCUA == NULL)
+	{
+		printk(KERN_ALERT "auraXX20:  Could not allocate memory for sab8253x_tableCUA.\n");
+		return -1;
+	}
+	memset(sab8253x_tableCUA, 0, NumSab8253xPorts*sizeof(struct tty_struct *));
+	sab8253x_tableSYN = (struct tty_struct **) kmalloc(NumSab8253xPorts*sizeof(struct tty_struct *), GFP_KERNEL);
+	if(sab8253x_tableSYN == NULL)
+	{
+		printk(KERN_ALERT "auraXX20:  Could not allocate memory for sab8253x_tableSYN.\n");
+		return -1;
+	}
+	memset(sab8253x_tableSYN, 0, NumSab8253xPorts*sizeof(struct tty_struct *));
+	
+	sab8253x_termios = (struct termios **) kmalloc(NumSab8253xPorts*sizeof(struct termios *), GFP_KERNEL);
+	if(sab8253x_termios == NULL)
+	{
+		printk(KERN_ALERT "auraXX20:  Could not allocate memory for sab8253x_termios.\n");
+		return -1;
+	}
+	memset(sab8253x_termios, 0, NumSab8253xPorts*sizeof(struct termios *));
+	sab8253x_termios_locked = (struct termios **) kmalloc(NumSab8253xPorts*sizeof(struct termios *), GFP_KERNEL);
+	if(sab8253x_termios_locked == NULL)
+	{
+		printk(KERN_ALERT "auraXX20:  Could not allocate memory for sab8253x_termios_locked.\n");
+		return -1;
+	}
+	memset(sab8253x_termios_locked, 0, NumSab8253xPorts*sizeof(struct termios *));
+	sync_sab8253x_serial_driver.num = sab8253x_callout_driver.num = sab8253x_serial_driver.num = NumSab8253xPorts;
+	sab8253x_serial_driver.table = sab8253x_tableASY;
+	sab8253x_callout_driver.table = sab8253x_tableCUA;
+	sync_sab8253x_serial_driver.table = sab8253x_tableSYN;
+	sync_sab8253x_serial_driver.termios = sab8253x_callout_driver.termios = sab8253x_serial_driver.termios = 
+		sab8253x_termios;
+	sync_sab8253x_serial_driver.termios_locked = sab8253x_callout_driver.termios_locked = 
+		sab8253x_serial_driver.termios_locked = sab8253x_termios_locked;
+	
+	if (tty_register_driver(&sab8253x_serial_driver) < 0)
+	{
+		printk(KERN_ALERT "auraXX20:  Could not register serial driver.\n");
+		return -1;
+	}
+	if (tty_register_driver(&sab8253x_callout_driver) < 0)
+	{
+		printk(KERN_ALERT "auraXX20:  Could not register call out device.\n");
+		return -1;
+	}
+	if (tty_register_driver(&sync_sab8253x_serial_driver) < 0)
+	{
+		printk(KERN_ALERT "auraXX20:  Could not register sync serial device.\n");
+		return -1;
+	}
+	return 0;
+	
+}
+
+void sab8253x_setup_ttydriver(void) 
+{
+#ifdef MODULE
+	extern int xx20_minorstart;
+#endif
+	init_bh(AURORA_BH, sab8253x_do_serial_bh);
+	
+	show_aurora_version();
+	
+	/* Initialize the tty_driver structure */
+	
+	memset(&sab8253x_serial_driver, 0, sizeof(struct tty_driver));
+	sab8253x_serial_driver.magic = TTY_DRIVER_MAGIC;
+	sab8253x_serial_driver.driver_name = "auraserial";
+	sab8253x_serial_driver.name = "ttyS";
+	sab8253x_serial_driver.major = TTY_MAJOR;
+#ifdef MODULE
+	sab8253x_serial_driver.minor_start = xx20_minorstart;
+#else
+	sab8253x_serial_driver.minor_start = GetMinorStart();
+#endif
+	sab8253x_serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	sab8253x_serial_driver.subtype = SERIAL_TYPE_NORMAL;
+	sab8253x_serial_driver.init_termios = tty_std_termios;
+	sab8253x_serial_driver.init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	sab8253x_serial_driver.flags = TTY_DRIVER_REAL_RAW;
+	sab8253x_serial_driver.refcount = &sab8253x_refcount;
+	
+	sab8253x_serial_driver.open = sab8253x_open;
+	sab8253x_serial_driver.close = sab8253x_close;
+	sab8253x_serial_driver.write = sab8253x_write;
+	sab8253x_serial_driver.put_char = NULL; /*sab8253x_put_char is evil.*/
+	sab8253x_serial_driver.flush_chars = sab8253x_flush_chars;
+	sab8253x_serial_driver.write_room = sab8253x_write_room;
+	sab8253x_serial_driver.chars_in_buffer = sab8253x_chars_in_buffer;
+	sab8253x_serial_driver.flush_buffer = sab8253x_flush_buffer;
+	sab8253x_serial_driver.ioctl = sab8253x_ioctl;
+	sab8253x_serial_driver.throttle = sab8253x_throttle;
+	sab8253x_serial_driver.unthrottle = sab8253x_unthrottle;
+	sab8253x_serial_driver.send_xchar = sab8253x_send_xchar;
+	sab8253x_serial_driver.set_termios = sab8253x_set_termios;
+	sab8253x_serial_driver.stop = sab8253x_stop;
+	sab8253x_serial_driver.start = sab8253x_start;
+	sab8253x_serial_driver.hangup = sab8253x_hangup;
+	sab8253x_serial_driver.break_ctl = sab8253x_break;
+	sab8253x_serial_driver.wait_until_sent = sab8253x_wait_until_sent;
+	sab8253x_serial_driver.read_proc = sab8253x_read_proc;
+	
+	/*
+	 * The callout device is just like normal device except for
+	 * major number and the subtype code.
+	 */
+	sab8253x_callout_driver = sab8253x_serial_driver;
+	sab8253x_callout_driver.name = "cua";
+	sab8253x_callout_driver.major = TTYAUX_MAJOR;
+	sab8253x_callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+	sab8253x_callout_driver.read_proc = 0;
+	sab8253x_callout_driver.proc_entry = 0;
+	
+	sync_sab8253x_serial_driver = sab8253x_serial_driver;
+	sync_sab8253x_serial_driver.name = "sttyS";
+	sync_sab8253x_serial_driver.major = 0;
+	sync_sab8253x_serial_driver.subtype = SERIAL_TYPE_SYNCTTY;
+	
+	sync_sab8253x_serial_driver.open = sab8253x_openS;
+	sync_sab8253x_serial_driver.close = sab8253x_closeS;
+	sync_sab8253x_serial_driver.write = sab8253x_writeS;
+	sync_sab8253x_serial_driver.put_char = NULL; /*sab8253x_put_char logic is evil*/
+	sync_sab8253x_serial_driver.flush_chars = sab8253x_flush_charsS;
+	sync_sab8253x_serial_driver.write_room = sab8253x_write_room;
+	sync_sab8253x_serial_driver.chars_in_buffer = sab8253x_chars_in_buffer;
+	sync_sab8253x_serial_driver.flush_buffer = sab8253x_flush_buffer;
+	sync_sab8253x_serial_driver.ioctl = sab8253x_ioctl;
+	sync_sab8253x_serial_driver.throttle = sab8253x_throttleS;
+	sync_sab8253x_serial_driver.unthrottle = sab8253x_unthrottleS;
+	sync_sab8253x_serial_driver.send_xchar = sab8253x_send_xcharS;
+	sync_sab8253x_serial_driver.set_termios = sab8253x_set_termiosS;
+	sync_sab8253x_serial_driver.stop = sab8253x_stopS;
+	sync_sab8253x_serial_driver.start = sab8253x_startS;
+	sync_sab8253x_serial_driver.hangup = sab8253x_hangupS;
+	sync_sab8253x_serial_driver.break_ctl = sab8253x_breakS;
+	sync_sab8253x_serial_driver.wait_until_sent = sab8253x_wait_until_sent;
+	sync_sab8253x_serial_driver.read_proc = 0;
+	sync_sab8253x_serial_driver.proc_entry = 0;
+}
+
+void sab8253x_setup_ttyport(struct sab_port *p_port) 
+{
+	p_port->magic = SAB_MAGIC;
+	p_port->custom_divisor = 16;
+	p_port->close_delay = 5*HZ/10;
+	p_port->closing_wait = 30*HZ;
+	p_port->tec_timeout = SAB8253X_MAX_TEC_DELAY;
+	p_port->cec_timeout = SAB8253X_MAX_CEC_DELAY;
+	p_port->x_char = 0;
+	p_port->event = 0;	
+	p_port->flags= FLAG8253X_BOOT_AUTOCONF | FLAG8253X_SKIP_TEST;
+	p_port->blocked_open = 0;
+	
+	p_port->all_sent = 1;	/* probably not needed */
+	
+	p_port->tqueue.sync = 0;	/* for later */
+	p_port->tqueue.routine = sab8253x_do_softint;
+	p_port->tqueue.data = p_port;
+	p_port->tqueue_hangup.sync = 0; /* for later */
+	p_port->tqueue_hangup.routine = sab8253x_do_serial_hangup;
+	p_port->tqueue_hangup.data = p_port;
+	p_port->callout_termios = sab8253x_callout_driver.init_termios;
+	p_port->normal_termios = sab8253x_serial_driver.init_termios; /* these are being shared */
+	/* between asynchronous and */
+	/* asynchronous ttys */
+	init_waitqueue_head(&p_port->open_wait);
+	init_waitqueue_head(&p_port->close_wait);
+	init_waitqueue_head(&p_port->delta_msr_wait);
+	init_waitqueue_head(&p_port->write_wait);
+	init_waitqueue_head(&p_port->read_wait);
+	
+	p_port->count = 0;		/* maybe not needed */
+	p_port->icount.cts = p_port->icount.dsr = 
+		p_port->icount.rng = p_port->icount.dcd = 0;
+	p_port->cts.val = p_port->dsr.val = 
+		p_port->dcd.val = 0;
+	p_port->icount.rx = p_port->icount.tx = 0;
+	p_port->icount.frame = p_port->icount.parity = 0;
+	p_port->icount.overrun = p_port->icount.brk = 0;
+	
+	p_port->xmit_fifo_size = 32;
+	p_port->recv_fifo_size = 32;
+	p_port->xmit_buf = NULL;
+	p_port->receive_chars = sab8253x_receive_chars;
+	p_port->transmit_chars = sab8253x_transmit_chars;
+	p_port->check_status = sab8253x_check_status;
+	p_port->receive_test = (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+				SAB82532_ISR0_RFO | SAB82532_ISR0_RPF);
+	p_port->transmit_test = (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR);
+	p_port->check_status_test = SAB82532_ISR1_BRK;
+	
+	/* put in default sync control channel values
+	 * not needed for async -- I think these work
+	 * for bisync*/
+	p_port->ccontrol.ccr0 = DEFAULT_CCR0;
+	p_port->ccontrol.ccr1 = DEFAULT_CCR1;
+	p_port->ccontrol.ccr2 = DEFAULT_CCR2;
+	p_port->ccontrol.ccr3 = DEFAULT_CCR3;
+	p_port->ccontrol.ccr4 = DEFAULT_CCR4;
+	p_port->ccontrol.mode = DEFAULT_MODE;
+	p_port->ccontrol.rlcr = DEFAULT_RLCR;
+}
+
+void sab8253x_cleanup_ttydriver(void)
+{
+	unsigned long flags;
+	int e1, e2;
+	
+	save_flags(flags);
+	cli();
+	
+	if(sab8253x_tableASY) 
+		kfree(sab8253x_tableASY);
+	if(sab8253x_tableCUA) 
+		kfree(sab8253x_tableCUA);
+	if(sab8253x_tableSYN) 
+		kfree(sab8253x_tableSYN);
+	if(sab8253x_termios) 
+		kfree(sab8253x_termios);
+	if(sab8253x_termios_locked) 
+		kfree(sab8253x_termios_locked);
+	
+	remove_bh(AURORA_BH);
+	if ((e1 = tty_unregister_driver(&sab8253x_serial_driver)))
+	{
+		printk("SERIAL: failed to unregister serial driver (%d)\n",
+		       e1);
+	}
+	if ((e2 = tty_unregister_driver(&sab8253x_callout_driver)))
+	{
+		printk("SERIAL: failed to unregister callout driver (%d)\n", 
+		       e2);
+	}
+	if ((e2 = tty_unregister_driver(&sync_sab8253x_serial_driver)))
+	{
+		printk("SERIAL: failed to unregister callout driver (%d)\n", 
+		       e2);
+	}
+	restore_flags(flags);  
+}
+
+/* THE CODE BELOW HAS NOT YET BEEN MODIFIED!!!! FW */
+#ifdef XCONFIG_SERIAL_CONSOLE
+
+static inline void
+sab8253x_console_putchar(struct sab8253x *info, char c)
+{
+	unsigned long flags;
+	
+	save_flags(flags); cli();
+	sab8253x_tec_wait(info);
+	WRITEB(port,tic,c);
+	restore_flags(flags);
+}
+
+static void
+sab8253x_console_write(struct console *con, const char *s, unsigned n)
+{
+	struct sab8253x *info;
+	int i;
+	
+	info = sab8253x_chain;
+	for (i = con->index; i; i--) {
+		info = info->next;
+		if (!info)
+			return;
+	}
+	
+	for (i = 0; i < n; i++) {
+		if (*s == '\n')
+			sab8253x_console_putchar(info, '\r');
+		sab8253x_console_putchar(info, *s++);
+	}
+	sab8253x_tec_wait(info);
+}
+
+static int
+sab8253x_console_wait_key(struct console *con)
+{
+	sleep_on(&keypress_wait);
+	return 0;
+}
+
+static kdev_t
+sab8253x_console_device(struct console *con)
+{
+	return MKDEV(TTY_MAJOR, 64 + con->index);
+}
+
+static int
+sab8253x_console_setup(struct console *con, char *options)
+{
+	struct sab8253x *info;
+	unsigned int	ebrg;
+	tcflag_t	cflag;
+	unsigned char	dafo;
+	int		i, bits;
+	unsigned long	flags;
+	
+	info = sab8253x_chain;
+	for (i = con->index; i; i--) 
+	{
+		info = info->next;
+		if (!info)
+			return -ENODEV;
+	}
+	info->is_console = 1;
+	
+	/*
+	 * Initialize the hardware
+	 */
+	sab8253x_init_line(info);
+	
+	/*
+	 * Finally, enable interrupts
+	 */
+	info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+		/*SAB82532_IMR0_TIME*/ | SAB82532_IMR0_PLLA/*| SAB82532_IMR0_CDSC*/;
+	WRITEB(port,imr0,info->interrupt_mask0);
+	info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
+		SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
+		SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
+		SAB82532_IMR1_XPR;
+	WRITEB(port,imr1,info->interrupt_mask1);
+	
+	printk("Console: ttyS%d (SAB82532)\n", info->line);
+	
+	sunserial_console_termios(con);
+	cflag = con->cflag;
+	
+	/* Byte size and parity */
+	switch (cflag & CSIZE) 
+	{
+	case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break;
+	case CS6: dafo = SAB82532_DAFO_CHL6; bits = 8; break;
+	case CS7: dafo = SAB82532_DAFO_CHL7; bits = 9; break;
+	case CS8: dafo = SAB82532_DAFO_CHL8; bits = 10; break;
+		/* Never happens, but GCC is too dumb to figure it out */
+	default:  dafo = SAB82532_DAFO_CHL5; bits = 7; break;
+	}
+	
+	if (cflag & CSTOPB) 
+	{
+		dafo |= SAB82532_DAFO_STOP;
+		bits++;
+	}
+	
+	if (cflag & PARENB) 
+	{
+		dafo |= SAB82532_DAFO_PARE;
+		bits++;
+	}
+	
+	if (cflag & PARODD) 
+	{
+#ifdef CMSPAR
+		if (cflag & CMSPAR)
+			dafo |= SAB82532_DAFO_PAR_MARK;
+		else
+#endif
+			dafo |= SAB82532_DAFO_PAR_ODD;
+	} 
+	else 
+	{
+#ifdef CMSPAR
+		if (cflag & CMSPAR)
+			dafo |= SAB82532_DAFO_PAR_SPACE;
+		else
+#endif
+			dafo |= SAB82532_DAFO_PAR_EVEN;
+	}
+	
+	/* Determine EBRG values based on baud rate */
+	i = cflag & CBAUD;
+	if (i & CBAUDEX) 
+	{
+		i &= ~(CBAUDEX);
+		if ((i < 1) || ((i + 15) >= NR_EBRG_VALUES))
+			cflag &= ~CBAUDEX;
+		else
+			i += 15;
+	}
+	ebrg = ebrg_tabl[i].n;
+	ebrg |= (ebrg_table[i].m << 6);
+	
+	info->baud = ebrg_table[i].baud;
+	if (info->baud)
+		info->timeout = (info->xmit_fifo_size * HZ * bits) / info->baud;
+	else
+		info->timeout = 0;
+	info->timeout += HZ / 50;		/* Add .02 seconds of slop */
+	
+	/* CTS flow control flags */
+	if (cflag & CRTSCTS)
+		info->flags |= FLAG8253X_CTS_FLOW;
+	else
+		info->flags &= ~(FLAG8253X_CTS_FLOW);
+	
+	if (cflag & CLOCAL)
+		info->flags &= ~(FLAG8253X_CHECK_CD);
+	else
+		info->flags |= FLAG8253X_CHECK_CD;
+	
+	save_flags(flags); cli();
+	sab8253x_cec_wait(info);
+	sab8253x_tec_wait(info);
+	WRITEB(port,dafo,dafo);
+	WRITEB(port,bgr,ebrg & 0xff);
+	info->regs->rw.ccr2 &= ~(0xc0);
+	info->regs->rw.ccr2 |= (ebrg >> 2) & 0xc0;
+	if (info->flags & FLAG8253X_CTS_FLOW) 
+	{
+		info->regs->rw.mode &= ~(SAB82532_MODE_RTS);
+		info->regs->rw.mode |= SAB82532_MODE_FRTS;
+		info->regs->rw.mode &= ~(SAB82532_MODE_FCTS);
+	} 
+	else 
+	{
+		info->regs->rw.mode |= SAB82532_MODE_RTS;
+		info->regs->rw.mode &= ~(SAB82532_MODE_FRTS);
+		info->regs->rw.mode |= SAB82532_MODE_FCTS;
+	}
+	info->regs->rw.pvr &= ~(info->pvr_dtr_bit);
+	info->regs->rw.mode |= SAB82532_MODE_RAC;
+	restore_flags(flags);
+	
+	return 0;
+}
+
+static struct console sab8253x_console = 
+{
+	"ttyS",
+	sab8253x_console_write,
+	NULL,
+	sab8253x_console_device,
+	sab8253x_console_wait_key,
+	NULL,
+	sab8253x_console_setup,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+
+int sab8253x_console_init(void)
+{
+	extern int con_is_present(void);
+	extern int su_console_registered;
+	
+	if (con_is_present() || su_console_registered)
+		return 0;
+	
+	if (!sab8253x_chain) 
+	{
+		prom_printf("sab8253x_console_setup: can't get SAB8253X chain");
+		prom_halt();
+	}
+	
+	sab8253x_console.index = serial_console - 1;
+	register_console(&sab8253x_console);
+	return 0;
+}
+
+#ifdef SERIAL_LOG_DEVICE
+
+static int serial_log_device = 0;
+
+static void
+dprint_init(int tty)
+{
+	serial_console = tty + 1;
+	sab8253x_console.index = tty;
+	sab8253x_console_setup(&sab8253x_console, "");
+	serial_console = 0;
+	serial_log_device = tty + 1;
+}
+
+int
+dprintf(const char *fmt, ...)
+{
+	static char buffer[4096];
+	va_list args;
+	int i;
+	
+	if (!serial_log_device)
+		return 0;
+	
+	va_start(args, fmt);
+	i = vsprintf(buffer, fmt, args);
+	va_end(args);
+	sab8253x_console.write(&sab8253x_console, buffer, i);
+	return i;
+}
+
+#endif /* SERIAL_LOG_DEVICE */
+#endif /* XCONFIG_SERIAL_CONSOLE */

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