tty: move a number of tty drivers from drivers/char/ to drivers/tty/
authorGreg Kroah-Hartman <gregkh@suse.de>
Wed, 23 Feb 2011 00:14:56 +0000 (16:14 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 23 Feb 2011 00:14:56 +0000 (16:14 -0800)
As planned by Arnd Bergmann, this moves the following drivers from
drivers/char/ to drivers/tty/ as that's where they really belong:
amiserial
nozomi
synclink
rocket
cyclades
moxa
mxser
isicom
bfin_jtag_comm

Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Jiri Slaby <jslaby@suse.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
34 files changed:
drivers/char/Kconfig
drivers/char/Makefile
drivers/char/amiserial.c [deleted file]
drivers/char/bfin_jtag_comm.c [deleted file]
drivers/char/cyclades.c [deleted file]
drivers/char/isicom.c [deleted file]
drivers/char/moxa.c [deleted file]
drivers/char/moxa.h [deleted file]
drivers/char/mxser.c [deleted file]
drivers/char/mxser.h [deleted file]
drivers/char/nozomi.c [deleted file]
drivers/char/rocket.c [deleted file]
drivers/char/rocket.h [deleted file]
drivers/char/rocket_int.h [deleted file]
drivers/char/synclink.c [deleted file]
drivers/char/synclink_gt.c [deleted file]
drivers/char/synclinkmp.c [deleted file]
drivers/tty/Kconfig
drivers/tty/Makefile
drivers/tty/amiserial.c [new file with mode: 0644]
drivers/tty/bfin_jtag_comm.c [new file with mode: 0644]
drivers/tty/cyclades.c [new file with mode: 0644]
drivers/tty/isicom.c [new file with mode: 0644]
drivers/tty/moxa.c [new file with mode: 0644]
drivers/tty/moxa.h [new file with mode: 0644]
drivers/tty/mxser.c [new file with mode: 0644]
drivers/tty/mxser.h [new file with mode: 0644]
drivers/tty/nozomi.c [new file with mode: 0644]
drivers/tty/rocket.c [new file with mode: 0644]
drivers/tty/rocket.h [new file with mode: 0644]
drivers/tty/rocket_int.h [new file with mode: 0644]
drivers/tty/synclink.c [new file with mode: 0644]
drivers/tty/synclink_gt.c [new file with mode: 0644]
drivers/tty/synclinkmp.c [new file with mode: 0644]

index 9b9ab867f50ee47a3251b86a852931df8d1d53d8..1adfac6a7b0b9b445164052dabe61a365c3cb110 100644 (file)
@@ -15,36 +15,6 @@ config DEVKMEM
          kind of kernel debugging operations.
          When in doubt, say "N".
 
-config BFIN_JTAG_COMM
-       tristate "Blackfin JTAG Communication"
-       depends on BLACKFIN
-       help
-         Add support for emulating a TTY device over the Blackfin JTAG.
-
-         To compile this driver as a module, choose M here: the
-         module will be called bfin_jtag_comm.
-
-config BFIN_JTAG_COMM_CONSOLE
-       bool "Console on Blackfin JTAG"
-       depends on BFIN_JTAG_COMM=y
-
-config SERIAL_NONSTANDARD
-       bool "Non-standard serial port support"
-       depends on HAS_IOMEM
-       ---help---
-         Say Y here if you have any non-standard serial boards -- boards
-         which aren't supported using the standard "dumb" serial driver.
-         This includes intelligent serial boards such as Cyclades,
-         Digiboards, etc. These are usually used for systems that need many
-         serial ports because they serve many terminals or dial-in
-         connections.
-
-         Note that the answer to this question won't directly affect the
-         kernel: saying N will just cause the configurator to skip all
-         the questions about non-standard serial boards.
-
-         Most people can say N here.
-
 config COMPUTONE
        tristate "Computone IntelliPort Plus serial support"
        depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
@@ -60,50 +30,6 @@ config COMPUTONE
          To compile this driver as module, choose M here: the
          module will be called ip2.
 
-config ROCKETPORT
-       tristate "Comtrol RocketPort support"
-       depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
-       help
-         This driver supports Comtrol RocketPort and RocketModem PCI boards.   
-          These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or
-          modems.  For information about the RocketPort/RocketModem  boards
-          and this driver read <file:Documentation/serial/rocket.txt>.
-
-         To compile this driver as a module, choose M here: the
-         module will be called rocket.
-
-         If you want to compile this driver into the kernel, say Y here.  If
-          you don't have a Comtrol RocketPort/RocketModem card installed, say N.
-
-config CYCLADES
-       tristate "Cyclades async mux support"
-       depends on SERIAL_NONSTANDARD && (PCI || ISA)
-       select FW_LOADER
-       ---help---
-         This driver supports Cyclades Z and Y multiserial boards.
-         You would need something like this to connect more than two modems to
-         your Linux box, for instance in order to become a dial-in server.
-
-         For information about the Cyclades-Z card, read
-         <file:Documentation/serial/README.cycladesZ>.
-
-         To compile this driver as a module, choose M here: the
-         module will be called cyclades.
-
-         If you haven't heard about it, it's safe to say N.
-
-config CYZ_INTR
-       bool "Cyclades-Z interrupt mode operation (EXPERIMENTAL)"
-       depends on EXPERIMENTAL && CYCLADES
-       help
-         The Cyclades-Z family of multiport cards allows 2 (two) driver op
-         modes: polling and interrupt. In polling mode, the driver will check
-         the status of the Cyclades-Z ports every certain amount of time
-         (which is called polling cycle and is configurable). In interrupt
-         mode, it will use an interrupt line (IRQ) in order to check the
-         status of the Cyclades-Z ports. The default op mode is polling. If
-         unsure, say N.
-
 config DIGIEPCA
        tristate "Digiboard Intelligent Async Support"
        depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
@@ -119,94 +45,6 @@ config DIGIEPCA
          To compile this driver as a module, choose M here: the
          module will be called epca.
 
-config MOXA_INTELLIO
-       tristate "Moxa Intellio support"
-       depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
-       select FW_LOADER
-       help
-         Say Y here if you have a Moxa Intellio multiport serial card.
-
-         To compile this driver as a module, choose M here: the
-         module will be called moxa.
-
-config MOXA_SMARTIO
-       tristate "Moxa SmartIO support v. 2.0"
-       depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA)
-       help
-         Say Y here if you have a Moxa SmartIO multiport serial card and/or
-         want to help develop a new version of this driver.
-
-         This is upgraded (1.9.1) driver from original Moxa drivers with
-         changes finally resulting in PCI probing.
-
-         This driver can also be built as a module. The module will be called
-         mxser. If you want to do that, say M here.
-
-config ISI
-       tristate "Multi-Tech multiport card support (EXPERIMENTAL)"
-       depends on SERIAL_NONSTANDARD && PCI
-       select FW_LOADER
-       help
-         This is a driver for the Multi-Tech cards which provide several
-         serial ports.  The driver is experimental and can currently only be
-         built as a module. The module will be called isicom.
-         If you want to do that, choose M here.
-
-config SYNCLINK
-       tristate "Microgate SyncLink card support"
-       depends on SERIAL_NONSTANDARD && PCI && ISA_DMA_API
-       help
-         Provides support for the SyncLink ISA and PCI multiprotocol serial
-         adapters. These adapters support asynchronous and HDLC bit
-         synchronous communication up to 10Mbps (PCI adapter).
-
-         This driver can only be built as a module ( = code which can be
-         inserted in and removed from the running kernel whenever you want).
-         The module will be called synclink.  If you want to do that, say M
-         here.
-
-config SYNCLINKMP
-       tristate "SyncLink Multiport support"
-       depends on SERIAL_NONSTANDARD && PCI
-       help
-         Enable support for the SyncLink Multiport (2 or 4 ports)
-         serial adapter, running asynchronous and HDLC communications up
-         to 2.048Mbps. Each ports is independently selectable for
-         RS-232, V.35, RS-449, RS-530, and X.21
-
-         This driver may be built as a module ( = code which can be
-         inserted in and removed from the running kernel whenever you want).
-         The module will be called synclinkmp.  If you want to do that, say M
-         here.
-
-config SYNCLINK_GT
-       tristate "SyncLink GT/AC support"
-       depends on SERIAL_NONSTANDARD && PCI
-       help
-         Support for SyncLink GT and SyncLink AC families of
-         synchronous and asynchronous serial adapters
-         manufactured by Microgate Systems, Ltd. (www.microgate.com)
-
-config N_HDLC
-       tristate "HDLC line discipline support"
-       depends on SERIAL_NONSTANDARD
-       help
-         Allows synchronous HDLC communications with tty device drivers that
-         support synchronous HDLC such as the Microgate SyncLink adapter.
-
-         This driver can be built as a module ( = code which can be
-         inserted in and removed from the running kernel whenever you want).
-         The module will be called n_hdlc. If you want to do that, say M
-         here.
-
-config N_GSM
-       tristate "GSM MUX line discipline support (EXPERIMENTAL)"
-       depends on EXPERIMENTAL
-       depends on NET
-       help
-         This line discipline provides support for the GSM MUX protocol and
-         presents the mux as a set of 61 individual tty devices.
-
 config RISCOM8
        tristate "SDL RISCom/8 card support"
        depends on SERIAL_NONSTANDARD
@@ -296,16 +134,6 @@ config ISTALLION
          To compile this driver as a module, choose M here: the
          module will be called istallion.
 
-config NOZOMI
-       tristate "HSDPA Broadband Wireless Data Card - Globe Trotter"
-       depends on PCI && EXPERIMENTAL
-       help
-         If you have a HSDPA driver Broadband Wireless Data Card -
-         Globe Trotter PCMCIA card, say Y here.
-
-         To compile this driver as a module, choose M here, the module
-         will be called nozomi.
-
 config A2232
        tristate "Commodore A2232 serial support (EXPERIMENTAL)"
        depends on EXPERIMENTAL && ZORRO && BROKEN
index 5bc765d4c3cae96a604c45162e8c4bd18f039900..f5dc7c9bce6ba00ebb2b7263e22e7ad9609b4e08 100644 (file)
@@ -5,29 +5,18 @@
 obj-y                          += mem.o random.o
 obj-$(CONFIG_TTY_PRINTK)       += ttyprintk.o
 obj-y                          += misc.o
-obj-$(CONFIG_BFIN_JTAG_COMM)   += bfin_jtag_comm.o
 obj-$(CONFIG_MVME147_SCC)      += generic_serial.o vme_scc.o
 obj-$(CONFIG_MVME162_SCC)      += generic_serial.o vme_scc.o
 obj-$(CONFIG_BVME6000_SCC)     += generic_serial.o vme_scc.o
-obj-$(CONFIG_ROCKETPORT)       += rocket.o
 obj-$(CONFIG_SERIAL167)                += serial167.o
-obj-$(CONFIG_CYCLADES)         += cyclades.o
 obj-$(CONFIG_STALLION)         += stallion.o
 obj-$(CONFIG_ISTALLION)                += istallion.o
-obj-$(CONFIG_NOZOMI)           += nozomi.o
 obj-$(CONFIG_DIGIEPCA)         += epca.o
 obj-$(CONFIG_SPECIALIX)                += specialix.o
-obj-$(CONFIG_MOXA_INTELLIO)    += moxa.o
 obj-$(CONFIG_A2232)            += ser_a2232.o generic_serial.o
 obj-$(CONFIG_ATARI_DSP56K)     += dsp56k.o
-obj-$(CONFIG_MOXA_SMARTIO)     += mxser.o
 obj-$(CONFIG_COMPUTONE)                += ip2/
 obj-$(CONFIG_RISCOM8)          += riscom8.o
-obj-$(CONFIG_ISI)              += isicom.o
-obj-$(CONFIG_SYNCLINK)         += synclink.o
-obj-$(CONFIG_SYNCLINKMP)       += synclinkmp.o
-obj-$(CONFIG_SYNCLINK_GT)      += synclink_gt.o
-obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o
 obj-$(CONFIG_SX)               += sx.o generic_serial.o
 obj-$(CONFIG_RIO)              += rio/ generic_serial.o
 obj-$(CONFIG_RAW_DRIVER)       += raw.o
diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c
deleted file mode 100644 (file)
index f214e50..0000000
+++ /dev/null
@@ -1,2178 +0,0 @@
-/*
- *  linux/drivers/char/amiserial.c
- *
- * Serial driver for the amiga builtin port.
- *
- * This code was created by taking serial.c version 4.30 from kernel
- * release 2.3.22, replacing all hardware related stuff with the
- * corresponding amiga hardware actions, and removing all irrelevant
- * code. As a consequence, it uses many of the constants and names
- * associated with the registers and bits of 16550 compatible UARTS -
- * but only to keep track of status, etc in the state variables. It
- * was done this was to make it easier to keep the code in line with
- * (non hardware specific) changes to serial.c.
- *
- * The port is registered with the tty driver as minor device 64, and
- * therefore other ports should should only use 65 upwards.
- *
- * Richard Lucock 28/12/99
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- *  Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 
- *             1998, 1999  Theodore Ts'o
- *
- */
-
-/*
- * Serial driver configuration section.  Here are the various options:
- *
- * SERIAL_PARANOIA_CHECK
- *             Check the magic number for the async_structure where
- *             ever possible.
- */
-
-#include <linux/delay.h>
-
-#undef SERIAL_PARANOIA_CHECK
-#define SERIAL_DO_RESTART
-
-/* Set of debugging defines */
-
-#undef SERIAL_DEBUG_INTR
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
-#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
-
-/* Sanity checks */
-
-#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
-#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
- tty->name, (info->flags), serial_driver->refcount,info->count,tty->count,s)
-#else
-#define DBG_CNT(s)
-#endif
-
-/*
- * End of serial driver configuration section.
- */
-
-#include <linux/module.h>
-
-#include <linux/types.h>
-#include <linux/serial.h>
-#include <linux/serialP.h>
-#include <linux/serial_reg.h>
-static char *serial_version = "4.30";
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/console.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/bitops.h>
-#include <linux/platform_device.h>
-
-#include <asm/setup.h>
-
-#include <asm/system.h>
-
-#include <asm/irq.h>
-
-#include <asm/amigahw.h>
-#include <asm/amigaints.h>
-
-#define custom amiga_custom
-static char *serial_name = "Amiga-builtin serial driver";
-
-static struct tty_driver *serial_driver;
-
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
-static struct async_struct *IRQ_ports;
-
-static unsigned char current_ctl_bits;
-
-static void change_speed(struct async_struct *info, struct ktermios *old);
-static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
-
-
-static struct serial_state rs_table[1];
-
-#define NR_PORTS ARRAY_SIZE(rs_table)
-
-#include <asm/uaccess.h>
-
-#define serial_isroot()        (capable(CAP_SYS_ADMIN))
-
-
-static inline int serial_paranoia_check(struct async_struct *info,
-                                       char *name, const char *routine)
-{
-#ifdef SERIAL_PARANOIA_CHECK
-       static const char *badmagic =
-               "Warning: bad magic number for serial struct (%s) in %s\n";
-       static const char *badinfo =
-               "Warning: null async_struct for (%s) in %s\n";
-
-       if (!info) {
-               printk(badinfo, name, routine);
-               return 1;
-       }
-       if (info->magic != SERIAL_MAGIC) {
-               printk(badmagic, name, routine);
-               return 1;
-       }
-#endif
-       return 0;
-}
-
-/* some serial hardware definitions */
-#define SDR_OVRUN   (1<<15)
-#define SDR_RBF     (1<<14)
-#define SDR_TBE     (1<<13)
-#define SDR_TSRE    (1<<12)
-
-#define SERPER_PARENB    (1<<15)
-
-#define AC_SETCLR   (1<<15)
-#define AC_UARTBRK  (1<<11)
-
-#define SER_DTR     (1<<7)
-#define SER_RTS     (1<<6)
-#define SER_DCD     (1<<5)
-#define SER_CTS     (1<<4)
-#define SER_DSR     (1<<3)
-
-static __inline__ void rtsdtr_ctrl(int bits)
-{
-    ciab.pra = ((bits & (SER_RTS | SER_DTR)) ^ (SER_RTS | SER_DTR)) | (ciab.pra & ~(SER_RTS | SER_DTR));
-}
-
-/*
- * ------------------------------------------------------------
- * rs_stop() and rs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter interrupts, as necessary.
- * ------------------------------------------------------------
- */
-static void rs_stop(struct tty_struct *tty)
-{
-       struct async_struct *info = tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->name, "rs_stop"))
-               return;
-
-       local_irq_save(flags);
-       if (info->IER & UART_IER_THRI) {
-               info->IER &= ~UART_IER_THRI;
-               /* disable Tx interrupt and remove any pending interrupts */
-               custom.intena = IF_TBE;
-               mb();
-               custom.intreq = IF_TBE;
-               mb();
-       }
-       local_irq_restore(flags);
-}
-
-static void rs_start(struct tty_struct *tty)
-{
-       struct async_struct *info = tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->name, "rs_start"))
-               return;
-
-       local_irq_save(flags);
-       if (info->xmit.head != info->xmit.tail
-           && info->xmit.buf
-           && !(info->IER & UART_IER_THRI)) {
-               info->IER |= UART_IER_THRI;
-               custom.intena = IF_SETCLR | IF_TBE;
-               mb();
-               /* set a pending Tx Interrupt, transmitter should restart now */
-               custom.intreq = IF_SETCLR | IF_TBE;
-               mb();
-       }
-       local_irq_restore(flags);
-}
-
-/*
- * ----------------------------------------------------------------------
- *
- * Here starts the interrupt handling routines.  All of the following
- * subroutines are declared as inline and are folded into
- * rs_interrupt().  They were separated out for readability's sake.
- *
- * Note: rs_interrupt() is a "fast" interrupt, which means that it
- * runs with interrupts turned off.  People who may want to modify
- * rs_interrupt() should try to keep the interrupt handler as fast as
- * possible.  After you are done making modifications, it is not a bad
- * idea to do:
- * 
- * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
- *
- * and look at the resulting assemble code in serial.s.
- *
- *                             - Ted Ts'o (tytso@mit.edu), 7-Mar-93
- * -----------------------------------------------------------------------
- */
-
-/*
- * This routine is used by the interrupt handler to schedule
- * processing in the software interrupt portion of the driver.
- */
-static void rs_sched_event(struct async_struct *info,
-                          int event)
-{
-       info->event |= 1 << event;
-       tasklet_schedule(&info->tlet);
-}
-
-static void receive_chars(struct async_struct *info)
-{
-        int status;
-       int serdatr;
-       struct tty_struct *tty = info->tty;
-       unsigned char ch, flag;
-       struct  async_icount *icount;
-       int oe = 0;
-
-       icount = &info->state->icount;
-
-       status = UART_LSR_DR; /* We obviously have a character! */
-       serdatr = custom.serdatr;
-       mb();
-       custom.intreq = IF_RBF;
-       mb();
-
-       if((serdatr & 0x1ff) == 0)
-           status |= UART_LSR_BI;
-       if(serdatr & SDR_OVRUN)
-           status |= UART_LSR_OE;
-
-       ch = serdatr & 0xff;
-       icount->rx++;
-
-#ifdef SERIAL_DEBUG_INTR
-       printk("DR%02x:%02x...", ch, status);
-#endif
-       flag = TTY_NORMAL;
-
-       /*
-        * We don't handle parity or frame errors - but I have left
-        * the code in, since I'm not sure that the errors can't be
-        * detected.
-        */
-
-       if (status & (UART_LSR_BI | UART_LSR_PE |
-                     UART_LSR_FE | UART_LSR_OE)) {
-         /*
-          * For statistics only
-          */
-         if (status & UART_LSR_BI) {
-           status &= ~(UART_LSR_FE | UART_LSR_PE);
-           icount->brk++;
-         } else if (status & UART_LSR_PE)
-           icount->parity++;
-         else if (status & UART_LSR_FE)
-           icount->frame++;
-         if (status & UART_LSR_OE)
-           icount->overrun++;
-
-         /*
-          * Now check to see if character should be
-          * ignored, and mask off conditions which
-          * should be ignored.
-          */
-         if (status & info->ignore_status_mask)
-           goto out;
-
-         status &= info->read_status_mask;
-
-         if (status & (UART_LSR_BI)) {
-#ifdef SERIAL_DEBUG_INTR
-           printk("handling break....");
-#endif
-           flag = TTY_BREAK;
-           if (info->flags & ASYNC_SAK)
-             do_SAK(tty);
-         } else if (status & UART_LSR_PE)
-           flag = TTY_PARITY;
-         else if (status & UART_LSR_FE)
-           flag = TTY_FRAME;
-         if (status & UART_LSR_OE) {
-           /*
-            * Overrun is special, since it's
-            * reported immediately, and doesn't
-            * affect the current character
-            */
-            oe = 1;
-         }
-       }
-       tty_insert_flip_char(tty, ch, flag);
-       if (oe == 1)
-               tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-       tty_flip_buffer_push(tty);
-out:
-       return;
-}
-
-static void transmit_chars(struct async_struct *info)
-{
-       custom.intreq = IF_TBE;
-       mb();
-       if (info->x_char) {
-               custom.serdat = info->x_char | 0x100;
-               mb();
-               info->state->icount.tx++;
-               info->x_char = 0;
-               return;
-       }
-       if (info->xmit.head == info->xmit.tail
-           || info->tty->stopped
-           || info->tty->hw_stopped) {
-               info->IER &= ~UART_IER_THRI;
-               custom.intena = IF_TBE;
-               mb();
-               return;
-       }
-
-       custom.serdat = info->xmit.buf[info->xmit.tail++] | 0x100;
-       mb();
-       info->xmit.tail = info->xmit.tail & (SERIAL_XMIT_SIZE-1);
-       info->state->icount.tx++;
-
-       if (CIRC_CNT(info->xmit.head,
-                    info->xmit.tail,
-                    SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
-               rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
-
-#ifdef SERIAL_DEBUG_INTR
-       printk("THRE...");
-#endif
-       if (info->xmit.head == info->xmit.tail) {
-               custom.intena = IF_TBE;
-               mb();
-               info->IER &= ~UART_IER_THRI;
-       }
-}
-
-static void check_modem_status(struct async_struct *info)
-{
-       unsigned char status = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
-       unsigned char dstatus;
-       struct  async_icount *icount;
-
-       /* Determine bits that have changed */
-       dstatus = status ^ current_ctl_bits;
-       current_ctl_bits = status;
-
-       if (dstatus) {
-               icount = &info->state->icount;
-               /* update input line counters */
-               if (dstatus & SER_DSR)
-                       icount->dsr++;
-               if (dstatus & SER_DCD) {
-                       icount->dcd++;
-#ifdef CONFIG_HARD_PPS
-                       if ((info->flags & ASYNC_HARDPPS_CD) &&
-                           !(status & SER_DCD))
-                               hardpps();
-#endif
-               }
-               if (dstatus & SER_CTS)
-                       icount->cts++;
-               wake_up_interruptible(&info->delta_msr_wait);
-       }
-
-       if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) {
-#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
-               printk("ttyS%d CD now %s...", info->line,
-                      (!(status & SER_DCD)) ? "on" : "off");
-#endif
-               if (!(status & SER_DCD))
-                       wake_up_interruptible(&info->open_wait);
-               else {
-#ifdef SERIAL_DEBUG_OPEN
-                       printk("doing serial hangup...");
-#endif
-                       if (info->tty)
-                               tty_hangup(info->tty);
-               }
-       }
-       if (info->flags & ASYNC_CTS_FLOW) {
-               if (info->tty->hw_stopped) {
-                       if (!(status & SER_CTS)) {
-#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
-                               printk("CTS tx start...");
-#endif
-                               info->tty->hw_stopped = 0;
-                               info->IER |= UART_IER_THRI;
-                               custom.intena = IF_SETCLR | IF_TBE;
-                               mb();
-                               /* set a pending Tx Interrupt, transmitter should restart now */
-                               custom.intreq = IF_SETCLR | IF_TBE;
-                               mb();
-                               rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
-                               return;
-                       }
-               } else {
-                       if ((status & SER_CTS)) {
-#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
-                               printk("CTS tx stop...");
-#endif
-                               info->tty->hw_stopped = 1;
-                               info->IER &= ~UART_IER_THRI;
-                               /* disable Tx interrupt and remove any pending interrupts */
-                               custom.intena = IF_TBE;
-                               mb();
-                               custom.intreq = IF_TBE;
-                               mb();
-                       }
-               }
-       }
-}
-
-static irqreturn_t ser_vbl_int( int irq, void *data)
-{
-        /* vbl is just a periodic interrupt we tie into to update modem status */
-       struct async_struct * info = IRQ_ports;
-       /*
-        * TBD - is it better to unregister from this interrupt or to
-        * ignore it if MSI is clear ?
-        */
-       if(info->IER & UART_IER_MSI)
-         check_modem_status(info);
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t ser_rx_int(int irq, void *dev_id)
-{
-       struct async_struct * info;
-
-#ifdef SERIAL_DEBUG_INTR
-       printk("ser_rx_int...");
-#endif
-
-       info = IRQ_ports;
-       if (!info || !info->tty)
-               return IRQ_NONE;
-
-       receive_chars(info);
-       info->last_active = jiffies;
-#ifdef SERIAL_DEBUG_INTR
-       printk("end.\n");
-#endif
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t ser_tx_int(int irq, void *dev_id)
-{
-       struct async_struct * info;
-
-       if (custom.serdatr & SDR_TBE) {
-#ifdef SERIAL_DEBUG_INTR
-         printk("ser_tx_int...");
-#endif
-
-         info = IRQ_ports;
-         if (!info || !info->tty)
-               return IRQ_NONE;
-
-         transmit_chars(info);
-         info->last_active = jiffies;
-#ifdef SERIAL_DEBUG_INTR
-         printk("end.\n");
-#endif
-       }
-       return IRQ_HANDLED;
-}
-
-/*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
- */
-
-/*
- * 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
- * rs_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 rs_sched_event(), and they get done here.
- */
-
-static void do_softint(unsigned long private_)
-{
-       struct async_struct     *info = (struct async_struct *) private_;
-       struct tty_struct       *tty;
-
-       tty = info->tty;
-       if (!tty)
-               return;
-
-       if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event))
-               tty_wakeup(tty);
-}
-
-/*
- * ---------------------------------------------------------------
- * Low level utility subroutines for the serial driver:  routines to
- * figure out the appropriate timeout for an interrupt chain, routines
- * to initialize and startup a serial port, and routines to shutdown a
- * serial port.  Useful stuff like that.
- * ---------------------------------------------------------------
- */
-
-static int startup(struct async_struct * info)
-{
-       unsigned long flags;
-       int     retval=0;
-       unsigned long page;
-
-       page = get_zeroed_page(GFP_KERNEL);
-       if (!page)
-               return -ENOMEM;
-
-       local_irq_save(flags);
-
-       if (info->flags & ASYNC_INITIALIZED) {
-               free_page(page);
-               goto errout;
-       }
-
-       if (info->xmit.buf)
-               free_page(page);
-       else
-               info->xmit.buf = (unsigned char *) page;
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("starting up ttys%d ...", info->line);
-#endif
-
-       /* Clear anything in the input buffer */
-
-       custom.intreq = IF_RBF;
-       mb();
-
-       retval = request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0, "serial status", info);
-       if (retval) {
-         if (serial_isroot()) {
-           if (info->tty)
-             set_bit(TTY_IO_ERROR,
-                     &info->tty->flags);
-           retval = 0;
-         }
-         goto errout;
-       }
-
-       /* enable both Rx and Tx interrupts */
-       custom.intena = IF_SETCLR | IF_RBF | IF_TBE;
-       mb();
-       info->IER = UART_IER_MSI;
-
-       /* remember current state of the DCD and CTS bits */
-       current_ctl_bits = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
-
-       IRQ_ports = info;
-
-       info->MCR = 0;
-       if (info->tty->termios->c_cflag & CBAUD)
-         info->MCR = SER_DTR | SER_RTS;
-       rtsdtr_ctrl(info->MCR);
-
-       if (info->tty)
-               clear_bit(TTY_IO_ERROR, &info->tty->flags);
-       info->xmit.head = info->xmit.tail = 0;
-
-       /*
-        * Set up the tty->alt_speed kludge
-        */
-       if (info->tty) {
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                       info->tty->alt_speed = 57600;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                       info->tty->alt_speed = 115200;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-                       info->tty->alt_speed = 230400;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-                       info->tty->alt_speed = 460800;
-       }
-
-       /*
-        * and set the speed of the serial port
-        */
-       change_speed(info, NULL);
-
-       info->flags |= ASYNC_INITIALIZED;
-       local_irq_restore(flags);
-       return 0;
-
-errout:
-       local_irq_restore(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 shutdown(struct async_struct * info)
-{
-       unsigned long   flags;
-       struct serial_state *state;
-
-       if (!(info->flags & ASYNC_INITIALIZED))
-               return;
-
-       state = info->state;
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("Shutting down serial port %d ....\n", info->line);
-#endif
-
-       local_irq_save(flags); /* 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(&info->delta_msr_wait);
-
-       IRQ_ports = NULL;
-
-       /*
-        * Free the IRQ, if necessary
-        */
-       free_irq(IRQ_AMIGA_VERTB, info);
-
-       if (info->xmit.buf) {
-               free_page((unsigned long) info->xmit.buf);
-               info->xmit.buf = NULL;
-       }
-
-       info->IER = 0;
-       custom.intena = IF_RBF | IF_TBE;
-       mb();
-
-       /* disable break condition */
-       custom.adkcon = AC_UARTBRK;
-       mb();
-
-       if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
-               info->MCR &= ~(SER_DTR|SER_RTS);
-       rtsdtr_ctrl(info->MCR);
-
-       if (info->tty)
-               set_bit(TTY_IO_ERROR, &info->tty->flags);
-
-       info->flags &= ~ASYNC_INITIALIZED;
-       local_irq_restore(flags);
-}
-
-
-/*
- * This routine is called to set the UART divisor registers to match
- * the specified baud rate for a serial port.
- */
-static void change_speed(struct async_struct *info,
-                        struct ktermios *old_termios)
-{
-       int     quot = 0, baud_base, baud;
-       unsigned cflag, cval = 0;
-       int     bits;
-       unsigned long   flags;
-
-       if (!info->tty || !info->tty->termios)
-               return;
-       cflag = info->tty->termios->c_cflag;
-
-       /* Byte size is always 8 bits plus parity bit if requested */
-
-       cval = 3; bits = 10;
-       if (cflag & CSTOPB) {
-               cval |= 0x04;
-               bits++;
-       }
-       if (cflag & PARENB) {
-               cval |= UART_LCR_PARITY;
-               bits++;
-       }
-       if (!(cflag & PARODD))
-               cval |= UART_LCR_EPAR;
-#ifdef CMSPAR
-       if (cflag & CMSPAR)
-               cval |= UART_LCR_SPAR;
-#endif
-
-       /* Determine divisor based on baud rate */
-       baud = tty_get_baud_rate(info->tty);
-       if (!baud)
-               baud = 9600;    /* B0 transition handled in rs_set_termios */
-       baud_base = info->state->baud_base;
-       if (baud == 38400 &&
-           ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
-               quot = info->state->custom_divisor;
-       else {
-               if (baud == 134)
-                       /* Special case since 134 is really 134.5 */
-                       quot = (2*baud_base / 269);
-               else if (baud)
-                       quot = baud_base / baud;
-       }
-       /* If the quotient is zero refuse the change */
-       if (!quot && old_termios) {
-               /* FIXME: Will need updating for new tty in the end */
-               info->tty->termios->c_cflag &= ~CBAUD;
-               info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
-               baud = tty_get_baud_rate(info->tty);
-               if (!baud)
-                       baud = 9600;
-               if (baud == 38400 &&
-                   ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
-                       quot = info->state->custom_divisor;
-               else {
-                       if (baud == 134)
-                               /* Special case since 134 is really 134.5 */
-                               quot = (2*baud_base / 269);
-                       else if (baud)
-                               quot = baud_base / baud;
-               }
-       }
-       /* As a last resort, if the quotient is zero, default to 9600 bps */
-       if (!quot)
-               quot = baud_base / 9600;
-       info->quot = quot;
-       info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
-       info->timeout += HZ/50;         /* Add .02 seconds of slop */
-
-       /* CTS flow control flag and modem status interrupts */
-       info->IER &= ~UART_IER_MSI;
-       if (info->flags & ASYNC_HARDPPS_CD)
-               info->IER |= UART_IER_MSI;
-       if (cflag & CRTSCTS) {
-               info->flags |= ASYNC_CTS_FLOW;
-               info->IER |= UART_IER_MSI;
-       } else
-               info->flags &= ~ASYNC_CTS_FLOW;
-       if (cflag & CLOCAL)
-               info->flags &= ~ASYNC_CHECK_CD;
-       else {
-               info->flags |= ASYNC_CHECK_CD;
-               info->IER |= UART_IER_MSI;
-       }
-       /* TBD:
-        * Does clearing IER_MSI imply that we should disable the VBL interrupt ?
-        */
-
-       /*
-        * Set up parity check flag
-        */
-
-       info->read_status_mask = UART_LSR_OE | UART_LSR_DR;
-       if (I_INPCK(info->tty))
-               info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
-       if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
-               info->read_status_mask |= UART_LSR_BI;
-
-       /*
-        * Characters to ignore
-        */
-       info->ignore_status_mask = 0;
-       if (I_IGNPAR(info->tty))
-               info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
-       if (I_IGNBRK(info->tty)) {
-               info->ignore_status_mask |= UART_LSR_BI;
-               /*
-                * If we're ignore parity and break indicators, ignore 
-                * overruns too.  (For real raw support).
-                */
-               if (I_IGNPAR(info->tty))
-                       info->ignore_status_mask |= UART_LSR_OE;
-       }
-       /*
-        * !!! ignore all characters if CREAD is not set
-        */
-       if ((cflag & CREAD) == 0)
-               info->ignore_status_mask |= UART_LSR_DR;
-       local_irq_save(flags);
-
-       {
-         short serper;
-
-       /* Set up the baud rate */
-         serper = quot - 1;
-
-       /* Enable or disable parity bit */
-
-       if(cval & UART_LCR_PARITY)
-         serper |= (SERPER_PARENB);
-
-       custom.serper = serper;
-       mb();
-       }
-
-       info->LCR = cval;                               /* Save LCR */
-       local_irq_restore(flags);
-}
-
-static int rs_put_char(struct tty_struct *tty, unsigned char ch)
-{
-       struct async_struct *info;
-       unsigned long flags;
-
-       info = tty->driver_data;
-
-       if (serial_paranoia_check(info, tty->name, "rs_put_char"))
-               return 0;
-
-       if (!info->xmit.buf)
-               return 0;
-
-       local_irq_save(flags);
-       if (CIRC_SPACE(info->xmit.head,
-                      info->xmit.tail,
-                      SERIAL_XMIT_SIZE) == 0) {
-               local_irq_restore(flags);
-               return 0;
-       }
-
-       info->xmit.buf[info->xmit.head++] = ch;
-       info->xmit.head &= SERIAL_XMIT_SIZE-1;
-       local_irq_restore(flags);
-       return 1;
-}
-
-static void rs_flush_chars(struct tty_struct *tty)
-{
-       struct async_struct *info = tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->name, "rs_flush_chars"))
-               return;
-
-       if (info->xmit.head == info->xmit.tail
-           || tty->stopped
-           || tty->hw_stopped
-           || !info->xmit.buf)
-               return;
-
-       local_irq_save(flags);
-       info->IER |= UART_IER_THRI;
-       custom.intena = IF_SETCLR | IF_TBE;
-       mb();
-       /* set a pending Tx Interrupt, transmitter should restart now */
-       custom.intreq = IF_SETCLR | IF_TBE;
-       mb();
-       local_irq_restore(flags);
-}
-
-static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count)
-{
-       int     c, ret = 0;
-       struct async_struct *info;
-       unsigned long flags;
-
-       info = tty->driver_data;
-
-       if (serial_paranoia_check(info, tty->name, "rs_write"))
-               return 0;
-
-       if (!info->xmit.buf)
-               return 0;
-
-       local_irq_save(flags);
-       while (1) {
-               c = CIRC_SPACE_TO_END(info->xmit.head,
-                                     info->xmit.tail,
-                                     SERIAL_XMIT_SIZE);
-               if (count < c)
-                       c = count;
-               if (c <= 0) {
-                       break;
-               }
-               memcpy(info->xmit.buf + info->xmit.head, buf, c);
-               info->xmit.head = ((info->xmit.head + c) &
-                                  (SERIAL_XMIT_SIZE-1));
-               buf += c;
-               count -= c;
-               ret += c;
-       }
-       local_irq_restore(flags);
-
-       if (info->xmit.head != info->xmit.tail
-           && !tty->stopped
-           && !tty->hw_stopped
-           && !(info->IER & UART_IER_THRI)) {
-               info->IER |= UART_IER_THRI;
-               local_irq_disable();
-               custom.intena = IF_SETCLR | IF_TBE;
-               mb();
-               /* set a pending Tx Interrupt, transmitter should restart now */
-               custom.intreq = IF_SETCLR | IF_TBE;
-               mb();
-               local_irq_restore(flags);
-       }
-       return ret;
-}
-
-static int rs_write_room(struct tty_struct *tty)
-{
-       struct async_struct *info = tty->driver_data;
-
-       if (serial_paranoia_check(info, tty->name, "rs_write_room"))
-               return 0;
-       return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-}
-
-static int rs_chars_in_buffer(struct tty_struct *tty)
-{
-       struct async_struct *info = tty->driver_data;
-
-       if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer"))
-               return 0;
-       return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-}
-
-static void rs_flush_buffer(struct tty_struct *tty)
-{
-       struct async_struct *info = tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->name, "rs_flush_buffer"))
-               return;
-       local_irq_save(flags);
-       info->xmit.head = info->xmit.tail = 0;
-       local_irq_restore(flags);
-       tty_wakeup(tty);
-}
-
-/*
- * This function is used to send a high-priority XON/XOFF character to
- * the device
- */
-static void rs_send_xchar(struct tty_struct *tty, char ch)
-{
-       struct async_struct *info = tty->driver_data;
-        unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->name, "rs_send_char"))
-               return;
-
-       info->x_char = ch;
-       if (ch) {
-               /* Make sure transmit interrupts are on */
-
-               /* Check this ! */
-               local_irq_save(flags);
-               if(!(custom.intenar & IF_TBE)) {
-                   custom.intena = IF_SETCLR | IF_TBE;
-                   mb();
-                   /* set a pending Tx Interrupt, transmitter should restart now */
-                   custom.intreq = IF_SETCLR | IF_TBE;
-                   mb();
-               }
-               local_irq_restore(flags);
-
-               info->IER |= UART_IER_THRI;
-       }
-}
-
-/*
- * ------------------------------------------------------------
- * rs_throttle()
- * 
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void rs_throttle(struct tty_struct * tty)
-{
-       struct async_struct *info = tty->driver_data;
-       unsigned long flags;
-#ifdef SERIAL_DEBUG_THROTTLE
-       char    buf[64];
-
-       printk("throttle %s: %d....\n", tty_name(tty, buf),
-              tty->ldisc.chars_in_buffer(tty));
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "rs_throttle"))
-               return;
-
-       if (I_IXOFF(tty))
-               rs_send_xchar(tty, STOP_CHAR(tty));
-
-       if (tty->termios->c_cflag & CRTSCTS)
-               info->MCR &= ~SER_RTS;
-
-       local_irq_save(flags);
-       rtsdtr_ctrl(info->MCR);
-       local_irq_restore(flags);
-}
-
-static void rs_unthrottle(struct tty_struct * tty)
-{
-       struct async_struct *info = tty->driver_data;
-       unsigned long flags;
-#ifdef SERIAL_DEBUG_THROTTLE
-       char    buf[64];
-
-       printk("unthrottle %s: %d....\n", tty_name(tty, buf),
-              tty->ldisc.chars_in_buffer(tty));
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "rs_unthrottle"))
-               return;
-
-       if (I_IXOFF(tty)) {
-               if (info->x_char)
-                       info->x_char = 0;
-               else
-                       rs_send_xchar(tty, START_CHAR(tty));
-       }
-       if (tty->termios->c_cflag & CRTSCTS)
-               info->MCR |= SER_RTS;
-       local_irq_save(flags);
-       rtsdtr_ctrl(info->MCR);
-       local_irq_restore(flags);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static int get_serial_info(struct async_struct * info,
-                          struct serial_struct __user * retinfo)
-{
-       struct serial_struct tmp;
-       struct serial_state *state = info->state;
-   
-       if (!retinfo)
-               return -EFAULT;
-       memset(&tmp, 0, sizeof(tmp));
-       tty_lock();
-       tmp.type = state->type;
-       tmp.line = state->line;
-       tmp.port = state->port;
-       tmp.irq = state->irq;
-       tmp.flags = state->flags;
-       tmp.xmit_fifo_size = state->xmit_fifo_size;
-       tmp.baud_base = state->baud_base;
-       tmp.close_delay = state->close_delay;
-       tmp.closing_wait = state->closing_wait;
-       tmp.custom_divisor = state->custom_divisor;
-       tty_unlock();
-       if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
-               return -EFAULT;
-       return 0;
-}
-
-static int set_serial_info(struct async_struct * info,
-                          struct serial_struct __user * new_info)
-{
-       struct serial_struct new_serial;
-       struct serial_state old_state, *state;
-       unsigned int            change_irq,change_port;
-       int                     retval = 0;
-
-       if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
-               return -EFAULT;
-
-       tty_lock();
-       state = info->state;
-       old_state = *state;
-  
-       change_irq = new_serial.irq != state->irq;
-       change_port = (new_serial.port != state->port);
-       if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size)) {
-         tty_unlock();
-         return -EINVAL;
-       }
-  
-       if (!serial_isroot()) {
-               if ((new_serial.baud_base != state->baud_base) ||
-                   (new_serial.close_delay != state->close_delay) ||
-                   (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
-                   ((new_serial.flags & ~ASYNC_USR_MASK) !=
-                    (state->flags & ~ASYNC_USR_MASK)))
-                       return -EPERM;
-               state->flags = ((state->flags & ~ASYNC_USR_MASK) |
-                              (new_serial.flags & ASYNC_USR_MASK));
-               info->flags = ((info->flags & ~ASYNC_USR_MASK) |
-                              (new_serial.flags & ASYNC_USR_MASK));
-               state->custom_divisor = new_serial.custom_divisor;
-               goto check_and_exit;
-       }
-
-       if (new_serial.baud_base < 9600) {
-               tty_unlock();
-               return -EINVAL;
-       }
-
-       /*
-        * OK, past this point, all the error checking has been done.
-        * At this point, we start making changes.....
-        */
-
-       state->baud_base = new_serial.baud_base;
-       state->flags = ((state->flags & ~ASYNC_FLAGS) |
-                       (new_serial.flags & ASYNC_FLAGS));
-       info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) |
-                      (info->flags & ASYNC_INTERNAL_FLAGS));
-       state->custom_divisor = new_serial.custom_divisor;
-       state->close_delay = new_serial.close_delay * HZ/100;
-       state->closing_wait = new_serial.closing_wait * HZ/100;
-       info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-
-check_and_exit:
-       if (info->flags & ASYNC_INITIALIZED) {
-               if (((old_state.flags & ASYNC_SPD_MASK) !=
-                    (state->flags & ASYNC_SPD_MASK)) ||
-                   (old_state.custom_divisor != state->custom_divisor)) {
-                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                               info->tty->alt_speed = 57600;
-                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                               info->tty->alt_speed = 115200;
-                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-                               info->tty->alt_speed = 230400;
-                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-                               info->tty->alt_speed = 460800;
-                       change_speed(info, NULL);
-               }
-       } else
-               retval = startup(info);
-       tty_unlock();
-       return retval;
-}
-
-
-/*
- * 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 get_lsr_info(struct async_struct * info, unsigned int __user *value)
-{
-       unsigned char status;
-       unsigned int result;
-       unsigned long flags;
-
-       local_irq_save(flags);
-       status = custom.serdatr;
-       mb();
-       local_irq_restore(flags);
-       result = ((status & SDR_TSRE) ? TIOCSER_TEMT : 0);
-       if (copy_to_user(value, &result, sizeof(int)))
-               return -EFAULT;
-       return 0;
-}
-
-
-static int rs_tiocmget(struct tty_struct *tty)
-{
-       struct async_struct * info = tty->driver_data;
-       unsigned char control, status;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
-               return -ENODEV;
-       if (tty->flags & (1 << TTY_IO_ERROR))
-               return -EIO;
-
-       control = info->MCR;
-       local_irq_save(flags);
-       status = ciab.pra;
-       local_irq_restore(flags);
-       return    ((control & SER_RTS) ? TIOCM_RTS : 0)
-               | ((control & SER_DTR) ? TIOCM_DTR : 0)
-               | (!(status  & SER_DCD) ? TIOCM_CAR : 0)
-               | (!(status  & SER_DSR) ? TIOCM_DSR : 0)
-               | (!(status  & SER_CTS) ? TIOCM_CTS : 0);
-}
-
-static int rs_tiocmset(struct tty_struct *tty, unsigned int set,
-                                               unsigned int clear)
-{
-       struct async_struct * info = tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
-               return -ENODEV;
-       if (tty->flags & (1 << TTY_IO_ERROR))
-               return -EIO;
-
-       local_irq_save(flags);
-       if (set & TIOCM_RTS)
-               info->MCR |= SER_RTS;
-       if (set & TIOCM_DTR)
-               info->MCR |= SER_DTR;
-       if (clear & TIOCM_RTS)
-               info->MCR &= ~SER_RTS;
-       if (clear & TIOCM_DTR)
-               info->MCR &= ~SER_DTR;
-       rtsdtr_ctrl(info->MCR);
-       local_irq_restore(flags);
-       return 0;
-}
-
-/*
- * rs_break() --- routine which turns the break handling on or off
- */
-static int rs_break(struct tty_struct *tty, int break_state)
-{
-       struct async_struct * info = tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->name, "rs_break"))
-               return -EINVAL;
-
-       local_irq_save(flags);
-       if (break_state == -1)
-         custom.adkcon = AC_SETCLR | AC_UARTBRK;
-       else
-         custom.adkcon = AC_UARTBRK;
-       mb();
-       local_irq_restore(flags);
-       return 0;
-}
-
-/*
- * 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.
- */
-static int rs_get_icount(struct tty_struct *tty,
-                               struct serial_icounter_struct *icount)
-{
-       struct async_struct *info = tty->driver_data;
-       struct async_icount cnow;
-       unsigned long flags;
-
-       local_irq_save(flags);
-       cnow = info->state->icount;
-       local_irq_restore(flags);
-       icount->cts = cnow.cts;
-       icount->dsr = cnow.dsr;
-       icount->rng = cnow.rng;
-       icount->dcd = cnow.dcd;
-       icount->rx = cnow.rx;
-       icount->tx = cnow.tx;
-       icount->frame = cnow.frame;
-       icount->overrun = cnow.overrun;
-       icount->parity = cnow.parity;
-       icount->brk = cnow.brk;
-       icount->buf_overrun = cnow.buf_overrun;
-
-       return 0;
-}
-
-static int rs_ioctl(struct tty_struct *tty,
-                   unsigned int cmd, unsigned long arg)
-{
-       struct async_struct * info = tty->driver_data;
-       struct async_icount cprev, cnow;        /* kernel counter temps */
-       void __user *argp = (void __user *)arg;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
-               return -ENODEV;
-
-       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-           (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
-           (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
-               if (tty->flags & (1 << TTY_IO_ERROR))
-                   return -EIO;
-       }
-
-       switch (cmd) {
-               case TIOCGSERIAL:
-                       return get_serial_info(info, argp);
-               case TIOCSSERIAL:
-                       return set_serial_info(info, argp);
-               case TIOCSERCONFIG:
-                       return 0;
-
-               case TIOCSERGETLSR: /* Get line status register */
-                       return get_lsr_info(info, argp);
-
-               case TIOCSERGSTRUCT:
-                       if (copy_to_user(argp,
-                                        info, sizeof(struct async_struct)))
-                               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:
-                       local_irq_save(flags);
-                       /* note the counters on entry */
-                       cprev = info->state->icount;
-                       local_irq_restore(flags);
-                       while (1) {
-                               interruptible_sleep_on(&info->delta_msr_wait);
-                               /* see if a signal did it */
-                               if (signal_pending(current))
-                                       return -ERESTARTSYS;
-                               local_irq_save(flags);
-                               cnow = info->state->icount; /* atomic copy */
-                               local_irq_restore(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 */
-
-               case TIOCSERGWILD:
-               case TIOCSERSWILD:
-                       /* "setserial -W" is called in Debian boot */
-                       printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
-                       return 0;
-
-               default:
-                       return -ENOIOCTLCMD;
-               }
-       return 0;
-}
-
-static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
-{
-       struct async_struct *info = tty->driver_data;
-       unsigned long flags;
-       unsigned int cflag = tty->termios->c_cflag;
-
-       change_speed(info, old_termios);
-
-       /* Handle transition to B0 status */
-       if ((old_termios->c_cflag & CBAUD) &&
-           !(cflag & CBAUD)) {
-               info->MCR &= ~(SER_DTR|SER_RTS);
-               local_irq_save(flags);
-               rtsdtr_ctrl(info->MCR);
-               local_irq_restore(flags);
-       }
-
-       /* Handle transition away from B0 status */
-       if (!(old_termios->c_cflag & CBAUD) &&
-           (cflag & CBAUD)) {
-               info->MCR |= SER_DTR;
-               if (!(tty->termios->c_cflag & CRTSCTS) || 
-                   !test_bit(TTY_THROTTLED, &tty->flags)) {
-                       info->MCR |= SER_RTS;
-               }
-               local_irq_save(flags);
-               rtsdtr_ctrl(info->MCR);
-               local_irq_restore(flags);
-       }
-
-       /* Handle turning off CRTSCTS */
-       if ((old_termios->c_cflag & CRTSCTS) &&
-           !(tty->termios->c_cflag & CRTSCTS)) {
-               tty->hw_stopped = 0;
-               rs_start(tty);
-       }
-
-#if 0
-       /*
-        * No need to wake up processes in open wait, since they
-        * sample the CLOCAL flag once, and don't recheck it.
-        * XXX  It's not clear whether the current behavior is correct
-        * or not.  Hence, this may change.....
-        */
-       if (!(old_termios->c_cflag & CLOCAL) &&
-           (tty->termios->c_cflag & CLOCAL))
-               wake_up_interruptible(&info->open_wait);
-#endif
-}
-
-/*
- * ------------------------------------------------------------
- * rs_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 rs_close(struct tty_struct *tty, struct file * filp)
-{
-       struct async_struct * info = tty->driver_data;
-       struct serial_state *state;
-       unsigned long flags;
-
-       if (!info || serial_paranoia_check(info, tty->name, "rs_close"))
-               return;
-
-       state = info->state;
-
-       local_irq_save(flags);
-
-       if (tty_hung_up_p(filp)) {
-               DBG_CNT("before DEC-hung");
-               local_irq_restore(flags);
-               return;
-       }
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("rs_close ttys%d, count = %d\n", info->line, state->count);
-#endif
-       if ((tty->count == 1) && (state->count != 1)) {
-               /*
-                * Uh, oh.  tty->count is 1, which means that the tty
-                * structure will be freed.  state->count should always
-                * be one in these conditions.  If it's greater than
-                * one, we've got real problems, since it means the
-                * serial port won't be shutdown.
-                */
-               printk("rs_close: bad serial port count; tty->count is 1, "
-                      "state->count is %d\n", state->count);
-               state->count = 1;
-       }
-       if (--state->count < 0) {
-               printk("rs_close: bad serial port count for ttys%d: %d\n",
-                      info->line, state->count);
-               state->count = 0;
-       }
-       if (state->count) {
-               DBG_CNT("before DEC-2");
-               local_irq_restore(flags);
-               return;
-       }
-       info->flags |= ASYNC_CLOSING;
-       /*
-        * 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 (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
-               tty_wait_until_sent(tty, info->closing_wait);
-       /*
-        * At this point we stop accepting input.  To do this, we
-        * disable the receive line status interrupts, and tell the
-        * interrupt driver to stop checking the data ready bit in the
-        * line status register.
-        */
-       info->read_status_mask &= ~UART_LSR_DR;
-       if (info->flags & ASYNC_INITIALIZED) {
-               /* disable receive interrupts */
-               custom.intena = IF_RBF;
-               mb();
-               /* clear any pending receive interrupt */
-               custom.intreq = IF_RBF;
-               mb();
-
-               /*
-                * Before we drop DTR, make sure the UART transmitter
-                * has completely drained; this is especially
-                * important if there is a transmit FIFO!
-                */
-               rs_wait_until_sent(tty, info->timeout);
-       }
-       shutdown(info);
-       rs_flush_buffer(tty);
-               
-       tty_ldisc_flush(tty);
-       tty->closing = 0;
-       info->event = 0;
-       info->tty = NULL;
-       if (info->blocked_open) {
-               if (info->close_delay) {
-                       msleep_interruptible(jiffies_to_msecs(info->close_delay));
-               }
-               wake_up_interruptible(&info->open_wait);
-       }
-       info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
-       wake_up_interruptible(&info->close_wait);
-       local_irq_restore(flags);
-}
-
-/*
- * rs_wait_until_sent() --- wait until the transmitter is empty
- */
-static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
-{
-       struct async_struct * info = tty->driver_data;
-       unsigned long orig_jiffies, char_time;
-       int tty_was_locked = tty_locked();
-       int lsr;
-
-       if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
-               return;
-
-       if (info->xmit_fifo_size == 0)
-               return; /* Just in case.... */
-
-       orig_jiffies = jiffies;
-
-       /*
-        * tty_wait_until_sent is called from lots of places,
-        * with or without the BTM.
-        */
-       if (!tty_was_locked)
-               tty_lock();
-       /*
-        * Set the check interval to be 1/5 of the estimated time to
-        * send a single character, and make it at least 1.  The check
-        * interval should also be less than the timeout.
-        * 
-        * Note: we have to use pretty tight timings here to satisfy
-        * the NIST-PCTS.
-        */
-       char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
-       char_time = char_time / 5;
-       if (char_time == 0)
-               char_time = 1;
-       if (timeout)
-         char_time = min_t(unsigned long, char_time, timeout);
-       /*
-        * If the transmitter hasn't cleared in twice the approximate
-        * amount of time to send the entire FIFO, it probably won't
-        * ever clear.  This assumes the UART isn't doing flow
-        * control, which is currently the case.  Hence, if it ever
-        * takes longer than info->timeout, this is probably due to a
-        * UART bug of some kind.  So, we clamp the timeout parameter at
-        * 2*info->timeout.
-        */
-       if (!timeout || timeout > 2*info->timeout)
-               timeout = 2*info->timeout;
-#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
-       printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
-       printk("jiff=%lu...", jiffies);
-#endif
-       while(!((lsr = custom.serdatr) & SDR_TSRE)) {
-#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
-               printk("serdatr = %d (jiff=%lu)...", lsr, jiffies);
-#endif
-               msleep_interruptible(jiffies_to_msecs(char_time));
-               if (signal_pending(current))
-                       break;
-               if (timeout && time_after(jiffies, orig_jiffies + timeout))
-                       break;
-       }
-       __set_current_state(TASK_RUNNING);
-       if (!tty_was_locked)
-               tty_unlock();
-#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
-       printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
-#endif
-}
-
-/*
- * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-static void rs_hangup(struct tty_struct *tty)
-{
-       struct async_struct * info = tty->driver_data;
-       struct serial_state *state = info->state;
-
-       if (serial_paranoia_check(info, tty->name, "rs_hangup"))
-               return;
-
-       state = info->state;
-
-       rs_flush_buffer(tty);
-       shutdown(info);
-       info->event = 0;
-       state->count = 0;
-       info->flags &= ~ASYNC_NORMAL_ACTIVE;
-       info->tty = NULL;
-       wake_up_interruptible(&info->open_wait);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_open() and friends
- * ------------------------------------------------------------
- */
-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;
-       unsigned long   flags;
-
-       /*
-        * If the device is in the middle of being closed, then block
-        * until it's done, and then try again.
-        */
-       if (tty_hung_up_p(filp) ||
-           (info->flags & ASYNC_CLOSING)) {
-               if (info->flags & ASYNC_CLOSING)
-                       interruptible_sleep_on(&info->close_wait);
-#ifdef SERIAL_DO_RESTART
-               return ((info->flags & ASYNC_HUP_NOTIFY) ?
-                       -EAGAIN : -ERESTARTSYS);
-#else
-               return -EAGAIN;
-#endif
-       }
-
-       /*
-        * If non-blocking mode is set, or the port is not enabled,
-        * then make the check up front and then exit.
-        */
-       if ((filp->f_flags & O_NONBLOCK) ||
-           (tty->flags & (1 << TTY_IO_ERROR))) {
-               info->flags |= ASYNC_NORMAL_ACTIVE;
-               return 0;
-       }
-
-       if (tty->termios->c_cflag & CLOCAL)
-               do_clocal = 1;
-
-       /*
-        * Block waiting for the carrier detect and the line to become
-        * free (i.e., not in use by the callout).  While we are in
-        * this loop, state->count is dropped by one, so that
-        * rs_close() knows when to free things.  We restore it upon
-        * exit, either normal or abnormal.
-        */
-       retval = 0;
-       add_wait_queue(&info->open_wait, &wait);
-#ifdef SERIAL_DEBUG_OPEN
-       printk("block_til_ready before block: ttys%d, count = %d\n",
-              state->line, state->count);
-#endif
-       local_irq_save(flags);
-       if (!tty_hung_up_p(filp)) {
-               extra_count = 1;
-               state->count--;
-       }
-       local_irq_restore(flags);
-       info->blocked_open++;
-       while (1) {
-               local_irq_save(flags);
-               if (tty->termios->c_cflag & CBAUD)
-                       rtsdtr_ctrl(SER_DTR|SER_RTS);
-               local_irq_restore(flags);
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (tty_hung_up_p(filp) ||
-                   !(info->flags & ASYNC_INITIALIZED)) {
-#ifdef SERIAL_DO_RESTART
-                       if (info->flags & ASYNC_HUP_NOTIFY)
-                               retval = -EAGAIN;
-                       else
-                               retval = -ERESTARTSYS;
-#else
-                       retval = -EAGAIN;
-#endif
-                       break;
-               }
-               if (!(info->flags & ASYNC_CLOSING) &&
-                   (do_clocal || (!(ciab.pra & SER_DCD)) ))
-                       break;
-               if (signal_pending(current)) {
-                       retval = -ERESTARTSYS;
-                       break;
-               }
-#ifdef SERIAL_DEBUG_OPEN
-               printk("block_til_ready blocking: ttys%d, count = %d\n",
-                      info->line, state->count);
-#endif
-               tty_unlock();
-               schedule();
-               tty_lock();
-       }
-       __set_current_state(TASK_RUNNING);
-       remove_wait_queue(&info->open_wait, &wait);
-       if (extra_count)
-               state->count++;
-       info->blocked_open--;
-#ifdef SERIAL_DEBUG_OPEN
-       printk("block_til_ready after blocking: ttys%d, count = %d\n",
-              info->line, state->count);
-#endif
-       if (retval)
-               return retval;
-       info->flags |= ASYNC_NORMAL_ACTIVE;
-       return 0;
-}
-
-static int get_async_struct(int line, struct async_struct **ret_info)
-{
-       struct async_struct *info;
-       struct serial_state *sstate;
-
-       sstate = rs_table + line;
-       sstate->count++;
-       if (sstate->info) {
-               *ret_info = sstate->info;
-               return 0;
-       }
-       info = kzalloc(sizeof(struct async_struct), GFP_KERNEL);
-       if (!info) {
-               sstate->count--;
-               return -ENOMEM;
-       }
-#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->xmit_fifo_size = sstate->xmit_fifo_size;
-       info->line = line;
-       tasklet_init(&info->tlet, do_softint, (unsigned long)info);
-       info->state = sstate;
-       if (sstate->info) {
-               kfree(info);
-               *ret_info = sstate->info;
-               return 0;
-       }
-       *ret_info = sstate->info = info;
-       return 0;
-}
-
-/*
- * 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 rs_open(struct tty_struct *tty, struct file * filp)
-{
-       struct async_struct     *info;
-       int                     retval, line;
-
-       line = tty->index;
-       if ((line < 0) || (line >= NR_PORTS)) {
-               return -ENODEV;
-       }
-       retval = get_async_struct(line, &info);
-       if (retval) {
-               return retval;
-       }
-       tty->driver_data = info;
-       info->tty = tty;
-       if (serial_paranoia_check(info, tty->name, "rs_open"))
-               return -ENODEV;
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("rs_open %s, count = %d\n", tty->name, info->state->count);
-#endif
-       info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-
-       /*
-        * If the port is the middle of closing, bail out now
-        */
-       if (tty_hung_up_p(filp) ||
-           (info->flags & ASYNC_CLOSING)) {
-               if (info->flags & ASYNC_CLOSING)
-                       interruptible_sleep_on(&info->close_wait);
-#ifdef SERIAL_DO_RESTART
-               return ((info->flags & ASYNC_HUP_NOTIFY) ?
-                       -EAGAIN : -ERESTARTSYS);
-#else
-               return -EAGAIN;
-#endif
-       }
-
-       /*
-        * Start up serial port
-        */
-       retval = startup(info);
-       if (retval) {
-               return retval;
-       }
-
-       retval = block_til_ready(tty, filp, info);
-       if (retval) {
-#ifdef SERIAL_DEBUG_OPEN
-               printk("rs_open returning after block_til_ready with %d\n",
-                      retval);
-#endif
-               return retval;
-       }
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("rs_open %s successful...", tty->name);
-#endif
-       return 0;
-}
-
-/*
- * /proc fs routines....
- */
-
-static inline void line_info(struct seq_file *m, struct serial_state *state)
-{
-       struct async_struct *info = state->info, scr_info;
-       char    stat_buf[30], control, status;
-       unsigned long flags;
-
-       seq_printf(m, "%d: uart:amiga_builtin",state->line);
-
-       /*
-        * Figure out the current RS-232 lines
-        */
-       if (!info) {
-               info = &scr_info;       /* This is just for serial_{in,out} */
-
-               info->magic = SERIAL_MAGIC;
-               info->flags = state->flags;
-               info->quot = 0;
-               info->tty = NULL;
-       }
-       local_irq_save(flags);
-       status = ciab.pra;
-       control = info ? info->MCR : status;
-       local_irq_restore(flags);
-
-       stat_buf[0] = 0;
-       stat_buf[1] = 0;
-       if(!(control & SER_RTS))
-               strcat(stat_buf, "|RTS");
-       if(!(status & SER_CTS))
-               strcat(stat_buf, "|CTS");
-       if(!(control & SER_DTR))
-               strcat(stat_buf, "|DTR");
-       if(!(status & SER_DSR))
-               strcat(stat_buf, "|DSR");
-       if(!(status & SER_DCD))
-               strcat(stat_buf, "|CD");
-
-       if (info->quot) {
-               seq_printf(m, " baud:%d", state->baud_base / info->quot);
-       }
-
-       seq_printf(m, " tx:%d rx:%d", state->icount.tx, state->icount.rx);
-
-       if (state->icount.frame)
-               seq_printf(m, " fe:%d", state->icount.frame);
-
-       if (state->icount.parity)
-               seq_printf(m, " pe:%d", state->icount.parity);
-
-       if (state->icount.brk)
-               seq_printf(m, " brk:%d", state->icount.brk);
-
-       if (state->icount.overrun)
-               seq_printf(m, " oe:%d", state->icount.overrun);
-
-       /*
-        * Last thing is the RS-232 status lines
-        */
-       seq_printf(m, " %s\n", stat_buf+1);
-}
-
-static int rs_proc_show(struct seq_file *m, void *v)
-{
-       seq_printf(m, "serinfo:1.0 driver:%s\n", serial_version);
-       line_info(m, &rs_table[0]);
-       return 0;
-}
-
-static int rs_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, rs_proc_show, NULL);
-}
-
-static const struct file_operations rs_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = rs_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-/*
- * ---------------------------------------------------------------------
- * rs_init() and friends
- *
- * rs_init() is called at boot-time to initialize the serial driver.
- * ---------------------------------------------------------------------
- */
-
-/*
- * This routine prints out the appropriate serial driver version
- * number, and identifies which options were configured into this
- * driver.
- */
-static void show_serial_version(void)
-{
-       printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
-}
-
-
-static const struct tty_operations serial_ops = {
-       .open = rs_open,
-       .close = rs_close,
-       .write = rs_write,
-       .put_char = rs_put_char,
-       .flush_chars = rs_flush_chars,
-       .write_room = rs_write_room,
-       .chars_in_buffer = rs_chars_in_buffer,
-       .flush_buffer = rs_flush_buffer,
-       .ioctl = rs_ioctl,
-       .throttle = rs_throttle,
-       .unthrottle = rs_unthrottle,
-       .set_termios = rs_set_termios,
-       .stop = rs_stop,
-       .start = rs_start,
-       .hangup = rs_hangup,
-       .break_ctl = rs_break,
-       .send_xchar = rs_send_xchar,
-       .wait_until_sent = rs_wait_until_sent,
-       .tiocmget = rs_tiocmget,
-       .tiocmset = rs_tiocmset,
-       .get_icount = rs_get_icount,
-       .proc_fops = &rs_proc_fops,
-};
-
-/*
- * The serial driver boot-time initialization code!
- */
-static int __init amiga_serial_probe(struct platform_device *pdev)
-{
-       unsigned long flags;
-       struct serial_state * state;
-       int error;
-
-       serial_driver = alloc_tty_driver(1);
-       if (!serial_driver)
-               return -ENOMEM;
-
-       IRQ_ports = NULL;
-
-       show_serial_version();
-
-       /* Initialize the tty_driver structure */
-
-       serial_driver->owner = THIS_MODULE;
-       serial_driver->driver_name = "amiserial";
-       serial_driver->name = "ttyS";
-       serial_driver->major = TTY_MAJOR;
-       serial_driver->minor_start = 64;
-       serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
-       serial_driver->subtype = SERIAL_TYPE_NORMAL;
-       serial_driver->init_termios = tty_std_termios;
-       serial_driver->init_termios.c_cflag =
-               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-       serial_driver->flags = TTY_DRIVER_REAL_RAW;
-       tty_set_operations(serial_driver, &serial_ops);
-
-       error = tty_register_driver(serial_driver);
-       if (error)
-               goto fail_put_tty_driver;
-
-       state = rs_table;
-       state->magic = SSTATE_MAGIC;
-       state->port = (int)&custom.serdatr; /* Just to give it a value */
-       state->line = 0;
-       state->custom_divisor = 0;
-       state->close_delay = 5*HZ/10;
-       state->closing_wait = 30*HZ;
-       state->icount.cts = state->icount.dsr = 
-         state->icount.rng = state->icount.dcd = 0;
-       state->icount.rx = state->icount.tx = 0;
-       state->icount.frame = state->icount.parity = 0;
-       state->icount.overrun = state->icount.brk = 0;
-
-       printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n",
-                      state->line);
-
-       /* Hardware set up */
-
-       state->baud_base = amiga_colorclock;
-       state->xmit_fifo_size = 1;
-
-       /* set ISRs, and then disable the rx interrupts */
-       error = request_irq(IRQ_AMIGA_TBE, ser_tx_int, 0, "serial TX", state);
-       if (error)
-               goto fail_unregister;
-
-       error = request_irq(IRQ_AMIGA_RBF, ser_rx_int, IRQF_DISABLED,
-                           "serial RX", state);
-       if (error)
-               goto fail_free_irq;
-
-       local_irq_save(flags);
-
-       /* turn off Rx and Tx interrupts */
-       custom.intena = IF_RBF | IF_TBE;
-       mb();
-
-       /* clear any pending interrupt */
-       custom.intreq = IF_RBF | IF_TBE;
-       mb();
-
-       local_irq_restore(flags);
-
-       /*
-        * set the appropriate directions for the modem control flags,
-        * and clear RTS and DTR
-        */
-       ciab.ddra |= (SER_DTR | SER_RTS);   /* outputs */
-       ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR);  /* inputs */
-
-       platform_set_drvdata(pdev, state);
-
-       return 0;
-
-fail_free_irq:
-       free_irq(IRQ_AMIGA_TBE, state);
-fail_unregister:
-       tty_unregister_driver(serial_driver);
-fail_put_tty_driver:
-       put_tty_driver(serial_driver);
-       return error;
-}
-
-static int __exit amiga_serial_remove(struct platform_device *pdev)
-{
-       int error;
-       struct serial_state *state = platform_get_drvdata(pdev);
-       struct async_struct *info = state->info;
-
-       /* printk("Unloading %s: version %s\n", serial_name, serial_version); */
-       tasklet_kill(&info->tlet);
-       if ((error = tty_unregister_driver(serial_driver)))
-               printk("SERIAL: failed to unregister serial driver (%d)\n",
-                      error);
-       put_tty_driver(serial_driver);
-
-       rs_table[0].info = NULL;
-       kfree(info);
-
-       free_irq(IRQ_AMIGA_TBE, rs_table);
-       free_irq(IRQ_AMIGA_RBF, rs_table);
-
-       platform_set_drvdata(pdev, NULL);
-
-       return error;
-}
-
-static struct platform_driver amiga_serial_driver = {
-       .remove = __exit_p(amiga_serial_remove),
-       .driver   = {
-               .name   = "amiga-serial",
-               .owner  = THIS_MODULE,
-       },
-};
-
-static int __init amiga_serial_init(void)
-{
-       return platform_driver_probe(&amiga_serial_driver, amiga_serial_probe);
-}
-
-module_init(amiga_serial_init);
-
-static void __exit amiga_serial_exit(void)
-{
-       platform_driver_unregister(&amiga_serial_driver);
-}
-
-module_exit(amiga_serial_exit);
-
-
-#if defined(CONFIG_SERIAL_CONSOLE) && !defined(MODULE)
-
-/*
- * ------------------------------------------------------------
- * Serial console driver
- * ------------------------------------------------------------
- */
-
-static void amiga_serial_putc(char c)
-{
-       custom.serdat = (unsigned char)c | 0x100;
-       while (!(custom.serdatr & 0x2000))
-               barrier();
-}
-
-/*
- *     Print a string to the serial port trying not to disturb
- *     any possible real use of the port...
- *
- *     The console must be locked when we get here.
- */
-static void serial_console_write(struct console *co, const char *s,
-                               unsigned count)
-{
-       unsigned short intena = custom.intenar;
-
-       custom.intena = IF_TBE;
-
-       while (count--) {
-               if (*s == '\n')
-                       amiga_serial_putc('\r');
-               amiga_serial_putc(*s++);
-       }
-
-       custom.intena = IF_SETCLR | (intena & IF_TBE);
-}
-
-static struct tty_driver *serial_console_device(struct console *c, int *index)
-{
-       *index = 0;
-       return serial_driver;
-}
-
-static struct console sercons = {
-       .name =         "ttyS",
-       .write =        serial_console_write,
-       .device =       serial_console_device,
-       .flags =        CON_PRINTBUFFER,
-       .index =        -1,
-};
-
-/*
- *     Register console.
- */
-static int __init amiserial_console_init(void)
-{
-       register_console(&sercons);
-       return 0;
-}
-console_initcall(amiserial_console_init);
-
-#endif /* CONFIG_SERIAL_CONSOLE && !MODULE */
-
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:amiga-serial");
diff --git a/drivers/char/bfin_jtag_comm.c b/drivers/char/bfin_jtag_comm.c
deleted file mode 100644 (file)
index 1640244..0000000
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * TTY over Blackfin JTAG Communication
- *
- * Copyright 2008-2009 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-#define DRV_NAME "bfin-jtag-comm"
-#define DEV_NAME "ttyBFJC"
-#define pr_fmt(fmt) DRV_NAME ": " fmt
-
-#include <linux/circ_buf.h>
-#include <linux/console.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/kernel.h>
-#include <linux/kthread.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-#include <asm/atomic.h>
-
-#define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); })
-
-/* See the Debug/Emulation chapter in the HRM */
-#define EMUDOF   0x00000001    /* EMUDAT_OUT full & valid */
-#define EMUDIF   0x00000002    /* EMUDAT_IN full & valid */
-#define EMUDOOVF 0x00000004    /* EMUDAT_OUT overflow */
-#define EMUDIOVF 0x00000008    /* EMUDAT_IN overflow */
-
-static inline uint32_t bfin_write_emudat(uint32_t emudat)
-{
-       __asm__ __volatile__("emudat = %0;" : : "d"(emudat));
-       return emudat;
-}
-
-static inline uint32_t bfin_read_emudat(void)
-{
-       uint32_t emudat;
-       __asm__ __volatile__("%0 = emudat;" : "=d"(emudat));
-       return emudat;
-}
-
-static inline uint32_t bfin_write_emudat_chars(char a, char b, char c, char d)
-{
-       return bfin_write_emudat((a << 0) | (b << 8) | (c << 16) | (d << 24));
-}
-
-#define CIRC_SIZE 2048 /* see comment in tty_io.c:do_tty_write() */
-#define CIRC_MASK (CIRC_SIZE - 1)
-#define circ_empty(circ)     ((circ)->head == (circ)->tail)
-#define circ_free(circ)      CIRC_SPACE((circ)->head, (circ)->tail, CIRC_SIZE)
-#define circ_cnt(circ)       CIRC_CNT((circ)->head, (circ)->tail, CIRC_SIZE)
-#define circ_byte(circ, idx) ((circ)->buf[(idx) & CIRC_MASK])
-
-static struct tty_driver *bfin_jc_driver;
-static struct task_struct *bfin_jc_kthread;
-static struct tty_struct * volatile bfin_jc_tty;
-static unsigned long bfin_jc_count;
-static DEFINE_MUTEX(bfin_jc_tty_mutex);
-static volatile struct circ_buf bfin_jc_write_buf;
-
-static int
-bfin_jc_emudat_manager(void *arg)
-{
-       uint32_t inbound_len = 0, outbound_len = 0;
-
-       while (!kthread_should_stop()) {
-               /* no one left to give data to, so sleep */
-               if (bfin_jc_tty == NULL && circ_empty(&bfin_jc_write_buf)) {
-                       pr_debug("waiting for readers\n");
-                       __set_current_state(TASK_UNINTERRUPTIBLE);
-                       schedule();
-                       __set_current_state(TASK_RUNNING);
-               }
-
-               /* no data available, so just chill */
-               if (!(bfin_read_DBGSTAT() & EMUDIF) && circ_empty(&bfin_jc_write_buf)) {
-                       pr_debug("waiting for data (in_len = %i) (circ: %i %i)\n",
-                               inbound_len, bfin_jc_write_buf.tail, bfin_jc_write_buf.head);
-                       if (inbound_len)
-                               schedule();
-                       else
-                               schedule_timeout_interruptible(HZ);
-                       continue;
-               }
-
-               /* if incoming data is ready, eat it */
-               if (bfin_read_DBGSTAT() & EMUDIF) {
-                       struct tty_struct *tty;
-                       mutex_lock(&bfin_jc_tty_mutex);
-                       tty = (struct tty_struct *)bfin_jc_tty;
-                       if (tty != NULL) {
-                               uint32_t emudat = bfin_read_emudat();
-                               if (inbound_len == 0) {
-                                       pr_debug("incoming length: 0x%08x\n", emudat);
-                                       inbound_len = emudat;
-                               } else {
-                                       size_t num_chars = (4 <= inbound_len ? 4 : inbound_len);
-                                       pr_debug("  incoming data: 0x%08x (pushing %zu)\n", emudat, num_chars);
-                                       inbound_len -= num_chars;
-                                       tty_insert_flip_string(tty, (unsigned char *)&emudat, num_chars);
-                                       tty_flip_buffer_push(tty);
-                               }
-                       }
-                       mutex_unlock(&bfin_jc_tty_mutex);
-               }
-
-               /* if outgoing data is ready, post it */
-               if (!(bfin_read_DBGSTAT() & EMUDOF) && !circ_empty(&bfin_jc_write_buf)) {
-                       if (outbound_len == 0) {
-                               outbound_len = circ_cnt(&bfin_jc_write_buf);
-                               bfin_write_emudat(outbound_len);
-                               pr_debug("outgoing length: 0x%08x\n", outbound_len);
-                       } else {
-                               struct tty_struct *tty;
-                               int tail = bfin_jc_write_buf.tail;
-                               size_t ate = (4 <= outbound_len ? 4 : outbound_len);
-                               uint32_t emudat =
-                               bfin_write_emudat_chars(
-                                       circ_byte(&bfin_jc_write_buf, tail + 0),
-                                       circ_byte(&bfin_jc_write_buf, tail + 1),
-                                       circ_byte(&bfin_jc_write_buf, tail + 2),
-                                       circ_byte(&bfin_jc_write_buf, tail + 3)
-                               );
-                               bfin_jc_write_buf.tail += ate;
-                               outbound_len -= ate;
-                               mutex_lock(&bfin_jc_tty_mutex);
-                               tty = (struct tty_struct *)bfin_jc_tty;
-                               if (tty)
-                                       tty_wakeup(tty);
-                               mutex_unlock(&bfin_jc_tty_mutex);
-                               pr_debug("  outgoing data: 0x%08x (pushing %zu)\n", emudat, ate);
-                       }
-               }
-       }
-
-       __set_current_state(TASK_RUNNING);
-       return 0;
-}
-
-static int
-bfin_jc_open(struct tty_struct *tty, struct file *filp)
-{
-       mutex_lock(&bfin_jc_tty_mutex);
-       pr_debug("open %lu\n", bfin_jc_count);
-       ++bfin_jc_count;
-       bfin_jc_tty = tty;
-       wake_up_process(bfin_jc_kthread);
-       mutex_unlock(&bfin_jc_tty_mutex);
-       return 0;
-}
-
-static void
-bfin_jc_close(struct tty_struct *tty, struct file *filp)
-{
-       mutex_lock(&bfin_jc_tty_mutex);
-       pr_debug("close %lu\n", bfin_jc_count);
-       if (--bfin_jc_count == 0)
-               bfin_jc_tty = NULL;
-       wake_up_process(bfin_jc_kthread);
-       mutex_unlock(&bfin_jc_tty_mutex);
-}
-
-/* XXX: we dont handle the put_char() case where we must handle count = 1 */
-static int
-bfin_jc_circ_write(const unsigned char *buf, int count)
-{
-       int i;
-       count = min(count, circ_free(&bfin_jc_write_buf));
-       pr_debug("going to write chunk of %i bytes\n", count);
-       for (i = 0; i < count; ++i)
-               circ_byte(&bfin_jc_write_buf, bfin_jc_write_buf.head + i) = buf[i];
-       bfin_jc_write_buf.head += i;
-       return i;
-}
-
-#ifndef CONFIG_BFIN_JTAG_COMM_CONSOLE
-# define console_lock()
-# define console_unlock()
-#endif
-static int
-bfin_jc_write(struct tty_struct *tty, const unsigned char *buf, int count)
-{
-       int i;
-       console_lock();
-       i = bfin_jc_circ_write(buf, count);
-       console_unlock();
-       wake_up_process(bfin_jc_kthread);
-       return i;
-}
-
-static void
-bfin_jc_flush_chars(struct tty_struct *tty)
-{
-       wake_up_process(bfin_jc_kthread);
-}
-
-static int
-bfin_jc_write_room(struct tty_struct *tty)
-{
-       return circ_free(&bfin_jc_write_buf);
-}
-
-static int
-bfin_jc_chars_in_buffer(struct tty_struct *tty)
-{
-       return circ_cnt(&bfin_jc_write_buf);
-}
-
-static void
-bfin_jc_wait_until_sent(struct tty_struct *tty, int timeout)
-{
-       unsigned long expire = jiffies + timeout;
-       while (!circ_empty(&bfin_jc_write_buf)) {
-               if (signal_pending(current))
-                       break;
-               if (time_after(jiffies, expire))
-                       break;
-       }
-}
-
-static const struct tty_operations bfin_jc_ops = {
-       .open            = bfin_jc_open,
-       .close           = bfin_jc_close,
-       .write           = bfin_jc_write,
-       /*.put_char        = bfin_jc_put_char,*/
-       .flush_chars     = bfin_jc_flush_chars,
-       .write_room      = bfin_jc_write_room,
-       .chars_in_buffer = bfin_jc_chars_in_buffer,
-       .wait_until_sent = bfin_jc_wait_until_sent,
-};
-
-static int __init bfin_jc_init(void)
-{
-       int ret;
-
-       bfin_jc_kthread = kthread_create(bfin_jc_emudat_manager, NULL, DRV_NAME);
-       if (IS_ERR(bfin_jc_kthread))
-               return PTR_ERR(bfin_jc_kthread);
-
-       ret = -ENOMEM;
-
-       bfin_jc_write_buf.head = bfin_jc_write_buf.tail = 0;
-       bfin_jc_write_buf.buf = kmalloc(CIRC_SIZE, GFP_KERNEL);
-       if (!bfin_jc_write_buf.buf)
-               goto err;
-
-       bfin_jc_driver = alloc_tty_driver(1);
-       if (!bfin_jc_driver)
-               goto err;
-
-       bfin_jc_driver->owner        = THIS_MODULE;
-       bfin_jc_driver->driver_name  = DRV_NAME;
-       bfin_jc_driver->name         = DEV_NAME;
-       bfin_jc_driver->type         = TTY_DRIVER_TYPE_SERIAL;
-       bfin_jc_driver->subtype      = SERIAL_TYPE_NORMAL;
-       bfin_jc_driver->init_termios = tty_std_termios;
-       tty_set_operations(bfin_jc_driver, &bfin_jc_ops);
-
-       ret = tty_register_driver(bfin_jc_driver);
-       if (ret)
-               goto err;
-
-       pr_init(KERN_INFO DRV_NAME ": initialized\n");
-
-       return 0;
-
- err:
-       put_tty_driver(bfin_jc_driver);
-       kfree(bfin_jc_write_buf.buf);
-       kthread_stop(bfin_jc_kthread);
-       return ret;
-}
-module_init(bfin_jc_init);
-
-static void __exit bfin_jc_exit(void)
-{
-       kthread_stop(bfin_jc_kthread);
-       kfree(bfin_jc_write_buf.buf);
-       tty_unregister_driver(bfin_jc_driver);
-       put_tty_driver(bfin_jc_driver);
-}
-module_exit(bfin_jc_exit);
-
-#if defined(CONFIG_BFIN_JTAG_COMM_CONSOLE) || defined(CONFIG_EARLY_PRINTK)
-static void
-bfin_jc_straight_buffer_write(const char *buf, unsigned count)
-{
-       unsigned ate = 0;
-       while (bfin_read_DBGSTAT() & EMUDOF)
-               continue;
-       bfin_write_emudat(count);
-       while (ate < count) {
-               while (bfin_read_DBGSTAT() & EMUDOF)
-                       continue;
-               bfin_write_emudat_chars(buf[ate], buf[ate+1], buf[ate+2], buf[ate+3]);
-               ate += 4;
-       }
-}
-#endif
-
-#ifdef CONFIG_BFIN_JTAG_COMM_CONSOLE
-static void
-bfin_jc_console_write(struct console *co, const char *buf, unsigned count)
-{
-       if (bfin_jc_kthread == NULL)
-               bfin_jc_straight_buffer_write(buf, count);
-       else
-               bfin_jc_circ_write(buf, count);
-}
-
-static struct tty_driver *
-bfin_jc_console_device(struct console *co, int *index)
-{
-       *index = co->index;
-       return bfin_jc_driver;
-}
-
-static struct console bfin_jc_console = {
-       .name    = DEV_NAME,
-       .write   = bfin_jc_console_write,
-       .device  = bfin_jc_console_device,
-       .flags   = CON_ANYTIME | CON_PRINTBUFFER,
-       .index   = -1,
-};
-
-static int __init bfin_jc_console_init(void)
-{
-       register_console(&bfin_jc_console);
-       return 0;
-}
-console_initcall(bfin_jc_console_init);
-#endif
-
-#ifdef CONFIG_EARLY_PRINTK
-static void __init
-bfin_jc_early_write(struct console *co, const char *buf, unsigned int count)
-{
-       bfin_jc_straight_buffer_write(buf, count);
-}
-
-static struct __initdata console bfin_jc_early_console = {
-       .name   = "early_BFJC",
-       .write   = bfin_jc_early_write,
-       .flags   = CON_ANYTIME | CON_PRINTBUFFER,
-       .index   = -1,
-};
-
-struct console * __init
-bfin_jc_early_init(unsigned int port, unsigned int cflag)
-{
-       return &bfin_jc_early_console;
-}
-#endif
-
-MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
-MODULE_DESCRIPTION("TTY over Blackfin JTAG Communication");
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
deleted file mode 100644 (file)
index c99728f..0000000
+++ /dev/null
@@ -1,4200 +0,0 @@
-#undef BLOCKMOVE
-#define        Z_WAKE
-#undef Z_EXT_CHARS_IN_BUFFER
-
-/*
- *  linux/drivers/char/cyclades.c
- *
- * This file contains the driver for the Cyclades async multiport
- * serial boards.
- *
- * Initially written by Randolph Bentson <bentson@grieg.seaslug.org>.
- * Modified and maintained by Marcio Saito <marcio@cyclades.com>.
- *
- * Copyright (C) 2007-2009 Jiri Slaby <jirislaby@gmail.com>
- *
- * Much of the design and some of the code came from serial.c
- * which was copyright (C) 1991, 1992  Linus Torvalds.  It was
- * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
- * and then fixed as suggested by Michael K. Johnson 12/12/92.
- * Converted to pci probing and cleaned up by Jiri Slaby.
- *
- */
-
-#define CY_VERSION     "2.6"
-
-/* If you need to install more boards than NR_CARDS, change the constant
-   in the definition below. No other change is necessary to support up to
-   eight boards. Beyond that you'll have to extend cy_isa_addresses. */
-
-#define NR_CARDS       4
-
-/*
-   If the total number of ports is larger than NR_PORTS, change this
-   constant in the definition below. No other change is necessary to
-   support more boards/ports. */
-
-#define NR_PORTS       256
-
-#define ZO_V1  0
-#define ZO_V2  1
-#define ZE_V1  2
-
-#define        SERIAL_PARANOIA_CHECK
-#undef CY_DEBUG_OPEN
-#undef CY_DEBUG_THROTTLE
-#undef CY_DEBUG_OTHER
-#undef CY_DEBUG_IO
-#undef CY_DEBUG_COUNT
-#undef CY_DEBUG_DTR
-#undef CY_DEBUG_WAIT_UNTIL_SENT
-#undef CY_DEBUG_INTERRUPTS
-#undef CY_16Y_HACK
-#undef CY_ENABLE_MONITORING
-#undef CY_PCI_DEBUG
-
-/*
- * Include section
- */
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/cyclades.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/bitops.h>
-#include <linux/firmware.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-
-#include <linux/io.h>
-#include <linux/uaccess.h>
-
-#include <linux/kernel.h>
-#include <linux/pci.h>
-
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-
-static void cy_send_xchar(struct tty_struct *tty, char ch);
-
-#ifndef SERIAL_XMIT_SIZE
-#define        SERIAL_XMIT_SIZE        (min(PAGE_SIZE, 4096))
-#endif
-
-#define STD_COM_FLAGS (0)
-
-/* firmware stuff */
-#define ZL_MAX_BLOCKS  16
-#define DRIVER_VERSION 0x02010203
-#define RAM_SIZE 0x80000
-
-enum zblock_type {
-       ZBLOCK_PRG = 0,
-       ZBLOCK_FPGA = 1
-};
-
-struct zfile_header {
-       char name[64];
-       char date[32];
-       char aux[32];
-       u32 n_config;
-       u32 config_offset;
-       u32 n_blocks;
-       u32 block_offset;
-       u32 reserved[9];
-} __attribute__ ((packed));
-
-struct zfile_config {
-       char name[64];
-       u32 mailbox;
-       u32 function;
-       u32 n_blocks;
-       u32 block_list[ZL_MAX_BLOCKS];
-} __attribute__ ((packed));
-
-struct zfile_block {
-       u32 type;
-       u32 file_offset;
-       u32 ram_offset;
-       u32 size;
-} __attribute__ ((packed));
-
-static struct tty_driver *cy_serial_driver;
-
-#ifdef CONFIG_ISA
-/* This is the address lookup table. The driver will probe for
-   Cyclom-Y/ISA boards at all addresses in here. If you want the
-   driver to probe addresses at a different address, add it to
-   this table.  If the driver is probing some other board and
-   causing problems, remove the offending address from this table.
-*/
-
-static unsigned int cy_isa_addresses[] = {
-       0xD0000,
-       0xD2000,
-       0xD4000,
-       0xD6000,
-       0xD8000,
-       0xDA000,
-       0xDC000,
-       0xDE000,
-       0, 0, 0, 0, 0, 0, 0, 0
-};
-
-#define NR_ISA_ADDRS ARRAY_SIZE(cy_isa_addresses)
-
-static long maddr[NR_CARDS];
-static int irq[NR_CARDS];
-
-module_param_array(maddr, long, NULL, 0);
-module_param_array(irq, int, NULL, 0);
-
-#endif                         /* CONFIG_ISA */
-
-/* This is the per-card data structure containing address, irq, number of
-   channels, etc. This driver supports a maximum of NR_CARDS cards.
-*/
-static struct cyclades_card cy_card[NR_CARDS];
-
-static int cy_next_channel;    /* next minor available */
-
-/*
- * This is used to look up the divisor speeds and the timeouts
- * We're normally limited to 15 distinct baud rates.  The extra
- * are accessed via settings in info->port.flags.
- *      0,     1,     2,     3,     4,     5,     6,     7,     8,     9,
- *     10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
- *                                               HI            VHI
- *     20
- */
-static const int baud_table[] = {
-       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
-       1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000,
-       230400, 0
-};
-
-static const char baud_co_25[] = {     /* 25 MHz clock option table */
-       /* value =>    00    01   02    03    04 */
-       /* divide by    8    32   128   512  2048 */
-       0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02,
-       0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-static const char baud_bpr_25[] = {    /* 25 MHz baud rate period table */
-       0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3,
-       0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15
-};
-
-static const char baud_co_60[] = {     /* 60 MHz clock option table (CD1400 J) */
-       /* value =>    00    01   02    03    04 */
-       /* divide by    8    32   128   512  2048 */
-       0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03,
-       0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00
-};
-
-static const char baud_bpr_60[] = {    /* 60 MHz baud rate period table (CD1400 J) */
-       0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62,
-       0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32,
-       0x21
-};
-
-static const char baud_cor3[] = {      /* receive threshold */
-       0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
-       0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07,
-       0x07
-};
-
-/*
- * The Cyclades driver implements HW flow control as any serial driver.
- * The cyclades_port structure member rflow and the vector rflow_thr
- * allows us to take advantage of a special feature in the CD1400 to avoid
- * data loss even when the system interrupt latency is too high. These flags
- * are to be used only with very special applications. Setting these flags
- * requires the use of a special cable (DTR and RTS reversed). In the new
- * CD1400-based boards (rev. 6.00 or later), there is no need for special
- * cables.
- */
-
-static const char rflow_thr[] = {      /* rflow threshold */
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
-       0x0a
-};
-
-/*  The Cyclom-Ye has placed the sequential chips in non-sequential
- *  address order.  This look-up table overcomes that problem.
- */
-static const unsigned int cy_chip_offset[] = { 0x0000,
-       0x0400,
-       0x0800,
-       0x0C00,
-       0x0200,
-       0x0600,
-       0x0A00,
-       0x0E00
-};
-
-/* PCI related definitions */
-
-#ifdef CONFIG_PCI
-static const struct pci_device_id cy_pci_dev_id[] = {
-       /* PCI < 1Mb */
-       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) },
-       /* PCI > 1Mb */
-       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Hi) },
-       /* 4Y PCI < 1Mb */
-       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Lo) },
-       /* 4Y PCI > 1Mb */
-       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Hi) },
-       /* 8Y PCI < 1Mb */
-       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Lo) },
-       /* 8Y PCI > 1Mb */
-       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Hi) },
-       /* Z PCI < 1Mb */
-       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Lo) },
-       /* Z PCI > 1Mb */
-       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Hi) },
-       { }                     /* end of table */
-};
-MODULE_DEVICE_TABLE(pci, cy_pci_dev_id);
-#endif
-
-static void cy_start(struct tty_struct *);
-static void cy_set_line_char(struct cyclades_port *, struct tty_struct *);
-static int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32);
-#ifdef CONFIG_ISA
-static unsigned detect_isa_irq(void __iomem *);
-#endif                         /* CONFIG_ISA */
-
-#ifndef CONFIG_CYZ_INTR
-static void cyz_poll(unsigned long);
-
-/* The Cyclades-Z polling cycle is defined by this variable */
-static long cyz_polling_cycle = CZ_DEF_POLL;
-
-static DEFINE_TIMER(cyz_timerlist, cyz_poll, 0, 0);
-
-#else                          /* CONFIG_CYZ_INTR */
-static void cyz_rx_restart(unsigned long);
-static struct timer_list cyz_rx_full_timer[NR_PORTS];
-#endif                         /* CONFIG_CYZ_INTR */
-
-static inline void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val)
-{
-       struct cyclades_card *card = port->card;
-
-       cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val);
-}
-
-static inline u8 cyy_readb(struct cyclades_port *port, u32 reg)
-{
-       struct cyclades_card *card = port->card;
-
-       return readb(port->u.cyy.base_addr + (reg << card->bus_index));
-}
-
-static inline bool cy_is_Z(struct cyclades_card *card)
-{
-       return card->num_chips == (unsigned int)-1;
-}
-
-static inline bool __cyz_fpga_loaded(struct RUNTIME_9060 __iomem *ctl_addr)
-{
-       return readl(&ctl_addr->init_ctrl) & (1 << 17);
-}
-
-static inline bool cyz_fpga_loaded(struct cyclades_card *card)
-{
-       return __cyz_fpga_loaded(card->ctl_addr.p9060);
-}
-
-static inline bool cyz_is_loaded(struct cyclades_card *card)
-{
-       struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS;
-
-       return (card->hw_ver == ZO_V1 || cyz_fpga_loaded(card)) &&
-                       readl(&fw_id->signature) == ZFIRM_ID;
-}
-
-static inline int serial_paranoia_check(struct cyclades_port *info,
-               const char *name, const char *routine)
-{
-#ifdef SERIAL_PARANOIA_CHECK
-       if (!info) {
-               printk(KERN_WARNING "cyc Warning: null cyclades_port for (%s) "
-                               "in %s\n", name, routine);
-               return 1;
-       }
-
-       if (info->magic != CYCLADES_MAGIC) {
-               printk(KERN_WARNING "cyc Warning: bad magic number for serial "
-                               "struct (%s) in %s\n", name, routine);
-               return 1;
-       }
-#endif
-       return 0;
-}
-
-/***********************************************************/
-/********* Start of block of Cyclom-Y specific code ********/
-
-/* This routine waits up to 1000 micro-seconds for the previous
-   command to the Cirrus chip to complete and then issues the
-   new command.  An error is returned if the previous command
-   didn't finish within the time limit.
-
-   This function is only called from inside spinlock-protected code.
- */
-static int __cyy_issue_cmd(void __iomem *base_addr, u8 cmd, int index)
-{
-       void __iomem *ccr = base_addr + (CyCCR << index);
-       unsigned int i;
-
-       /* Check to see that the previous command has completed */
-       for (i = 0; i < 100; i++) {
-               if (readb(ccr) == 0)
-                       break;
-               udelay(10L);
-       }
-       /* if the CCR never cleared, the previous command
-          didn't finish within the "reasonable time" */
-       if (i == 100)
-               return -1;
-
-       /* Issue the new command */
-       cy_writeb(ccr, cmd);
-
-       return 0;
-}
-
-static inline int cyy_issue_cmd(struct cyclades_port *port, u8 cmd)
-{
-       return __cyy_issue_cmd(port->u.cyy.base_addr, cmd,
-                       port->card->bus_index);
-}
-
-#ifdef CONFIG_ISA
-/* ISA interrupt detection code */
-static unsigned detect_isa_irq(void __iomem *address)
-{
-       int irq;
-       unsigned long irqs, flags;
-       int save_xir, save_car;
-       int index = 0;          /* IRQ probing is only for ISA */
-
-       /* forget possible initially masked and pending IRQ */
-       irq = probe_irq_off(probe_irq_on());
-
-       /* Clear interrupts on the board first */
-       cy_writeb(address + (Cy_ClrIntr << index), 0);
-       /* Cy_ClrIntr is 0x1800 */
-
-       irqs = probe_irq_on();
-       /* Wait ... */
-       msleep(5);
-
-       /* Enable the Tx interrupts on the CD1400 */
-       local_irq_save(flags);
-       cy_writeb(address + (CyCAR << index), 0);
-       __cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index);
-
-       cy_writeb(address + (CyCAR << index), 0);
-       cy_writeb(address + (CySRER << index),
-                 readb(address + (CySRER << index)) | CyTxRdy);
-       local_irq_restore(flags);
-
-       /* Wait ... */
-       msleep(5);
-
-       /* Check which interrupt is in use */
-       irq = probe_irq_off(irqs);
-
-       /* Clean up */
-       save_xir = (u_char) readb(address + (CyTIR << index));
-       save_car = readb(address + (CyCAR << index));
-       cy_writeb(address + (CyCAR << index), (save_xir & 0x3));
-       cy_writeb(address + (CySRER << index),
-                 readb(address + (CySRER << index)) & ~CyTxRdy);
-       cy_writeb(address + (CyTIR << index), (save_xir & 0x3f));
-       cy_writeb(address + (CyCAR << index), (save_car));
-       cy_writeb(address + (Cy_ClrIntr << index), 0);
-       /* Cy_ClrIntr is 0x1800 */
-
-       return (irq > 0) ? irq : 0;
-}
-#endif                         /* CONFIG_ISA */
-
-static void cyy_chip_rx(struct cyclades_card *cinfo, int chip,
-               void __iomem *base_addr)
-{
-       struct cyclades_port *info;
-       struct tty_struct *tty;
-       int len, index = cinfo->bus_index;
-       u8 ivr, save_xir, channel, save_car, data, char_count;
-
-#ifdef CY_DEBUG_INTERRUPTS
-       printk(KERN_DEBUG "cyy_interrupt: rcvd intr, chip %d\n", chip);
-#endif
-       /* determine the channel & change to that context */
-       save_xir = readb(base_addr + (CyRIR << index));
-       channel = save_xir & CyIRChannel;
-       info = &cinfo->ports[channel + chip * 4];
-       save_car = cyy_readb(info, CyCAR);
-       cyy_writeb(info, CyCAR, save_xir);
-       ivr = cyy_readb(info, CyRIVR) & CyIVRMask;
-
-       tty = tty_port_tty_get(&info->port);
-       /* if there is nowhere to put the data, discard it */
-       if (tty == NULL) {
-               if (ivr == CyIVRRxEx) { /* exception */
-                       data = cyy_readb(info, CyRDSR);
-               } else {        /* normal character reception */
-                       char_count = cyy_readb(info, CyRDCR);
-                       while (char_count--)
-                               data = cyy_readb(info, CyRDSR);
-               }
-               goto end;
-       }
-       /* there is an open port for this data */
-       if (ivr == CyIVRRxEx) { /* exception */
-               data = cyy_readb(info, CyRDSR);
-
-               /* For statistics only */
-               if (data & CyBREAK)
-                       info->icount.brk++;
-               else if (data & CyFRAME)
-                       info->icount.frame++;
-               else if (data & CyPARITY)
-                       info->icount.parity++;
-               else if (data & CyOVERRUN)
-                       info->icount.overrun++;
-
-               if (data & info->ignore_status_mask) {
-                       info->icount.rx++;
-                       tty_kref_put(tty);
-                       return;
-               }
-               if (tty_buffer_request_room(tty, 1)) {
-                       if (data & info->read_status_mask) {
-                               if (data & CyBREAK) {
-                                       tty_insert_flip_char(tty,
-                                               cyy_readb(info, CyRDSR),
-                                               TTY_BREAK);
-                                       info->icount.rx++;
-                                       if (info->port.flags & ASYNC_SAK)
-                                               do_SAK(tty);
-                               } else if (data & CyFRAME) {
-                                       tty_insert_flip_char(tty,
-                                               cyy_readb(info, CyRDSR),
-                                               TTY_FRAME);
-                                       info->icount.rx++;
-                                       info->idle_stats.frame_errs++;
-                               } else if (data & CyPARITY) {
-                                       /* Pieces of seven... */
-                                       tty_insert_flip_char(tty,
-                                               cyy_readb(info, CyRDSR),
-                                               TTY_PARITY);
-                                       info->icount.rx++;
-                                       info->idle_stats.parity_errs++;
-                               } else if (data & CyOVERRUN) {
-                                       tty_insert_flip_char(tty, 0,
-                                                       TTY_OVERRUN);
-                                       info->icount.rx++;
-                                       /* If the flip buffer itself is
-                                          overflowing, we still lose
-                                          the next incoming character.
-                                        */
-                                       tty_insert_flip_char(tty,
-                                               cyy_readb(info, CyRDSR),
-                                               TTY_FRAME);
-                                       info->icount.rx++;
-                                       info->idle_stats.overruns++;
-                               /* These two conditions may imply */
-                               /* a normal read should be done. */
-                               /* } else if(data & CyTIMEOUT) { */
-                               /* } else if(data & CySPECHAR) { */
-                               } else {
-                                       tty_insert_flip_char(tty, 0,
-                                                       TTY_NORMAL);
-                                       info->icount.rx++;
-                               }
-                       } else {
-                               tty_insert_flip_char(tty, 0, TTY_NORMAL);
-                               info->icount.rx++;
-                       }
-               } else {
-                       /* there was a software buffer overrun and nothing
-                        * could be done about it!!! */
-                       info->icount.buf_overrun++;
-                       info->idle_stats.overruns++;
-               }
-       } else {        /* normal character reception */
-               /* load # chars available from the chip */
-               char_count = cyy_readb(info, CyRDCR);
-
-#ifdef CY_ENABLE_MONITORING
-               ++info->mon.int_count;
-               info->mon.char_count += char_count;
-               if (char_count > info->mon.char_max)
-                       info->mon.char_max = char_count;
-               info->mon.char_last = char_count;
-#endif
-               len = tty_buffer_request_room(tty, char_count);
-               while (len--) {
-                       data = cyy_readb(info, CyRDSR);
-                       tty_insert_flip_char(tty, data, TTY_NORMAL);
-                       info->idle_stats.recv_bytes++;
-                       info->icount.rx++;
-#ifdef CY_16Y_HACK
-                       udelay(10L);
-#endif
-               }
-               info->idle_stats.recv_idle = jiffies;
-       }
-       tty_schedule_flip(tty);
-       tty_kref_put(tty);
-end:
-       /* end of service */
-       cyy_writeb(info, CyRIR, save_xir & 0x3f);
-       cyy_writeb(info, CyCAR, save_car);
-}
-
-static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip,
-               void __iomem *base_addr)
-{
-       struct cyclades_port *info;
-       struct tty_struct *tty;
-       int char_count, index = cinfo->bus_index;
-       u8 save_xir, channel, save_car, outch;
-
-       /* Since we only get here when the transmit buffer
-          is empty, we know we can always stuff a dozen
-          characters. */
-#ifdef CY_DEBUG_INTERRUPTS
-       printk(KERN_DEBUG "cyy_interrupt: xmit intr, chip %d\n", chip);
-#endif
-
-       /* determine the channel & change to that context */
-       save_xir = readb(base_addr + (CyTIR << index));
-       channel = save_xir & CyIRChannel;
-       save_car = readb(base_addr + (CyCAR << index));
-       cy_writeb(base_addr + (CyCAR << index), save_xir);
-
-       info = &cinfo->ports[channel + chip * 4];
-       tty = tty_port_tty_get(&info->port);
-       if (tty == NULL) {
-               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy);
-               goto end;
-       }
-
-       /* load the on-chip space for outbound data */
-       char_count = info->xmit_fifo_size;
-
-       if (info->x_char) {     /* send special char */
-               outch = info->x_char;
-               cyy_writeb(info, CyTDR, outch);
-               char_count--;
-               info->icount.tx++;
-               info->x_char = 0;
-       }
-
-       if (info->breakon || info->breakoff) {
-               if (info->breakon) {
-                       cyy_writeb(info, CyTDR, 0);
-                       cyy_writeb(info, CyTDR, 0x81);
-                       info->breakon = 0;
-                       char_count -= 2;
-               }
-               if (info->breakoff) {
-                       cyy_writeb(info, CyTDR, 0);
-                       cyy_writeb(info, CyTDR, 0x83);
-                       info->breakoff = 0;
-                       char_count -= 2;
-               }
-       }
-
-       while (char_count-- > 0) {
-               if (!info->xmit_cnt) {
-                       if (cyy_readb(info, CySRER) & CyTxMpty) {
-                               cyy_writeb(info, CySRER,
-                                       cyy_readb(info, CySRER) & ~CyTxMpty);
-                       } else {
-                               cyy_writeb(info, CySRER, CyTxMpty |
-                                       (cyy_readb(info, CySRER) & ~CyTxRdy));
-                       }
-                       goto done;
-               }
-               if (info->port.xmit_buf == NULL) {
-                       cyy_writeb(info, CySRER,
-                               cyy_readb(info, CySRER) & ~CyTxRdy);
-                       goto done;
-               }
-               if (tty->stopped || tty->hw_stopped) {
-                       cyy_writeb(info, CySRER,
-                               cyy_readb(info, CySRER) & ~CyTxRdy);
-                       goto done;
-               }
-               /* Because the Embedded Transmit Commands have been enabled,
-                * we must check to see if the escape character, NULL, is being
-                * sent. If it is, we must ensure that there is room for it to
-                * be doubled in the output stream.  Therefore we no longer
-                * advance the pointer when the character is fetched, but
-                * rather wait until after the check for a NULL output
-                * character. This is necessary because there may not be room
-                * for the two chars needed to send a NULL.)
-                */
-               outch = info->port.xmit_buf[info->xmit_tail];
-               if (outch) {
-                       info->xmit_cnt--;
-                       info->xmit_tail = (info->xmit_tail + 1) &
-                                       (SERIAL_XMIT_SIZE - 1);
-                       cyy_writeb(info, CyTDR, outch);
-                       info->icount.tx++;
-               } else {
-                       if (char_count > 1) {
-                               info->xmit_cnt--;
-                               info->xmit_tail = (info->xmit_tail + 1) &
-                                       (SERIAL_XMIT_SIZE - 1);
-                               cyy_writeb(info, CyTDR, outch);
-                               cyy_writeb(info, CyTDR, 0);
-                               info->icount.tx++;
-                               char_count--;
-                       }
-               }
-       }
-
-done:
-       tty_wakeup(tty);
-       tty_kref_put(tty);
-end:
-       /* end of service */
-       cyy_writeb(info, CyTIR, save_xir & 0x3f);
-       cyy_writeb(info, CyCAR, save_car);
-}
-
-static void cyy_chip_modem(struct cyclades_card *cinfo, int chip,
-               void __iomem *base_addr)
-{
-       struct cyclades_port *info;
-       struct tty_struct *tty;
-       int index = cinfo->bus_index;
-       u8 save_xir, channel, save_car, mdm_change, mdm_status;
-
-       /* determine the channel & change to that context */
-       save_xir = readb(base_addr + (CyMIR << index));
-       channel = save_xir & CyIRChannel;
-       info = &cinfo->ports[channel + chip * 4];
-       save_car = cyy_readb(info, CyCAR);
-       cyy_writeb(info, CyCAR, save_xir);
-
-       mdm_change = cyy_readb(info, CyMISR);
-       mdm_status = cyy_readb(info, CyMSVR1);
-
-       tty = tty_port_tty_get(&info->port);
-       if (!tty)
-               goto end;
-
-       if (mdm_change & CyANY_DELTA) {
-               /* For statistics only */
-               if (mdm_change & CyDCD)
-                       info->icount.dcd++;
-               if (mdm_change & CyCTS)
-                       info->icount.cts++;
-               if (mdm_change & CyDSR)
-                       info->icount.dsr++;
-               if (mdm_change & CyRI)
-                       info->icount.rng++;
-
-               wake_up_interruptible(&info->port.delta_msr_wait);
-       }
-
-       if ((mdm_change & CyDCD) && (info->port.flags & ASYNC_CHECK_CD)) {
-               if (mdm_status & CyDCD)
-                       wake_up_interruptible(&info->port.open_wait);
-               else
-                       tty_hangup(tty);
-       }
-       if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) {
-               if (tty->hw_stopped) {
-                       if (mdm_status & CyCTS) {
-                               /* cy_start isn't used
-                                  because... !!! */
-                               tty->hw_stopped = 0;
-                               cyy_writeb(info, CySRER,
-                                       cyy_readb(info, CySRER) | CyTxRdy);
-                               tty_wakeup(tty);
-                       }
-               } else {
-                       if (!(mdm_status & CyCTS)) {
-                               /* cy_stop isn't used
-                                  because ... !!! */
-                               tty->hw_stopped = 1;
-                               cyy_writeb(info, CySRER,
-                                       cyy_readb(info, CySRER) & ~CyTxRdy);
-                       }
-               }
-       }
-/*     if (mdm_change & CyDSR) {
-       }
-       if (mdm_change & CyRI) {
-       }*/
-       tty_kref_put(tty);
-end:
-       /* end of service */
-       cyy_writeb(info, CyMIR, save_xir & 0x3f);
-       cyy_writeb(info, CyCAR, save_car);
-}
-
-/* The real interrupt service routine is called
-   whenever the card wants its hand held--chars
-   received, out buffer empty, modem change, etc.
- */
-static irqreturn_t cyy_interrupt(int irq, void *dev_id)
-{
-       int status;
-       struct cyclades_card *cinfo = dev_id;
-       void __iomem *base_addr, *card_base_addr;
-       unsigned int chip, too_many, had_work;
-       int index;
-
-       if (unlikely(cinfo == NULL)) {
-#ifdef CY_DEBUG_INTERRUPTS
-               printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n",
-                               irq);
-#endif
-               return IRQ_NONE;        /* spurious interrupt */
-       }
-
-       card_base_addr = cinfo->base_addr;
-       index = cinfo->bus_index;
-
-       /* card was not initialized yet (e.g. DEBUG_SHIRQ) */
-       if (unlikely(card_base_addr == NULL))
-               return IRQ_HANDLED;
-
-       /* This loop checks all chips in the card.  Make a note whenever
-          _any_ chip had some work to do, as this is considered an
-          indication that there will be more to do.  Only when no chip
-          has any work does this outermost loop exit.
-        */
-       do {
-               had_work = 0;
-               for (chip = 0; chip < cinfo->num_chips; chip++) {
-                       base_addr = cinfo->base_addr +
-                                       (cy_chip_offset[chip] << index);
-                       too_many = 0;
-                       while ((status = readb(base_addr +
-                                               (CySVRR << index))) != 0x00) {
-                               had_work++;
-                       /* The purpose of the following test is to ensure that
-                          no chip can monopolize the driver.  This forces the
-                          chips to be checked in a round-robin fashion (after
-                          draining each of a bunch (1000) of characters).
-                        */
-                               if (1000 < too_many++)
-                                       break;
-                               spin_lock(&cinfo->card_lock);
-                               if (status & CySRReceive) /* rx intr */
-                                       cyy_chip_rx(cinfo, chip, base_addr);
-                               if (status & CySRTransmit) /* tx intr */
-                                       cyy_chip_tx(cinfo, chip, base_addr);
-                               if (status & CySRModem) /* modem intr */
-                                       cyy_chip_modem(cinfo, chip, base_addr);
-                               spin_unlock(&cinfo->card_lock);
-                       }
-               }
-       } while (had_work);
-
-       /* clear interrupts */
-       spin_lock(&cinfo->card_lock);
-       cy_writeb(card_base_addr + (Cy_ClrIntr << index), 0);
-       /* Cy_ClrIntr is 0x1800 */
-       spin_unlock(&cinfo->card_lock);
-       return IRQ_HANDLED;
-}                              /* cyy_interrupt */
-
-static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set,
-               unsigned int clear)
-{
-       struct cyclades_card *card = info->card;
-       int channel = info->line - card->first_line;
-       u32 rts, dtr, msvrr, msvrd;
-
-       channel &= 0x03;
-
-       if (info->rtsdtr_inv) {
-               msvrr = CyMSVR2;
-               msvrd = CyMSVR1;
-               rts = CyDTR;
-               dtr = CyRTS;
-       } else {
-               msvrr = CyMSVR1;
-               msvrd = CyMSVR2;
-               rts = CyRTS;
-               dtr = CyDTR;
-       }
-       if (set & TIOCM_RTS) {
-               cyy_writeb(info, CyCAR, channel);
-               cyy_writeb(info, msvrr, rts);
-       }
-       if (clear & TIOCM_RTS) {
-               cyy_writeb(info, CyCAR, channel);
-               cyy_writeb(info, msvrr, ~rts);
-       }
-       if (set & TIOCM_DTR) {
-               cyy_writeb(info, CyCAR, channel);
-               cyy_writeb(info, msvrd, dtr);
-#ifdef CY_DEBUG_DTR
-               printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n");
-               printk(KERN_DEBUG "     status: 0x%x, 0x%x\n",
-                       cyy_readb(info, CyMSVR1),
-                       cyy_readb(info, CyMSVR2));
-#endif
-       }
-       if (clear & TIOCM_DTR) {
-               cyy_writeb(info, CyCAR, channel);
-               cyy_writeb(info, msvrd, ~dtr);
-#ifdef CY_DEBUG_DTR
-               printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n");
-               printk(KERN_DEBUG "     status: 0x%x, 0x%x\n",
-                       cyy_readb(info, CyMSVR1),
-                       cyy_readb(info, CyMSVR2));
-#endif
-       }
-}
-
-/***********************************************************/
-/********* End of block of Cyclom-Y specific code **********/
-/******** Start of block of Cyclades-Z specific code *******/
-/***********************************************************/
-
-static int
-cyz_fetch_msg(struct cyclades_card *cinfo,
-               __u32 *channel, __u8 *cmd, __u32 *param)
-{
-       struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
-       unsigned long loc_doorbell;
-
-       loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell);
-       if (loc_doorbell) {
-               *cmd = (char)(0xff & loc_doorbell);
-               *channel = readl(&board_ctrl->fwcmd_channel);
-               *param = (__u32) readl(&board_ctrl->fwcmd_param);
-               cy_writel(&cinfo->ctl_addr.p9060->loc_doorbell, 0xffffffff);
-               return 1;
-       }
-       return 0;
-}                              /* cyz_fetch_msg */
-
-static int
-cyz_issue_cmd(struct cyclades_card *cinfo,
-               __u32 channel, __u8 cmd, __u32 param)
-{
-       struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
-       __u32 __iomem *pci_doorbell;
-       unsigned int index;
-
-       if (!cyz_is_loaded(cinfo))
-               return -1;
-
-       index = 0;
-       pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell;
-       while ((readl(pci_doorbell) & 0xff) != 0) {
-               if (index++ == 1000)
-                       return (int)(readl(pci_doorbell) & 0xff);
-               udelay(50L);
-       }
-       cy_writel(&board_ctrl->hcmd_channel, channel);
-       cy_writel(&board_ctrl->hcmd_param, param);
-       cy_writel(pci_doorbell, (long)cmd);
-
-       return 0;
-}                              /* cyz_issue_cmd */
-
-static void cyz_handle_rx(struct cyclades_port *info, struct tty_struct *tty)
-{
-       struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl;
-       struct cyclades_card *cinfo = info->card;
-       unsigned int char_count;
-       int len;
-#ifdef BLOCKMOVE
-       unsigned char *buf;
-#else
-       char data;
-#endif
-       __u32 rx_put, rx_get, new_rx_get, rx_bufsize, rx_bufaddr;
-
-       rx_get = new_rx_get = readl(&buf_ctrl->rx_get);
-       rx_put = readl(&buf_ctrl->rx_put);
-       rx_bufsize = readl(&buf_ctrl->rx_bufsize);
-       rx_bufaddr = readl(&buf_ctrl->rx_bufaddr);
-       if (rx_put >= rx_get)
-               char_count = rx_put - rx_get;
-       else
-               char_count = rx_put - rx_get + rx_bufsize;
-
-       if (char_count) {
-#ifdef CY_ENABLE_MONITORING
-               info->mon.int_count++;
-               info->mon.char_count += char_count;
-               if (char_count > info->mon.char_max)
-                       info->mon.char_max = char_count;
-               info->mon.char_last = char_count;
-#endif
-               if (tty == NULL) {
-                       /* flush received characters */
-                       new_rx_get = (new_rx_get + char_count) &
-                                       (rx_bufsize - 1);
-                       info->rflush_count++;
-               } else {
-#ifdef BLOCKMOVE
-               /* we'd like to use memcpy(t, f, n) and memset(s, c, count)
-                  for performance, but because of buffer boundaries, there
-                  may be several steps to the operation */
-                       while (1) {
-                               len = tty_prepare_flip_string(tty, &buf,
-                                               char_count);
-                               if (!len)
-                                       break;
-
-                               len = min_t(unsigned int, min(len, char_count),
-                                               rx_bufsize - new_rx_get);
-
-                               memcpy_fromio(buf, cinfo->base_addr +
-                                               rx_bufaddr + new_rx_get, len);
-
-                               new_rx_get = (new_rx_get + len) &
-                                               (rx_bufsize - 1);
-                               char_count -= len;
-                               info->icount.rx += len;
-                               info->idle_stats.recv_bytes += len;
-                       }
-#else
-                       len = tty_buffer_request_room(tty, char_count);
-                       while (len--) {
-                               data = readb(cinfo->base_addr + rx_bufaddr +
-                                               new_rx_get);
-                               new_rx_get = (new_rx_get + 1) &
-                                                       (rx_bufsize - 1);
-                               tty_insert_flip_char(tty, data, TTY_NORMAL);
-                               info->idle_stats.recv_bytes++;
-                               info->icount.rx++;
-                       }
-#endif
-#ifdef CONFIG_CYZ_INTR
-               /* Recalculate the number of chars in the RX buffer and issue
-                  a cmd in case it's higher than the RX high water mark */
-                       rx_put = readl(&buf_ctrl->rx_put);
-                       if (rx_put >= rx_get)
-                               char_count = rx_put - rx_get;
-                       else
-                               char_count = rx_put - rx_get + rx_bufsize;
-                       if (char_count >= readl(&buf_ctrl->rx_threshold) &&
-                                       !timer_pending(&cyz_rx_full_timer[
-                                                       info->line]))
-                               mod_timer(&cyz_rx_full_timer[info->line],
-                                               jiffies + 1);
-#endif
-                       info->idle_stats.recv_idle = jiffies;
-                       tty_schedule_flip(tty);
-               }
-               /* Update rx_get */
-               cy_writel(&buf_ctrl->rx_get, new_rx_get);
-       }
-}
-
-static void cyz_handle_tx(struct cyclades_port *info, struct tty_struct *tty)
-{
-       struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl;
-       struct cyclades_card *cinfo = info->card;
-       u8 data;
-       unsigned int char_count;
-#ifdef BLOCKMOVE
-       int small_count;
-#endif
-       __u32 tx_put, tx_get, tx_bufsize, tx_bufaddr;
-
-       if (info->xmit_cnt <= 0)        /* Nothing to transmit */
-               return;
-
-       tx_get = readl(&buf_ctrl->tx_get);
-       tx_put = readl(&buf_ctrl->tx_put);
-       tx_bufsize = readl(&buf_ctrl->tx_bufsize);
-       tx_bufaddr = readl(&buf_ctrl->tx_bufaddr);
-       if (tx_put >= tx_get)
-               char_count = tx_get - tx_put - 1 + tx_bufsize;
-       else
-               char_count = tx_get - tx_put - 1;
-
-       if (char_count) {
-
-               if (tty == NULL)
-                       goto ztxdone;
-
-               if (info->x_char) {     /* send special char */
-                       data = info->x_char;
-
-                       cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data);
-                       tx_put = (tx_put + 1) & (tx_bufsize - 1);
-                       info->x_char = 0;
-                       char_count--;
-                       info->icount.tx++;
-               }
-#ifdef BLOCKMOVE
-               while (0 < (small_count = min_t(unsigned int,
-                               tx_bufsize - tx_put, min_t(unsigned int,
-                                       (SERIAL_XMIT_SIZE - info->xmit_tail),
-                                       min_t(unsigned int, info->xmit_cnt,
-                                               char_count))))) {
-
-                       memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr +
-                                       tx_put),
-                                       &info->port.xmit_buf[info->xmit_tail],
-                                       small_count);
-
-                       tx_put = (tx_put + small_count) & (tx_bufsize - 1);
-                       char_count -= small_count;
-                       info->icount.tx += small_count;
-                       info->xmit_cnt -= small_count;
-                       info->xmit_tail = (info->xmit_tail + small_count) &
-                                       (SERIAL_XMIT_SIZE - 1);
-               }
-#else
-               while (info->xmit_cnt && char_count) {
-                       data = info->port.xmit_buf[info->xmit_tail];
-                       info->xmit_cnt--;
-                       info->xmit_tail = (info->xmit_tail + 1) &
-                                       (SERIAL_XMIT_SIZE - 1);
-
-                       cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data);
-                       tx_put = (tx_put + 1) & (tx_bufsize - 1);
-                       char_count--;
-                       info->icount.tx++;
-               }
-#endif
-               tty_wakeup(tty);
-ztxdone:
-               /* Update tx_put */
-               cy_writel(&buf_ctrl->tx_put, tx_put);
-       }
-}
-
-static void cyz_handle_cmd(struct cyclades_card *cinfo)
-{
-       struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
-       struct tty_struct *tty;
-       struct cyclades_port *info;
-       __u32 channel, param, fw_ver;
-       __u8 cmd;
-       int special_count;
-       int delta_count;
-
-       fw_ver = readl(&board_ctrl->fw_version);
-
-       while (cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1) {
-               special_count = 0;
-               delta_count = 0;
-               info = &cinfo->ports[channel];
-               tty = tty_port_tty_get(&info->port);
-               if (tty == NULL)
-                       continue;
-
-               switch (cmd) {
-               case C_CM_PR_ERROR:
-                       tty_insert_flip_char(tty, 0, TTY_PARITY);
-                       info->icount.rx++;
-                       special_count++;
-                       break;
-               case C_CM_FR_ERROR:
-                       tty_insert_flip_char(tty, 0, TTY_FRAME);
-                       info->icount.rx++;
-                       special_count++;
-                       break;
-               case C_CM_RXBRK:
-                       tty_insert_flip_char(tty, 0, TTY_BREAK);
-                       info->icount.rx++;
-                       special_count++;
-                       break;
-               case C_CM_MDCD:
-                       info->icount.dcd++;
-                       delta_count++;
-                       if (info->port.flags & ASYNC_CHECK_CD) {
-                               u32 dcd = fw_ver > 241 ? param :
-                                       readl(&info->u.cyz.ch_ctrl->rs_status);
-                               if (dcd & C_RS_DCD)
-                                       wake_up_interruptible(&info->port.open_wait);
-                               else
-                                       tty_hangup(tty);
-                       }
-                       break;
-               case C_CM_MCTS:
-                       info->icount.cts++;
-                       delta_count++;
-                       break;
-               case C_CM_MRI:
-                       info->icount.rng++;
-                       delta_count++;
-                       break;
-               case C_CM_MDSR:
-                       info->icount.dsr++;
-                       delta_count++;
-                       break;
-#ifdef Z_WAKE
-               case C_CM_IOCTLW:
-                       complete(&info->shutdown_wait);
-                       break;
-#endif
-#ifdef CONFIG_CYZ_INTR
-               case C_CM_RXHIWM:
-               case C_CM_RXNNDT:
-               case C_CM_INTBACK2:
-                       /* Reception Interrupt */
-#ifdef CY_DEBUG_INTERRUPTS
-                       printk(KERN_DEBUG "cyz_interrupt: rcvd intr, card %d, "
-                                       "port %ld\n", info->card, channel);
-#endif
-                       cyz_handle_rx(info, tty);
-                       break;
-               case C_CM_TXBEMPTY:
-               case C_CM_TXLOWWM:
-               case C_CM_INTBACK:
-                       /* Transmission Interrupt */
-#ifdef CY_DEBUG_INTERRUPTS
-                       printk(KERN_DEBUG "cyz_interrupt: xmit intr, card %d, "
-                                       "port %ld\n", info->card, channel);
-#endif
-                       cyz_handle_tx(info, tty);
-                       break;
-#endif                         /* CONFIG_CYZ_INTR */
-               case C_CM_FATAL:
-                       /* should do something with this !!! */
-                       break;
-               default:
-                       break;
-               }
-               if (delta_count)
-                       wake_up_interruptible(&info->port.delta_msr_wait);
-               if (special_count)
-                       tty_schedule_flip(tty);
-               tty_kref_put(tty);
-       }
-}
-
-#ifdef CONFIG_CYZ_INTR
-static irqreturn_t cyz_interrupt(int irq, void *dev_id)
-{
-       struct cyclades_card *cinfo = dev_id;
-
-       if (unlikely(!cyz_is_loaded(cinfo))) {
-#ifdef CY_DEBUG_INTERRUPTS
-               printk(KERN_DEBUG "cyz_interrupt: board not yet loaded "
-                               "(IRQ%d).\n", irq);
-#endif
-               return IRQ_NONE;
-       }
-
-       /* Handle the interrupts */
-       cyz_handle_cmd(cinfo);
-
-       return IRQ_HANDLED;
-}                              /* cyz_interrupt */
-
-static void cyz_rx_restart(unsigned long arg)
-{
-       struct cyclades_port *info = (struct cyclades_port *)arg;
-       struct cyclades_card *card = info->card;
-       int retval;
-       __u32 channel = info->line - card->first_line;
-       unsigned long flags;
-
-       spin_lock_irqsave(&card->card_lock, flags);
-       retval = cyz_issue_cmd(card, channel, C_CM_INTBACK2, 0L);
-       if (retval != 0) {
-               printk(KERN_ERR "cyc:cyz_rx_restart retval on ttyC%d was %x\n",
-                       info->line, retval);
-       }
-       spin_unlock_irqrestore(&card->card_lock, flags);
-}
-
-#else                          /* CONFIG_CYZ_INTR */
-
-static void cyz_poll(unsigned long arg)
-{
-       struct cyclades_card *cinfo;
-       struct cyclades_port *info;
-       unsigned long expires = jiffies + HZ;
-       unsigned int port, card;
-
-       for (card = 0; card < NR_CARDS; card++) {
-               cinfo = &cy_card[card];
-
-               if (!cy_is_Z(cinfo))
-                       continue;
-               if (!cyz_is_loaded(cinfo))
-                       continue;
-
-       /* Skip first polling cycle to avoid racing conditions with the FW */
-               if (!cinfo->intr_enabled) {
-                       cinfo->intr_enabled = 1;
-                       continue;
-               }
-
-               cyz_handle_cmd(cinfo);
-
-               for (port = 0; port < cinfo->nports; port++) {
-                       struct tty_struct *tty;
-
-                       info = &cinfo->ports[port];
-                       tty = tty_port_tty_get(&info->port);
-                       /* OK to pass NULL to the handle functions below.
-                          They need to drop the data in that case. */
-
-                       if (!info->throttle)
-                               cyz_handle_rx(info, tty);
-                       cyz_handle_tx(info, tty);
-                       tty_kref_put(tty);
-               }
-               /* poll every 'cyz_polling_cycle' period */
-               expires = jiffies + cyz_polling_cycle;
-       }
-       mod_timer(&cyz_timerlist, expires);
-}                              /* cyz_poll */
-
-#endif                         /* CONFIG_CYZ_INTR */
-
-/********** End of block of Cyclades-Z specific code *********/
-/***********************************************************/
-
-/* This is called whenever a port becomes active;
-   interrupts are enabled and DTR & RTS are turned on.
- */
-static int cy_startup(struct cyclades_port *info, struct tty_struct *tty)
-{
-       struct cyclades_card *card;
-       unsigned long flags;
-       int retval = 0;
-       int channel;
-       unsigned long page;
-
-       card = info->card;
-       channel = info->line - card->first_line;
-
-       page = get_zeroed_page(GFP_KERNEL);
-       if (!page)
-               return -ENOMEM;
-
-       spin_lock_irqsave(&card->card_lock, flags);
-
-       if (info->port.flags & ASYNC_INITIALIZED)
-               goto errout;
-
-       if (!info->type) {
-               set_bit(TTY_IO_ERROR, &tty->flags);
-               goto errout;
-       }
-
-       if (info->port.xmit_buf)
-               free_page(page);
-       else
-               info->port.xmit_buf = (unsigned char *)page;
-
-       spin_unlock_irqrestore(&card->card_lock, flags);
-
-       cy_set_line_char(info, tty);
-
-       if (!cy_is_Z(card)) {
-               channel &= 0x03;
-
-               spin_lock_irqsave(&card->card_lock, flags);
-
-               cyy_writeb(info, CyCAR, channel);
-
-               cyy_writeb(info, CyRTPR,
-                       (info->default_timeout ? info->default_timeout : 0x02));
-               /* 10ms rx timeout */
-
-               cyy_issue_cmd(info, CyCHAN_CTL | CyENB_RCVR | CyENB_XMTR);
-
-               cyy_change_rts_dtr(info, TIOCM_RTS | TIOCM_DTR, 0);
-
-               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyRxData);
-       } else {
-               struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
-
-               if (!cyz_is_loaded(card))
-                       return -ENODEV;
-
-#ifdef CY_DEBUG_OPEN
-               printk(KERN_DEBUG "cyc startup Z card %d, channel %d, "
-                       "base_addr %p\n", card, channel, card->base_addr);
-#endif
-               spin_lock_irqsave(&card->card_lock, flags);
-
-               cy_writel(&ch_ctrl->op_mode, C_CH_ENABLE);
-#ifdef Z_WAKE
-#ifdef CONFIG_CYZ_INTR
-               cy_writel(&ch_ctrl->intr_enable,
-                         C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM |
-                         C_IN_RXNNDT | C_IN_IOCTLW | C_IN_MDCD);
-#else
-               cy_writel(&ch_ctrl->intr_enable,
-                         C_IN_IOCTLW | C_IN_MDCD);
-#endif                         /* CONFIG_CYZ_INTR */
-#else
-#ifdef CONFIG_CYZ_INTR
-               cy_writel(&ch_ctrl->intr_enable,
-                         C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM |
-                         C_IN_RXNNDT | C_IN_MDCD);
-#else
-               cy_writel(&ch_ctrl->intr_enable, C_IN_MDCD);
-#endif                         /* CONFIG_CYZ_INTR */
-#endif                         /* Z_WAKE */
-
-               retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L);
-               if (retval != 0) {
-                       printk(KERN_ERR "cyc:startup(1) retval on ttyC%d was "
-                               "%x\n", info->line, retval);
-               }
-
-               /* Flush RX buffers before raising DTR and RTS */
-               retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_RX, 0L);
-               if (retval != 0) {
-                       printk(KERN_ERR "cyc:startup(2) retval on ttyC%d was "
-                               "%x\n", info->line, retval);
-               }
-
-               /* set timeout !!! */
-               /* set RTS and DTR !!! */
-               tty_port_raise_dtr_rts(&info->port);
-
-               /* enable send, recv, modem !!! */
-       }
-
-       info->port.flags |= ASYNC_INITIALIZED;
-
-       clear_bit(TTY_IO_ERROR, &tty->flags);
-       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-       info->breakon = info->breakoff = 0;
-       memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats));
-       info->idle_stats.in_use =
-       info->idle_stats.recv_idle =
-       info->idle_stats.xmit_idle = jiffies;
-
-       spin_unlock_irqrestore(&card->card_lock, flags);
-
-#ifdef CY_DEBUG_OPEN
-       printk(KERN_DEBUG "cyc startup done\n");
-#endif
-       return 0;
-
-errout:
-       spin_unlock_irqrestore(&card->card_lock, flags);
-       free_page(page);
-       return retval;
-}                              /* startup */
-
-static void start_xmit(struct cyclades_port *info)
-{
-       struct cyclades_card *card = info->card;
-       unsigned long flags;
-       int channel = info->line - card->first_line;
-
-       if (!cy_is_Z(card)) {
-               spin_lock_irqsave(&card->card_lock, flags);
-               cyy_writeb(info, CyCAR, channel & 0x03);
-               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy);
-               spin_unlock_irqrestore(&card->card_lock, flags);
-       } else {
-#ifdef CONFIG_CYZ_INTR
-               int retval;
-
-               spin_lock_irqsave(&card->card_lock, flags);
-               retval = cyz_issue_cmd(card, channel, C_CM_INTBACK, 0L);
-               if (retval != 0) {
-                       printk(KERN_ERR "cyc:start_xmit retval on ttyC%d was "
-                               "%x\n", info->line, retval);
-               }
-               spin_unlock_irqrestore(&card->card_lock, flags);
-#else                          /* CONFIG_CYZ_INTR */
-               /* Don't have to do anything at this time */
-#endif                         /* CONFIG_CYZ_INTR */
-       }
-}                              /* start_xmit */
-
-/*
- * This routine shuts down a serial port; interrupts are disabled,
- * and DTR is dropped if the hangup on close termio flag is on.
- */
-static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty)
-{
-       struct cyclades_card *card;
-       unsigned long flags;
-       int channel;
-
-       if (!(info->port.flags & ASYNC_INITIALIZED))
-               return;
-
-       card = info->card;
-       channel = info->line - card->first_line;
-       if (!cy_is_Z(card)) {
-               spin_lock_irqsave(&card->card_lock, flags);
-
-               /* Clear delta_msr_wait queue to avoid mem leaks. */
-               wake_up_interruptible(&info->port.delta_msr_wait);
-
-               if (info->port.xmit_buf) {
-                       unsigned char *temp;
-                       temp = info->port.xmit_buf;
-                       info->port.xmit_buf = NULL;
-                       free_page((unsigned long)temp);
-               }
-               if (tty->termios->c_cflag & HUPCL)
-                       cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR);
-
-               cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR);
-               /* it may be appropriate to clear _XMIT at
-                  some later date (after testing)!!! */
-
-               set_bit(TTY_IO_ERROR, &tty->flags);
-               info->port.flags &= ~ASYNC_INITIALIZED;
-               spin_unlock_irqrestore(&card->card_lock, flags);
-       } else {
-#ifdef CY_DEBUG_OPEN
-               printk(KERN_DEBUG "cyc shutdown Z card %d, channel %d, "
-                       "base_addr %p\n", card, channel, card->base_addr);
-#endif
-
-               if (!cyz_is_loaded(card))
-                       return;
-
-               spin_lock_irqsave(&card->card_lock, flags);
-
-               if (info->port.xmit_buf) {
-                       unsigned char *temp;
-                       temp = info->port.xmit_buf;
-                       info->port.xmit_buf = NULL;
-                       free_page((unsigned long)temp);
-               }
-
-               if (tty->termios->c_cflag & HUPCL)
-                       tty_port_lower_dtr_rts(&info->port);
-
-               set_bit(TTY_IO_ERROR, &tty->flags);
-               info->port.flags &= ~ASYNC_INITIALIZED;
-
-               spin_unlock_irqrestore(&card->card_lock, flags);
-       }
-
-#ifdef CY_DEBUG_OPEN
-       printk(KERN_DEBUG "cyc shutdown done\n");
-#endif
-}                              /* shutdown */
-
-/*
- * ------------------------------------------------------------
- * cy_open() and friends
- * ------------------------------------------------------------
- */
-
-/*
- * This routine is called whenever a serial port is opened.  It
- * performs the serial-specific initialization for the tty structure.
- */
-static int cy_open(struct tty_struct *tty, struct file *filp)
-{
-       struct cyclades_port *info;
-       unsigned int i, line;
-       int retval;
-
-       line = tty->index;
-       if (tty->index < 0 || NR_PORTS <= line)
-               return -ENODEV;
-
-       for (i = 0; i < NR_CARDS; i++)
-               if (line < cy_card[i].first_line + cy_card[i].nports &&
-                               line >= cy_card[i].first_line)
-                       break;
-       if (i >= NR_CARDS)
-               return -ENODEV;
-       info = &cy_card[i].ports[line - cy_card[i].first_line];
-       if (info->line < 0)
-               return -ENODEV;
-
-       /* If the card's firmware hasn't been loaded,
-          treat it as absent from the system.  This
-          will make the user pay attention.
-        */
-       if (cy_is_Z(info->card)) {
-               struct cyclades_card *cinfo = info->card;
-               struct FIRM_ID __iomem *firm_id = cinfo->base_addr + ID_ADDRESS;
-
-               if (!cyz_is_loaded(cinfo)) {
-                       if (cinfo->hw_ver == ZE_V1 && cyz_fpga_loaded(cinfo) &&
-                                       readl(&firm_id->signature) ==
-                                       ZFIRM_HLT) {
-                               printk(KERN_ERR "cyc:Cyclades-Z Error: you "
-                                       "need an external power supply for "
-                                       "this number of ports.\nFirmware "
-                                       "halted.\n");
-                       } else {
-                               printk(KERN_ERR "cyc:Cyclades-Z firmware not "
-                                       "yet loaded\n");
-                       }
-                       return -ENODEV;
-               }
-#ifdef CONFIG_CYZ_INTR
-               else {
-               /* In case this Z board is operating in interrupt mode, its
-                  interrupts should be enabled as soon as the first open
-                  happens to one of its ports. */
-                       if (!cinfo->intr_enabled) {
-                               u16 intr;
-
-                               /* Enable interrupts on the PLX chip */
-                               intr = readw(&cinfo->ctl_addr.p9060->
-                                               intr_ctrl_stat) | 0x0900;
-                               cy_writew(&cinfo->ctl_addr.p9060->
-                                               intr_ctrl_stat, intr);
-                               /* Enable interrupts on the FW */
-                               retval = cyz_issue_cmd(cinfo, 0,
-                                               C_CM_IRQ_ENBL, 0L);
-                               if (retval != 0) {
-                                       printk(KERN_ERR "cyc:IRQ enable retval "
-                                               "was %x\n", retval);
-                               }
-                               cinfo->intr_enabled = 1;
-                       }
-               }
-#endif                         /* CONFIG_CYZ_INTR */
-               /* Make sure this Z port really exists in hardware */
-               if (info->line > (cinfo->first_line + cinfo->nports - 1))
-                       return -ENODEV;
-       }
-#ifdef CY_DEBUG_OTHER
-       printk(KERN_DEBUG "cyc:cy_open ttyC%d\n", info->line);
-#endif
-       tty->driver_data = info;
-       if (serial_paranoia_check(info, tty->name, "cy_open"))
-               return -ENODEV;
-
-#ifdef CY_DEBUG_OPEN
-       printk(KERN_DEBUG "cyc:cy_open ttyC%d, count = %d\n", info->line,
-                       info->port.count);
-#endif
-       info->port.count++;
-#ifdef CY_DEBUG_COUNT
-       printk(KERN_DEBUG "cyc:cy_open (%d): incrementing count to %d\n",
-               current->pid, info->port.count);
-#endif
-
-       /*
-        * If the port is the middle of closing, bail out now
-        */
-       if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) {
-               wait_event_interruptible_tty(info->port.close_wait,
-                               !(info->port.flags & ASYNC_CLOSING));
-               return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS;
-       }
-
-       /*
-        * Start up serial port
-        */
-       retval = cy_startup(info, tty);
-       if (retval)
-               return retval;
-
-       retval = tty_port_block_til_ready(&info->port, tty, filp);
-       if (retval) {
-#ifdef CY_DEBUG_OPEN
-               printk(KERN_DEBUG "cyc:cy_open returning after block_til_ready "
-                       "with %d\n", retval);
-#endif
-               return retval;
-       }
-
-       info->throttle = 0;
-       tty_port_tty_set(&info->port, tty);
-
-#ifdef CY_DEBUG_OPEN
-       printk(KERN_DEBUG "cyc:cy_open done\n");
-#endif
-       return 0;
-}                              /* cy_open */
-
-/*
- * cy_wait_until_sent() --- wait until the transmitter is empty
- */
-static void cy_wait_until_sent(struct tty_struct *tty, int timeout)
-{
-       struct cyclades_card *card;
-       struct cyclades_port *info = tty->driver_data;
-       unsigned long orig_jiffies;
-       int char_time;
-
-       if (serial_paranoia_check(info, tty->name, "cy_wait_until_sent"))
-               return;
-
-       if (info->xmit_fifo_size == 0)
-               return;         /* Just in case.... */
-
-       orig_jiffies = jiffies;
-       /*
-        * Set the check interval to be 1/5 of the estimated time to
-        * send a single character, and make it at least 1.  The check
-        * interval should also be less than the timeout.
-        *
-        * Note: we have to use pretty tight timings here to satisfy
-        * the NIST-PCTS.
-        */
-       char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size;
-       char_time = char_time / 5;
-       if (char_time <= 0)
-               char_time = 1;
-       if (timeout < 0)
-               timeout = 0;
-       if (timeout)
-               char_time = min(char_time, timeout);
-       /*
-        * If the transmitter hasn't cleared in twice the approximate
-        * amount of time to send the entire FIFO, it probably won't
-        * ever clear.  This assumes the UART isn't doing flow
-        * control, which is currently the case.  Hence, if it ever
-        * takes longer than info->timeout, this is probably due to a
-        * UART bug of some kind.  So, we clamp the timeout parameter at
-        * 2*info->timeout.
-        */
-       if (!timeout || timeout > 2 * info->timeout)
-               timeout = 2 * info->timeout;
-#ifdef CY_DEBUG_WAIT_UNTIL_SENT
-       printk(KERN_DEBUG "In cy_wait_until_sent(%d) check=%d, jiff=%lu...",
-               timeout, char_time, jiffies);
-#endif
-       card = info->card;
-       if (!cy_is_Z(card)) {
-               while (cyy_readb(info, CySRER) & CyTxRdy) {
-#ifdef CY_DEBUG_WAIT_UNTIL_SENT
-                       printk(KERN_DEBUG "Not clean (jiff=%lu)...", jiffies);
-#endif
-                       if (msleep_interruptible(jiffies_to_msecs(char_time)))
-                               break;
-                       if (timeout && time_after(jiffies, orig_jiffies +
-                                       timeout))
-                               break;
-               }
-       }
-       /* Run one more char cycle */
-       msleep_interruptible(jiffies_to_msecs(char_time * 5));
-#ifdef CY_DEBUG_WAIT_UNTIL_SENT
-       printk(KERN_DEBUG "Clean (jiff=%lu)...done\n", jiffies);
-#endif
-}
-
-static void cy_flush_buffer(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-       struct cyclades_card *card;
-       int channel, retval;
-       unsigned long flags;
-
-#ifdef CY_DEBUG_IO
-       printk(KERN_DEBUG "cyc:cy_flush_buffer ttyC%d\n", info->line);
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_flush_buffer"))
-               return;
-
-       card = info->card;
-       channel = info->line - card->first_line;
-
-       spin_lock_irqsave(&card->card_lock, flags);
-       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-       spin_unlock_irqrestore(&card->card_lock, flags);
-
-       if (cy_is_Z(card)) {    /* If it is a Z card, flush the on-board
-                                          buffers as well */
-               spin_lock_irqsave(&card->card_lock, flags);
-               retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_TX, 0L);
-               if (retval != 0) {
-                       printk(KERN_ERR "cyc: flush_buffer retval on ttyC%d "
-                               "was %x\n", info->line, retval);
-               }
-               spin_unlock_irqrestore(&card->card_lock, flags);
-       }
-       tty_wakeup(tty);
-}                              /* cy_flush_buffer */
-
-
-static void cy_do_close(struct tty_port *port)
-{
-       struct cyclades_port *info = container_of(port, struct cyclades_port,
-                                                               port);
-       struct cyclades_card *card;
-       unsigned long flags;
-       int channel;
-
-       card = info->card;
-       channel = info->line - card->first_line;
-       spin_lock_irqsave(&card->card_lock, flags);
-
-       if (!cy_is_Z(card)) {
-               /* Stop accepting input */
-               cyy_writeb(info, CyCAR, channel & 0x03);
-               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyRxData);
-               if (info->port.flags & ASYNC_INITIALIZED) {
-                       /* Waiting for on-board buffers to be empty before
-                          closing the port */
-                       spin_unlock_irqrestore(&card->card_lock, flags);
-                       cy_wait_until_sent(port->tty, info->timeout);
-                       spin_lock_irqsave(&card->card_lock, flags);
-               }
-       } else {
-#ifdef Z_WAKE
-               /* Waiting for on-board buffers to be empty before closing
-                  the port */
-               struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
-               int retval;
-
-               if (readl(&ch_ctrl->flow_status) != C_FS_TXIDLE) {
-                       retval = cyz_issue_cmd(card, channel, C_CM_IOCTLW, 0L);
-                       if (retval != 0) {
-                               printk(KERN_DEBUG "cyc:cy_close retval on "
-                                       "ttyC%d was %x\n", info->line, retval);
-                       }
-                       spin_unlock_irqrestore(&card->card_lock, flags);
-                       wait_for_completion_interruptible(&info->shutdown_wait);
-                       spin_lock_irqsave(&card->card_lock, flags);
-               }
-#endif
-       }
-       spin_unlock_irqrestore(&card->card_lock, flags);
-       cy_shutdown(info, port->tty);
-}
-
-/*
- * This routine is called when a particular tty device is closed.
- */
-static void cy_close(struct tty_struct *tty, struct file *filp)
-{
-       struct cyclades_port *info = tty->driver_data;
-       if (!info || serial_paranoia_check(info, tty->name, "cy_close"))
-               return;
-       tty_port_close(&info->port, tty, filp);
-}                              /* cy_close */
-
-/* This routine gets called when tty_write has put something into
- * the write_queue.  The characters may come from user space or
- * kernel space.
- *
- * This routine will return the number of characters actually
- * accepted for writing.
- *
- * If the port is not already transmitting stuff, start it off by
- * enabling interrupts.  The interrupt service routine will then
- * ensure that the characters are sent.
- * If the port is already active, there is no need to kick it.
- *
- */
-static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count)
-{
-       struct cyclades_port *info = tty->driver_data;
-       unsigned long flags;
-       int c, ret = 0;
-
-#ifdef CY_DEBUG_IO
-       printk(KERN_DEBUG "cyc:cy_write ttyC%d\n", info->line);
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_write"))
-               return 0;
-
-       if (!info->port.xmit_buf)
-               return 0;
-
-       spin_lock_irqsave(&info->card->card_lock, flags);
-       while (1) {
-               c = min(count, (int)(SERIAL_XMIT_SIZE - info->xmit_cnt - 1));
-               c = min(c, (int)(SERIAL_XMIT_SIZE - info->xmit_head));
-
-               if (c <= 0)
-                       break;
-
-               memcpy(info->port.xmit_buf + info->xmit_head, buf, c);
-               info->xmit_head = (info->xmit_head + c) &
-                       (SERIAL_XMIT_SIZE - 1);
-               info->xmit_cnt += c;
-               buf += c;
-               count -= c;
-               ret += c;
-       }
-       spin_unlock_irqrestore(&info->card->card_lock, flags);
-
-       info->idle_stats.xmit_bytes += ret;
-       info->idle_stats.xmit_idle = jiffies;
-
-       if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped)
-               start_xmit(info);
-
-       return ret;
-}                              /* cy_write */
-
-/*
- * This routine is called by the kernel to write a single
- * character to the tty device.  If the kernel uses this routine,
- * it must call the flush_chars() routine (if defined) when it is
- * done stuffing characters into the driver.  If there is no room
- * in the queue, the character is ignored.
- */
-static int cy_put_char(struct tty_struct *tty, unsigned char ch)
-{
-       struct cyclades_port *info = tty->driver_data;
-       unsigned long flags;
-
-#ifdef CY_DEBUG_IO
-       printk(KERN_DEBUG "cyc:cy_put_char ttyC%d\n", info->line);
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_put_char"))
-               return 0;
-
-       if (!info->port.xmit_buf)
-               return 0;
-
-       spin_lock_irqsave(&info->card->card_lock, flags);
-       if (info->xmit_cnt >= (int)(SERIAL_XMIT_SIZE - 1)) {
-               spin_unlock_irqrestore(&info->card->card_lock, flags);
-               return 0;
-       }
-
-       info->port.xmit_buf[info->xmit_head++] = ch;
-       info->xmit_head &= SERIAL_XMIT_SIZE - 1;
-       info->xmit_cnt++;
-       info->idle_stats.xmit_bytes++;
-       info->idle_stats.xmit_idle = jiffies;
-       spin_unlock_irqrestore(&info->card->card_lock, flags);
-       return 1;
-}                              /* cy_put_char */
-
-/*
- * This routine is called by the kernel after it has written a
- * series of characters to the tty device using put_char().
- */
-static void cy_flush_chars(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-
-#ifdef CY_DEBUG_IO
-       printk(KERN_DEBUG "cyc:cy_flush_chars ttyC%d\n", info->line);
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_flush_chars"))
-               return;
-
-       if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
-                       !info->port.xmit_buf)
-               return;
-
-       start_xmit(info);
-}                              /* cy_flush_chars */
-
-/*
- * This routine returns the numbers of characters the tty driver
- * will accept for queuing to be written.  This number is subject
- * to change as output buffers get emptied, or if the output flow
- * control is activated.
- */
-static int cy_write_room(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-       int ret;
-
-#ifdef CY_DEBUG_IO
-       printk(KERN_DEBUG "cyc:cy_write_room ttyC%d\n", info->line);
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_write_room"))
-               return 0;
-       ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
-       if (ret < 0)
-               ret = 0;
-       return ret;
-}                              /* cy_write_room */
-
-static int cy_chars_in_buffer(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-
-       if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer"))
-               return 0;
-
-#ifdef Z_EXT_CHARS_IN_BUFFER
-       if (!cy_is_Z(info->card)) {
-#endif                         /* Z_EXT_CHARS_IN_BUFFER */
-#ifdef CY_DEBUG_IO
-               printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n",
-                       info->line, info->xmit_cnt);
-#endif
-               return info->xmit_cnt;
-#ifdef Z_EXT_CHARS_IN_BUFFER
-       } else {
-               struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl;
-               int char_count;
-               __u32 tx_put, tx_get, tx_bufsize;
-
-               tx_get = readl(&buf_ctrl->tx_get);
-               tx_put = readl(&buf_ctrl->tx_put);
-               tx_bufsize = readl(&buf_ctrl->tx_bufsize);
-               if (tx_put >= tx_get)
-                       char_count = tx_put - tx_get;
-               else
-                       char_count = tx_put - tx_get + tx_bufsize;
-#ifdef CY_DEBUG_IO
-               printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n",
-                       info->line, info->xmit_cnt + char_count);
-#endif
-               return info->xmit_cnt + char_count;
-       }
-#endif                         /* Z_EXT_CHARS_IN_BUFFER */
-}                              /* cy_chars_in_buffer */
-
-/*
- * ------------------------------------------------------------
- * cy_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static void cyy_baud_calc(struct cyclades_port *info, __u32 baud)
-{
-       int co, co_val, bpr;
-       __u32 cy_clock = ((info->chip_rev >= CD1400_REV_J) ? 60000000 :
-                       25000000);
-
-       if (baud == 0) {
-               info->tbpr = info->tco = info->rbpr = info->rco = 0;
-               return;
-       }
-
-       /* determine which prescaler to use */
-       for (co = 4, co_val = 2048; co; co--, co_val >>= 2) {
-               if (cy_clock / co_val / baud > 63)
-                       break;
-       }
-
-       bpr = (cy_clock / co_val * 2 / baud + 1) / 2;
-       if (bpr > 255)
-               bpr = 255;
-
-       info->tbpr = info->rbpr = bpr;
-       info->tco = info->rco = co;
-}
-
-/*
- * This routine finds or computes the various line characteristics.
- * It used to be called config_setup
- */
-static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty)
-{
-       struct cyclades_card *card;
-       unsigned long flags;
-       int channel;
-       unsigned cflag, iflag;
-       int baud, baud_rate = 0;
-       int i;
-
-       if (!tty->termios) /* XXX can this happen at all? */
-               return;
-
-       if (info->line == -1)
-               return;
-
-       cflag = tty->termios->c_cflag;
-       iflag = tty->termios->c_iflag;
-
-       /*
-        * Set up the tty->alt_speed kludge
-        */
-       if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-               tty->alt_speed = 57600;
-       if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-               tty->alt_speed = 115200;
-       if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-               tty->alt_speed = 230400;
-       if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-               tty->alt_speed = 460800;
-
-       card = info->card;
-       channel = info->line - card->first_line;
-
-       if (!cy_is_Z(card)) {
-               u32 cflags;
-
-               /* baud rate */
-               baud = tty_get_baud_rate(tty);
-               if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
-                               ASYNC_SPD_CUST) {
-                       if (info->custom_divisor)
-                               baud_rate = info->baud / info->custom_divisor;
-                       else
-                               baud_rate = info->baud;
-               } else if (baud > CD1400_MAX_SPEED) {
-                       baud = CD1400_MAX_SPEED;
-               }
-               /* find the baud index */
-               for (i = 0; i < 20; i++) {
-                       if (baud == baud_table[i])
-                               break;
-               }
-               if (i == 20)
-                       i = 19; /* CD1400_MAX_SPEED */
-
-               if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
-                               ASYNC_SPD_CUST) {
-                       cyy_baud_calc(info, baud_rate);
-               } else {
-                       if (info->chip_rev >= CD1400_REV_J) {
-                               /* It is a CD1400 rev. J or later */
-                               info->tbpr = baud_bpr_60[i];    /* Tx BPR */
-                               info->tco = baud_co_60[i];      /* Tx CO */
-                               info->rbpr = baud_bpr_60[i];    /* Rx BPR */
-                               info->rco = baud_co_60[i];      /* Rx CO */
-                       } else {
-                               info->tbpr = baud_bpr_25[i];    /* Tx BPR */
-                               info->tco = baud_co_25[i];      /* Tx CO */
-                               info->rbpr = baud_bpr_25[i];    /* Rx BPR */
-                               info->rco = baud_co_25[i];      /* Rx CO */
-                       }
-               }
-               if (baud_table[i] == 134) {
-                       /* get it right for 134.5 baud */
-                       info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) +
-                                       2;
-               } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
-                               ASYNC_SPD_CUST) {
-                       info->timeout = (info->xmit_fifo_size * HZ * 15 /
-                                       baud_rate) + 2;
-               } else if (baud_table[i]) {
-                       info->timeout = (info->xmit_fifo_size * HZ * 15 /
-                                       baud_table[i]) + 2;
-                       /* this needs to be propagated into the card info */
-               } else {
-                       info->timeout = 0;
-               }
-               /* By tradition (is it a standard?) a baud rate of zero
-                  implies the line should be/has been closed.  A bit
-                  later in this routine such a test is performed. */
-
-               /* byte size and parity */
-               info->cor5 = 0;
-               info->cor4 = 0;
-               /* receive threshold */
-               info->cor3 = (info->default_threshold ?
-                               info->default_threshold : baud_cor3[i]);
-               info->cor2 = CyETC;
-               switch (cflag & CSIZE) {
-               case CS5:
-                       info->cor1 = Cy_5_BITS;
-                       break;
-               case CS6:
-                       info->cor1 = Cy_6_BITS;
-                       break;
-               case CS7:
-                       info->cor1 = Cy_7_BITS;
-                       break;
-               case CS8:
-                       info->cor1 = Cy_8_BITS;
-                       break;
-               }
-               if (cflag & CSTOPB)
-                       info->cor1 |= Cy_2_STOP;
-
-               if (cflag & PARENB) {
-                       if (cflag & PARODD)
-                               info->cor1 |= CyPARITY_O;
-                       else
-                               info->cor1 |= CyPARITY_E;
-               } else
-                       info->cor1 |= CyPARITY_NONE;
-
-               /* CTS flow control flag */
-               if (cflag & CRTSCTS) {
-                       info->port.flags |= ASYNC_CTS_FLOW;
-                       info->cor2 |= CyCtsAE;
-               } else {
-                       info->port.flags &= ~ASYNC_CTS_FLOW;
-                       info->cor2 &= ~CyCtsAE;
-               }
-               if (cflag & CLOCAL)
-                       info->port.flags &= ~ASYNC_CHECK_CD;
-               else
-                       info->port.flags |= ASYNC_CHECK_CD;
-
-        /***********************************************
-           The hardware option, CyRtsAO, presents RTS when
-           the chip has characters to send.  Since most modems
-           use RTS as reverse (inbound) flow control, this
-           option is not used.  If inbound flow control is
-           necessary, DTR can be programmed to provide the
-           appropriate signals for use with a non-standard
-           cable.  Contact Marcio Saito for details.
-        ***********************************************/
-
-               channel &= 0x03;
-
-               spin_lock_irqsave(&card->card_lock, flags);
-               cyy_writeb(info, CyCAR, channel);
-
-               /* tx and rx baud rate */
-
-               cyy_writeb(info, CyTCOR, info->tco);
-               cyy_writeb(info, CyTBPR, info->tbpr);
-               cyy_writeb(info, CyRCOR, info->rco);
-               cyy_writeb(info, CyRBPR, info->rbpr);
-
-               /* set line characteristics  according configuration */
-
-               cyy_writeb(info, CySCHR1, START_CHAR(tty));
-               cyy_writeb(info, CySCHR2, STOP_CHAR(tty));
-               cyy_writeb(info, CyCOR1, info->cor1);
-               cyy_writeb(info, CyCOR2, info->cor2);
-               cyy_writeb(info, CyCOR3, info->cor3);
-               cyy_writeb(info, CyCOR4, info->cor4);
-               cyy_writeb(info, CyCOR5, info->cor5);
-
-               cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR1ch | CyCOR2ch |
-                               CyCOR3ch);
-
-               /* !!! Is this needed? */
-               cyy_writeb(info, CyCAR, channel);
-               cyy_writeb(info, CyRTPR,
-                       (info->default_timeout ? info->default_timeout : 0x02));
-               /* 10ms rx timeout */
-
-               cflags = CyCTS;
-               if (!C_CLOCAL(tty))
-                       cflags |= CyDSR | CyRI | CyDCD;
-               /* without modem intr */
-               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyMdmCh);
-               /* act on 1->0 modem transitions */
-               if ((cflag & CRTSCTS) && info->rflow)
-                       cyy_writeb(info, CyMCOR1, cflags | rflow_thr[i]);
-               else
-                       cyy_writeb(info, CyMCOR1, cflags);
-               /* act on 0->1 modem transitions */
-               cyy_writeb(info, CyMCOR2, cflags);
-
-               if (i == 0)     /* baud rate is zero, turn off line */
-                       cyy_change_rts_dtr(info, 0, TIOCM_DTR);
-               else
-                       cyy_change_rts_dtr(info, TIOCM_DTR, 0);
-
-               clear_bit(TTY_IO_ERROR, &tty->flags);
-               spin_unlock_irqrestore(&card->card_lock, flags);
-
-       } else {
-               struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
-               __u32 sw_flow;
-               int retval;
-
-               if (!cyz_is_loaded(card))
-                       return;
-
-               /* baud rate */
-               baud = tty_get_baud_rate(tty);
-               if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
-                               ASYNC_SPD_CUST) {
-                       if (info->custom_divisor)
-                               baud_rate = info->baud / info->custom_divisor;
-                       else
-                               baud_rate = info->baud;
-               } else if (baud > CYZ_MAX_SPEED) {
-                       baud = CYZ_MAX_SPEED;
-               }
-               cy_writel(&ch_ctrl->comm_baud, baud);
-
-               if (baud == 134) {
-                       /* get it right for 134.5 baud */
-                       info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) +
-                                       2;
-               } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
-                               ASYNC_SPD_CUST) {
-                       info->timeout = (info->xmit_fifo_size * HZ * 15 /
-                                       baud_rate) + 2;
-               } else if (baud) {
-                       info->timeout = (info->xmit_fifo_size * HZ * 15 /
-                                       baud) + 2;
-                       /* this needs to be propagated into the card info */
-               } else {
-                       info->timeout = 0;
-               }
-
-               /* byte size and parity */
-               switch (cflag & CSIZE) {
-               case CS5:
-                       cy_writel(&ch_ctrl->comm_data_l, C_DL_CS5);
-                       break;
-               case CS6:
-                       cy_writel(&ch_ctrl->comm_data_l, C_DL_CS6);
-                       break;
-               case CS7:
-                       cy_writel(&ch_ctrl->comm_data_l, C_DL_CS7);
-                       break;
-               case CS8:
-                       cy_writel(&ch_ctrl->comm_data_l, C_DL_CS8);
-                       break;
-               }
-               if (cflag & CSTOPB) {
-                       cy_writel(&ch_ctrl->comm_data_l,
-                                 readl(&ch_ctrl->comm_data_l) | C_DL_2STOP);
-               } else {
-                       cy_writel(&ch_ctrl->comm_data_l,
-                                 readl(&ch_ctrl->comm_data_l) | C_DL_1STOP);
-               }
-               if (cflag & PARENB) {
-                       if (cflag & PARODD)
-                               cy_writel(&ch_ctrl->comm_parity, C_PR_ODD);
-                       else
-                               cy_writel(&ch_ctrl->comm_parity, C_PR_EVEN);
-               } else
-                       cy_writel(&ch_ctrl->comm_parity, C_PR_NONE);
-
-               /* CTS flow control flag */
-               if (cflag & CRTSCTS) {
-                       cy_writel(&ch_ctrl->hw_flow,
-                               readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS);
-               } else {
-                       cy_writel(&ch_ctrl->hw_flow, readl(&ch_ctrl->hw_flow) &
-                                       ~(C_RS_CTS | C_RS_RTS));
-               }
-               /* As the HW flow control is done in firmware, the driver
-                  doesn't need to care about it */
-               info->port.flags &= ~ASYNC_CTS_FLOW;
-
-               /* XON/XOFF/XANY flow control flags */
-               sw_flow = 0;
-               if (iflag & IXON) {
-                       sw_flow |= C_FL_OXX;
-                       if (iflag & IXANY)
-                               sw_flow |= C_FL_OIXANY;
-               }
-               cy_writel(&ch_ctrl->sw_flow, sw_flow);
-
-               retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L);
-               if (retval != 0) {
-                       printk(KERN_ERR "cyc:set_line_char retval on ttyC%d "
-                               "was %x\n", info->line, retval);
-               }
-
-               /* CD sensitivity */
-               if (cflag & CLOCAL)
-                       info->port.flags &= ~ASYNC_CHECK_CD;
-               else
-                       info->port.flags |= ASYNC_CHECK_CD;
-
-               if (baud == 0) {        /* baud rate is zero, turn off line */
-                       cy_writel(&ch_ctrl->rs_control,
-                                 readl(&ch_ctrl->rs_control) & ~C_RS_DTR);
-#ifdef CY_DEBUG_DTR
-                       printk(KERN_DEBUG "cyc:set_line_char dropping Z DTR\n");
-#endif
-               } else {
-                       cy_writel(&ch_ctrl->rs_control,
-                                 readl(&ch_ctrl->rs_control) | C_RS_DTR);
-#ifdef CY_DEBUG_DTR
-                       printk(KERN_DEBUG "cyc:set_line_char raising Z DTR\n");
-#endif
-               }
-
-               retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L);
-               if (retval != 0) {
-                       printk(KERN_ERR "cyc:set_line_char(2) retval on ttyC%d "
-                               "was %x\n", info->line, retval);
-               }
-
-               clear_bit(TTY_IO_ERROR, &tty->flags);
-       }
-}                              /* set_line_char */
-
-static int cy_get_serial_info(struct cyclades_port *info,
-               struct serial_struct __user *retinfo)
-{
-       struct cyclades_card *cinfo = info->card;
-       struct serial_struct tmp = {
-               .type = info->type,
-               .line = info->line,
-               .port = (info->card - cy_card) * 0x100 + info->line -
-                       cinfo->first_line,
-               .irq = cinfo->irq,
-               .flags = info->port.flags,
-               .close_delay = info->port.close_delay,
-               .closing_wait = info->port.closing_wait,
-               .baud_base = info->baud,
-               .custom_divisor = info->custom_divisor,
-               .hub6 = 0,              /*!!! */
-       };
-       return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;
-}
-
-static int
-cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty,
-               struct serial_struct __user *new_info)
-{
-       struct serial_struct new_serial;
-       int ret;
-
-       if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
-               return -EFAULT;
-
-       mutex_lock(&info->port.mutex);
-       if (!capable(CAP_SYS_ADMIN)) {
-               if (new_serial.close_delay != info->port.close_delay ||
-                               new_serial.baud_base != info->baud ||
-                               (new_serial.flags & ASYNC_FLAGS &
-                                       ~ASYNC_USR_MASK) !=
-                               (info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))
-               {
-                       mutex_unlock(&info->port.mutex);
-                       return -EPERM;
-               }
-               info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) |
-                               (new_serial.flags & ASYNC_USR_MASK);
-               info->baud = new_serial.baud_base;
-               info->custom_divisor = new_serial.custom_divisor;
-               goto check_and_exit;
-       }
-
-       /*
-        * OK, past this point, all the error checking has been done.
-        * At this point, we start making changes.....
-        */
-
-       info->baud = new_serial.baud_base;
-       info->custom_divisor = new_serial.custom_divisor;
-       info->port.flags = (info->port.flags & ~ASYNC_FLAGS) |
-                       (new_serial.flags & ASYNC_FLAGS);
-       info->port.close_delay = new_serial.close_delay * HZ / 100;
-       info->port.closing_wait = new_serial.closing_wait * HZ / 100;
-
-check_and_exit:
-       if (info->port.flags & ASYNC_INITIALIZED) {
-               cy_set_line_char(info, tty);
-               ret = 0;
-       } else {
-               ret = cy_startup(info, tty);
-       }
-       mutex_unlock(&info->port.mutex);
-       return ret;
-}                              /* set_serial_info */
-
-/*
- * 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 get_lsr_info(struct cyclades_port *info, unsigned int __user *value)
-{
-       struct cyclades_card *card = info->card;
-       unsigned int result;
-       unsigned long flags;
-       u8 status;
-
-       if (!cy_is_Z(card)) {
-               spin_lock_irqsave(&card->card_lock, flags);
-               status = cyy_readb(info, CySRER) & (CyTxRdy | CyTxMpty);
-               spin_unlock_irqrestore(&card->card_lock, flags);
-               result = (status ? 0 : TIOCSER_TEMT);
-       } else {
-               /* Not supported yet */
-               return -EINVAL;
-       }
-       return put_user(result, (unsigned long __user *)value);
-}
-
-static int cy_tiocmget(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-       struct cyclades_card *card;
-       int result;
-
-       if (serial_paranoia_check(info, tty->name, __func__))
-               return -ENODEV;
-
-       card = info->card;
-
-       if (!cy_is_Z(card)) {
-               unsigned long flags;
-               int channel = info->line - card->first_line;
-               u8 status;
-
-               spin_lock_irqsave(&card->card_lock, flags);
-               cyy_writeb(info, CyCAR, channel & 0x03);
-               status = cyy_readb(info, CyMSVR1);
-               status |= cyy_readb(info, CyMSVR2);
-               spin_unlock_irqrestore(&card->card_lock, flags);
-
-               if (info->rtsdtr_inv) {
-                       result = ((status & CyRTS) ? TIOCM_DTR : 0) |
-                               ((status & CyDTR) ? TIOCM_RTS : 0);
-               } else {
-                       result = ((status & CyRTS) ? TIOCM_RTS : 0) |
-                               ((status & CyDTR) ? TIOCM_DTR : 0);
-               }
-               result |= ((status & CyDCD) ? TIOCM_CAR : 0) |
-                       ((status & CyRI) ? TIOCM_RNG : 0) |
-                       ((status & CyDSR) ? TIOCM_DSR : 0) |
-                       ((status & CyCTS) ? TIOCM_CTS : 0);
-       } else {
-               u32 lstatus;
-
-               if (!cyz_is_loaded(card)) {
-                       result = -ENODEV;
-                       goto end;
-               }
-
-               lstatus = readl(&info->u.cyz.ch_ctrl->rs_status);
-               result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) |
-                       ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) |
-                       ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) |
-                       ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) |
-                       ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) |
-                       ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0);
-       }
-end:
-       return result;
-}                              /* cy_tiomget */
-
-static int
-cy_tiocmset(struct tty_struct *tty,
-               unsigned int set, unsigned int clear)
-{
-       struct cyclades_port *info = tty->driver_data;
-       struct cyclades_card *card;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->name, __func__))
-               return -ENODEV;
-
-       card = info->card;
-       if (!cy_is_Z(card)) {
-               spin_lock_irqsave(&card->card_lock, flags);
-               cyy_change_rts_dtr(info, set, clear);
-               spin_unlock_irqrestore(&card->card_lock, flags);
-       } else {
-               struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
-               int retval, channel = info->line - card->first_line;
-               u32 rs;
-
-               if (!cyz_is_loaded(card))
-                       return -ENODEV;
-
-               spin_lock_irqsave(&card->card_lock, flags);
-               rs = readl(&ch_ctrl->rs_control);
-               if (set & TIOCM_RTS)
-                       rs |= C_RS_RTS;
-               if (clear & TIOCM_RTS)
-                       rs &= ~C_RS_RTS;
-               if (set & TIOCM_DTR) {
-                       rs |= C_RS_DTR;
-#ifdef CY_DEBUG_DTR
-                       printk(KERN_DEBUG "cyc:set_modem_info raising Z DTR\n");
-#endif
-               }
-               if (clear & TIOCM_DTR) {
-                       rs &= ~C_RS_DTR;
-#ifdef CY_DEBUG_DTR
-                       printk(KERN_DEBUG "cyc:set_modem_info clearing "
-                               "Z DTR\n");
-#endif
-               }
-               cy_writel(&ch_ctrl->rs_control, rs);
-               retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L);
-               spin_unlock_irqrestore(&card->card_lock, flags);
-               if (retval != 0) {
-                       printk(KERN_ERR "cyc:set_modem_info retval on ttyC%d "
-                               "was %x\n", info->line, retval);
-               }
-       }
-       return 0;
-}
-
-/*
- * cy_break() --- routine which turns the break handling on or off
- */
-static int cy_break(struct tty_struct *tty, int break_state)
-{
-       struct cyclades_port *info = tty->driver_data;
-       struct cyclades_card *card;
-       unsigned long flags;
-       int retval = 0;
-
-       if (serial_paranoia_check(info, tty->name, "cy_break"))
-               return -EINVAL;
-
-       card = info->card;
-
-       spin_lock_irqsave(&card->card_lock, flags);
-       if (!cy_is_Z(card)) {
-               /* Let the transmit ISR take care of this (since it
-                  requires stuffing characters into the output stream).
-                */
-               if (break_state == -1) {
-                       if (!info->breakon) {
-                               info->breakon = 1;
-                               if (!info->xmit_cnt) {
-                                       spin_unlock_irqrestore(&card->card_lock, flags);
-                                       start_xmit(info);
-                                       spin_lock_irqsave(&card->card_lock, flags);
-                               }
-                       }
-               } else {
-                       if (!info->breakoff) {
-                               info->breakoff = 1;
-                               if (!info->xmit_cnt) {
-                                       spin_unlock_irqrestore(&card->card_lock, flags);
-                                       start_xmit(info);
-                                       spin_lock_irqsave(&card->card_lock, flags);
-                               }
-                       }
-               }
-       } else {
-               if (break_state == -1) {
-                       retval = cyz_issue_cmd(card,
-                               info->line - card->first_line,
-                               C_CM_SET_BREAK, 0L);
-                       if (retval != 0) {
-                               printk(KERN_ERR "cyc:cy_break (set) retval on "
-                                       "ttyC%d was %x\n", info->line, retval);
-                       }
-               } else {
-                       retval = cyz_issue_cmd(card,
-                               info->line - card->first_line,
-                               C_CM_CLR_BREAK, 0L);
-                       if (retval != 0) {
-                               printk(KERN_DEBUG "cyc:cy_break (clr) retval "
-                                       "on ttyC%d was %x\n", info->line,
-                                       retval);
-                       }
-               }
-       }
-       spin_unlock_irqrestore(&card->card_lock, flags);
-       return retval;
-}                              /* cy_break */
-
-static int set_threshold(struct cyclades_port *info, unsigned long value)
-{
-       struct cyclades_card *card = info->card;
-       unsigned long flags;
-
-       if (!cy_is_Z(card)) {
-               info->cor3 &= ~CyREC_FIFO;
-               info->cor3 |= value & CyREC_FIFO;
-
-               spin_lock_irqsave(&card->card_lock, flags);
-               cyy_writeb(info, CyCOR3, info->cor3);
-               cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR3ch);
-               spin_unlock_irqrestore(&card->card_lock, flags);
-       }
-       return 0;
-}                              /* set_threshold */
-
-static int get_threshold(struct cyclades_port *info,
-                                               unsigned long __user *value)
-{
-       struct cyclades_card *card = info->card;
-
-       if (!cy_is_Z(card)) {
-               u8 tmp = cyy_readb(info, CyCOR3) & CyREC_FIFO;
-               return put_user(tmp, value);
-       }
-       return 0;
-}                              /* get_threshold */
-
-static int set_timeout(struct cyclades_port *info, unsigned long value)
-{
-       struct cyclades_card *card = info->card;
-       unsigned long flags;
-
-       if (!cy_is_Z(card)) {
-               spin_lock_irqsave(&card->card_lock, flags);
-               cyy_writeb(info, CyRTPR, value & 0xff);
-               spin_unlock_irqrestore(&card->card_lock, flags);
-       }
-       return 0;
-}                              /* set_timeout */
-
-static int get_timeout(struct cyclades_port *info,
-                                               unsigned long __user *value)
-{
-       struct cyclades_card *card = info->card;
-
-       if (!cy_is_Z(card)) {
-               u8 tmp = cyy_readb(info, CyRTPR);
-               return put_user(tmp, value);
-       }
-       return 0;
-}                              /* get_timeout */
-
-static int cy_cflags_changed(struct cyclades_port *info, unsigned long arg,
-               struct cyclades_icount *cprev)
-{
-       struct cyclades_icount cnow;
-       unsigned long flags;
-       int ret;
-
-       spin_lock_irqsave(&info->card->card_lock, flags);
-       cnow = info->icount;    /* atomic copy */
-       spin_unlock_irqrestore(&info->card->card_lock, flags);
-
-       ret =   ((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));
-
-       *cprev = cnow;
-
-       return ret;
-}
-
-/*
- * This routine allows the tty driver to implement device-
- * specific ioctl's.  If the ioctl number passed in cmd is
- * not recognized by the driver, it should return ENOIOCTLCMD.
- */
-static int
-cy_ioctl(struct tty_struct *tty,
-        unsigned int cmd, unsigned long arg)
-{
-       struct cyclades_port *info = tty->driver_data;
-       struct cyclades_icount cnow;    /* kernel counter temps */
-       int ret_val = 0;
-       unsigned long flags;
-       void __user *argp = (void __user *)arg;
-
-       if (serial_paranoia_check(info, tty->name, "cy_ioctl"))
-               return -ENODEV;
-
-#ifdef CY_DEBUG_OTHER
-       printk(KERN_DEBUG "cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n",
-               info->line, cmd, arg);
-#endif
-
-       switch (cmd) {
-       case CYGETMON:
-               if (copy_to_user(argp, &info->mon, sizeof(info->mon))) {
-                       ret_val = -EFAULT;
-                       break;
-               }
-               memset(&info->mon, 0, sizeof(info->mon));
-               break;
-       case CYGETTHRESH:
-               ret_val = get_threshold(info, argp);
-               break;
-       case CYSETTHRESH:
-               ret_val = set_threshold(info, arg);
-               break;
-       case CYGETDEFTHRESH:
-               ret_val = put_user(info->default_threshold,
-                               (unsigned long __user *)argp);
-               break;
-       case CYSETDEFTHRESH:
-               info->default_threshold = arg & 0x0f;
-               break;
-       case CYGETTIMEOUT:
-               ret_val = get_timeout(info, argp);
-               break;
-       case CYSETTIMEOUT:
-               ret_val = set_timeout(info, arg);
-               break;
-       case CYGETDEFTIMEOUT:
-               ret_val = put_user(info->default_timeout,
-                               (unsigned long __user *)argp);
-               break;
-       case CYSETDEFTIMEOUT:
-               info->default_timeout = arg & 0xff;
-               break;
-       case CYSETRFLOW:
-               info->rflow = (int)arg;
-               break;
-       case CYGETRFLOW:
-               ret_val = info->rflow;
-               break;
-       case CYSETRTSDTR_INV:
-               info->rtsdtr_inv = (int)arg;
-               break;
-       case CYGETRTSDTR_INV:
-               ret_val = info->rtsdtr_inv;
-               break;
-       case CYGETCD1400VER:
-               ret_val = info->chip_rev;
-               break;
-#ifndef CONFIG_CYZ_INTR
-       case CYZSETPOLLCYCLE:
-               cyz_polling_cycle = (arg * HZ) / 1000;
-               break;
-       case CYZGETPOLLCYCLE:
-               ret_val = (cyz_polling_cycle * 1000) / HZ;
-               break;
-#endif                         /* CONFIG_CYZ_INTR */
-       case CYSETWAIT:
-               info->port.closing_wait = (unsigned short)arg * HZ / 100;
-               break;
-       case CYGETWAIT:
-               ret_val = info->port.closing_wait / (HZ / 100);
-               break;
-       case TIOCGSERIAL:
-               ret_val = cy_get_serial_info(info, argp);
-               break;
-       case TIOCSSERIAL:
-               ret_val = cy_set_serial_info(info, tty, argp);
-               break;
-       case TIOCSERGETLSR:     /* Get line status register */
-               ret_val = get_lsr_info(info, argp);
-               break;
-               /*
-                * 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:
-               spin_lock_irqsave(&info->card->card_lock, flags);
-               /* note the counters on entry */
-               cnow = info->icount;
-               spin_unlock_irqrestore(&info->card->card_lock, flags);
-               ret_val = wait_event_interruptible(info->port.delta_msr_wait,
-                               cy_cflags_changed(info, arg, &cnow));
-               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.
-                */
-       default:
-               ret_val = -ENOIOCTLCMD;
-       }
-
-#ifdef CY_DEBUG_OTHER
-       printk(KERN_DEBUG "cyc:cy_ioctl done\n");
-#endif
-       return ret_val;
-}                              /* cy_ioctl */
-
-static int cy_get_icount(struct tty_struct *tty,
-                               struct serial_icounter_struct *sic)
-{
-       struct cyclades_port *info = tty->driver_data;
-       struct cyclades_icount cnow;    /* Used to snapshot */
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->card->card_lock, flags);
-       cnow = info->icount;
-       spin_unlock_irqrestore(&info->card->card_lock, flags);
-
-       sic->cts = cnow.cts;
-       sic->dsr = cnow.dsr;
-       sic->rng = cnow.rng;
-       sic->dcd = cnow.dcd;
-       sic->rx = cnow.rx;
-       sic->tx = cnow.tx;
-       sic->frame = cnow.frame;
-       sic->overrun = cnow.overrun;
-       sic->parity = cnow.parity;
-       sic->brk = cnow.brk;
-       sic->buf_overrun = cnow.buf_overrun;
-       return 0;
-}
-
-/*
- * This routine allows the tty driver to be notified when
- * device's termios settings have changed.  Note that a
- * well-designed tty driver should be prepared to accept the case
- * where old == NULL, and try to do something rational.
- */
-static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
-{
-       struct cyclades_port *info = tty->driver_data;
-
-#ifdef CY_DEBUG_OTHER
-       printk(KERN_DEBUG "cyc:cy_set_termios ttyC%d\n", info->line);
-#endif
-
-       cy_set_line_char(info, tty);
-
-       if ((old_termios->c_cflag & CRTSCTS) &&
-                       !(tty->termios->c_cflag & CRTSCTS)) {
-               tty->hw_stopped = 0;
-               cy_start(tty);
-       }
-#if 0
-       /*
-        * No need to wake up processes in open wait, since they
-        * sample the CLOCAL flag once, and don't recheck it.
-        * XXX  It's not clear whether the current behavior is correct
-        * or not.  Hence, this may change.....
-        */
-       if (!(old_termios->c_cflag & CLOCAL) &&
-           (tty->termios->c_cflag & CLOCAL))
-               wake_up_interruptible(&info->port.open_wait);
-#endif
-}                              /* cy_set_termios */
-
-/* This function is used to send a high-priority XON/XOFF character to
-   the device.
-*/
-static void cy_send_xchar(struct tty_struct *tty, char ch)
-{
-       struct cyclades_port *info = tty->driver_data;
-       struct cyclades_card *card;
-       int channel;
-
-       if (serial_paranoia_check(info, tty->name, "cy_send_xchar"))
-               return;
-
-       info->x_char = ch;
-
-       if (ch)
-               cy_start(tty);
-
-       card = info->card;
-       channel = info->line - card->first_line;
-
-       if (cy_is_Z(card)) {
-               if (ch == STOP_CHAR(tty))
-                       cyz_issue_cmd(card, channel, C_CM_SENDXOFF, 0L);
-               else if (ch == START_CHAR(tty))
-                       cyz_issue_cmd(card, channel, C_CM_SENDXON, 0L);
-       }
-}
-
-/* This routine is called by the upper-layer tty layer to signal
-   that incoming characters should be throttled because the input
-   buffers are close to full.
- */
-static void cy_throttle(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-       struct cyclades_card *card;
-       unsigned long flags;
-
-#ifdef CY_DEBUG_THROTTLE
-       char buf[64];
-
-       printk(KERN_DEBUG "cyc:throttle %s: %ld...ttyC%d\n", tty_name(tty, buf),
-                       tty->ldisc.chars_in_buffer(tty), info->line);
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_throttle"))
-               return;
-
-       card = info->card;
-
-       if (I_IXOFF(tty)) {
-               if (!cy_is_Z(card))
-                       cy_send_xchar(tty, STOP_CHAR(tty));
-               else
-                       info->throttle = 1;
-       }
-
-       if (tty->termios->c_cflag & CRTSCTS) {
-               if (!cy_is_Z(card)) {
-                       spin_lock_irqsave(&card->card_lock, flags);
-                       cyy_change_rts_dtr(info, 0, TIOCM_RTS);
-                       spin_unlock_irqrestore(&card->card_lock, flags);
-               } else {
-                       info->throttle = 1;
-               }
-       }
-}                              /* cy_throttle */
-
-/*
- * This routine notifies the tty driver that it should signal
- * that characters can now be sent to the tty without fear of
- * overrunning the input buffers of the line disciplines.
- */
-static void cy_unthrottle(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-       struct cyclades_card *card;
-       unsigned long flags;
-
-#ifdef CY_DEBUG_THROTTLE
-       char buf[64];
-
-       printk(KERN_DEBUG "cyc:unthrottle %s: %ld...ttyC%d\n",
-               tty_name(tty, buf), tty_chars_in_buffer(tty), info->line);
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_unthrottle"))
-               return;
-
-       if (I_IXOFF(tty)) {
-               if (info->x_char)
-                       info->x_char = 0;
-               else
-                       cy_send_xchar(tty, START_CHAR(tty));
-       }
-
-       if (tty->termios->c_cflag & CRTSCTS) {
-               card = info->card;
-               if (!cy_is_Z(card)) {
-                       spin_lock_irqsave(&card->card_lock, flags);
-                       cyy_change_rts_dtr(info, TIOCM_RTS, 0);
-                       spin_unlock_irqrestore(&card->card_lock, flags);
-               } else {
-                       info->throttle = 0;
-               }
-       }
-}                              /* cy_unthrottle */
-
-/* cy_start and cy_stop provide software output flow control as a
-   function of XON/XOFF, software CTS, and other such stuff.
-*/
-static void cy_stop(struct tty_struct *tty)
-{
-       struct cyclades_card *cinfo;
-       struct cyclades_port *info = tty->driver_data;
-       int channel;
-       unsigned long flags;
-
-#ifdef CY_DEBUG_OTHER
-       printk(KERN_DEBUG "cyc:cy_stop ttyC%d\n", info->line);
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_stop"))
-               return;
-
-       cinfo = info->card;
-       channel = info->line - cinfo->first_line;
-       if (!cy_is_Z(cinfo)) {
-               spin_lock_irqsave(&cinfo->card_lock, flags);
-               cyy_writeb(info, CyCAR, channel & 0x03);
-               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy);
-               spin_unlock_irqrestore(&cinfo->card_lock, flags);
-       }
-}                              /* cy_stop */
-
-static void cy_start(struct tty_struct *tty)
-{
-       struct cyclades_card *cinfo;
-       struct cyclades_port *info = tty->driver_data;
-       int channel;
-       unsigned long flags;
-
-#ifdef CY_DEBUG_OTHER
-       printk(KERN_DEBUG "cyc:cy_start ttyC%d\n", info->line);
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_start"))
-               return;
-
-       cinfo = info->card;
-       channel = info->line - cinfo->first_line;
-       if (!cy_is_Z(cinfo)) {
-               spin_lock_irqsave(&cinfo->card_lock, flags);
-               cyy_writeb(info, CyCAR, channel & 0x03);
-               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy);
-               spin_unlock_irqrestore(&cinfo->card_lock, flags);
-       }
-}                              /* cy_start */
-
-/*
- * cy_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-static void cy_hangup(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-
-#ifdef CY_DEBUG_OTHER
-       printk(KERN_DEBUG "cyc:cy_hangup ttyC%d\n", info->line);
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_hangup"))
-               return;
-
-       cy_flush_buffer(tty);
-       cy_shutdown(info, tty);
-       tty_port_hangup(&info->port);
-}                              /* cy_hangup */
-
-static int cyy_carrier_raised(struct tty_port *port)
-{
-       struct cyclades_port *info = container_of(port, struct cyclades_port,
-                       port);
-       struct cyclades_card *cinfo = info->card;
-       unsigned long flags;
-       int channel = info->line - cinfo->first_line;
-       u32 cd;
-
-       spin_lock_irqsave(&cinfo->card_lock, flags);
-       cyy_writeb(info, CyCAR, channel & 0x03);
-       cd = cyy_readb(info, CyMSVR1) & CyDCD;
-       spin_unlock_irqrestore(&cinfo->card_lock, flags);
-
-       return cd;
-}
-
-static void cyy_dtr_rts(struct tty_port *port, int raise)
-{
-       struct cyclades_port *info = container_of(port, struct cyclades_port,
-                       port);
-       struct cyclades_card *cinfo = info->card;
-       unsigned long flags;
-
-       spin_lock_irqsave(&cinfo->card_lock, flags);
-       cyy_change_rts_dtr(info, raise ? TIOCM_RTS | TIOCM_DTR : 0,
-                       raise ? 0 : TIOCM_RTS | TIOCM_DTR);
-       spin_unlock_irqrestore(&cinfo->card_lock, flags);
-}
-
-static int cyz_carrier_raised(struct tty_port *port)
-{
-       struct cyclades_port *info = container_of(port, struct cyclades_port,
-                       port);
-
-       return readl(&info->u.cyz.ch_ctrl->rs_status) & C_RS_DCD;
-}
-
-static void cyz_dtr_rts(struct tty_port *port, int raise)
-{
-       struct cyclades_port *info = container_of(port, struct cyclades_port,
-                       port);
-       struct cyclades_card *cinfo = info->card;
-       struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
-       int ret, channel = info->line - cinfo->first_line;
-       u32 rs;
-
-       rs = readl(&ch_ctrl->rs_control);
-       if (raise)
-               rs |= C_RS_RTS | C_RS_DTR;
-       else
-               rs &= ~(C_RS_RTS | C_RS_DTR);
-       cy_writel(&ch_ctrl->rs_control, rs);
-       ret = cyz_issue_cmd(cinfo, channel, C_CM_IOCTLM, 0L);
-       if (ret != 0)
-               printk(KERN_ERR "%s: retval on ttyC%d was %x\n",
-                               __func__, info->line, ret);
-#ifdef CY_DEBUG_DTR
-       printk(KERN_DEBUG "%s: raising Z DTR\n", __func__);
-#endif
-}
-
-static const struct tty_port_operations cyy_port_ops = {
-       .carrier_raised = cyy_carrier_raised,
-       .dtr_rts = cyy_dtr_rts,
-       .shutdown = cy_do_close,
-};
-
-static const struct tty_port_operations cyz_port_ops = {
-       .carrier_raised = cyz_carrier_raised,
-       .dtr_rts = cyz_dtr_rts,
-       .shutdown = cy_do_close,
-};
-
-/*
- * ---------------------------------------------------------------------
- * cy_init() and friends
- *
- * cy_init() is called at boot-time to initialize the serial driver.
- * ---------------------------------------------------------------------
- */
-
-static int __devinit cy_init_card(struct cyclades_card *cinfo)
-{
-       struct cyclades_port *info;
-       unsigned int channel, port;
-
-       spin_lock_init(&cinfo->card_lock);
-       cinfo->intr_enabled = 0;
-
-       cinfo->ports = kcalloc(cinfo->nports, sizeof(*cinfo->ports),
-                       GFP_KERNEL);
-       if (cinfo->ports == NULL) {
-               printk(KERN_ERR "Cyclades: cannot allocate ports\n");
-               return -ENOMEM;
-       }
-
-       for (channel = 0, port = cinfo->first_line; channel < cinfo->nports;
-                       channel++, port++) {
-               info = &cinfo->ports[channel];
-               tty_port_init(&info->port);
-               info->magic = CYCLADES_MAGIC;
-               info->card = cinfo;
-               info->line = port;
-
-               info->port.closing_wait = CLOSING_WAIT_DELAY;
-               info->port.close_delay = 5 * HZ / 10;
-               info->port.flags = STD_COM_FLAGS;
-               init_completion(&info->shutdown_wait);
-
-               if (cy_is_Z(cinfo)) {
-                       struct FIRM_ID *firm_id = cinfo->base_addr + ID_ADDRESS;
-                       struct ZFW_CTRL *zfw_ctrl;
-
-                       info->port.ops = &cyz_port_ops;
-                       info->type = PORT_STARTECH;
-
-                       zfw_ctrl = cinfo->base_addr +
-                               (readl(&firm_id->zfwctrl_addr) & 0xfffff);
-                       info->u.cyz.ch_ctrl = &zfw_ctrl->ch_ctrl[channel];
-                       info->u.cyz.buf_ctrl = &zfw_ctrl->buf_ctrl[channel];
-
-                       if (cinfo->hw_ver == ZO_V1)
-                               info->xmit_fifo_size = CYZ_FIFO_SIZE;
-                       else
-                               info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE;
-#ifdef CONFIG_CYZ_INTR
-                       setup_timer(&cyz_rx_full_timer[port],
-                               cyz_rx_restart, (unsigned long)info);
-#endif
-               } else {
-                       unsigned short chip_number;
-                       int index = cinfo->bus_index;
-
-                       info->port.ops = &cyy_port_ops;
-                       info->type = PORT_CIRRUS;
-                       info->xmit_fifo_size = CyMAX_CHAR_FIFO;
-                       info->cor1 = CyPARITY_NONE | Cy_1_STOP | Cy_8_BITS;
-                       info->cor2 = CyETC;
-                       info->cor3 = 0x08;      /* _very_ small rcv threshold */
-
-                       chip_number = channel / CyPORTS_PER_CHIP;
-                       info->u.cyy.base_addr = cinfo->base_addr +
-                               (cy_chip_offset[chip_number] << index);
-                       info->chip_rev = cyy_readb(info, CyGFRCR);
-
-                       if (info->chip_rev >= CD1400_REV_J) {
-                               /* It is a CD1400 rev. J or later */
-                               info->tbpr = baud_bpr_60[13];   /* Tx BPR */
-                               info->tco = baud_co_60[13];     /* Tx CO */
-                               info->rbpr = baud_bpr_60[13];   /* Rx BPR */
-                               info->rco = baud_co_60[13];     /* Rx CO */
-                               info->rtsdtr_inv = 1;
-                       } else {
-                               info->tbpr = baud_bpr_25[13];   /* Tx BPR */
-                               info->tco = baud_co_25[13];     /* Tx CO */
-                               info->rbpr = baud_bpr_25[13];   /* Rx BPR */
-                               info->rco = baud_co_25[13];     /* Rx CO */
-                               info->rtsdtr_inv = 0;
-                       }
-                       info->read_status_mask = CyTIMEOUT | CySPECHAR |
-                               CyBREAK | CyPARITY | CyFRAME | CyOVERRUN;
-               }
-
-       }
-
-#ifndef CONFIG_CYZ_INTR
-       if (cy_is_Z(cinfo) && !timer_pending(&cyz_timerlist)) {
-               mod_timer(&cyz_timerlist, jiffies + 1);
-#ifdef CY_PCI_DEBUG
-               printk(KERN_DEBUG "Cyclades-Z polling initialized\n");
-#endif
-       }
-#endif
-       return 0;
-}
-
-/* initialize chips on Cyclom-Y card -- return number of valid
-   chips (which is number of ports/4) */
-static unsigned short __devinit cyy_init_card(void __iomem *true_base_addr,
-               int index)
-{
-       unsigned int chip_number;
-       void __iomem *base_addr;
-
-       cy_writeb(true_base_addr + (Cy_HwReset << index), 0);
-       /* Cy_HwReset is 0x1400 */
-       cy_writeb(true_base_addr + (Cy_ClrIntr << index), 0);
-       /* Cy_ClrIntr is 0x1800 */
-       udelay(500L);
-
-       for (chip_number = 0; chip_number < CyMAX_CHIPS_PER_CARD;
-                                                       chip_number++) {
-               base_addr =
-                   true_base_addr + (cy_chip_offset[chip_number] << index);
-               mdelay(1);
-               if (readb(base_addr + (CyCCR << index)) != 0x00) {
-                       /*************
-                       printk(" chip #%d at %#6lx is never idle (CCR != 0)\n",
-                       chip_number, (unsigned long)base_addr);
-                       *************/
-                       return chip_number;
-               }
-
-               cy_writeb(base_addr + (CyGFRCR << index), 0);
-               udelay(10L);
-
-               /* The Cyclom-16Y does not decode address bit 9 and therefore
-                  cannot distinguish between references to chip 0 and a non-
-                  existent chip 4.  If the preceding clearing of the supposed
-                  chip 4 GFRCR register appears at chip 0, there is no chip 4
-                  and this must be a Cyclom-16Y, not a Cyclom-32Ye.
-                */
-               if (chip_number == 4 && readb(true_base_addr +
-                               (cy_chip_offset[0] << index) +
-                               (CyGFRCR << index)) == 0) {
-                       return chip_number;
-               }
-
-               cy_writeb(base_addr + (CyCCR << index), CyCHIP_RESET);
-               mdelay(1);
-
-               if (readb(base_addr + (CyGFRCR << index)) == 0x00) {
-                       /*
-                          printk(" chip #%d at %#6lx is not responding ",
-                          chip_number, (unsigned long)base_addr);
-                          printk("(GFRCR stayed 0)\n",
-                        */
-                       return chip_number;
-               }
-               if ((0xf0 & (readb(base_addr + (CyGFRCR << index)))) !=
-                               0x40) {
-                       /*
-                       printk(" chip #%d at %#6lx is not valid (GFRCR == "
-                                       "%#2x)\n",
-                                       chip_number, (unsigned long)base_addr,
-                                       base_addr[CyGFRCR<<index]);
-                        */
-                       return chip_number;
-               }
-               cy_writeb(base_addr + (CyGCR << index), CyCH0_SERIAL);
-               if (readb(base_addr + (CyGFRCR << index)) >= CD1400_REV_J) {
-                       /* It is a CD1400 rev. J or later */
-                       /* Impossible to reach 5ms with this chip.
-                          Changed to 2ms instead (f = 500 Hz). */
-                       cy_writeb(base_addr + (CyPPR << index), CyCLOCK_60_2MS);
-               } else {
-                       /* f = 200 Hz */
-                       cy_writeb(base_addr + (CyPPR << index), CyCLOCK_25_5MS);
-               }
-
-               /*
-                  printk(" chip #%d at %#6lx is rev 0x%2x\n",
-                  chip_number, (unsigned long)base_addr,
-                  readb(base_addr+(CyGFRCR<<index)));
-                */
-       }
-       return chip_number;
-}                              /* cyy_init_card */
-
-/*
- * ---------------------------------------------------------------------
- * cy_detect_isa() - Probe for Cyclom-Y/ISA boards.
- * sets global variables and return the number of ISA boards found.
- * ---------------------------------------------------------------------
- */
-static int __init cy_detect_isa(void)
-{
-#ifdef CONFIG_ISA
-       unsigned short cy_isa_irq, nboard;
-       void __iomem *cy_isa_address;
-       unsigned short i, j, cy_isa_nchan;
-       int isparam = 0;
-
-       nboard = 0;
-
-       /* Check for module parameters */
-       for (i = 0; i < NR_CARDS; i++) {
-               if (maddr[i] || i) {
-                       isparam = 1;
-                       cy_isa_addresses[i] = maddr[i];
-               }
-               if (!maddr[i])
-                       break;
-       }
-
-       /* scan the address table probing for Cyclom-Y/ISA boards */
-       for (i = 0; i < NR_ISA_ADDRS; i++) {
-               unsigned int isa_address = cy_isa_addresses[i];
-               if (isa_address == 0x0000)
-                       return nboard;
-
-               /* probe for CD1400... */
-               cy_isa_address = ioremap_nocache(isa_address, CyISA_Ywin);
-               if (cy_isa_address == NULL) {
-                       printk(KERN_ERR "Cyclom-Y/ISA: can't remap base "
-                                       "address\n");
-                       continue;
-               }
-               cy_isa_nchan = CyPORTS_PER_CHIP *
-                       cyy_init_card(cy_isa_address, 0);
-               if (cy_isa_nchan == 0) {
-                       iounmap(cy_isa_address);
-                       continue;
-               }
-
-               if (isparam && i < NR_CARDS && irq[i])
-                       cy_isa_irq = irq[i];
-               else
-                       /* find out the board's irq by probing */
-                       cy_isa_irq = detect_isa_irq(cy_isa_address);
-               if (cy_isa_irq == 0) {
-                       printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but the "
-                               "IRQ could not be detected.\n",
-                               (unsigned long)cy_isa_address);
-                       iounmap(cy_isa_address);
-                       continue;
-               }
-
-               if ((cy_next_channel + cy_isa_nchan) > NR_PORTS) {
-                       printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no "
-                               "more channels are available. Change NR_PORTS "
-                               "in cyclades.c and recompile kernel.\n",
-                               (unsigned long)cy_isa_address);
-                       iounmap(cy_isa_address);
-                       return nboard;
-               }
-               /* fill the next cy_card structure available */
-               for (j = 0; j < NR_CARDS; j++) {
-                       if (cy_card[j].base_addr == NULL)
-                               break;
-               }
-               if (j == NR_CARDS) {    /* no more cy_cards available */
-                       printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no "
-                               "more cards can be used. Change NR_CARDS in "
-                               "cyclades.c and recompile kernel.\n",
-                               (unsigned long)cy_isa_address);
-                       iounmap(cy_isa_address);
-                       return nboard;
-               }
-
-               /* allocate IRQ */
-               if (request_irq(cy_isa_irq, cyy_interrupt,
-                               IRQF_DISABLED, "Cyclom-Y", &cy_card[j])) {
-                       printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but "
-                               "could not allocate IRQ#%d.\n",
-                               (unsigned long)cy_isa_address, cy_isa_irq);
-                       iounmap(cy_isa_address);
-                       return nboard;
-               }
-
-               /* set cy_card */
-               cy_card[j].base_addr = cy_isa_address;
-               cy_card[j].ctl_addr.p9050 = NULL;
-               cy_card[j].irq = (int)cy_isa_irq;
-               cy_card[j].bus_index = 0;
-               cy_card[j].first_line = cy_next_channel;
-               cy_card[j].num_chips = cy_isa_nchan / CyPORTS_PER_CHIP;
-               cy_card[j].nports = cy_isa_nchan;
-               if (cy_init_card(&cy_card[j])) {
-                       cy_card[j].base_addr = NULL;
-                       free_irq(cy_isa_irq, &cy_card[j]);
-                       iounmap(cy_isa_address);
-                       continue;
-               }
-               nboard++;
-
-               printk(KERN_INFO "Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d found: "
-                       "%d channels starting from port %d\n",
-                       j + 1, (unsigned long)cy_isa_address,
-                       (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)),
-                       cy_isa_irq, cy_isa_nchan, cy_next_channel);
-
-               for (j = cy_next_channel;
-                               j < cy_next_channel + cy_isa_nchan; j++)
-                       tty_register_device(cy_serial_driver, j, NULL);
-               cy_next_channel += cy_isa_nchan;
-       }
-       return nboard;
-#else
-       return 0;
-#endif                         /* CONFIG_ISA */
-}                              /* cy_detect_isa */
-
-#ifdef CONFIG_PCI
-static inline int __devinit cyc_isfwstr(const char *str, unsigned int size)
-{
-       unsigned int a;
-
-       for (a = 0; a < size && *str; a++, str++)
-               if (*str & 0x80)
-                       return -EINVAL;
-
-       for (; a < size; a++, str++)
-               if (*str)
-                       return -EINVAL;
-
-       return 0;
-}
-
-static inline void __devinit cyz_fpga_copy(void __iomem *fpga, const u8 *data,
-               unsigned int size)
-{
-       for (; size > 0; size--) {
-               cy_writel(fpga, *data++);
-               udelay(10);
-       }
-}
-
-static void __devinit plx_init(struct pci_dev *pdev, int irq,
-               struct RUNTIME_9060 __iomem *addr)
-{
-       /* Reset PLX */
-       cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x40000000);
-       udelay(100L);
-       cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x40000000);
-
-       /* Reload Config. Registers from EEPROM */
-       cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x20000000);
-       udelay(100L);
-       cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x20000000);
-
-       /* For some yet unknown reason, once the PLX9060 reloads the EEPROM,
-        * the IRQ is lost and, thus, we have to re-write it to the PCI config.
-        * registers. This will remain here until we find a permanent fix.
-        */
-       pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq);
-}
-
-static int __devinit __cyz_load_fw(const struct firmware *fw,
-               const char *name, const u32 mailbox, void __iomem *base,
-               void __iomem *fpga)
-{
-       const void *ptr = fw->data;
-       const struct zfile_header *h = ptr;
-       const struct zfile_config *c, *cs;
-       const struct zfile_block *b, *bs;
-       unsigned int a, tmp, len = fw->size;
-#define BAD_FW KERN_ERR "Bad firmware: "
-       if (len < sizeof(*h)) {
-               printk(BAD_FW "too short: %u<%zu\n", len, sizeof(*h));
-               return -EINVAL;
-       }
-
-       cs = ptr + h->config_offset;
-       bs = ptr + h->block_offset;
-
-       if ((void *)(cs + h->n_config) > ptr + len ||
-                       (void *)(bs + h->n_blocks) > ptr + len) {
-               printk(BAD_FW "too short");
-               return  -EINVAL;
-       }
-
-       if (cyc_isfwstr(h->name, sizeof(h->name)) ||
-                       cyc_isfwstr(h->date, sizeof(h->date))) {
-               printk(BAD_FW "bad formatted header string\n");
-               return -EINVAL;
-       }
-
-       if (strncmp(name, h->name, sizeof(h->name))) {
-               printk(BAD_FW "bad name '%s' (expected '%s')\n", h->name, name);
-               return -EINVAL;
-       }
-
-       tmp = 0;
-       for (c = cs; c < cs + h->n_config; c++) {
-               for (a = 0; a < c->n_blocks; a++)
-                       if (c->block_list[a] > h->n_blocks) {
-                               printk(BAD_FW "bad block ref number in cfgs\n");
-                               return -EINVAL;
-                       }
-               if (c->mailbox == mailbox && c->function == 0) /* 0 is normal */
-                       tmp++;
-       }
-       if (!tmp) {
-               printk(BAD_FW "nothing appropriate\n");
-               return -EINVAL;
-       }
-
-       for (b = bs; b < bs + h->n_blocks; b++)
-               if (b->file_offset + b->size > len) {
-                       printk(BAD_FW "bad block data offset\n");
-                       return -EINVAL;
-               }
-
-       /* everything is OK, let's seek'n'load it */
-       for (c = cs; c < cs + h->n_config; c++)
-               if (c->mailbox == mailbox && c->function == 0)
-                       break;
-
-       for (a = 0; a < c->n_blocks; a++) {
-               b = &bs[c->block_list[a]];
-               if (b->type == ZBLOCK_FPGA) {
-                       if (fpga != NULL)
-                               cyz_fpga_copy(fpga, ptr + b->file_offset,
-                                               b->size);
-               } else {
-                       if (base != NULL)
-                               memcpy_toio(base + b->ram_offset,
-                                              ptr + b->file_offset, b->size);
-               }
-       }
-#undef BAD_FW
-       return 0;
-}
-
-static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
-               struct RUNTIME_9060 __iomem *ctl_addr, int irq)
-{
-       const struct firmware *fw;
-       struct FIRM_ID __iomem *fid = base_addr + ID_ADDRESS;
-       struct CUSTOM_REG __iomem *cust = base_addr;
-       struct ZFW_CTRL __iomem *pt_zfwctrl;
-       void __iomem *tmp;
-       u32 mailbox, status, nchan;
-       unsigned int i;
-       int retval;
-
-       retval = request_firmware(&fw, "cyzfirm.bin", &pdev->dev);
-       if (retval) {
-               dev_err(&pdev->dev, "can't get firmware\n");
-               goto err;
-       }
-
-       /* Check whether the firmware is already loaded and running. If
-          positive, skip this board */
-       if (__cyz_fpga_loaded(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) {
-               u32 cntval = readl(base_addr + 0x190);
-
-               udelay(100);
-               if (cntval != readl(base_addr + 0x190)) {
-                       /* FW counter is working, FW is running */
-                       dev_dbg(&pdev->dev, "Cyclades-Z FW already loaded. "
-                                       "Skipping board.\n");
-                       retval = 0;
-                       goto err_rel;
-               }
-       }
-
-       /* start boot */
-       cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) &
-                       ~0x00030800UL);
-
-       mailbox = readl(&ctl_addr->mail_box_0);
-
-       if (mailbox == 0 || __cyz_fpga_loaded(ctl_addr)) {
-               /* stops CPU and set window to beginning of RAM */
-               cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
-               cy_writel(&cust->cpu_stop, 0);
-               cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
-               udelay(100);
-       }
-
-       plx_init(pdev, irq, ctl_addr);
-
-       if (mailbox != 0) {
-               /* load FPGA */
-               retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, NULL,
-                               base_addr);
-               if (retval)
-                       goto err_rel;
-               if (!__cyz_fpga_loaded(ctl_addr)) {
-                       dev_err(&pdev->dev, "fw upload successful, but fw is "
-                                       "not loaded\n");
-                       goto err_rel;
-               }
-       }
-
-       /* stops CPU and set window to beginning of RAM */
-       cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
-       cy_writel(&cust->cpu_stop, 0);
-       cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
-       udelay(100);
-
-       /* clear memory */
-       for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++)
-               cy_writeb(tmp, 255);
-       if (mailbox != 0) {
-               /* set window to last 512K of RAM */
-               cy_writel(&ctl_addr->loc_addr_base, WIN_RAM + RAM_SIZE);
-               for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++)
-                       cy_writeb(tmp, 255);
-               /* set window to beginning of RAM */
-               cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
-       }
-
-       retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, base_addr, NULL);
-       release_firmware(fw);
-       if (retval)
-               goto err;
-
-       /* finish boot and start boards */
-       cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
-       cy_writel(&cust->cpu_start, 0);
-       cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
-       i = 0;
-       while ((status = readl(&fid->signature)) != ZFIRM_ID && i++ < 40)
-               msleep(100);
-       if (status != ZFIRM_ID) {
-               if (status == ZFIRM_HLT) {
-                       dev_err(&pdev->dev, "you need an external power supply "
-                               "for this number of ports. Firmware halted and "
-                               "board reset.\n");
-                       retval = -EIO;
-                       goto err;
-               }
-               dev_warn(&pdev->dev, "fid->signature = 0x%x... Waiting "
-                               "some more time\n", status);
-               while ((status = readl(&fid->signature)) != ZFIRM_ID &&
-                               i++ < 200)
-                       msleep(100);
-               if (status != ZFIRM_ID) {
-                       dev_err(&pdev->dev, "Board not started in 20 seconds! "
-                                       "Giving up. (fid->signature = 0x%x)\n",
-                                       status);
-                       dev_info(&pdev->dev, "*** Warning ***: if you are "
-                               "upgrading the FW, please power cycle the "
-                               "system before loading the new FW to the "
-                               "Cyclades-Z.\n");
-
-                       if (__cyz_fpga_loaded(ctl_addr))
-                               plx_init(pdev, irq, ctl_addr);
-
-                       retval = -EIO;
-                       goto err;
-               }
-               dev_dbg(&pdev->dev, "Firmware started after %d seconds.\n",
-                               i / 10);
-       }
-       pt_zfwctrl = base_addr + readl(&fid->zfwctrl_addr);
-
-       dev_dbg(&pdev->dev, "fid=> %p, zfwctrl_addr=> %x, npt_zfwctrl=> %p\n",
-                       base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr),
-                       base_addr + readl(&fid->zfwctrl_addr));
-
-       nchan = readl(&pt_zfwctrl->board_ctrl.n_channel);
-       dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u\n",
-               readl(&pt_zfwctrl->board_ctrl.fw_version), nchan);
-
-       if (nchan == 0) {
-               dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please "
-                       "check the connection between the Z host card and the "
-                       "serial expanders.\n");
-
-               if (__cyz_fpga_loaded(ctl_addr))
-                       plx_init(pdev, irq, ctl_addr);
-
-               dev_info(&pdev->dev, "Null number of ports detected. Board "
-                               "reset.\n");
-               retval = 0;
-               goto err;
-       }
-
-       cy_writel(&pt_zfwctrl->board_ctrl.op_system, C_OS_LINUX);
-       cy_writel(&pt_zfwctrl->board_ctrl.dr_version, DRIVER_VERSION);
-
-       /*
-          Early firmware failed to start looking for commands.
-          This enables firmware interrupts for those commands.
-        */
-       cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) |
-                       (1 << 17));
-       cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) |
-                       0x00030800UL);
-
-       return nchan;
-err_rel:
-       release_firmware(fw);
-err:
-       return retval;
-}
-
-static int __devinit cy_pci_probe(struct pci_dev *pdev,
-               const struct pci_device_id *ent)
-{
-       void __iomem *addr0 = NULL, *addr2 = NULL;
-       char *card_name = NULL;
-       u32 uninitialized_var(mailbox);
-       unsigned int device_id, nchan = 0, card_no, i;
-       unsigned char plx_ver;
-       int retval, irq;
-
-       retval = pci_enable_device(pdev);
-       if (retval) {
-               dev_err(&pdev->dev, "cannot enable device\n");
-               goto err;
-       }
-
-       /* read PCI configuration area */
-       irq = pdev->irq;
-       device_id = pdev->device & ~PCI_DEVICE_ID_MASK;
-
-#if defined(__alpha__)
-       if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) {   /* below 1M? */
-               dev_err(&pdev->dev, "Cyclom-Y/PCI not supported for low "
-                       "addresses on Alpha systems.\n");
-               retval = -EIO;
-               goto err_dis;
-       }
-#endif
-       if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo) {
-               dev_err(&pdev->dev, "Cyclades-Z/PCI not supported for low "
-                       "addresses\n");
-               retval = -EIO;
-               goto err_dis;
-       }
-
-       if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) {
-               dev_warn(&pdev->dev, "PCI I/O bit incorrectly set. Ignoring "
-                               "it...\n");
-               pdev->resource[2].flags &= ~IORESOURCE_IO;
-       }
-
-       retval = pci_request_regions(pdev, "cyclades");
-       if (retval) {
-               dev_err(&pdev->dev, "failed to reserve resources\n");
-               goto err_dis;
-       }
-
-       retval = -EIO;
-       if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
-                       device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
-               card_name = "Cyclom-Y";
-
-               addr0 = ioremap_nocache(pci_resource_start(pdev, 0),
-                               CyPCI_Yctl);
-               if (addr0 == NULL) {
-                       dev_err(&pdev->dev, "can't remap ctl region\n");
-                       goto err_reg;
-               }
-               addr2 = ioremap_nocache(pci_resource_start(pdev, 2),
-                               CyPCI_Ywin);
-               if (addr2 == NULL) {
-                       dev_err(&pdev->dev, "can't remap base region\n");
-                       goto err_unmap;
-               }
-
-               nchan = CyPORTS_PER_CHIP * cyy_init_card(addr2, 1);
-               if (nchan == 0) {
-                       dev_err(&pdev->dev, "Cyclom-Y PCI host card with no "
-                                       "Serial-Modules\n");
-                       goto err_unmap;
-               }
-       } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) {
-               struct RUNTIME_9060 __iomem *ctl_addr;
-
-               ctl_addr = addr0 = ioremap_nocache(pci_resource_start(pdev, 0),
-                               CyPCI_Zctl);
-               if (addr0 == NULL) {
-                       dev_err(&pdev->dev, "can't remap ctl region\n");
-                       goto err_reg;
-               }
-
-               /* Disable interrupts on the PLX before resetting it */
-               cy_writew(&ctl_addr->intr_ctrl_stat,
-                               readw(&ctl_addr->intr_ctrl_stat) & ~0x0900);
-
-               plx_init(pdev, irq, addr0);
-
-               mailbox = readl(&ctl_addr->mail_box_0);
-
-               addr2 = ioremap_nocache(pci_resource_start(pdev, 2),
-                               mailbox == ZE_V1 ? CyPCI_Ze_win : CyPCI_Zwin);
-               if (addr2 == NULL) {
-                       dev_err(&pdev->dev, "can't remap base region\n");
-                       goto err_unmap;
-               }
-
-               if (mailbox == ZE_V1) {
-                       card_name = "Cyclades-Ze";
-               } else {
-                       card_name = "Cyclades-8Zo";
-#ifdef CY_PCI_DEBUG
-                       if (mailbox == ZO_V1) {
-                               cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
-                               dev_info(&pdev->dev, "Cyclades-8Zo/PCI: FPGA "
-                                       "id %lx, ver %lx\n", (ulong)(0xff &
-                                       readl(&((struct CUSTOM_REG *)addr2)->
-                                               fpga_id)), (ulong)(0xff &
-                                       readl(&((struct CUSTOM_REG *)addr2)->
-                                               fpga_version)));
-                               cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
-                       } else {
-                               dev_info(&pdev->dev, "Cyclades-Z/PCI: New "
-                                       "Cyclades-Z board.  FPGA not loaded\n");
-                       }
-#endif
-                       /* The following clears the firmware id word.  This
-                          ensures that the driver will not attempt to talk to
-                          the board until it has been properly initialized.
-                        */
-                       if ((mailbox == ZO_V1) || (mailbox == ZO_V2))
-                               cy_writel(addr2 + ID_ADDRESS, 0L);
-               }
-
-               retval = cyz_load_fw(pdev, addr2, addr0, irq);
-               if (retval <= 0)
-                       goto err_unmap;
-               nchan = retval;
-       }
-
-       if ((cy_next_channel + nchan) > NR_PORTS) {
-               dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no "
-                       "channels are available. Change NR_PORTS in "
-                       "cyclades.c and recompile kernel.\n");
-               goto err_unmap;
-       }
-       /* fill the next cy_card structure available */
-       for (card_no = 0; card_no < NR_CARDS; card_no++) {
-               if (cy_card[card_no].base_addr == NULL)
-                       break;
-       }
-       if (card_no == NR_CARDS) {      /* no more cy_cards available */
-               dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no "
-                       "more cards can be used. Change NR_CARDS in "
-                       "cyclades.c and recompile kernel.\n");
-               goto err_unmap;
-       }
-
-       if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
-                       device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
-               /* allocate IRQ */
-               retval = request_irq(irq, cyy_interrupt,
-                               IRQF_SHARED, "Cyclom-Y", &cy_card[card_no]);
-               if (retval) {
-                       dev_err(&pdev->dev, "could not allocate IRQ\n");
-                       goto err_unmap;
-               }
-               cy_card[card_no].num_chips = nchan / CyPORTS_PER_CHIP;
-       } else {
-               struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS;
-               struct ZFW_CTRL __iomem *zfw_ctrl;
-
-               zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff);
-
-               cy_card[card_no].hw_ver = mailbox;
-               cy_card[card_no].num_chips = (unsigned int)-1;
-               cy_card[card_no].board_ctrl = &zfw_ctrl->board_ctrl;
-#ifdef CONFIG_CYZ_INTR
-               /* allocate IRQ only if board has an IRQ */
-               if (irq != 0 && irq != 255) {
-                       retval = request_irq(irq, cyz_interrupt,
-                                       IRQF_SHARED, "Cyclades-Z",
-                                       &cy_card[card_no]);
-                       if (retval) {
-                               dev_err(&pdev->dev, "could not allocate IRQ\n");
-                               goto err_unmap;
-                       }
-               }
-#endif                         /* CONFIG_CYZ_INTR */
-       }
-
-       /* set cy_card */
-       cy_card[card_no].base_addr = addr2;
-       cy_card[card_no].ctl_addr.p9050 = addr0;
-       cy_card[card_no].irq = irq;
-       cy_card[card_no].bus_index = 1;
-       cy_card[card_no].first_line = cy_next_channel;
-       cy_card[card_no].nports = nchan;
-       retval = cy_init_card(&cy_card[card_no]);
-       if (retval)
-               goto err_null;
-
-       pci_set_drvdata(pdev, &cy_card[card_no]);
-
-       if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
-                       device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
-               /* enable interrupts in the PCI interface */
-               plx_ver = readb(addr2 + CyPLX_VER) & 0x0f;
-               switch (plx_ver) {
-               case PLX_9050:
-                       cy_writeb(addr0 + 0x4c, 0x43);
-                       break;
-
-               case PLX_9060:
-               case PLX_9080:
-               default:        /* Old boards, use PLX_9060 */
-               {
-                       struct RUNTIME_9060 __iomem *ctl_addr = addr0;
-                       plx_init(pdev, irq, ctl_addr);
-                       cy_writew(&ctl_addr->intr_ctrl_stat,
-                               readw(&ctl_addr->intr_ctrl_stat) | 0x0900);
-                       break;
-               }
-               }
-       }
-
-       dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from "
-               "port %d.\n", card_name, card_no + 1, nchan, cy_next_channel);
-       for (i = cy_next_channel; i < cy_next_channel + nchan; i++)
-               tty_register_device(cy_serial_driver, i, &pdev->dev);
-       cy_next_channel += nchan;
-
-       return 0;
-err_null:
-       cy_card[card_no].base_addr = NULL;
-       free_irq(irq, &cy_card[card_no]);
-err_unmap:
-       iounmap(addr0);
-       if (addr2)
-               iounmap(addr2);
-err_reg:
-       pci_release_regions(pdev);
-err_dis:
-       pci_disable_device(pdev);
-err:
-       return retval;
-}
-
-static void __devexit cy_pci_remove(struct pci_dev *pdev)
-{
-       struct cyclades_card *cinfo = pci_get_drvdata(pdev);
-       unsigned int i;
-
-       /* non-Z with old PLX */
-       if (!cy_is_Z(cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) ==
-                       PLX_9050)
-               cy_writeb(cinfo->ctl_addr.p9050 + 0x4c, 0);
-       else
-#ifndef CONFIG_CYZ_INTR
-               if (!cy_is_Z(cinfo))
-#endif
-               cy_writew(&cinfo->ctl_addr.p9060->intr_ctrl_stat,
-                       readw(&cinfo->ctl_addr.p9060->intr_ctrl_stat) &
-                       ~0x0900);
-
-       iounmap(cinfo->base_addr);
-       if (cinfo->ctl_addr.p9050)
-               iounmap(cinfo->ctl_addr.p9050);
-       if (cinfo->irq
-#ifndef CONFIG_CYZ_INTR
-               && !cy_is_Z(cinfo)
-#endif /* CONFIG_CYZ_INTR */
-               )
-               free_irq(cinfo->irq, cinfo);
-       pci_release_regions(pdev);
-
-       cinfo->base_addr = NULL;
-       for (i = cinfo->first_line; i < cinfo->first_line +
-                       cinfo->nports; i++)
-               tty_unregister_device(cy_serial_driver, i);
-       cinfo->nports = 0;
-       kfree(cinfo->ports);
-}
-
-static struct pci_driver cy_pci_driver = {
-       .name = "cyclades",
-       .id_table = cy_pci_dev_id,
-       .probe = cy_pci_probe,
-       .remove = __devexit_p(cy_pci_remove)
-};
-#endif
-
-static int cyclades_proc_show(struct seq_file *m, void *v)
-{
-       struct cyclades_port *info;
-       unsigned int i, j;
-       __u32 cur_jifs = jiffies;
-
-       seq_puts(m, "Dev TimeOpen   BytesOut  IdleOut    BytesIn   "
-                       "IdleIn  Overruns  Ldisc\n");
-
-       /* Output one line for each known port */
-       for (i = 0; i < NR_CARDS; i++)
-               for (j = 0; j < cy_card[i].nports; j++) {
-                       info = &cy_card[i].ports[j];
-
-                       if (info->port.count) {
-                               /* XXX is the ldisc num worth this? */
-                               struct tty_struct *tty;
-                               struct tty_ldisc *ld;
-                               int num = 0;
-                               tty = tty_port_tty_get(&info->port);
-                               if (tty) {
-                                       ld = tty_ldisc_ref(tty);
-                                       if (ld) {
-                                               num = ld->ops->num;
-                                               tty_ldisc_deref(ld);
-                                       }
-                                       tty_kref_put(tty);
-                               }
-                               seq_printf(m, "%3d %8lu %10lu %8lu "
-                                       "%10lu %8lu %9lu %6d\n", info->line,
-                                       (cur_jifs - info->idle_stats.in_use) /
-                                       HZ, info->idle_stats.xmit_bytes,
-                                       (cur_jifs - info->idle_stats.xmit_idle)/
-                                       HZ, info->idle_stats.recv_bytes,
-                                       (cur_jifs - info->idle_stats.recv_idle)/
-                                       HZ, info->idle_stats.overruns,
-                                       num);
-                       } else
-                               seq_printf(m, "%3d %8lu %10lu %8lu "
-                                       "%10lu %8lu %9lu %6ld\n",
-                                       info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L);
-               }
-       return 0;
-}
-
-static int cyclades_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, cyclades_proc_show, NULL);
-}
-
-static const struct file_operations cyclades_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = cyclades_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-/* The serial driver boot-time initialization code!
-    Hardware I/O ports are mapped to character special devices on a
-    first found, first allocated manner.  That is, this code searches
-    for Cyclom cards in the system.  As each is found, it is probed
-    to discover how many chips (and thus how many ports) are present.
-    These ports are mapped to the tty ports 32 and upward in monotonic
-    fashion.  If an 8-port card is replaced with a 16-port card, the
-    port mapping on a following card will shift.
-
-    This approach is different from what is used in the other serial
-    device driver because the Cyclom is more properly a multiplexer,
-    not just an aggregation of serial ports on one card.
-
-    If there are more cards with more ports than have been
-    statically allocated above, a warning is printed and the
-    extra ports are ignored.
- */
-
-static const struct tty_operations cy_ops = {
-       .open = cy_open,
-       .close = cy_close,
-       .write = cy_write,
-       .put_char = cy_put_char,
-       .flush_chars = cy_flush_chars,
-       .write_room = cy_write_room,
-       .chars_in_buffer = cy_chars_in_buffer,
-       .flush_buffer = cy_flush_buffer,
-       .ioctl = cy_ioctl,
-       .throttle = cy_throttle,
-       .unthrottle = cy_unthrottle,
-       .set_termios = cy_set_termios,
-       .stop = cy_stop,
-       .start = cy_start,
-       .hangup = cy_hangup,
-       .break_ctl = cy_break,
-       .wait_until_sent = cy_wait_until_sent,
-       .tiocmget = cy_tiocmget,
-       .tiocmset = cy_tiocmset,
-       .get_icount = cy_get_icount,
-       .proc_fops = &cyclades_proc_fops,
-};
-
-static int __init cy_init(void)
-{
-       unsigned int nboards;
-       int retval = -ENOMEM;
-
-       cy_serial_driver = alloc_tty_driver(NR_PORTS);
-       if (!cy_serial_driver)
-               goto err;
-
-       printk(KERN_INFO "Cyclades driver " CY_VERSION " (built %s %s)\n",
-                       __DATE__, __TIME__);
-
-       /* Initialize the tty_driver structure */
-
-       cy_serial_driver->owner = THIS_MODULE;
-       cy_serial_driver->driver_name = "cyclades";
-       cy_serial_driver->name = "ttyC";
-       cy_serial_driver->major = CYCLADES_MAJOR;
-       cy_serial_driver->minor_start = 0;
-       cy_serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
-       cy_serial_driver->subtype = SERIAL_TYPE_NORMAL;
-       cy_serial_driver->init_termios = tty_std_termios;
-       cy_serial_driver->init_termios.c_cflag =
-           B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-       cy_serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
-       tty_set_operations(cy_serial_driver, &cy_ops);
-
-       retval = tty_register_driver(cy_serial_driver);
-       if (retval) {
-               printk(KERN_ERR "Couldn't register Cyclades serial driver\n");
-               goto err_frtty;
-       }
-
-       /* the code below is responsible to find the boards. Each different
-          type of board has its own detection routine. If a board is found,
-          the next cy_card structure available is set by the detection
-          routine. These functions are responsible for checking the
-          availability of cy_card and cy_port data structures and updating
-          the cy_next_channel. */
-
-       /* look for isa boards */
-       nboards = cy_detect_isa();
-
-#ifdef CONFIG_PCI
-       /* look for pci boards */
-       retval = pci_register_driver(&cy_pci_driver);
-       if (retval && !nboards) {
-               tty_unregister_driver(cy_serial_driver);
-               goto err_frtty;
-       }
-#endif
-
-       return 0;
-err_frtty:
-       put_tty_driver(cy_serial_driver);
-err:
-       return retval;
-}                              /* cy_init */
-
-static void __exit cy_cleanup_module(void)
-{
-       struct cyclades_card *card;
-       unsigned int i, e1;
-
-#ifndef CONFIG_CYZ_INTR
-       del_timer_sync(&cyz_timerlist);
-#endif /* CONFIG_CYZ_INTR */
-
-       e1 = tty_unregister_driver(cy_serial_driver);
-       if (e1)
-               printk(KERN_ERR "failed to unregister Cyclades serial "
-                               "driver(%d)\n", e1);
-
-#ifdef CONFIG_PCI
-       pci_unregister_driver(&cy_pci_driver);
-#endif
-
-       for (i = 0; i < NR_CARDS; i++) {
-               card = &cy_card[i];
-               if (card->base_addr) {
-                       /* clear interrupt */
-                       cy_writeb(card->base_addr + Cy_ClrIntr, 0);
-                       iounmap(card->base_addr);
-                       if (card->ctl_addr.p9050)
-                               iounmap(card->ctl_addr.p9050);
-                       if (card->irq
-#ifndef CONFIG_CYZ_INTR
-                               && !cy_is_Z(card)
-#endif /* CONFIG_CYZ_INTR */
-                               )
-                               free_irq(card->irq, card);
-                       for (e1 = card->first_line; e1 < card->first_line +
-                                       card->nports; e1++)
-                               tty_unregister_device(cy_serial_driver, e1);
-                       kfree(card->ports);
-               }
-       }
-
-       put_tty_driver(cy_serial_driver);
-} /* cy_cleanup_module */
-
-module_init(cy_init);
-module_exit(cy_cleanup_module);
-
-MODULE_LICENSE("GPL");
-MODULE_VERSION(CY_VERSION);
-MODULE_ALIAS_CHARDEV_MAJOR(CYCLADES_MAJOR);
-MODULE_FIRMWARE("cyzfirm.bin");
diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c
deleted file mode 100644 (file)
index db1cf9c..0000000
+++ /dev/null
@@ -1,1736 +0,0 @@
-/*
- *     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.
- *
- *     Original driver code supplied by Multi-Tech
- *
- *     Changes
- *     1/9/98  alan@lxorguk.ukuu.org.uk
- *                                     Merge to 2.0.x kernel tree
- *                                     Obtain and use official major/minors
- *                                     Loader switched to a misc device
- *                                     (fixed range check bug as a side effect)
- *                                     Printk clean up
- *     9/12/98 alan@lxorguk.ukuu.org.uk
- *                                     Rough port to 2.1.x
- *
- *     10/6/99 sameer                  Merged the ISA and PCI drivers to
- *                                     a new unified driver.
- *
- *     3/9/99  sameer                  Added support for ISI4616 cards.
- *
- *     16/9/99 sameer                  We do not force RTS low anymore.
- *                                     This is to prevent the firmware
- *                                     from getting confused.
- *
- *     26/10/99 sameer                 Cosmetic changes:The driver now
- *                                     dumps the Port Count information
- *                                     along with I/O address and IRQ.
- *
- *     13/12/99 sameer                 Fixed the problem with IRQ sharing.
- *
- *     10/5/00  sameer                 Fixed isicom_shutdown_board()
- *                                     to not lower DTR on all the ports
- *                                     when the last port on the card is
- *                                     closed.
- *
- *     10/5/00  sameer                 Signal mask setup command added
- *                                     to  isicom_setup_port and
- *                                     isicom_shutdown_port.
- *
- *     24/5/00  sameer                 The driver is now SMP aware.
- *
- *
- *     27/11/00 Vinayak P Risbud       Fixed the Driver Crash Problem
- *
- *
- *     03/01/01  anil .s               Added support for resetting the
- *                                     internal modems on ISI cards.
- *
- *     08/02/01  anil .s               Upgraded the driver for kernel
- *                                     2.4.x
- *
- *     11/04/01  Kevin                 Fixed firmware load problem with
- *                                     ISIHP-4X card
- *
- *     30/04/01  anil .s               Fixed the remote login through
- *                                     ISI port problem. Now the link
- *                                     does not go down before password
- *                                     prompt.
- *
- *     03/05/01  anil .s               Fixed the problem with IRQ sharing
- *                                     among ISI-PCI cards.
- *
- *     03/05/01  anil .s               Added support to display the version
- *                                     info during insmod as well as module
- *                                     listing by lsmod.
- *
- *     10/05/01  anil .s               Done the modifications to the source
- *                                     file and Install script so that the
- *                                     same installation can be used for
- *                                     2.2.x and 2.4.x kernel.
- *
- *     06/06/01  anil .s               Now we drop both dtr and rts during
- *                                     shutdown_port as well as raise them
- *                                     during isicom_config_port.
- *
- *     09/06/01 acme@conectiva.com.br  use capable, not suser, do
- *                                     restore_flags on failure in
- *                                     isicom_send_break, verify put_user
- *                                     result
- *
- *     11/02/03  ranjeeth              Added support for 230 Kbps and 460 Kbps
- *                                     Baud index extended to 21
- *
- *     20/03/03  ranjeeth              Made to work for Linux Advanced server.
- *                                     Taken care of license warning.
- *
- *     10/12/03  Ravindra              Made to work for Fedora Core 1 of
- *                                     Red Hat Distribution
- *
- *     06/01/05  Alan Cox              Merged the ISI and base kernel strands
- *                                     into a single 2.6 driver
- *
- *     ***********************************************************
- *
- *     To use this driver you also need the support package. You
- *     can find this in RPM format on
- *             ftp://ftp.linux.org.uk/pub/linux/alan
- *
- *     You can find the original tools for this direct from Multitech
- *             ftp://ftp.multitech.com/ISI-Cards/
- *
- *     Having installed the cards the module options (/etc/modprobe.conf)
- *
- *     options isicom   io=card1,card2,card3,card4 irq=card1,card2,card3,card4
- *
- *     Omit those entries for boards you don't have installed.
- *
- *     TODO
- *             Merge testing
- *             64-bit verification
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/firmware.h>
-#include <linux/kernel.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/termios.h>
-#include <linux/fs.h>
-#include <linux/sched.h>
-#include <linux/serial.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-
-#include <linux/uaccess.h>
-#include <linux/io.h>
-#include <asm/system.h>
-
-#include <linux/pci.h>
-
-#include <linux/isicom.h>
-
-#define InterruptTheCard(base) outw(0, (base) + 0xc)
-#define ClearInterrupt(base) inw((base) + 0x0a)
-
-#ifdef DEBUG
-#define isicom_paranoia_check(a, b, c) __isicom_paranoia_check((a), (b), (c))
-#else
-#define isicom_paranoia_check(a, b, c) 0
-#endif
-
-static int isicom_probe(struct pci_dev *, const struct pci_device_id *);
-static void __devexit isicom_remove(struct pci_dev *);
-
-static struct pci_device_id isicom_pci_tbl[] = {
-       { PCI_DEVICE(VENDOR_ID, 0x2028) },
-       { PCI_DEVICE(VENDOR_ID, 0x2051) },
-       { PCI_DEVICE(VENDOR_ID, 0x2052) },
-       { PCI_DEVICE(VENDOR_ID, 0x2053) },
-       { PCI_DEVICE(VENDOR_ID, 0x2054) },
-       { PCI_DEVICE(VENDOR_ID, 0x2055) },
-       { PCI_DEVICE(VENDOR_ID, 0x2056) },
-       { PCI_DEVICE(VENDOR_ID, 0x2057) },
-       { PCI_DEVICE(VENDOR_ID, 0x2058) },
-       { 0 }
-};
-MODULE_DEVICE_TABLE(pci, isicom_pci_tbl);
-
-static struct pci_driver isicom_driver = {
-       .name           = "isicom",
-       .id_table       = isicom_pci_tbl,
-       .probe          = isicom_probe,
-       .remove         = __devexit_p(isicom_remove)
-};
-
-static int prev_card = 3;      /*      start servicing isi_card[0]     */
-static struct tty_driver *isicom_normal;
-
-static void isicom_tx(unsigned long _data);
-static void isicom_start(struct tty_struct *tty);
-
-static DEFINE_TIMER(tx, isicom_tx, 0, 0);
-
-/*   baud index mappings from linux defns to isi */
-
-static signed char linuxb_to_isib[] = {
-       -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21
-};
-
-struct isi_board {
-       unsigned long           base;
-       int                     irq;
-       unsigned char           port_count;
-       unsigned short          status;
-       unsigned short          port_status; /* each bit for each port */
-       unsigned short          shift_count;
-       struct isi_port         *ports;
-       signed char             count;
-       spinlock_t              card_lock; /* Card wide lock 11/5/00 -sameer */
-       unsigned long           flags;
-       unsigned int            index;
-};
-
-struct isi_port {
-       unsigned short          magic;
-       struct tty_port         port;
-       u16                     channel;
-       u16                     status;
-       struct isi_board        *card;
-       unsigned char           *xmit_buf;
-       int                     xmit_head;
-       int                     xmit_tail;
-       int                     xmit_cnt;
-};
-
-static struct isi_board isi_card[BOARD_COUNT];
-static struct isi_port  isi_ports[PORT_COUNT];
-
-/*
- *     Locking functions for card level locking. We need to own both
- *     the kernel lock for the card and have the card in a position that
- *     it wants to talk.
- */
-
-static inline int WaitTillCardIsFree(unsigned long base)
-{
-       unsigned int count = 0;
-       unsigned int a = in_atomic(); /* do we run under spinlock? */
-
-       while (!(inw(base + 0xe) & 0x1) && count++ < 100)
-               if (a)
-                       mdelay(1);
-               else
-                       msleep(1);
-
-       return !(inw(base + 0xe) & 0x1);
-}
-
-static int lock_card(struct isi_board *card)
-{
-       unsigned long base = card->base;
-       unsigned int retries, a;
-
-       for (retries = 0; retries < 10; retries++) {
-               spin_lock_irqsave(&card->card_lock, card->flags);
-               for (a = 0; a < 10; a++) {
-                       if (inw(base + 0xe) & 0x1)
-                               return 1;
-                       udelay(10);
-               }
-               spin_unlock_irqrestore(&card->card_lock, card->flags);
-               msleep(10);
-       }
-       pr_warning("Failed to lock Card (0x%lx)\n", card->base);
-
-       return 0;       /* Failed to acquire the card! */
-}
-
-static void unlock_card(struct isi_board *card)
-{
-       spin_unlock_irqrestore(&card->card_lock, card->flags);
-}
-
-/*
- *  ISI Card specific ops ...
- */
-
-/* card->lock HAS to be held */
-static void raise_dtr(struct isi_port *port)
-{
-       struct isi_board *card = port->card;
-       unsigned long base = card->base;
-       u16 channel = port->channel;
-
-       if (WaitTillCardIsFree(base))
-               return;
-
-       outw(0x8000 | (channel << card->shift_count) | 0x02, base);
-       outw(0x0504, base);
-       InterruptTheCard(base);
-       port->status |= ISI_DTR;
-}
-
-/* card->lock HAS to be held */
-static inline void drop_dtr(struct isi_port *port)
-{
-       struct isi_board *card = port->card;
-       unsigned long base = card->base;
-       u16 channel = port->channel;
-
-       if (WaitTillCardIsFree(base))
-               return;
-
-       outw(0x8000 | (channel << card->shift_count) | 0x02, base);
-       outw(0x0404, base);
-       InterruptTheCard(base);
-       port->status &= ~ISI_DTR;
-}
-
-/* card->lock HAS to be held */
-static inline void raise_rts(struct isi_port *port)
-{
-       struct isi_board *card = port->card;
-       unsigned long base = card->base;
-       u16 channel = port->channel;
-
-       if (WaitTillCardIsFree(base))
-               return;
-
-       outw(0x8000 | (channel << card->shift_count) | 0x02, base);
-       outw(0x0a04, base);
-       InterruptTheCard(base);
-       port->status |= ISI_RTS;
-}
-
-/* card->lock HAS to be held */
-static inline void drop_rts(struct isi_port *port)
-{
-       struct isi_board *card = port->card;
-       unsigned long base = card->base;
-       u16 channel = port->channel;
-
-       if (WaitTillCardIsFree(base))
-               return;
-
-       outw(0x8000 | (channel << card->shift_count) | 0x02, base);
-       outw(0x0804, base);
-       InterruptTheCard(base);
-       port->status &= ~ISI_RTS;
-}
-
-/* card->lock MUST NOT be held */
-
-static void isicom_dtr_rts(struct tty_port *port, int on)
-{
-       struct isi_port *ip = container_of(port, struct isi_port, port);
-       struct isi_board *card = ip->card;
-       unsigned long base = card->base;
-       u16 channel = ip->channel;
-
-       if (!lock_card(card))
-               return;
-
-       if (on) {
-               outw(0x8000 | (channel << card->shift_count) | 0x02, base);
-               outw(0x0f04, base);
-               InterruptTheCard(base);
-               ip->status |= (ISI_DTR | ISI_RTS);
-       } else {
-               outw(0x8000 | (channel << card->shift_count) | 0x02, base);
-               outw(0x0C04, base);
-               InterruptTheCard(base);
-               ip->status &= ~(ISI_DTR | ISI_RTS);
-       }
-       unlock_card(card);
-}
-
-/* card->lock HAS to be held */
-static void drop_dtr_rts(struct isi_port *port)
-{
-       struct isi_board *card = port->card;
-       unsigned long base = card->base;
-       u16 channel = port->channel;
-
-       if (WaitTillCardIsFree(base))
-               return;
-
-       outw(0x8000 | (channel << card->shift_count) | 0x02, base);
-       outw(0x0c04, base);
-       InterruptTheCard(base);
-       port->status &= ~(ISI_RTS | ISI_DTR);
-}
-
-/*
- *     ISICOM Driver specific routines ...
- *
- */
-
-static inline int __isicom_paranoia_check(struct isi_port const *port,
-       char *name, const char *routine)
-{
-       if (!port) {
-               pr_warning("Warning: bad isicom magic for dev %s in %s.\n",
-                          name, routine);
-               return 1;
-       }
-       if (port->magic != ISICOM_MAGIC) {
-               pr_warning("Warning: NULL isicom port for dev %s in %s.\n",
-                          name, routine);
-               return 1;
-       }
-
-       return 0;
-}
-
-/*
- *     Transmitter.
- *
- *     We shovel data into the card buffers on a regular basis. The card
- *     will do the rest of the work for us.
- */
-
-static void isicom_tx(unsigned long _data)
-{
-       unsigned long flags, base;
-       unsigned int retries;
-       short count = (BOARD_COUNT-1), card;
-       short txcount, wrd, residue, word_count, cnt;
-       struct isi_port *port;
-       struct tty_struct *tty;
-
-       /*      find next active board  */
-       card = (prev_card + 1) & 0x0003;
-       while (count-- > 0) {
-               if (isi_card[card].status & BOARD_ACTIVE)
-                       break;
-               card = (card + 1) & 0x0003;
-       }
-       if (!(isi_card[card].status & BOARD_ACTIVE))
-               goto sched_again;
-
-       prev_card = card;
-
-       count = isi_card[card].port_count;
-       port = isi_card[card].ports;
-       base = isi_card[card].base;
-
-       spin_lock_irqsave(&isi_card[card].card_lock, flags);
-       for (retries = 0; retries < 100; retries++) {
-               if (inw(base + 0xe) & 0x1)
-                       break;
-               udelay(2);
-       }
-       if (retries >= 100)
-               goto unlock;
-
-       tty = tty_port_tty_get(&port->port);
-       if (tty == NULL)
-               goto put_unlock;
-
-       for (; count > 0; count--, port++) {
-               /* port not active or tx disabled to force flow control */
-               if (!(port->port.flags & ASYNC_INITIALIZED) ||
-                               !(port->status & ISI_TXOK))
-                       continue;
-
-               txcount = min_t(short, TX_SIZE, port->xmit_cnt);
-               if (txcount <= 0 || tty->stopped || tty->hw_stopped)
-                       continue;
-
-               if (!(inw(base + 0x02) & (1 << port->channel)))
-                       continue;
-
-               pr_debug("txing %d bytes, port%d.\n",
-                        txcount, port->channel + 1);
-               outw((port->channel << isi_card[card].shift_count) | txcount,
-                       base);
-               residue = NO;
-               wrd = 0;
-               while (1) {
-                       cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE
-                                       - port->xmit_tail));
-                       if (residue == YES) {
-                               residue = NO;
-                               if (cnt > 0) {
-                                       wrd |= (port->port.xmit_buf[port->xmit_tail]
-                                                                       << 8);
-                                       port->xmit_tail = (port->xmit_tail + 1)
-                                               & (SERIAL_XMIT_SIZE - 1);
-                                       port->xmit_cnt--;
-                                       txcount--;
-                                       cnt--;
-                                       outw(wrd, base);
-                               } else {
-                                       outw(wrd, base);
-                                       break;
-                               }
-                       }
-                       if (cnt <= 0)
-                               break;
-                       word_count = cnt >> 1;
-                       outsw(base, port->port.xmit_buf+port->xmit_tail, word_count);
-                       port->xmit_tail = (port->xmit_tail
-                               + (word_count << 1)) & (SERIAL_XMIT_SIZE - 1);
-                       txcount -= (word_count << 1);
-                       port->xmit_cnt -= (word_count << 1);
-                       if (cnt & 0x0001) {
-                               residue = YES;
-                               wrd = port->port.xmit_buf[port->xmit_tail];
-                               port->xmit_tail = (port->xmit_tail + 1)
-                                       & (SERIAL_XMIT_SIZE - 1);
-                               port->xmit_cnt--;
-                               txcount--;
-                       }
-               }
-
-               InterruptTheCard(base);
-               if (port->xmit_cnt <= 0)
-                       port->status &= ~ISI_TXOK;
-               if (port->xmit_cnt <= WAKEUP_CHARS)
-                       tty_wakeup(tty);
-       }
-
-put_unlock:
-       tty_kref_put(tty);
-unlock:
-       spin_unlock_irqrestore(&isi_card[card].card_lock, flags);
-       /*      schedule another tx for hopefully in about 10ms */
-sched_again:
-       mod_timer(&tx, jiffies + msecs_to_jiffies(10));
-}
-
-/*
- *     Main interrupt handler routine
- */
-
-static irqreturn_t isicom_interrupt(int irq, void *dev_id)
-{
-       struct isi_board *card = dev_id;
-       struct isi_port *port;
-       struct tty_struct *tty;
-       unsigned long base;
-       u16 header, word_count, count, channel;
-       short byte_count;
-       unsigned char *rp;
-
-       if (!card || !(card->status & FIRMWARE_LOADED))
-               return IRQ_NONE;
-
-       base = card->base;
-
-       /* did the card interrupt us? */
-       if (!(inw(base + 0x0e) & 0x02))
-               return IRQ_NONE;
-
-       spin_lock(&card->card_lock);
-
-       /*
-        * disable any interrupts from the PCI card and lower the
-        * interrupt line
-        */
-       outw(0x8000, base+0x04);
-       ClearInterrupt(base);
-
-       inw(base);              /* get the dummy word out */
-       header = inw(base);
-       channel = (header & 0x7800) >> card->shift_count;
-       byte_count = header & 0xff;
-
-       if (channel + 1 > card->port_count) {
-               pr_warning("%s(0x%lx): %d(channel) > port_count.\n",
-                          __func__, base, channel+1);
-               outw(0x0000, base+0x04); /* enable interrupts */
-               spin_unlock(&card->card_lock);
-               return IRQ_HANDLED;
-       }
-       port = card->ports + channel;
-       if (!(port->port.flags & ASYNC_INITIALIZED)) {
-               outw(0x0000, base+0x04); /* enable interrupts */
-               spin_unlock(&card->card_lock);
-               return IRQ_HANDLED;
-       }
-
-       tty = tty_port_tty_get(&port->port);
-       if (tty == NULL) {
-               word_count = byte_count >> 1;
-               while (byte_count > 1) {
-                       inw(base);
-                       byte_count -= 2;
-               }
-               if (byte_count & 0x01)
-                       inw(base);
-               outw(0x0000, base+0x04); /* enable interrupts */
-               spin_unlock(&card->card_lock);
-               return IRQ_HANDLED;
-       }
-
-       if (header & 0x8000) {          /* Status Packet */
-               header = inw(base);
-               switch (header & 0xff) {
-               case 0: /* Change in EIA signals */
-                       if (port->port.flags & ASYNC_CHECK_CD) {
-                               if (port->status & ISI_DCD) {
-                                       if (!(header & ISI_DCD)) {
-                                       /* Carrier has been lost  */
-                                               pr_debug("%s: DCD->low.\n",
-                                                        __func__);
-                                               port->status &= ~ISI_DCD;
-                                               tty_hangup(tty);
-                                       }
-                               } else if (header & ISI_DCD) {
-                               /* Carrier has been detected */
-                                       pr_debug("%s: DCD->high.\n",
-                                               __func__);
-                                       port->status |= ISI_DCD;
-                                       wake_up_interruptible(&port->port.open_wait);
-                               }
-                       } else {
-                               if (header & ISI_DCD)
-                                       port->status |= ISI_DCD;
-                               else
-                                       port->status &= ~ISI_DCD;
-                       }
-
-                       if (port->port.flags & ASYNC_CTS_FLOW) {
-                               if (tty->hw_stopped) {
-                                       if (header & ISI_CTS) {
-                                               port->port.tty->hw_stopped = 0;
-                                               /* start tx ing */
-                                               port->status |= (ISI_TXOK
-                                                       | ISI_CTS);
-                                               tty_wakeup(tty);
-                                       }
-                               } else if (!(header & ISI_CTS)) {
-                                       tty->hw_stopped = 1;
-                                       /* stop tx ing */
-                                       port->status &= ~(ISI_TXOK | ISI_CTS);
-                               }
-                       } else {
-                               if (header & ISI_CTS)
-                                       port->status |= ISI_CTS;
-                               else
-                                       port->status &= ~ISI_CTS;
-                       }
-
-                       if (header & ISI_DSR)
-                               port->status |= ISI_DSR;
-                       else
-                               port->status &= ~ISI_DSR;
-
-                       if (header & ISI_RI)
-                               port->status |= ISI_RI;
-                       else
-                               port->status &= ~ISI_RI;
-
-                       break;
-
-               case 1: /* Received Break !!! */
-                       tty_insert_flip_char(tty, 0, TTY_BREAK);
-                       if (port->port.flags & ASYNC_SAK)
-                               do_SAK(tty);
-                       tty_flip_buffer_push(tty);
-                       break;
-
-               case 2: /* Statistics            */
-                       pr_debug("%s: stats!!!\n", __func__);
-                       break;
-
-               default:
-                       pr_debug("%s: Unknown code in status packet.\n",
-                                __func__);
-                       break;
-               }
-       } else {                                /* Data   Packet */
-
-               count = tty_prepare_flip_string(tty, &rp, byte_count & ~1);
-               pr_debug("%s: Can rx %d of %d bytes.\n",
-                        __func__, count, byte_count);
-               word_count = count >> 1;
-               insw(base, rp, word_count);
-               byte_count -= (word_count << 1);
-               if (count & 0x0001) {
-                       tty_insert_flip_char(tty,  inw(base) & 0xff,
-                               TTY_NORMAL);
-                       byte_count -= 2;
-               }
-               if (byte_count > 0) {
-                       pr_debug("%s(0x%lx:%d): Flip buffer overflow! dropping bytes...\n",
-                                __func__, base, channel + 1);
-               /* drain out unread xtra data */
-               while (byte_count > 0) {
-                               inw(base);
-                               byte_count -= 2;
-                       }
-               }
-               tty_flip_buffer_push(tty);
-       }
-       outw(0x0000, base+0x04); /* enable interrupts */
-       spin_unlock(&card->card_lock);
-       tty_kref_put(tty);
-
-       return IRQ_HANDLED;
-}
-
-static void isicom_config_port(struct tty_struct *tty)
-{
-       struct isi_port *port = tty->driver_data;
-       struct isi_board *card = port->card;
-       unsigned long baud;
-       unsigned long base = card->base;
-       u16 channel_setup, channel = port->channel,
-               shift_count = card->shift_count;
-       unsigned char flow_ctrl;
-
-       /* FIXME: Switch to new tty baud API */
-       baud = C_BAUD(tty);
-       if (baud & CBAUDEX) {
-               baud &= ~CBAUDEX;
-
-               /*  if CBAUDEX bit is on and the baud is set to either 50 or 75
-                *  then the card is programmed for 57.6Kbps or 115Kbps
-                *  respectively.
-                */
-
-               /* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */
-               if (baud < 1 || baud > 4)
-                       tty->termios->c_cflag &= ~CBAUDEX;
-               else
-                       baud += 15;
-       }
-       if (baud == 15) {
-
-               /*  the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set
-                *  by the set_serial_info ioctl ... this is done by
-                *  the 'setserial' utility.
-                */
-
-               if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                       baud++; /*  57.6 Kbps */
-               if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                       baud += 2; /*  115  Kbps */
-               if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-                       baud += 3; /* 230 kbps*/
-               if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-                       baud += 4; /* 460 kbps*/
-       }
-       if (linuxb_to_isib[baud] == -1) {
-               /* hang up */
-               drop_dtr(port);
-               return;
-       } else
-               raise_dtr(port);
-
-       if (WaitTillCardIsFree(base) == 0) {
-               outw(0x8000 | (channel << shift_count) | 0x03, base);
-               outw(linuxb_to_isib[baud] << 8 | 0x03, base);
-               channel_setup = 0;
-               switch (C_CSIZE(tty)) {
-               case CS5:
-                       channel_setup |= ISICOM_CS5;
-                       break;
-               case CS6:
-                       channel_setup |= ISICOM_CS6;
-                       break;
-               case CS7:
-                       channel_setup |= ISICOM_CS7;
-                       break;
-               case CS8:
-                       channel_setup |= ISICOM_CS8;
-                       break;
-               }
-
-               if (C_CSTOPB(tty))
-                       channel_setup |= ISICOM_2SB;
-               if (C_PARENB(tty)) {
-                       channel_setup |= ISICOM_EVPAR;
-                       if (C_PARODD(tty))
-                               channel_setup |= ISICOM_ODPAR;
-               }
-               outw(channel_setup, base);
-               InterruptTheCard(base);
-       }
-       if (C_CLOCAL(tty))
-               port->port.flags &= ~ASYNC_CHECK_CD;
-       else
-               port->port.flags |= ASYNC_CHECK_CD;
-
-       /* flow control settings ...*/
-       flow_ctrl = 0;
-       port->port.flags &= ~ASYNC_CTS_FLOW;
-       if (C_CRTSCTS(tty)) {
-               port->port.flags |= ASYNC_CTS_FLOW;
-               flow_ctrl |= ISICOM_CTSRTS;
-       }
-       if (I_IXON(tty))
-               flow_ctrl |= ISICOM_RESPOND_XONXOFF;
-       if (I_IXOFF(tty))
-               flow_ctrl |= ISICOM_INITIATE_XONXOFF;
-
-       if (WaitTillCardIsFree(base) == 0) {
-               outw(0x8000 | (channel << shift_count) | 0x04, base);
-               outw(flow_ctrl << 8 | 0x05, base);
-               outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base);
-               InterruptTheCard(base);
-       }
-
-       /*      rx enabled -> enable port for rx on the card    */
-       if (C_CREAD(tty)) {
-               card->port_status |= (1 << channel);
-               outw(card->port_status, base + 0x02);
-       }
-}
-
-/* open et all */
-
-static inline void isicom_setup_board(struct isi_board *bp)
-{
-       int channel;
-       struct isi_port *port;
-
-       bp->count++;
-       if (!(bp->status & BOARD_INIT)) {
-               port = bp->ports;
-               for (channel = 0; channel < bp->port_count; channel++, port++)
-                       drop_dtr_rts(port);
-       }
-       bp->status |= BOARD_ACTIVE | BOARD_INIT;
-}
-
-/* Activate and thus setup board are protected from races against shutdown
-   by the tty_port mutex */
-
-static int isicom_activate(struct tty_port *tport, struct tty_struct *tty)
-{
-       struct isi_port *port = container_of(tport, struct isi_port, port);
-       struct isi_board *card = port->card;
-       unsigned long flags;
-
-       if (tty_port_alloc_xmit_buf(tport) < 0)
-               return -ENOMEM;
-
-       spin_lock_irqsave(&card->card_lock, flags);
-       isicom_setup_board(card);
-
-       port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
-
-       /*      discard any residual data       */
-       if (WaitTillCardIsFree(card->base) == 0) {
-               outw(0x8000 | (port->channel << card->shift_count) | 0x02,
-                               card->base);
-               outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base);
-               InterruptTheCard(card->base);
-       }
-       isicom_config_port(tty);
-       spin_unlock_irqrestore(&card->card_lock, flags);
-
-       return 0;
-}
-
-static int isicom_carrier_raised(struct tty_port *port)
-{
-       struct isi_port *ip = container_of(port, struct isi_port, port);
-       return (ip->status & ISI_DCD)?1 : 0;
-}
-
-static struct tty_port *isicom_find_port(struct tty_struct *tty)
-{
-       struct isi_port *port;
-       struct isi_board *card;
-       unsigned int board;
-       int line = tty->index;
-
-       if (line < 0 || line > PORT_COUNT-1)
-               return NULL;
-       board = BOARD(line);
-       card = &isi_card[board];
-
-       if (!(card->status & FIRMWARE_LOADED))
-               return NULL;
-
-       /*  open on a port greater than the port count for the card !!! */
-       if (line > ((board * 16) + card->port_count - 1))
-               return NULL;
-
-       port = &isi_ports[line];
-       if (isicom_paranoia_check(port, tty->name, "isicom_open"))
-               return NULL;
-
-       return &port->port;
-}
-
-static int isicom_open(struct tty_struct *tty, struct file *filp)
-{
-       struct isi_port *port;
-       struct tty_port *tport;
-
-       tport = isicom_find_port(tty);
-       if (tport == NULL)
-               return -ENODEV;
-       port = container_of(tport, struct isi_port, port);
-
-       tty->driver_data = port;
-       return tty_port_open(tport, tty, filp);
-}
-
-/* close et all */
-
-/* card->lock HAS to be held */
-static void isicom_shutdown_port(struct isi_port *port)
-{
-       struct isi_board *card = port->card;
-
-       if (--card->count < 0) {
-               pr_debug("%s: bad board(0x%lx) count %d.\n",
-                        __func__, card->base, card->count);
-               card->count = 0;
-       }
-       /* last port was closed, shutdown that board too */
-       if (!card->count)
-               card->status &= BOARD_ACTIVE;
-}
-
-static void isicom_flush_buffer(struct tty_struct *tty)
-{
-       struct isi_port *port = tty->driver_data;
-       struct isi_board *card = port->card;
-       unsigned long flags;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_flush_buffer"))
-               return;
-
-       spin_lock_irqsave(&card->card_lock, flags);
-       port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
-       spin_unlock_irqrestore(&card->card_lock, flags);
-
-       tty_wakeup(tty);
-}
-
-static void isicom_shutdown(struct tty_port *port)
-{
-       struct isi_port *ip = container_of(port, struct isi_port, port);
-       struct isi_board *card = ip->card;
-       unsigned long flags;
-
-       /* indicate to the card that no more data can be received
-          on this port */
-       spin_lock_irqsave(&card->card_lock, flags);
-       card->port_status &= ~(1 << ip->channel);
-       outw(card->port_status, card->base + 0x02);
-       isicom_shutdown_port(ip);
-       spin_unlock_irqrestore(&card->card_lock, flags);
-       tty_port_free_xmit_buf(port);
-}
-
-static void isicom_close(struct tty_struct *tty, struct file *filp)
-{
-       struct isi_port *ip = tty->driver_data;
-       struct tty_port *port;
-
-       if (ip == NULL)
-               return;
-
-       port = &ip->port;
-       if (isicom_paranoia_check(ip, tty->name, "isicom_close"))
-               return;
-       tty_port_close(port, tty, filp);
-}
-
-/* write et all */
-static int isicom_write(struct tty_struct *tty,        const unsigned char *buf,
-       int count)
-{
-       struct isi_port *port = tty->driver_data;
-       struct isi_board *card = port->card;
-       unsigned long flags;
-       int cnt, total = 0;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_write"))
-               return 0;
-
-       spin_lock_irqsave(&card->card_lock, flags);
-
-       while (1) {
-               cnt = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt
-                               - 1, SERIAL_XMIT_SIZE - port->xmit_head));
-               if (cnt <= 0)
-                       break;
-
-               memcpy(port->port.xmit_buf + port->xmit_head, buf, cnt);
-               port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE
-                       - 1);
-               port->xmit_cnt += cnt;
-               buf += cnt;
-               count -= cnt;
-               total += cnt;
-       }
-       if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped)
-               port->status |= ISI_TXOK;
-       spin_unlock_irqrestore(&card->card_lock, flags);
-       return total;
-}
-
-/* put_char et all */
-static int isicom_put_char(struct tty_struct *tty, unsigned char ch)
-{
-       struct isi_port *port = tty->driver_data;
-       struct isi_board *card = port->card;
-       unsigned long flags;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_put_char"))
-               return 0;
-
-       spin_lock_irqsave(&card->card_lock, flags);
-       if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
-               spin_unlock_irqrestore(&card->card_lock, flags);
-               return 0;
-       }
-
-       port->port.xmit_buf[port->xmit_head++] = ch;
-       port->xmit_head &= (SERIAL_XMIT_SIZE - 1);
-       port->xmit_cnt++;
-       spin_unlock_irqrestore(&card->card_lock, flags);
-       return 1;
-}
-
-/* flush_chars et all */
-static void isicom_flush_chars(struct tty_struct *tty)
-{
-       struct isi_port *port = tty->driver_data;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_flush_chars"))
-               return;
-
-       if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
-                       !port->port.xmit_buf)
-               return;
-
-       /* this tells the transmitter to consider this port for
-          data output to the card ... that's the best we can do. */
-       port->status |= ISI_TXOK;
-}
-
-/* write_room et all */
-static int isicom_write_room(struct tty_struct *tty)
-{
-       struct isi_port *port = tty->driver_data;
-       int free;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_write_room"))
-               return 0;
-
-       free = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
-       if (free < 0)
-               free = 0;
-       return free;
-}
-
-/* chars_in_buffer et all */
-static int isicom_chars_in_buffer(struct tty_struct *tty)
-{
-       struct isi_port *port = tty->driver_data;
-       if (isicom_paranoia_check(port, tty->name, "isicom_chars_in_buffer"))
-               return 0;
-       return port->xmit_cnt;
-}
-
-/* ioctl et all */
-static int isicom_send_break(struct tty_struct *tty, int length)
-{
-       struct isi_port *port = tty->driver_data;
-       struct isi_board *card = port->card;
-       unsigned long base = card->base;
-
-       if (length == -1)
-               return -EOPNOTSUPP;
-
-       if (!lock_card(card))
-               return -EINVAL;
-
-       outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base);
-       outw((length & 0xff) << 8 | 0x00, base);
-       outw((length & 0xff00), base);
-       InterruptTheCard(base);
-
-       unlock_card(card);
-       return 0;
-}
-
-static int isicom_tiocmget(struct tty_struct *tty)
-{
-       struct isi_port *port = tty->driver_data;
-       /* just send the port status */
-       u16 status = port->status;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
-               return -ENODEV;
-
-       return  ((status & ISI_RTS) ? TIOCM_RTS : 0) |
-               ((status & ISI_DTR) ? TIOCM_DTR : 0) |
-               ((status & ISI_DCD) ? TIOCM_CAR : 0) |
-               ((status & ISI_DSR) ? TIOCM_DSR : 0) |
-               ((status & ISI_CTS) ? TIOCM_CTS : 0) |
-               ((status & ISI_RI ) ? TIOCM_RI  : 0);
-}
-
-static int isicom_tiocmset(struct tty_struct *tty,
-                                       unsigned int set, unsigned int clear)
-{
-       struct isi_port *port = tty->driver_data;
-       unsigned long flags;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
-               return -ENODEV;
-
-       spin_lock_irqsave(&port->card->card_lock, flags);
-       if (set & TIOCM_RTS)
-               raise_rts(port);
-       if (set & TIOCM_DTR)
-               raise_dtr(port);
-
-       if (clear & TIOCM_RTS)
-               drop_rts(port);
-       if (clear & TIOCM_DTR)
-               drop_dtr(port);
-       spin_unlock_irqrestore(&port->card->card_lock, flags);
-
-       return 0;
-}
-
-static int isicom_set_serial_info(struct tty_struct *tty,
-                                       struct serial_struct __user *info)
-{
-       struct isi_port *port = tty->driver_data;
-       struct serial_struct newinfo;
-       int reconfig_port;
-
-       if (copy_from_user(&newinfo, info, sizeof(newinfo)))
-               return -EFAULT;
-
-       mutex_lock(&port->port.mutex);
-       reconfig_port = ((port->port.flags & ASYNC_SPD_MASK) !=
-               (newinfo.flags & ASYNC_SPD_MASK));
-
-       if (!capable(CAP_SYS_ADMIN)) {
-               if ((newinfo.close_delay != port->port.close_delay) ||
-                               (newinfo.closing_wait != port->port.closing_wait) ||
-                               ((newinfo.flags & ~ASYNC_USR_MASK) !=
-                               (port->port.flags & ~ASYNC_USR_MASK))) {
-                       mutex_unlock(&port->port.mutex);
-                       return -EPERM;
-               }
-               port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
-                               (newinfo.flags & ASYNC_USR_MASK));
-       } else {
-               port->port.close_delay = newinfo.close_delay;
-               port->port.closing_wait = newinfo.closing_wait;
-               port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
-                               (newinfo.flags & ASYNC_FLAGS));
-       }
-       if (reconfig_port) {
-               unsigned long flags;
-               spin_lock_irqsave(&port->card->card_lock, flags);
-               isicom_config_port(tty);
-               spin_unlock_irqrestore(&port->card->card_lock, flags);
-       }
-       mutex_unlock(&port->port.mutex);
-       return 0;
-}
-
-static int isicom_get_serial_info(struct isi_port *port,
-       struct serial_struct __user *info)
-{
-       struct serial_struct out_info;
-
-       mutex_lock(&port->port.mutex);
-       memset(&out_info, 0, sizeof(out_info));
-/*     out_info.type = ? */
-       out_info.line = port - isi_ports;
-       out_info.port = port->card->base;
-       out_info.irq = port->card->irq;
-       out_info.flags = port->port.flags;
-/*     out_info.baud_base = ? */
-       out_info.close_delay = port->port.close_delay;
-       out_info.closing_wait = port->port.closing_wait;
-       mutex_unlock(&port->port.mutex);
-       if (copy_to_user(info, &out_info, sizeof(out_info)))
-               return -EFAULT;
-       return 0;
-}
-
-static int isicom_ioctl(struct tty_struct *tty,
-       unsigned int cmd, unsigned long arg)
-{
-       struct isi_port *port = tty->driver_data;
-       void __user *argp = (void __user *)arg;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
-               return -ENODEV;
-
-       switch (cmd) {
-       case TIOCGSERIAL:
-               return isicom_get_serial_info(port, argp);
-
-       case TIOCSSERIAL:
-               return isicom_set_serial_info(tty, argp);
-
-       default:
-               return -ENOIOCTLCMD;
-       }
-       return 0;
-}
-
-/* set_termios et all */
-static void isicom_set_termios(struct tty_struct *tty,
-       struct ktermios *old_termios)
-{
-       struct isi_port *port = tty->driver_data;
-       unsigned long flags;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_set_termios"))
-               return;
-
-       if (tty->termios->c_cflag == old_termios->c_cflag &&
-                       tty->termios->c_iflag == old_termios->c_iflag)
-               return;
-
-       spin_lock_irqsave(&port->card->card_lock, flags);
-       isicom_config_port(tty);
-       spin_unlock_irqrestore(&port->card->card_lock, flags);
-
-       if ((old_termios->c_cflag & CRTSCTS) &&
-                       !(tty->termios->c_cflag & CRTSCTS)) {
-               tty->hw_stopped = 0;
-               isicom_start(tty);
-       }
-}
-
-/* throttle et all */
-static void isicom_throttle(struct tty_struct *tty)
-{
-       struct isi_port *port = tty->driver_data;
-       struct isi_board *card = port->card;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_throttle"))
-               return;
-
-       /* tell the card that this port cannot handle any more data for now */
-       card->port_status &= ~(1 << port->channel);
-       outw(card->port_status, card->base + 0x02);
-}
-
-/* unthrottle et all */
-static void isicom_unthrottle(struct tty_struct *tty)
-{
-       struct isi_port *port = tty->driver_data;
-       struct isi_board *card = port->card;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_unthrottle"))
-               return;
-
-       /* tell the card that this port is ready to accept more data */
-       card->port_status |= (1 << port->channel);
-       outw(card->port_status, card->base + 0x02);
-}
-
-/* stop et all */
-static void isicom_stop(struct tty_struct *tty)
-{
-       struct isi_port *port = tty->driver_data;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_stop"))
-               return;
-
-       /* this tells the transmitter not to consider this port for
-          data output to the card. */
-       port->status &= ~ISI_TXOK;
-}
-
-/* start et all */
-static void isicom_start(struct tty_struct *tty)
-{
-       struct isi_port *port = tty->driver_data;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_start"))
-               return;
-
-       /* this tells the transmitter to consider this port for
-          data output to the card. */
-       port->status |= ISI_TXOK;
-}
-
-static void isicom_hangup(struct tty_struct *tty)
-{
-       struct isi_port *port = tty->driver_data;
-
-       if (isicom_paranoia_check(port, tty->name, "isicom_hangup"))
-               return;
-       tty_port_hangup(&port->port);
-}
-
-
-/*
- * Driver init and deinit functions
- */
-
-static const struct tty_operations isicom_ops = {
-       .open                   = isicom_open,
-       .close                  = isicom_close,
-       .write                  = isicom_write,
-       .put_char               = isicom_put_char,
-       .flush_chars            = isicom_flush_chars,
-       .write_room             = isicom_write_room,
-       .chars_in_buffer        = isicom_chars_in_buffer,
-       .ioctl                  = isicom_ioctl,
-       .set_termios            = isicom_set_termios,
-       .throttle               = isicom_throttle,
-       .unthrottle             = isicom_unthrottle,
-       .stop                   = isicom_stop,
-       .start                  = isicom_start,
-       .hangup                 = isicom_hangup,
-       .flush_buffer           = isicom_flush_buffer,
-       .tiocmget               = isicom_tiocmget,
-       .tiocmset               = isicom_tiocmset,
-       .break_ctl              = isicom_send_break,
-};
-
-static const struct tty_port_operations isicom_port_ops = {
-       .carrier_raised         = isicom_carrier_raised,
-       .dtr_rts                = isicom_dtr_rts,
-       .activate               = isicom_activate,
-       .shutdown               = isicom_shutdown,
-};
-
-static int __devinit reset_card(struct pci_dev *pdev,
-       const unsigned int card, unsigned int *signature)
-{
-       struct isi_board *board = pci_get_drvdata(pdev);
-       unsigned long base = board->base;
-       unsigned int sig, portcount = 0;
-       int retval = 0;
-
-       dev_dbg(&pdev->dev, "ISILoad:Resetting Card%d at 0x%lx\n", card + 1,
-               base);
-
-       inw(base + 0x8);
-
-       msleep(10);
-
-       outw(0, base + 0x8); /* Reset */
-
-       msleep(1000);
-
-       sig = inw(base + 0x4) & 0xff;
-
-       if (sig != 0xa5 && sig != 0xbb && sig != 0xcc && sig != 0xdd &&
-                       sig != 0xee) {
-               dev_warn(&pdev->dev, "ISILoad:Card%u reset failure (Possible "
-                       "bad I/O Port Address 0x%lx).\n", card + 1, base);
-               dev_dbg(&pdev->dev, "Sig=0x%x\n", sig);
-               retval = -EIO;
-               goto end;
-       }
-
-       msleep(10);
-
-       portcount = inw(base + 0x2);
-       if (!(inw(base + 0xe) & 0x1) || (portcount != 0 && portcount != 4 &&
-                               portcount != 8 && portcount != 16)) {
-               dev_err(&pdev->dev, "ISILoad:PCI Card%d reset failure.\n",
-                       card + 1);
-               retval = -EIO;
-               goto end;
-       }
-
-       switch (sig) {
-       case 0xa5:
-       case 0xbb:
-       case 0xdd:
-               board->port_count = (portcount == 4) ? 4 : 8;
-               board->shift_count = 12;
-               break;
-       case 0xcc:
-       case 0xee:
-               board->port_count = 16;
-               board->shift_count = 11;
-               break;
-       }
-       dev_info(&pdev->dev, "-Done\n");
-       *signature = sig;
-
-end:
-       return retval;
-}
-
-static int __devinit load_firmware(struct pci_dev *pdev,
-       const unsigned int index, const unsigned int signature)
-{
-       struct isi_board *board = pci_get_drvdata(pdev);
-       const struct firmware *fw;
-       unsigned long base = board->base;
-       unsigned int a;
-       u16 word_count, status;
-       int retval = -EIO;
-       char *name;
-       u8 *data;
-
-       struct stframe {
-               u16     addr;
-               u16     count;
-               u8      data[0];
-       } *frame;
-
-       switch (signature) {
-       case 0xa5:
-               name = "isi608.bin";
-               break;
-       case 0xbb:
-               name = "isi608em.bin";
-               break;
-       case 0xcc:
-               name = "isi616em.bin";
-               break;
-       case 0xdd:
-               name = "isi4608.bin";
-               break;
-       case 0xee:
-               name = "isi4616.bin";
-               break;
-       default:
-               dev_err(&pdev->dev, "Unknown signature.\n");
-               goto end;
-       }
-
-       retval = request_firmware(&fw, name, &pdev->dev);
-       if (retval)
-               goto end;
-
-       retval = -EIO;
-
-       for (frame = (struct stframe *)fw->data;
-                       frame < (struct stframe *)(fw->data + fw->size);
-                       frame = (struct stframe *)((u8 *)(frame + 1) +
-                               frame->count)) {
-               if (WaitTillCardIsFree(base))
-                       goto errrelfw;
-
-               outw(0xf0, base);       /* start upload sequence */
-               outw(0x00, base);
-               outw(frame->addr, base); /* lsb of address */
-
-               word_count = frame->count / 2 + frame->count % 2;
-               outw(word_count, base);
-               InterruptTheCard(base);
-
-               udelay(100); /* 0x2f */
-
-               if (WaitTillCardIsFree(base))
-                       goto errrelfw;
-
-               status = inw(base + 0x4);
-               if (status != 0) {
-                       dev_warn(&pdev->dev, "Card%d rejected load header:\n"
-                                "Address:0x%x\n"
-                                "Count:0x%x\n"
-                                "Status:0x%x\n",
-                                index + 1, frame->addr, frame->count, status);
-                       goto errrelfw;
-               }
-               outsw(base, frame->data, word_count);
-
-               InterruptTheCard(base);
-
-               udelay(50); /* 0x0f */
-
-               if (WaitTillCardIsFree(base))
-                       goto errrelfw;
-
-               status = inw(base + 0x4);
-               if (status != 0) {
-                       dev_err(&pdev->dev, "Card%d got out of sync.Card "
-                               "Status:0x%x\n", index + 1, status);
-                       goto errrelfw;
-               }
-       }
-
-/* XXX: should we test it by reading it back and comparing with original like
- * in load firmware package? */
-       for (frame = (struct stframe *)fw->data;
-                       frame < (struct stframe *)(fw->data + fw->size);
-                       frame = (struct stframe *)((u8 *)(frame + 1) +
-                               frame->count)) {
-               if (WaitTillCardIsFree(base))
-                       goto errrelfw;
-
-               outw(0xf1, base); /* start download sequence */
-               outw(0x00, base);
-               outw(frame->addr, base); /* lsb of address */
-
-               word_count = (frame->count >> 1) + frame->count % 2;
-               outw(word_count + 1, base);
-               InterruptTheCard(base);
-
-               udelay(50); /* 0xf */
-
-               if (WaitTillCardIsFree(base))
-                       goto errrelfw;
-
-               status = inw(base + 0x4);
-               if (status != 0) {
-                       dev_warn(&pdev->dev, "Card%d rejected verify header:\n"
-                                "Address:0x%x\n"
-                                "Count:0x%x\n"
-                                "Status: 0x%x\n",
-                                index + 1, frame->addr, frame->count, status);
-                       goto errrelfw;
-               }
-
-               data = kmalloc(word_count * 2, GFP_KERNEL);
-               if (data == NULL) {
-                       dev_err(&pdev->dev, "Card%d, firmware upload "
-                               "failed, not enough memory\n", index + 1);
-                       goto errrelfw;
-               }
-               inw(base);
-               insw(base, data, word_count);
-               InterruptTheCard(base);
-
-               for (a = 0; a < frame->count; a++)
-                       if (data[a] != frame->data[a]) {
-                               kfree(data);
-                               dev_err(&pdev->dev, "Card%d, firmware upload "
-                                       "failed\n", index + 1);
-                               goto errrelfw;
-                       }
-               kfree(data);
-
-               udelay(50); /* 0xf */
-
-               if (WaitTillCardIsFree(base))
-                       goto errrelfw;
-
-               status = inw(base + 0x4);
-               if (status != 0) {
-                       dev_err(&pdev->dev, "Card%d verify got out of sync. "
-                               "Card Status:0x%x\n", index + 1, status);
-                       goto errrelfw;
-               }
-       }
-
-       /* xfer ctrl */
-       if (WaitTillCardIsFree(base))
-               goto errrelfw;
-
-       outw(0xf2, base);
-       outw(0x800, base);
-       outw(0x0, base);
-       outw(0x0, base);
-       InterruptTheCard(base);
-       outw(0x0, base + 0x4); /* for ISI4608 cards */
-
-       board->status |= FIRMWARE_LOADED;
-       retval = 0;
-
-errrelfw:
-       release_firmware(fw);
-end:
-       return retval;
-}
-
-/*
- *     Insmod can set static symbols so keep these static
- */
-static unsigned int card_count;
-
-static int __devinit isicom_probe(struct pci_dev *pdev,
-       const struct pci_device_id *ent)
-{
-       unsigned int uninitialized_var(signature), index;
-       int retval = -EPERM;
-       struct isi_board *board = NULL;
-
-       if (card_count >= BOARD_COUNT)
-               goto err;
-
-       retval = pci_enable_device(pdev);
-       if (retval) {
-               dev_err(&pdev->dev, "failed to enable\n");
-               goto err;
-       }
-
-       dev_info(&pdev->dev, "ISI PCI Card(Device ID 0x%x)\n", ent->device);
-
-       /* allot the first empty slot in the array */
-       for (index = 0; index < BOARD_COUNT; index++) {
-               if (isi_card[index].base == 0) {
-                       board = &isi_card[index];
-                       break;
-               }
-       }
-       if (index == BOARD_COUNT) {
-               retval = -ENODEV;
-               goto err_disable;
-       }
-
-       board->index = index;
-       board->base = pci_resource_start(pdev, 3);
-       board->irq = pdev->irq;
-       card_count++;
-
-       pci_set_drvdata(pdev, board);
-
-       retval = pci_request_region(pdev, 3, ISICOM_NAME);
-       if (retval) {
-               dev_err(&pdev->dev, "I/O Region 0x%lx-0x%lx is busy. Card%d "
-                       "will be disabled.\n", board->base, board->base + 15,
-                       index + 1);
-               retval = -EBUSY;
-               goto errdec;
-       }
-
-       retval = request_irq(board->irq, isicom_interrupt,
-                       IRQF_SHARED | IRQF_DISABLED, ISICOM_NAME, board);
-       if (retval < 0) {
-               dev_err(&pdev->dev, "Could not install handler at Irq %d. "
-                       "Card%d will be disabled.\n", board->irq, index + 1);
-               goto errunrr;
-       }
-
-       retval = reset_card(pdev, index, &signature);
-       if (retval < 0)
-               goto errunri;
-
-       retval = load_firmware(pdev, index, signature);
-       if (retval < 0)
-               goto errunri;
-
-       for (index = 0; index < board->port_count; index++)
-               tty_register_device(isicom_normal, board->index * 16 + index,
-                               &pdev->dev);
-
-       return 0;
-
-errunri:
-       free_irq(board->irq, board);
-errunrr:
-       pci_release_region(pdev, 3);
-errdec:
-       board->base = 0;
-       card_count--;
-err_disable:
-       pci_disable_device(pdev);
-err:
-       return retval;
-}
-
-static void __devexit isicom_remove(struct pci_dev *pdev)
-{
-       struct isi_board *board = pci_get_drvdata(pdev);
-       unsigned int i;
-
-       for (i = 0; i < board->port_count; i++)
-               tty_unregister_device(isicom_normal, board->index * 16 + i);
-
-       free_irq(board->irq, board);
-       pci_release_region(pdev, 3);
-       board->base = 0;
-       card_count--;
-       pci_disable_device(pdev);
-}
-
-static int __init isicom_init(void)
-{
-       int retval, idx, channel;
-       struct isi_port *port;
-
-       for (idx = 0; idx < BOARD_COUNT; idx++) {
-               port = &isi_ports[idx * 16];
-               isi_card[idx].ports = port;
-               spin_lock_init(&isi_card[idx].card_lock);
-               for (channel = 0; channel < 16; channel++, port++) {
-                       tty_port_init(&port->port);
-                       port->port.ops = &isicom_port_ops;
-                       port->magic = ISICOM_MAGIC;
-                       port->card = &isi_card[idx];
-                       port->channel = channel;
-                       port->port.close_delay = 50 * HZ/100;
-                       port->port.closing_wait = 3000 * HZ/100;
-                       port->status = 0;
-                       /*  . . .  */
-               }
-               isi_card[idx].base = 0;
-               isi_card[idx].irq = 0;
-       }
-
-       /* tty driver structure initialization */
-       isicom_normal = alloc_tty_driver(PORT_COUNT);
-       if (!isicom_normal) {
-               retval = -ENOMEM;
-               goto error;
-       }
-
-       isicom_normal->owner                    = THIS_MODULE;
-       isicom_normal->name                     = "ttyM";
-       isicom_normal->major                    = ISICOM_NMAJOR;
-       isicom_normal->minor_start              = 0;
-       isicom_normal->type                     = TTY_DRIVER_TYPE_SERIAL;
-       isicom_normal->subtype                  = SERIAL_TYPE_NORMAL;
-       isicom_normal->init_termios             = tty_std_termios;
-       isicom_normal->init_termios.c_cflag     = B9600 | CS8 | CREAD | HUPCL |
-               CLOCAL;
-       isicom_normal->flags                    = TTY_DRIVER_REAL_RAW |
-               TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK;
-       tty_set_operations(isicom_normal, &isicom_ops);
-
-       retval = tty_register_driver(isicom_normal);
-       if (retval) {
-               pr_debug("Couldn't register the dialin driver\n");
-               goto err_puttty;
-       }
-
-       retval = pci_register_driver(&isicom_driver);
-       if (retval < 0) {
-               pr_err("Unable to register pci driver.\n");
-               goto err_unrtty;
-       }
-
-       mod_timer(&tx, jiffies + 1);
-
-       return 0;
-err_unrtty:
-       tty_unregister_driver(isicom_normal);
-err_puttty:
-       put_tty_driver(isicom_normal);
-error:
-       return retval;
-}
-
-static void __exit isicom_exit(void)
-{
-       del_timer_sync(&tx);
-
-       pci_unregister_driver(&isicom_driver);
-       tty_unregister_driver(isicom_normal);
-       put_tty_driver(isicom_normal);
-}
-
-module_init(isicom_init);
-module_exit(isicom_exit);
-
-MODULE_AUTHOR("MultiTech");
-MODULE_DESCRIPTION("Driver for the ISI series of cards by MultiTech");
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE("isi608.bin");
-MODULE_FIRMWARE("isi608em.bin");
-MODULE_FIRMWARE("isi616em.bin");
-MODULE_FIRMWARE("isi4608.bin");
-MODULE_FIRMWARE("isi4616.bin");
diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
deleted file mode 100644 (file)
index 35b0c38..0000000
+++ /dev/null
@@ -1,2092 +0,0 @@
-/*****************************************************************************/
-/*
- *           moxa.c  -- MOXA Intellio family multiport serial driver.
- *
- *      Copyright (C) 1999-2000  Moxa Technologies (support@moxa.com).
- *      Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
- *
- *      This code is loosely based on the Linux serial driver, written by
- *      Linus Torvalds, Theodore T'so and others.
- *
- *      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.
- */
-
-/*
- *    MOXA Intellio Series Driver
- *      for             : LINUX
- *      date            : 1999/1/7
- *      version         : 5.1
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/errno.h>
-#include <linux/firmware.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/serial.h>
-#include <linux/tty_driver.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/bitops.h>
-#include <linux/slab.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-
-#include "moxa.h"
-
-#define MOXA_VERSION           "6.0k"
-
-#define MOXA_FW_HDRLEN         32
-
-#define MOXAMAJOR              172
-
-#define MAX_BOARDS             4       /* Don't change this value */
-#define MAX_PORTS_PER_BOARD    32      /* Don't change this value */
-#define MAX_PORTS              (MAX_BOARDS * MAX_PORTS_PER_BOARD)
-
-#define MOXA_IS_320(brd) ((brd)->boardType == MOXA_BOARD_C320_ISA || \
-               (brd)->boardType == MOXA_BOARD_C320_PCI)
-
-/*
- *    Define the Moxa PCI vendor and device IDs.
- */
-#define MOXA_BUS_TYPE_ISA      0
-#define MOXA_BUS_TYPE_PCI      1
-
-enum {
-       MOXA_BOARD_C218_PCI = 1,
-       MOXA_BOARD_C218_ISA,
-       MOXA_BOARD_C320_PCI,
-       MOXA_BOARD_C320_ISA,
-       MOXA_BOARD_CP204J,
-};
-
-static char *moxa_brdname[] =
-{
-       "C218 Turbo PCI series",
-       "C218 Turbo ISA series",
-       "C320 Turbo PCI series",
-       "C320 Turbo ISA series",
-       "CP-204J series",
-};
-
-#ifdef CONFIG_PCI
-static struct pci_device_id moxa_pcibrds[] = {
-       { PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C218),
-               .driver_data = MOXA_BOARD_C218_PCI },
-       { PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C320),
-               .driver_data = MOXA_BOARD_C320_PCI },
-       { PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP204J),
-               .driver_data = MOXA_BOARD_CP204J },
-       { 0 }
-};
-MODULE_DEVICE_TABLE(pci, moxa_pcibrds);
-#endif /* CONFIG_PCI */
-
-struct moxa_port;
-
-static struct moxa_board_conf {
-       int boardType;
-       int numPorts;
-       int busType;
-
-       unsigned int ready;
-
-       struct moxa_port *ports;
-
-       void __iomem *basemem;
-       void __iomem *intNdx;
-       void __iomem *intPend;
-       void __iomem *intTable;
-} moxa_boards[MAX_BOARDS];
-
-struct mxser_mstatus {
-       tcflag_t cflag;
-       int cts;
-       int dsr;
-       int ri;
-       int dcd;
-};
-
-struct moxaq_str {
-       int inq;
-       int outq;
-};
-
-struct moxa_port {
-       struct tty_port port;
-       struct moxa_board_conf *board;
-       void __iomem *tableAddr;
-
-       int type;
-       int cflag;
-       unsigned long statusflags;
-
-       u8 DCDState;            /* Protected by the port lock */
-       u8 lineCtrl;
-       u8 lowChkFlag;
-};
-
-struct mon_str {
-       int tick;
-       int rxcnt[MAX_PORTS];
-       int txcnt[MAX_PORTS];
-};
-
-/* statusflags */
-#define TXSTOPPED      1
-#define LOWWAIT        2
-#define EMPTYWAIT      3
-
-#define SERIAL_DO_RESTART
-
-#define WAKEUP_CHARS           256
-
-static int ttymajor = MOXAMAJOR;
-static struct mon_str moxaLog;
-static unsigned int moxaFuncTout = HZ / 2;
-static unsigned int moxaLowWaterChk;
-static DEFINE_MUTEX(moxa_openlock);
-static DEFINE_SPINLOCK(moxa_lock);
-
-static unsigned long baseaddr[MAX_BOARDS];
-static unsigned int type[MAX_BOARDS];
-static unsigned int numports[MAX_BOARDS];
-
-MODULE_AUTHOR("William Chen");
-MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver");
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE("c218tunx.cod");
-MODULE_FIRMWARE("cp204unx.cod");
-MODULE_FIRMWARE("c320tunx.cod");
-
-module_param_array(type, uint, NULL, 0);
-MODULE_PARM_DESC(type, "card type: C218=2, C320=4");
-module_param_array(baseaddr, ulong, NULL, 0);
-MODULE_PARM_DESC(baseaddr, "base address");
-module_param_array(numports, uint, NULL, 0);
-MODULE_PARM_DESC(numports, "numports (ignored for C218)");
-
-module_param(ttymajor, int, 0);
-
-/*
- * static functions:
- */
-static int moxa_open(struct tty_struct *, struct file *);
-static void moxa_close(struct tty_struct *, struct file *);
-static int moxa_write(struct tty_struct *, const unsigned char *, int);
-static int moxa_write_room(struct tty_struct *);
-static void moxa_flush_buffer(struct tty_struct *);
-static int moxa_chars_in_buffer(struct tty_struct *);
-static void moxa_set_termios(struct tty_struct *, struct ktermios *);
-static void moxa_stop(struct tty_struct *);
-static void moxa_start(struct tty_struct *);
-static void moxa_hangup(struct tty_struct *);
-static int moxa_tiocmget(struct tty_struct *tty);
-static int moxa_tiocmset(struct tty_struct *tty,
-                        unsigned int set, unsigned int clear);
-static void moxa_poll(unsigned long);
-static void moxa_set_tty_param(struct tty_struct *, struct ktermios *);
-static void moxa_shutdown(struct tty_port *);
-static int moxa_carrier_raised(struct tty_port *);
-static void moxa_dtr_rts(struct tty_port *, int);
-/*
- * moxa board interface functions:
- */
-static void MoxaPortEnable(struct moxa_port *);
-static void MoxaPortDisable(struct moxa_port *);
-static int MoxaPortSetTermio(struct moxa_port *, struct ktermios *, speed_t);
-static int MoxaPortGetLineOut(struct moxa_port *, int *, int *);
-static void MoxaPortLineCtrl(struct moxa_port *, int, int);
-static void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int);
-static int MoxaPortLineStatus(struct moxa_port *);
-static void MoxaPortFlushData(struct moxa_port *, int);
-static int MoxaPortWriteData(struct tty_struct *, const unsigned char *, int);
-static int MoxaPortReadData(struct moxa_port *);
-static int MoxaPortTxQueue(struct moxa_port *);
-static int MoxaPortRxQueue(struct moxa_port *);
-static int MoxaPortTxFree(struct moxa_port *);
-static void MoxaPortTxDisable(struct moxa_port *);
-static void MoxaPortTxEnable(struct moxa_port *);
-static int moxa_get_serial_info(struct moxa_port *, struct serial_struct __user *);
-static int moxa_set_serial_info(struct moxa_port *, struct serial_struct __user *);
-static void MoxaSetFifo(struct moxa_port *port, int enable);
-
-/*
- * I/O functions
- */
-
-static DEFINE_SPINLOCK(moxafunc_lock);
-
-static void moxa_wait_finish(void __iomem *ofsAddr)
-{
-       unsigned long end = jiffies + moxaFuncTout;
-
-       while (readw(ofsAddr + FuncCode) != 0)
-               if (time_after(jiffies, end))
-                       return;
-       if (readw(ofsAddr + FuncCode) != 0 && printk_ratelimit())
-               printk(KERN_WARNING "moxa function expired\n");
-}
-
-static void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg)
-{
-        unsigned long flags;
-        spin_lock_irqsave(&moxafunc_lock, flags);
-       writew(arg, ofsAddr + FuncArg);
-       writew(cmd, ofsAddr + FuncCode);
-       moxa_wait_finish(ofsAddr);
-       spin_unlock_irqrestore(&moxafunc_lock, flags);
-}
-
-static int moxafuncret(void __iomem *ofsAddr, u16 cmd, u16 arg)
-{
-        unsigned long flags;
-        u16 ret;
-        spin_lock_irqsave(&moxafunc_lock, flags);
-       writew(arg, ofsAddr + FuncArg);
-       writew(cmd, ofsAddr + FuncCode);
-       moxa_wait_finish(ofsAddr);
-       ret = readw(ofsAddr + FuncArg);
-       spin_unlock_irqrestore(&moxafunc_lock, flags);
-       return ret;
-}
-
-static void moxa_low_water_check(void __iomem *ofsAddr)
-{
-       u16 rptr, wptr, mask, len;
-
-       if (readb(ofsAddr + FlagStat) & Xoff_state) {
-               rptr = readw(ofsAddr + RXrptr);
-               wptr = readw(ofsAddr + RXwptr);
-               mask = readw(ofsAddr + RX_mask);
-               len = (wptr - rptr) & mask;
-               if (len <= Low_water)
-                       moxafunc(ofsAddr, FC_SendXon, 0);
-       }
-}
-
-/*
- * TTY operations
- */
-
-static int moxa_ioctl(struct tty_struct *tty,
-                     unsigned int cmd, unsigned long arg)
-{
-       struct moxa_port *ch = tty->driver_data;
-       void __user *argp = (void __user *)arg;
-       int status, ret = 0;
-
-       if (tty->index == MAX_PORTS) {
-               if (cmd != MOXA_GETDATACOUNT && cmd != MOXA_GET_IOQUEUE &&
-                               cmd != MOXA_GETMSTATUS)
-                       return -EINVAL;
-       } else if (!ch)
-               return -ENODEV;
-
-       switch (cmd) {
-       case MOXA_GETDATACOUNT:
-               moxaLog.tick = jiffies;
-               if (copy_to_user(argp, &moxaLog, sizeof(moxaLog)))
-                       ret = -EFAULT;
-               break;
-       case MOXA_FLUSH_QUEUE:
-               MoxaPortFlushData(ch, arg);
-               break;
-       case MOXA_GET_IOQUEUE: {
-               struct moxaq_str __user *argm = argp;
-               struct moxaq_str tmp;
-               struct moxa_port *p;
-               unsigned int i, j;
-
-               for (i = 0; i < MAX_BOARDS; i++) {
-                       p = moxa_boards[i].ports;
-                       for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
-                               memset(&tmp, 0, sizeof(tmp));
-                               spin_lock_bh(&moxa_lock);
-                               if (moxa_boards[i].ready) {
-                                       tmp.inq = MoxaPortRxQueue(p);
-                                       tmp.outq = MoxaPortTxQueue(p);
-                               }
-                               spin_unlock_bh(&moxa_lock);
-                               if (copy_to_user(argm, &tmp, sizeof(tmp)))
-                                       return -EFAULT;
-                       }
-               }
-               break;
-       } case MOXA_GET_OQUEUE:
-               status = MoxaPortTxQueue(ch);
-               ret = put_user(status, (unsigned long __user *)argp);
-               break;
-       case MOXA_GET_IQUEUE:
-               status = MoxaPortRxQueue(ch);
-               ret = put_user(status, (unsigned long __user *)argp);
-               break;
-       case MOXA_GETMSTATUS: {
-               struct mxser_mstatus __user *argm = argp;
-               struct mxser_mstatus tmp;
-               struct moxa_port *p;
-               unsigned int i, j;
-
-               for (i = 0; i < MAX_BOARDS; i++) {
-                       p = moxa_boards[i].ports;
-                       for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
-                               struct tty_struct *ttyp;
-                               memset(&tmp, 0, sizeof(tmp));
-                               spin_lock_bh(&moxa_lock);
-                               if (!moxa_boards[i].ready) {
-                                       spin_unlock_bh(&moxa_lock);
-                                       goto copy;
-                                }
-
-                               status = MoxaPortLineStatus(p);
-                               spin_unlock_bh(&moxa_lock);
-
-                               if (status & 1)
-                                       tmp.cts = 1;
-                               if (status & 2)
-                                       tmp.dsr = 1;
-                               if (status & 4)
-                                       tmp.dcd = 1;
-
-                               ttyp = tty_port_tty_get(&p->port);
-                               if (!ttyp || !ttyp->termios)
-                                       tmp.cflag = p->cflag;
-                               else
-                                       tmp.cflag = ttyp->termios->c_cflag;
-                               tty_kref_put(tty);
-copy:
-                               if (copy_to_user(argm, &tmp, sizeof(tmp)))
-                                       return -EFAULT;
-                       }
-               }
-               break;
-       }
-       case TIOCGSERIAL:
-               mutex_lock(&ch->port.mutex);
-               ret = moxa_get_serial_info(ch, argp);
-               mutex_unlock(&ch->port.mutex);
-               break;
-       case TIOCSSERIAL:
-               mutex_lock(&ch->port.mutex);
-               ret = moxa_set_serial_info(ch, argp);
-               mutex_unlock(&ch->port.mutex);
-               break;
-       default:
-               ret = -ENOIOCTLCMD;
-       }
-       return ret;
-}
-
-static int moxa_break_ctl(struct tty_struct *tty, int state)
-{
-       struct moxa_port *port = tty->driver_data;
-
-       moxafunc(port->tableAddr, state ? FC_SendBreak : FC_StopBreak,
-                       Magic_code);
-       return 0;
-}
-
-static const struct tty_operations moxa_ops = {
-       .open = moxa_open,
-       .close = moxa_close,
-       .write = moxa_write,
-       .write_room = moxa_write_room,
-       .flush_buffer = moxa_flush_buffer,
-       .chars_in_buffer = moxa_chars_in_buffer,
-       .ioctl = moxa_ioctl,
-       .set_termios = moxa_set_termios,
-       .stop = moxa_stop,
-       .start = moxa_start,
-       .hangup = moxa_hangup,
-       .break_ctl = moxa_break_ctl,
-       .tiocmget = moxa_tiocmget,
-       .tiocmset = moxa_tiocmset,
-};
-
-static const struct tty_port_operations moxa_port_ops = {
-       .carrier_raised = moxa_carrier_raised,
-       .dtr_rts = moxa_dtr_rts,
-       .shutdown = moxa_shutdown,
-};
-
-static struct tty_driver *moxaDriver;
-static DEFINE_TIMER(moxaTimer, moxa_poll, 0, 0);
-
-/*
- * HW init
- */
-
-static int moxa_check_fw_model(struct moxa_board_conf *brd, u8 model)
-{
-       switch (brd->boardType) {
-       case MOXA_BOARD_C218_ISA:
-       case MOXA_BOARD_C218_PCI:
-               if (model != 1)
-                       goto err;
-               break;
-       case MOXA_BOARD_CP204J:
-               if (model != 3)
-                       goto err;
-               break;
-       default:
-               if (model != 2)
-                       goto err;
-               break;
-       }
-       return 0;
-err:
-       return -EINVAL;
-}
-
-static int moxa_check_fw(const void *ptr)
-{
-       const __le16 *lptr = ptr;
-
-       if (*lptr != cpu_to_le16(0x7980))
-               return -EINVAL;
-
-       return 0;
-}
-
-static int moxa_load_bios(struct moxa_board_conf *brd, const u8 *buf,
-               size_t len)
-{
-       void __iomem *baseAddr = brd->basemem;
-       u16 tmp;
-
-       writeb(HW_reset, baseAddr + Control_reg);       /* reset */
-       msleep(10);
-       memset_io(baseAddr, 0, 4096);
-       memcpy_toio(baseAddr, buf, len);        /* download BIOS */
-       writeb(0, baseAddr + Control_reg);      /* restart */
-
-       msleep(2000);
-
-       switch (brd->boardType) {
-       case MOXA_BOARD_C218_ISA:
-       case MOXA_BOARD_C218_PCI:
-               tmp = readw(baseAddr + C218_key);
-               if (tmp != C218_KeyCode)
-                       goto err;
-               break;
-       case MOXA_BOARD_CP204J:
-               tmp = readw(baseAddr + C218_key);
-               if (tmp != CP204J_KeyCode)
-                       goto err;
-               break;
-       default:
-               tmp = readw(baseAddr + C320_key);
-               if (tmp != C320_KeyCode)
-                       goto err;
-               tmp = readw(baseAddr + C320_status);
-               if (tmp != STS_init) {
-                       printk(KERN_ERR "MOXA: bios upload failed -- CPU/Basic "
-                                       "module not found\n");
-                       return -EIO;
-               }
-               break;
-       }
-
-       return 0;
-err:
-       printk(KERN_ERR "MOXA: bios upload failed -- board not found\n");
-       return -EIO;
-}
-
-static int moxa_load_320b(struct moxa_board_conf *brd, const u8 *ptr,
-               size_t len)
-{
-       void __iomem *baseAddr = brd->basemem;
-
-       if (len < 7168) {
-               printk(KERN_ERR "MOXA: invalid 320 bios -- too short\n");
-               return -EINVAL;
-       }
-
-       writew(len - 7168 - 2, baseAddr + C320bapi_len);
-       writeb(1, baseAddr + Control_reg);      /* Select Page 1 */
-       memcpy_toio(baseAddr + DynPage_addr, ptr, 7168);
-       writeb(2, baseAddr + Control_reg);      /* Select Page 2 */
-       memcpy_toio(baseAddr + DynPage_addr, ptr + 7168, len - 7168);
-
-       return 0;
-}
-
-static int moxa_real_load_code(struct moxa_board_conf *brd, const void *ptr,
-               size_t len)
-{
-       void __iomem *baseAddr = brd->basemem;
-       const __le16 *uptr = ptr;
-       size_t wlen, len2, j;
-       unsigned long key, loadbuf, loadlen, checksum, checksum_ok;
-       unsigned int i, retry;
-       u16 usum, keycode;
-
-       keycode = (brd->boardType == MOXA_BOARD_CP204J) ? CP204J_KeyCode :
-                               C218_KeyCode;
-
-       switch (brd->boardType) {
-       case MOXA_BOARD_CP204J:
-       case MOXA_BOARD_C218_ISA:
-       case MOXA_BOARD_C218_PCI:
-               key = C218_key;
-               loadbuf = C218_LoadBuf;
-               loadlen = C218DLoad_len;
-               checksum = C218check_sum;
-               checksum_ok = C218chksum_ok;
-               break;
-       default:
-               key = C320_key;
-               keycode = C320_KeyCode;
-               loadbuf = C320_LoadBuf;
-               loadlen = C320DLoad_len;
-               checksum = C320check_sum;
-               checksum_ok = C320chksum_ok;
-               break;
-       }
-
-       usum = 0;
-       wlen = len >> 1;
-       for (i = 0; i < wlen; i++)
-               usum += le16_to_cpu(uptr[i]);
-       retry = 0;
-       do {
-               wlen = len >> 1;
-               j = 0;
-               while (wlen) {
-                       len2 = (wlen > 2048) ? 2048 : wlen;
-                       wlen -= len2;
-                       memcpy_toio(baseAddr + loadbuf, ptr + j, len2 << 1);
-                       j += len2 << 1;
-
-                       writew(len2, baseAddr + loadlen);
-                       writew(0, baseAddr + key);
-                       for (i = 0; i < 100; i++) {
-                               if (readw(baseAddr + key) == keycode)
-                                       break;
-                               msleep(10);
-                       }
-                       if (readw(baseAddr + key) != keycode)
-                               return -EIO;
-               }
-               writew(0, baseAddr + loadlen);
-               writew(usum, baseAddr + checksum);
-               writew(0, baseAddr + key);
-               for (i = 0; i < 100; i++) {
-                       if (readw(baseAddr + key) == keycode)
-                               break;
-                       msleep(10);
-               }
-               retry++;
-       } while ((readb(baseAddr + checksum_ok) != 1) && (retry < 3));
-       if (readb(baseAddr + checksum_ok) != 1)
-               return -EIO;
-
-       writew(0, baseAddr + key);
-       for (i = 0; i < 600; i++) {
-               if (readw(baseAddr + Magic_no) == Magic_code)
-                       break;
-               msleep(10);
-       }
-       if (readw(baseAddr + Magic_no) != Magic_code)
-               return -EIO;
-
-       if (MOXA_IS_320(brd)) {
-               if (brd->busType == MOXA_BUS_TYPE_PCI) {        /* ASIC board */
-                       writew(0x3800, baseAddr + TMS320_PORT1);
-                       writew(0x3900, baseAddr + TMS320_PORT2);
-                       writew(28499, baseAddr + TMS320_CLOCK);
-               } else {
-                       writew(0x3200, baseAddr + TMS320_PORT1);
-                       writew(0x3400, baseAddr + TMS320_PORT2);
-                       writew(19999, baseAddr + TMS320_CLOCK);
-               }
-       }
-       writew(1, baseAddr + Disable_IRQ);
-       writew(0, baseAddr + Magic_no);
-       for (i = 0; i < 500; i++) {
-               if (readw(baseAddr + Magic_no) == Magic_code)
-                       break;
-               msleep(10);
-       }
-       if (readw(baseAddr + Magic_no) != Magic_code)
-               return -EIO;
-
-       if (MOXA_IS_320(brd)) {
-               j = readw(baseAddr + Module_cnt);
-               if (j <= 0)
-                       return -EIO;
-               brd->numPorts = j * 8;
-               writew(j, baseAddr + Module_no);
-               writew(0, baseAddr + Magic_no);
-               for (i = 0; i < 600; i++) {
-                       if (readw(baseAddr + Magic_no) == Magic_code)
-                               break;
-                       msleep(10);
-               }
-               if (readw(baseAddr + Magic_no) != Magic_code)
-                       return -EIO;
-       }
-       brd->intNdx = baseAddr + IRQindex;
-       brd->intPend = baseAddr + IRQpending;
-       brd->intTable = baseAddr + IRQtable;
-
-       return 0;
-}
-
-static int moxa_load_code(struct moxa_board_conf *brd, const void *ptr,
-               size_t len)
-{
-       void __iomem *ofsAddr, *baseAddr = brd->basemem;
-       struct moxa_port *port;
-       int retval, i;
-
-       if (len % 2) {
-               printk(KERN_ERR "MOXA: bios length is not even\n");
-               return -EINVAL;
-       }
-
-       retval = moxa_real_load_code(brd, ptr, len); /* may change numPorts */
-       if (retval)
-               return retval;
-
-       switch (brd->boardType) {
-       case MOXA_BOARD_C218_ISA:
-       case MOXA_BOARD_C218_PCI:
-       case MOXA_BOARD_CP204J:
-               port = brd->ports;
-               for (i = 0; i < brd->numPorts; i++, port++) {
-                       port->board = brd;
-                       port->DCDState = 0;
-                       port->tableAddr = baseAddr + Extern_table +
-                                       Extern_size * i;
-                       ofsAddr = port->tableAddr;
-                       writew(C218rx_mask, ofsAddr + RX_mask);
-                       writew(C218tx_mask, ofsAddr + TX_mask);
-                       writew(C218rx_spage + i * C218buf_pageno, ofsAddr + Page_rxb);
-                       writew(readw(ofsAddr + Page_rxb) + C218rx_pageno, ofsAddr + EndPage_rxb);
-
-                       writew(C218tx_spage + i * C218buf_pageno, ofsAddr + Page_txb);
-                       writew(readw(ofsAddr + Page_txb) + C218tx_pageno, ofsAddr + EndPage_txb);
-
-               }
-               break;
-       default:
-               port = brd->ports;
-               for (i = 0; i < brd->numPorts; i++, port++) {
-                       port->board = brd;
-                       port->DCDState = 0;
-                       port->tableAddr = baseAddr + Extern_table +
-                                       Extern_size * i;
-                       ofsAddr = port->tableAddr;
-                       switch (brd->numPorts) {
-                       case 8:
-                               writew(C320p8rx_mask, ofsAddr + RX_mask);
-                               writew(C320p8tx_mask, ofsAddr + TX_mask);
-                               writew(C320p8rx_spage + i * C320p8buf_pgno, ofsAddr + Page_rxb);
-                               writew(readw(ofsAddr + Page_rxb) + C320p8rx_pgno, ofsAddr + EndPage_rxb);
-                               writew(C320p8tx_spage + i * C320p8buf_pgno, ofsAddr + Page_txb);
-                               writew(readw(ofsAddr + Page_txb) + C320p8tx_pgno, ofsAddr + EndPage_txb);
-
-                               break;
-                       case 16:
-                               writew(C320p16rx_mask, ofsAddr + RX_mask);
-                               writew(C320p16tx_mask, ofsAddr + TX_mask);
-                               writew(C320p16rx_spage + i * C320p16buf_pgno, ofsAddr + Page_rxb);
-                               writew(readw(ofsAddr + Page_rxb) + C320p16rx_pgno, ofsAddr + EndPage_rxb);
-                               writew(C320p16tx_spage + i * C320p16buf_pgno, ofsAddr + Page_txb);
-                               writew(readw(ofsAddr + Page_txb) + C320p16tx_pgno, ofsAddr + EndPage_txb);
-                               break;
-
-                       case 24:
-                               writew(C320p24rx_mask, ofsAddr + RX_mask);
-                               writew(C320p24tx_mask, ofsAddr + TX_mask);
-                               writew(C320p24rx_spage + i * C320p24buf_pgno, ofsAddr + Page_rxb);
-                               writew(readw(ofsAddr + Page_rxb) + C320p24rx_pgno, ofsAddr + EndPage_rxb);
-                               writew(C320p24tx_spage + i * C320p24buf_pgno, ofsAddr + Page_txb);
-                               writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb);
-                               break;
-                       case 32:
-                               writew(C320p32rx_mask, ofsAddr + RX_mask);
-                               writew(C320p32tx_mask, ofsAddr + TX_mask);
-                               writew(C320p32tx_ofs, ofsAddr + Ofs_txb);
-                               writew(C320p32rx_spage + i * C320p32buf_pgno, ofsAddr + Page_rxb);
-                               writew(readb(ofsAddr + Page_rxb), ofsAddr + EndPage_rxb);
-                               writew(C320p32tx_spage + i * C320p32buf_pgno, ofsAddr + Page_txb);
-                               writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb);
-                               break;
-                       }
-               }
-               break;
-       }
-       return 0;
-}
-
-static int moxa_load_fw(struct moxa_board_conf *brd, const struct firmware *fw)
-{
-       const void *ptr = fw->data;
-       char rsn[64];
-       u16 lens[5];
-       size_t len;
-       unsigned int a, lenp, lencnt;
-       int ret = -EINVAL;
-       struct {
-               __le32 magic;   /* 0x34303430 */
-               u8 reserved1[2];
-               u8 type;        /* UNIX = 3 */
-               u8 model;       /* C218T=1, C320T=2, CP204=3 */
-               u8 reserved2[8];
-               __le16 len[5];
-       } const *hdr = ptr;
-
-       BUILD_BUG_ON(ARRAY_SIZE(hdr->len) != ARRAY_SIZE(lens));
-
-       if (fw->size < MOXA_FW_HDRLEN) {
-               strcpy(rsn, "too short (even header won't fit)");
-               goto err;
-       }
-       if (hdr->magic != cpu_to_le32(0x30343034)) {
-               sprintf(rsn, "bad magic: %.8x", le32_to_cpu(hdr->magic));
-               goto err;
-       }
-       if (hdr->type != 3) {
-               sprintf(rsn, "not for linux, type is %u", hdr->type);
-               goto err;
-       }
-       if (moxa_check_fw_model(brd, hdr->model)) {
-               sprintf(rsn, "not for this card, model is %u", hdr->model);
-               goto err;
-       }
-
-       len = MOXA_FW_HDRLEN;
-       lencnt = hdr->model == 2 ? 5 : 3;
-       for (a = 0; a < ARRAY_SIZE(lens); a++) {
-               lens[a] = le16_to_cpu(hdr->len[a]);
-               if (lens[a] && len + lens[a] <= fw->size &&
-                               moxa_check_fw(&fw->data[len]))
-                       printk(KERN_WARNING "MOXA firmware: unexpected input "
-                               "at offset %u, but going on\n", (u32)len);
-               if (!lens[a] && a < lencnt) {
-                       sprintf(rsn, "too few entries in fw file");
-                       goto err;
-               }
-               len += lens[a];
-       }
-
-       if (len != fw->size) {
-               sprintf(rsn, "bad length: %u (should be %u)", (u32)fw->size,
-                               (u32)len);
-               goto err;
-       }
-
-       ptr += MOXA_FW_HDRLEN;
-       lenp = 0; /* bios */
-
-       strcpy(rsn, "read above");
-
-       ret = moxa_load_bios(brd, ptr, lens[lenp]);
-       if (ret)
-               goto err;
-
-       /* we skip the tty section (lens[1]), since we don't need it */
-       ptr += lens[lenp] + lens[lenp + 1];
-       lenp += 2; /* comm */
-
-       if (hdr->model == 2) {
-               ret = moxa_load_320b(brd, ptr, lens[lenp]);
-               if (ret)
-                       goto err;
-               /* skip another tty */
-               ptr += lens[lenp] + lens[lenp + 1];
-               lenp += 2;
-       }
-
-       ret = moxa_load_code(brd, ptr, lens[lenp]);
-       if (ret)
-               goto err;
-
-       return 0;
-err:
-       printk(KERN_ERR "firmware failed to load, reason: %s\n", rsn);
-       return ret;
-}
-
-static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev)
-{
-       const struct firmware *fw;
-       const char *file;
-       struct moxa_port *p;
-       unsigned int i;
-       int ret;
-
-       brd->ports = kcalloc(MAX_PORTS_PER_BOARD, sizeof(*brd->ports),
-                       GFP_KERNEL);
-       if (brd->ports == NULL) {
-               printk(KERN_ERR "cannot allocate memory for ports\n");
-               ret = -ENOMEM;
-               goto err;
-       }
-
-       for (i = 0, p = brd->ports; i < MAX_PORTS_PER_BOARD; i++, p++) {
-               tty_port_init(&p->port);
-               p->port.ops = &moxa_port_ops;
-               p->type = PORT_16550A;
-               p->cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
-       }
-
-       switch (brd->boardType) {
-       case MOXA_BOARD_C218_ISA:
-       case MOXA_BOARD_C218_PCI:
-               file = "c218tunx.cod";
-               break;
-       case MOXA_BOARD_CP204J:
-               file = "cp204unx.cod";
-               break;
-       default:
-               file = "c320tunx.cod";
-               break;
-       }
-
-       ret = request_firmware(&fw, file, dev);
-       if (ret) {
-               printk(KERN_ERR "MOXA: request_firmware failed. Make sure "
-                               "you've placed '%s' file into your firmware "
-                               "loader directory (e.g. /lib/firmware)\n",
-                               file);
-               goto err_free;
-       }
-
-       ret = moxa_load_fw(brd, fw);
-
-       release_firmware(fw);
-
-       if (ret)
-               goto err_free;
-
-       spin_lock_bh(&moxa_lock);
-       brd->ready = 1;
-       if (!timer_pending(&moxaTimer))
-               mod_timer(&moxaTimer, jiffies + HZ / 50);
-       spin_unlock_bh(&moxa_lock);
-
-       return 0;
-err_free:
-       kfree(brd->ports);
-err:
-       return ret;
-}
-
-static void moxa_board_deinit(struct moxa_board_conf *brd)
-{
-       unsigned int a, opened;
-
-       mutex_lock(&moxa_openlock);
-       spin_lock_bh(&moxa_lock);
-       brd->ready = 0;
-       spin_unlock_bh(&moxa_lock);
-
-       /* pci hot-un-plug support */
-       for (a = 0; a < brd->numPorts; a++)
-               if (brd->ports[a].port.flags & ASYNC_INITIALIZED) {
-                       struct tty_struct *tty = tty_port_tty_get(
-                                               &brd->ports[a].port);
-                       if (tty) {
-                               tty_hangup(tty);
-                               tty_kref_put(tty);
-                       }
-               }
-       while (1) {
-               opened = 0;
-               for (a = 0; a < brd->numPorts; a++)
-                       if (brd->ports[a].port.flags & ASYNC_INITIALIZED)
-                               opened++;
-               mutex_unlock(&moxa_openlock);
-               if (!opened)
-                       break;
-               msleep(50);
-               mutex_lock(&moxa_openlock);
-       }
-
-       iounmap(brd->basemem);
-       brd->basemem = NULL;
-       kfree(brd->ports);
-}
-
-#ifdef CONFIG_PCI
-static int __devinit moxa_pci_probe(struct pci_dev *pdev,
-               const struct pci_device_id *ent)
-{
-       struct moxa_board_conf *board;
-       unsigned int i;
-       int board_type = ent->driver_data;
-       int retval;
-
-       retval = pci_enable_device(pdev);
-       if (retval) {
-               dev_err(&pdev->dev, "can't enable pci device\n");
-               goto err;
-       }
-
-       for (i = 0; i < MAX_BOARDS; i++)
-               if (moxa_boards[i].basemem == NULL)
-                       break;
-
-       retval = -ENODEV;
-       if (i >= MAX_BOARDS) {
-               dev_warn(&pdev->dev, "more than %u MOXA Intellio family boards "
-                               "found. Board is ignored.\n", MAX_BOARDS);
-               goto err;
-       }
-
-       board = &moxa_boards[i];
-
-       retval = pci_request_region(pdev, 2, "moxa-base");
-       if (retval) {
-               dev_err(&pdev->dev, "can't request pci region 2\n");
-               goto err;
-       }
-
-       board->basemem = ioremap_nocache(pci_resource_start(pdev, 2), 0x4000);
-       if (board->basemem == NULL) {
-               dev_err(&pdev->dev, "can't remap io space 2\n");
-               goto err_reg;
-       }
-
-       board->boardType = board_type;
-       switch (board_type) {
-       case MOXA_BOARD_C218_ISA:
-       case MOXA_BOARD_C218_PCI:
-               board->numPorts = 8;
-               break;
-
-       case MOXA_BOARD_CP204J:
-               board->numPorts = 4;
-               break;
-       default:
-               board->numPorts = 0;
-               break;
-       }
-       board->busType = MOXA_BUS_TYPE_PCI;
-
-       retval = moxa_init_board(board, &pdev->dev);
-       if (retval)
-               goto err_base;
-
-       pci_set_drvdata(pdev, board);
-
-       dev_info(&pdev->dev, "board '%s' ready (%u ports, firmware loaded)\n",
-                       moxa_brdname[board_type - 1], board->numPorts);
-
-       return 0;
-err_base:
-       iounmap(board->basemem);
-       board->basemem = NULL;
-err_reg:
-       pci_release_region(pdev, 2);
-err:
-       return retval;
-}
-
-static void __devexit moxa_pci_remove(struct pci_dev *pdev)
-{
-       struct moxa_board_conf *brd = pci_get_drvdata(pdev);
-
-       moxa_board_deinit(brd);
-
-       pci_release_region(pdev, 2);
-}
-
-static struct pci_driver moxa_pci_driver = {
-       .name = "moxa",
-       .id_table = moxa_pcibrds,
-       .probe = moxa_pci_probe,
-       .remove = __devexit_p(moxa_pci_remove)
-};
-#endif /* CONFIG_PCI */
-
-static int __init moxa_init(void)
-{
-       unsigned int isabrds = 0;
-       int retval = 0;
-       struct moxa_board_conf *brd = moxa_boards;
-       unsigned int i;
-
-       printk(KERN_INFO "MOXA Intellio family driver version %s\n",
-                       MOXA_VERSION);
-       moxaDriver = alloc_tty_driver(MAX_PORTS + 1);
-       if (!moxaDriver)
-               return -ENOMEM;
-
-       moxaDriver->owner = THIS_MODULE;
-       moxaDriver->name = "ttyMX";
-       moxaDriver->major = ttymajor;
-       moxaDriver->minor_start = 0;
-       moxaDriver->type = TTY_DRIVER_TYPE_SERIAL;
-       moxaDriver->subtype = SERIAL_TYPE_NORMAL;
-       moxaDriver->init_termios = tty_std_termios;
-       moxaDriver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
-       moxaDriver->init_termios.c_ispeed = 9600;
-       moxaDriver->init_termios.c_ospeed = 9600;
-       moxaDriver->flags = TTY_DRIVER_REAL_RAW;
-       tty_set_operations(moxaDriver, &moxa_ops);
-
-       if (tty_register_driver(moxaDriver)) {
-               printk(KERN_ERR "can't register MOXA Smartio tty driver!\n");
-               put_tty_driver(moxaDriver);
-               return -1;
-       }
-
-       /* Find the boards defined from module args. */
-
-       for (i = 0; i < MAX_BOARDS; i++) {
-               if (!baseaddr[i])
-                       break;
-               if (type[i] == MOXA_BOARD_C218_ISA ||
-                               type[i] == MOXA_BOARD_C320_ISA) {
-                       pr_debug("Moxa board %2d: %s board(baseAddr=%lx)\n",
-                                       isabrds + 1, moxa_brdname[type[i] - 1],
-                                       baseaddr[i]);
-                       brd->boardType = type[i];
-                       brd->numPorts = type[i] == MOXA_BOARD_C218_ISA ? 8 :
-                                       numports[i];
-                       brd->busType = MOXA_BUS_TYPE_ISA;
-                       brd->basemem = ioremap_nocache(baseaddr[i], 0x4000);
-                       if (!brd->basemem) {
-                               printk(KERN_ERR "MOXA: can't remap %lx\n",
-                                               baseaddr[i]);
-                               continue;
-                       }
-                       if (moxa_init_board(brd, NULL)) {
-                               iounmap(brd->basemem);
-                               brd->basemem = NULL;
-                               continue;
-                       }
-
-                       printk(KERN_INFO "MOXA isa board found at 0x%.8lu and "
-                                       "ready (%u ports, firmware loaded)\n",
-                                       baseaddr[i], brd->numPorts);
-
-                       brd++;
-                       isabrds++;
-               }
-       }
-
-#ifdef CONFIG_PCI
-       retval = pci_register_driver(&moxa_pci_driver);
-       if (retval) {
-               printk(KERN_ERR "Can't register MOXA pci driver!\n");
-               if (isabrds)
-                       retval = 0;
-       }
-#endif
-
-       return retval;
-}
-
-static void __exit moxa_exit(void)
-{
-       unsigned int i;
-
-#ifdef CONFIG_PCI
-       pci_unregister_driver(&moxa_pci_driver);
-#endif
-
-       for (i = 0; i < MAX_BOARDS; i++) /* ISA boards */
-               if (moxa_boards[i].ready)
-                       moxa_board_deinit(&moxa_boards[i]);
-
-       del_timer_sync(&moxaTimer);
-
-       if (tty_unregister_driver(moxaDriver))
-               printk(KERN_ERR "Couldn't unregister MOXA Intellio family "
-                               "serial driver\n");
-       put_tty_driver(moxaDriver);
-}
-
-module_init(moxa_init);
-module_exit(moxa_exit);
-
-static void moxa_shutdown(struct tty_port *port)
-{
-       struct moxa_port *ch = container_of(port, struct moxa_port, port);
-        MoxaPortDisable(ch);
-       MoxaPortFlushData(ch, 2);
-       clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
-}
-
-static int moxa_carrier_raised(struct tty_port *port)
-{
-       struct moxa_port *ch = container_of(port, struct moxa_port, port);
-       int dcd;
-
-       spin_lock_irq(&port->lock);
-       dcd = ch->DCDState;
-       spin_unlock_irq(&port->lock);
-       return dcd;
-}
-
-static void moxa_dtr_rts(struct tty_port *port, int onoff)
-{
-       struct moxa_port *ch = container_of(port, struct moxa_port, port);
-       MoxaPortLineCtrl(ch, onoff, onoff);
-}
-
-
-static int moxa_open(struct tty_struct *tty, struct file *filp)
-{
-       struct moxa_board_conf *brd;
-       struct moxa_port *ch;
-       int port;
-       int retval;
-
-       port = tty->index;
-       if (port == MAX_PORTS) {
-               return capable(CAP_SYS_ADMIN) ? 0 : -EPERM;
-       }
-       if (mutex_lock_interruptible(&moxa_openlock))
-               return -ERESTARTSYS;
-       brd = &moxa_boards[port / MAX_PORTS_PER_BOARD];
-       if (!brd->ready) {
-               mutex_unlock(&moxa_openlock);
-               return -ENODEV;
-       }
-
-       if (port % MAX_PORTS_PER_BOARD >= brd->numPorts) {
-               mutex_unlock(&moxa_openlock);
-               return -ENODEV;
-       }
-
-       ch = &brd->ports[port % MAX_PORTS_PER_BOARD];
-       ch->port.count++;
-       tty->driver_data = ch;
-       tty_port_tty_set(&ch->port, tty);
-       mutex_lock(&ch->port.mutex);
-       if (!(ch->port.flags & ASYNC_INITIALIZED)) {
-               ch->statusflags = 0;
-               moxa_set_tty_param(tty, tty->termios);
-               MoxaPortLineCtrl(ch, 1, 1);
-               MoxaPortEnable(ch);
-               MoxaSetFifo(ch, ch->type == PORT_16550A);
-               ch->port.flags |= ASYNC_INITIALIZED;
-       }
-       mutex_unlock(&ch->port.mutex);
-       mutex_unlock(&moxa_openlock);
-
-       retval = tty_port_block_til_ready(&ch->port, tty, filp);
-       if (retval == 0)
-               set_bit(ASYNCB_NORMAL_ACTIVE, &ch->port.flags);
-       return retval;
-}
-
-static void moxa_close(struct tty_struct *tty, struct file *filp)
-{
-       struct moxa_port *ch = tty->driver_data;
-       ch->cflag = tty->termios->c_cflag;
-       tty_port_close(&ch->port, tty, filp);
-}
-
-static int moxa_write(struct tty_struct *tty,
-                     const unsigned char *buf, int count)
-{
-       struct moxa_port *ch = tty->driver_data;
-       int len;
-
-       if (ch == NULL)
-               return 0;
-
-       spin_lock_bh(&moxa_lock);
-       len = MoxaPortWriteData(tty, buf, count);
-       spin_unlock_bh(&moxa_lock);
-
-       set_bit(LOWWAIT, &ch->statusflags);
-       return len;
-}
-
-static int moxa_write_room(struct tty_struct *tty)
-{
-       struct moxa_port *ch;
-
-       if (tty->stopped)
-               return 0;
-       ch = tty->driver_data;
-       if (ch == NULL)
-               return 0;
-       return MoxaPortTxFree(ch);
-}
-
-static void moxa_flush_buffer(struct tty_struct *tty)
-{
-       struct moxa_port *ch = tty->driver_data;
-
-       if (ch == NULL)
-               return;
-       MoxaPortFlushData(ch, 1);
-       tty_wakeup(tty);
-}
-
-static int moxa_chars_in_buffer(struct tty_struct *tty)
-{
-       struct moxa_port *ch = tty->driver_data;
-       int chars;
-
-       chars = MoxaPortTxQueue(ch);
-       if (chars)
-               /*
-                * Make it possible to wakeup anything waiting for output
-                * in tty_ioctl.c, etc.
-                */
-               set_bit(EMPTYWAIT, &ch->statusflags);
-       return chars;
-}
-
-static int moxa_tiocmget(struct tty_struct *tty)
-{
-       struct moxa_port *ch = tty->driver_data;
-       int flag = 0, dtr, rts;
-
-       MoxaPortGetLineOut(ch, &dtr, &rts);
-       if (dtr)
-               flag |= TIOCM_DTR;
-       if (rts)
-               flag |= TIOCM_RTS;
-       dtr = MoxaPortLineStatus(ch);
-       if (dtr & 1)
-               flag |= TIOCM_CTS;
-       if (dtr & 2)
-               flag |= TIOCM_DSR;
-       if (dtr & 4)
-               flag |= TIOCM_CD;
-       return flag;
-}
-
-static int moxa_tiocmset(struct tty_struct *tty,
-                        unsigned int set, unsigned int clear)
-{
-       struct moxa_port *ch;
-       int port;
-       int dtr, rts;
-
-       port = tty->index;
-       mutex_lock(&moxa_openlock);
-       ch = tty->driver_data;
-       if (!ch) {
-               mutex_unlock(&moxa_openlock);
-               return -EINVAL;
-       }
-
-       MoxaPortGetLineOut(ch, &dtr, &rts);
-       if (set & TIOCM_RTS)
-               rts = 1;
-       if (set & TIOCM_DTR)
-               dtr = 1;
-       if (clear & TIOCM_RTS)
-               rts = 0;
-       if (clear & TIOCM_DTR)
-               dtr = 0;
-       MoxaPortLineCtrl(ch, dtr, rts);
-       mutex_unlock(&moxa_openlock);
-       return 0;
-}
-
-static void moxa_set_termios(struct tty_struct *tty,
-               struct ktermios *old_termios)
-{
-       struct moxa_port *ch = tty->driver_data;
-
-       if (ch == NULL)
-               return;
-       moxa_set_tty_param(tty, old_termios);
-       if (!(old_termios->c_cflag & CLOCAL) && C_CLOCAL(tty))
-               wake_up_interruptible(&ch->port.open_wait);
-}
-
-static void moxa_stop(struct tty_struct *tty)
-{
-       struct moxa_port *ch = tty->driver_data;
-
-       if (ch == NULL)
-               return;
-       MoxaPortTxDisable(ch);
-       set_bit(TXSTOPPED, &ch->statusflags);
-}
-
-
-static void moxa_start(struct tty_struct *tty)
-{
-       struct moxa_port *ch = tty->driver_data;
-
-       if (ch == NULL)
-               return;
-
-       if (!(ch->statusflags & TXSTOPPED))
-               return;
-
-       MoxaPortTxEnable(ch);
-       clear_bit(TXSTOPPED, &ch->statusflags);
-}
-
-static void moxa_hangup(struct tty_struct *tty)
-{
-       struct moxa_port *ch = tty->driver_data;
-       tty_port_hangup(&ch->port);
-}
-
-static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd)
-{
-       struct tty_struct *tty;
-       unsigned long flags;
-       dcd = !!dcd;
-
-       spin_lock_irqsave(&p->port.lock, flags);
-       if (dcd != p->DCDState) {
-               p->DCDState = dcd;
-               spin_unlock_irqrestore(&p->port.lock, flags);
-               tty = tty_port_tty_get(&p->port);
-               if (tty && C_CLOCAL(tty) && !dcd)
-                       tty_hangup(tty);
-               tty_kref_put(tty);
-       }
-       else
-               spin_unlock_irqrestore(&p->port.lock, flags);
-}
-
-static int moxa_poll_port(struct moxa_port *p, unsigned int handle,
-               u16 __iomem *ip)
-{
-       struct tty_struct *tty = tty_port_tty_get(&p->port);
-       void __iomem *ofsAddr;
-       unsigned int inited = p->port.flags & ASYNC_INITIALIZED;
-       u16 intr;
-
-       if (tty) {
-               if (test_bit(EMPTYWAIT, &p->statusflags) &&
-                               MoxaPortTxQueue(p) == 0) {
-                       clear_bit(EMPTYWAIT, &p->statusflags);
-                       tty_wakeup(tty);
-               }
-               if (test_bit(LOWWAIT, &p->statusflags) && !tty->stopped &&
-                               MoxaPortTxQueue(p) <= WAKEUP_CHARS) {
-                       clear_bit(LOWWAIT, &p->statusflags);
-                       tty_wakeup(tty);
-               }
-
-               if (inited && !test_bit(TTY_THROTTLED, &tty->flags) &&
-                               MoxaPortRxQueue(p) > 0) { /* RX */
-                       MoxaPortReadData(p);
-                       tty_schedule_flip(tty);
-               }
-       } else {
-               clear_bit(EMPTYWAIT, &p->statusflags);
-               MoxaPortFlushData(p, 0); /* flush RX */
-       }
-
-       if (!handle) /* nothing else to do */
-               goto put;
-
-       intr = readw(ip); /* port irq status */
-       if (intr == 0)
-               goto put;
-
-       writew(0, ip); /* ACK port */
-       ofsAddr = p->tableAddr;
-       if (intr & IntrTx) /* disable tx intr */
-               writew(readw(ofsAddr + HostStat) & ~WakeupTx,
-                               ofsAddr + HostStat);
-
-       if (!inited)
-               goto put;
-
-       if (tty && (intr & IntrBreak) && !I_IGNBRK(tty)) { /* BREAK */
-               tty_insert_flip_char(tty, 0, TTY_BREAK);
-               tty_schedule_flip(tty);
-       }
-
-       if (intr & IntrLine)
-               moxa_new_dcdstate(p, readb(ofsAddr + FlagStat) & DCD_state);
-put:
-       tty_kref_put(tty);
-
-       return 0;
-}
-
-static void moxa_poll(unsigned long ignored)
-{
-       struct moxa_board_conf *brd;
-       u16 __iomem *ip;
-       unsigned int card, port, served = 0;
-
-       spin_lock(&moxa_lock);
-       for (card = 0; card < MAX_BOARDS; card++) {
-               brd = &moxa_boards[card];
-               if (!brd->ready)
-                       continue;
-
-               served++;
-
-               ip = NULL;
-               if (readb(brd->intPend) == 0xff)
-                       ip = brd->intTable + readb(brd->intNdx);
-
-               for (port = 0; port < brd->numPorts; port++)
-                       moxa_poll_port(&brd->ports[port], !!ip, ip + port);
-
-               if (ip)
-                       writeb(0, brd->intPend); /* ACK */
-
-               if (moxaLowWaterChk) {
-                       struct moxa_port *p = brd->ports;
-                       for (port = 0; port < brd->numPorts; port++, p++)
-                               if (p->lowChkFlag) {
-                                       p->lowChkFlag = 0;
-                                       moxa_low_water_check(p->tableAddr);
-                               }
-               }
-       }
-       moxaLowWaterChk = 0;
-
-       if (served)
-               mod_timer(&moxaTimer, jiffies + HZ / 50);
-       spin_unlock(&moxa_lock);
-}
-
-/******************************************************************************/
-
-static void moxa_set_tty_param(struct tty_struct *tty, struct ktermios *old_termios)
-{
-       register struct ktermios *ts = tty->termios;
-       struct moxa_port *ch = tty->driver_data;
-       int rts, cts, txflow, rxflow, xany, baud;
-
-       rts = cts = txflow = rxflow = xany = 0;
-       if (ts->c_cflag & CRTSCTS)
-               rts = cts = 1;
-       if (ts->c_iflag & IXON)
-               txflow = 1;
-       if (ts->c_iflag & IXOFF)
-               rxflow = 1;
-       if (ts->c_iflag & IXANY)
-               xany = 1;
-
-       /* Clear the features we don't support */
-       ts->c_cflag &= ~CMSPAR;
-       MoxaPortFlowCtrl(ch, rts, cts, txflow, rxflow, xany);
-       baud = MoxaPortSetTermio(ch, ts, tty_get_baud_rate(tty));
-       if (baud == -1)
-               baud = tty_termios_baud_rate(old_termios);
-       /* Not put the baud rate into the termios data */
-       tty_encode_baud_rate(tty, baud, baud);
-}
-
-/*****************************************************************************
- *     Driver level functions:                                              *
- *****************************************************************************/
-
-static void MoxaPortFlushData(struct moxa_port *port, int mode)
-{
-       void __iomem *ofsAddr;
-       if (mode < 0 || mode > 2)
-               return;
-       ofsAddr = port->tableAddr;
-       moxafunc(ofsAddr, FC_FlushQueue, mode);
-       if (mode != 1) {
-               port->lowChkFlag = 0;
-               moxa_low_water_check(ofsAddr);
-       }
-}
-
-/*
- *    Moxa Port Number Description:
- *
- *      MOXA serial driver supports up to 4 MOXA-C218/C320 boards. And,
- *      the port number using in MOXA driver functions will be 0 to 31 for
- *      first MOXA board, 32 to 63 for second, 64 to 95 for third and 96
- *      to 127 for fourth. For example, if you setup three MOXA boards,
- *      first board is C218, second board is C320-16 and third board is
- *      C320-32. The port number of first board (C218 - 8 ports) is from
- *      0 to 7. The port number of second board (C320 - 16 ports) is form
- *      32 to 47. The port number of third board (C320 - 32 ports) is from
- *      64 to 95. And those port numbers form 8 to 31, 48 to 63 and 96 to
- *      127 will be invalid.
- *
- *
- *      Moxa Functions Description:
- *
- *      Function 1:     Driver initialization routine, this routine must be
- *                      called when initialized driver.
- *      Syntax:
- *      void MoxaDriverInit();
- *
- *
- *      Function 2:     Moxa driver private IOCTL command processing.
- *      Syntax:
- *      int  MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port);
- *
- *           unsigned int cmd   : IOCTL command
- *           unsigned long arg  : IOCTL argument
- *           int port           : port number (0 - 127)
- *
- *           return:    0  (OK)
- *                      -EINVAL
- *                      -ENOIOCTLCMD
- *
- *
- *      Function 6:     Enable this port to start Tx/Rx data.
- *      Syntax:
- *      void MoxaPortEnable(int port);
- *           int port           : port number (0 - 127)
- *
- *
- *      Function 7:     Disable this port
- *      Syntax:
- *      void MoxaPortDisable(int port);
- *           int port           : port number (0 - 127)
- *
- *
- *      Function 10:    Setting baud rate of this port.
- *      Syntax:
- *      speed_t MoxaPortSetBaud(int port, speed_t baud);
- *           int port           : port number (0 - 127)
- *           long baud          : baud rate (50 - 115200)
- *
- *           return:    0       : this port is invalid or baud < 50
- *                      50 - 115200 : the real baud rate set to the port, if
- *                                    the argument baud is large than maximun
- *                                    available baud rate, the real setting
- *                                    baud rate will be the maximun baud rate.
- *
- *
- *      Function 12:    Configure the port.
- *      Syntax:
- *      int  MoxaPortSetTermio(int port, struct ktermios *termio, speed_t baud);
- *           int port           : port number (0 - 127)
- *           struct ktermios * termio : termio structure pointer
- *          speed_t baud       : baud rate
- *
- *           return:    -1      : this port is invalid or termio == NULL
- *                      0       : setting O.K.
- *
- *
- *      Function 13:    Get the DTR/RTS state of this port.
- *      Syntax:
- *      int  MoxaPortGetLineOut(int port, int *dtrState, int *rtsState);
- *           int port           : port number (0 - 127)
- *           int * dtrState     : pointer to INT to receive the current DTR
- *                                state. (if NULL, this function will not
- *                                write to this address)
- *           int * rtsState     : pointer to INT to receive the current RTS
- *                                state. (if NULL, this function will not
- *                                write to this address)
- *
- *           return:    -1      : this port is invalid
- *                      0       : O.K.
- *
- *
- *      Function 14:    Setting the DTR/RTS output state of this port.
- *      Syntax:
- *      void MoxaPortLineCtrl(int port, int dtrState, int rtsState);
- *           int port           : port number (0 - 127)
- *           int dtrState       : DTR output state (0: off, 1: on)
- *           int rtsState       : RTS output state (0: off, 1: on)
- *
- *
- *      Function 15:    Setting the flow control of this port.
- *      Syntax:
- *      void MoxaPortFlowCtrl(int port, int rtsFlow, int ctsFlow, int rxFlow,
- *                            int txFlow,int xany);
- *           int port           : port number (0 - 127)
- *           int rtsFlow        : H/W RTS flow control (0: no, 1: yes)
- *           int ctsFlow        : H/W CTS flow control (0: no, 1: yes)
- *           int rxFlow         : S/W Rx XON/XOFF flow control (0: no, 1: yes)
- *           int txFlow         : S/W Tx XON/XOFF flow control (0: no, 1: yes)
- *           int xany           : S/W XANY flow control (0: no, 1: yes)
- *
- *
- *      Function 16:    Get ths line status of this port
- *      Syntax:
- *      int  MoxaPortLineStatus(int port);
- *           int port           : port number (0 - 127)
- *
- *           return:    Bit 0 - CTS state (0: off, 1: on)
- *                      Bit 1 - DSR state (0: off, 1: on)
- *                      Bit 2 - DCD state (0: off, 1: on)
- *
- *
- *      Function 19:    Flush the Rx/Tx buffer data of this port.
- *      Syntax:
- *      void MoxaPortFlushData(int port, int mode);
- *           int port           : port number (0 - 127)
- *           int mode    
- *                      0       : flush the Rx buffer 
- *                      1       : flush the Tx buffer 
- *                      2       : flush the Rx and Tx buffer 
- *
- *
- *      Function 20:    Write data.
- *      Syntax:
- *      int  MoxaPortWriteData(int port, unsigned char * buffer, int length);
- *           int port           : port number (0 - 127)
- *           unsigned char * buffer     : pointer to write data buffer.
- *           int length         : write data length
- *
- *           return:    0 - length      : real write data length
- *
- *
- *      Function 21:    Read data.
- *      Syntax:
- *      int  MoxaPortReadData(int port, struct tty_struct *tty);
- *           int port           : port number (0 - 127)
- *          struct tty_struct *tty : tty for data
- *
- *           return:    0 - length      : real read data length
- *
- *
- *      Function 24:    Get the Tx buffer current queued data bytes
- *      Syntax:
- *      int  MoxaPortTxQueue(int port);
- *           int port           : port number (0 - 127)
- *
- *           return:    ..      : Tx buffer current queued data bytes
- *
- *
- *      Function 25:    Get the Tx buffer current free space
- *      Syntax:
- *      int  MoxaPortTxFree(int port);
- *           int port           : port number (0 - 127)
- *
- *           return:    ..      : Tx buffer current free space
- *
- *
- *      Function 26:    Get the Rx buffer current queued data bytes
- *      Syntax:
- *      int  MoxaPortRxQueue(int port);
- *           int port           : port number (0 - 127)
- *
- *           return:    ..      : Rx buffer current queued data bytes
- *
- *
- *      Function 28:    Disable port data transmission.
- *      Syntax:
- *      void MoxaPortTxDisable(int port);
- *           int port           : port number (0 - 127)
- *
- *
- *      Function 29:    Enable port data transmission.
- *      Syntax:
- *      void MoxaPortTxEnable(int port);
- *           int port           : port number (0 - 127)
- *
- *
- *      Function 31:    Get the received BREAK signal count and reset it.
- *      Syntax:
- *      int  MoxaPortResetBrkCnt(int port);
- *           int port           : port number (0 - 127)
- *
- *           return:    0 - ..  : BREAK signal count
- *
- *
- */
-
-static void MoxaPortEnable(struct moxa_port *port)
-{
-       void __iomem *ofsAddr;
-       u16 lowwater = 512;
-
-       ofsAddr = port->tableAddr;
-       writew(lowwater, ofsAddr + Low_water);
-       if (MOXA_IS_320(port->board))
-               moxafunc(ofsAddr, FC_SetBreakIrq, 0);
-       else
-               writew(readw(ofsAddr + HostStat) | WakeupBreak,
-                               ofsAddr + HostStat);
-
-       moxafunc(ofsAddr, FC_SetLineIrq, Magic_code);
-       moxafunc(ofsAddr, FC_FlushQueue, 2);
-
-       moxafunc(ofsAddr, FC_EnableCH, Magic_code);
-       MoxaPortLineStatus(port);
-}
-
-static void MoxaPortDisable(struct moxa_port *port)
-{
-       void __iomem *ofsAddr = port->tableAddr;
-
-       moxafunc(ofsAddr, FC_SetFlowCtl, 0);    /* disable flow control */
-       moxafunc(ofsAddr, FC_ClrLineIrq, Magic_code);
-       writew(0, ofsAddr + HostStat);
-       moxafunc(ofsAddr, FC_DisableCH, Magic_code);
-}
-
-static speed_t MoxaPortSetBaud(struct moxa_port *port, speed_t baud)
-{
-       void __iomem *ofsAddr = port->tableAddr;
-       unsigned int clock, val;
-       speed_t max;
-
-       max = MOXA_IS_320(port->board) ? 460800 : 921600;
-       if (baud < 50)
-               return 0;
-       if (baud > max)
-               baud = max;
-       clock = 921600;
-       val = clock / baud;
-       moxafunc(ofsAddr, FC_SetBaud, val);
-       baud = clock / val;
-       return baud;
-}
-
-static int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio,
-               speed_t baud)
-{
-       void __iomem *ofsAddr;
-       tcflag_t cflag;
-       tcflag_t mode = 0;
-
-       ofsAddr = port->tableAddr;
-       cflag = termio->c_cflag;        /* termio->c_cflag */
-
-       mode = termio->c_cflag & CSIZE;
-       if (mode == CS5)
-               mode = MX_CS5;
-       else if (mode == CS6)
-               mode = MX_CS6;
-       else if (mode == CS7)
-               mode = MX_CS7;
-       else if (mode == CS8)
-               mode = MX_CS8;
-
-       if (termio->c_cflag & CSTOPB) {
-               if (mode == MX_CS5)
-                       mode |= MX_STOP15;
-               else
-                       mode |= MX_STOP2;
-       } else
-               mode |= MX_STOP1;
-
-       if (termio->c_cflag & PARENB) {
-               if (termio->c_cflag & PARODD)
-                       mode |= MX_PARODD;
-               else
-                       mode |= MX_PAREVEN;
-       } else
-               mode |= MX_PARNONE;
-
-       moxafunc(ofsAddr, FC_SetDataMode, (u16)mode);
-
-       if (MOXA_IS_320(port->board) && baud >= 921600)
-               return -1;
-
-       baud = MoxaPortSetBaud(port, baud);
-
-       if (termio->c_iflag & (IXON | IXOFF | IXANY)) {
-               spin_lock_irq(&moxafunc_lock);
-               writeb(termio->c_cc[VSTART], ofsAddr + FuncArg);
-               writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1);
-               writeb(FC_SetXonXoff, ofsAddr + FuncCode);
-               moxa_wait_finish(ofsAddr);
-               spin_unlock_irq(&moxafunc_lock);
-
-       }
-       return baud;
-}
-
-static int MoxaPortGetLineOut(struct moxa_port *port, int *dtrState,
-               int *rtsState)
-{
-       if (dtrState)
-               *dtrState = !!(port->lineCtrl & DTR_ON);
-       if (rtsState)
-               *rtsState = !!(port->lineCtrl & RTS_ON);
-
-       return 0;
-}
-
-static void MoxaPortLineCtrl(struct moxa_port *port, int dtr, int rts)
-{
-       u8 mode = 0;
-
-       if (dtr)
-               mode |= DTR_ON;
-       if (rts)
-               mode |= RTS_ON;
-       port->lineCtrl = mode;
-       moxafunc(port->tableAddr, FC_LineControl, mode);
-}
-
-static void MoxaPortFlowCtrl(struct moxa_port *port, int rts, int cts,
-               int txflow, int rxflow, int txany)
-{
-       int mode = 0;
-
-       if (rts)
-               mode |= RTS_FlowCtl;
-       if (cts)
-               mode |= CTS_FlowCtl;
-       if (txflow)
-               mode |= Tx_FlowCtl;
-       if (rxflow)
-               mode |= Rx_FlowCtl;
-       if (txany)
-               mode |= IXM_IXANY;
-       moxafunc(port->tableAddr, FC_SetFlowCtl, mode);
-}
-
-static int MoxaPortLineStatus(struct moxa_port *port)
-{
-       void __iomem *ofsAddr;
-       int val;
-
-       ofsAddr = port->tableAddr;
-       if (MOXA_IS_320(port->board))
-               val = moxafuncret(ofsAddr, FC_LineStatus, 0);
-       else
-               val = readw(ofsAddr + FlagStat) >> 4;
-       val &= 0x0B;
-       if (val & 8)
-               val |= 4;
-       moxa_new_dcdstate(port, val & 8);
-       val &= 7;
-       return val;
-}
-
-static int MoxaPortWriteData(struct tty_struct *tty,
-               const unsigned char *buffer, int len)
-{
-       struct moxa_port *port = tty->driver_data;
-       void __iomem *baseAddr, *ofsAddr, *ofs;
-       unsigned int c, total;
-       u16 head, tail, tx_mask, spage, epage;
-       u16 pageno, pageofs, bufhead;
-
-       ofsAddr = port->tableAddr;
-       baseAddr = port->board->basemem;
-       tx_mask = readw(ofsAddr + TX_mask);
-       spage = readw(ofsAddr + Page_txb);
-       epage = readw(ofsAddr + EndPage_txb);
-       tail = readw(ofsAddr + TXwptr);
-       head = readw(ofsAddr + TXrptr);
-       c = (head > tail) ? (head - tail - 1) : (head - tail + tx_mask);
-       if (c > len)
-               c = len;
-       moxaLog.txcnt[port->port.tty->index] += c;
-       total = c;
-       if (spage == epage) {
-               bufhead = readw(ofsAddr + Ofs_txb);
-               writew(spage, baseAddr + Control_reg);
-               while (c > 0) {
-                       if (head > tail)
-                               len = head - tail - 1;
-                       else
-                               len = tx_mask + 1 - tail;
-                       len = (c > len) ? len : c;
-                       ofs = baseAddr + DynPage_addr + bufhead + tail;
-                       memcpy_toio(ofs, buffer, len);
-                       buffer += len;
-                       tail = (tail + len) & tx_mask;
-                       c -= len;
-               }
-       } else {
-               pageno = spage + (tail >> 13);
-               pageofs = tail & Page_mask;
-               while (c > 0) {
-                       len = Page_size - pageofs;
-                       if (len > c)
-                               len = c;
-                       writeb(pageno, baseAddr + Control_reg);
-                       ofs = baseAddr + DynPage_addr + pageofs;
-                       memcpy_toio(ofs, buffer, len);
-                       buffer += len;
-                       if (++pageno == epage)
-                               pageno = spage;
-                       pageofs = 0;
-                       c -= len;
-               }
-               tail = (tail + total) & tx_mask;
-       }
-       writew(tail, ofsAddr + TXwptr);
-       writeb(1, ofsAddr + CD180TXirq);        /* start to send */
-       return total;
-}
-
-static int MoxaPortReadData(struct moxa_port *port)
-{
-       struct tty_struct *tty = port->port.tty;
-       unsigned char *dst;
-       void __iomem *baseAddr, *ofsAddr, *ofs;
-       unsigned int count, len, total;
-       u16 tail, rx_mask, spage, epage;
-       u16 pageno, pageofs, bufhead, head;
-
-       ofsAddr = port->tableAddr;
-       baseAddr = port->board->basemem;
-       head = readw(ofsAddr + RXrptr);
-       tail = readw(ofsAddr + RXwptr);
-       rx_mask = readw(ofsAddr + RX_mask);
-       spage = readw(ofsAddr + Page_rxb);
-       epage = readw(ofsAddr + EndPage_rxb);
-       count = (tail >= head) ? (tail - head) : (tail - head + rx_mask + 1);
-       if (count == 0)
-               return 0;
-
-       total = count;
-       moxaLog.rxcnt[tty->index] += total;
-       if (spage == epage) {
-               bufhead = readw(ofsAddr + Ofs_rxb);
-               writew(spage, baseAddr + Control_reg);
-               while (count > 0) {
-                       ofs = baseAddr + DynPage_addr + bufhead + head;
-                       len = (tail >= head) ? (tail - head) :
-                                       (rx_mask + 1 - head);
-                       len = tty_prepare_flip_string(tty, &dst,
-                                       min(len, count));
-                       memcpy_fromio(dst, ofs, len);
-                       head = (head + len) & rx_mask;
-                       count -= len;
-               }
-       } else {
-               pageno = spage + (head >> 13);
-               pageofs = head & Page_mask;
-               while (count > 0) {
-                       writew(pageno, baseAddr + Control_reg);
-                       ofs = baseAddr + DynPage_addr + pageofs;
-                       len = tty_prepare_flip_string(tty, &dst,
-                                       min(Page_size - pageofs, count));
-                       memcpy_fromio(dst, ofs, len);
-
-                       count -= len;
-                       pageofs = (pageofs + len) & Page_mask;
-                       if (pageofs == 0 && ++pageno == epage)
-                               pageno = spage;
-               }
-               head = (head + total) & rx_mask;
-       }
-       writew(head, ofsAddr + RXrptr);
-       if (readb(ofsAddr + FlagStat) & Xoff_state) {
-               moxaLowWaterChk = 1;
-               port->lowChkFlag = 1;
-       }
-       return total;
-}
-
-
-static int MoxaPortTxQueue(struct moxa_port *port)
-{
-       void __iomem *ofsAddr = port->tableAddr;
-       u16 rptr, wptr, mask;
-
-       rptr = readw(ofsAddr + TXrptr);
-       wptr = readw(ofsAddr + TXwptr);
-       mask = readw(ofsAddr + TX_mask);
-       return (wptr - rptr) & mask;
-}
-
-static int MoxaPortTxFree(struct moxa_port *port)
-{
-       void __iomem *ofsAddr = port->tableAddr;
-       u16 rptr, wptr, mask;
-
-       rptr = readw(ofsAddr + TXrptr);
-       wptr = readw(ofsAddr + TXwptr);
-       mask = readw(ofsAddr + TX_mask);
-       return mask - ((wptr - rptr) & mask);
-}
-
-static int MoxaPortRxQueue(struct moxa_port *port)
-{
-       void __iomem *ofsAddr = port->tableAddr;
-       u16 rptr, wptr, mask;
-
-       rptr = readw(ofsAddr + RXrptr);
-       wptr = readw(ofsAddr + RXwptr);
-       mask = readw(ofsAddr + RX_mask);
-       return (wptr - rptr) & mask;
-}
-
-static void MoxaPortTxDisable(struct moxa_port *port)
-{
-       moxafunc(port->tableAddr, FC_SetXoffState, Magic_code);
-}
-
-static void MoxaPortTxEnable(struct moxa_port *port)
-{
-       moxafunc(port->tableAddr, FC_SetXonState, Magic_code);
-}
-
-static int moxa_get_serial_info(struct moxa_port *info,
-               struct serial_struct __user *retinfo)
-{
-       struct serial_struct tmp = {
-               .type = info->type,
-               .line = info->port.tty->index,
-               .flags = info->port.flags,
-               .baud_base = 921600,
-               .close_delay = info->port.close_delay
-       };
-       return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;
-}
-
-
-static int moxa_set_serial_info(struct moxa_port *info,
-               struct serial_struct __user *new_info)
-{
-       struct serial_struct new_serial;
-
-       if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
-               return -EFAULT;
-
-       if (new_serial.irq != 0 || new_serial.port != 0 ||
-                       new_serial.custom_divisor != 0 ||
-                       new_serial.baud_base != 921600)
-               return -EPERM;
-
-       if (!capable(CAP_SYS_ADMIN)) {
-               if (((new_serial.flags & ~ASYNC_USR_MASK) !=
-                    (info->port.flags & ~ASYNC_USR_MASK)))
-                       return -EPERM;
-       } else
-               info->port.close_delay = new_serial.close_delay * HZ / 100;
-
-       new_serial.flags = (new_serial.flags & ~ASYNC_FLAGS);
-       new_serial.flags |= (info->port.flags & ASYNC_FLAGS);
-
-       MoxaSetFifo(info, new_serial.type == PORT_16550A);
-
-       info->type = new_serial.type;
-       return 0;
-}
-
-
-
-/*****************************************************************************
- *     Static local functions:                                              *
- *****************************************************************************/
-
-static void MoxaSetFifo(struct moxa_port *port, int enable)
-{
-       void __iomem *ofsAddr = port->tableAddr;
-
-       if (!enable) {
-               moxafunc(ofsAddr, FC_SetRxFIFOTrig, 0);
-               moxafunc(ofsAddr, FC_SetTxFIFOCnt, 1);
-       } else {
-               moxafunc(ofsAddr, FC_SetRxFIFOTrig, 3);
-               moxafunc(ofsAddr, FC_SetTxFIFOCnt, 16);
-       }
-}
diff --git a/drivers/char/moxa.h b/drivers/char/moxa.h
deleted file mode 100644 (file)
index 87d16ce..0000000
+++ /dev/null
@@ -1,304 +0,0 @@
-#ifndef MOXA_H_FILE
-#define MOXA_H_FILE
-
-#define        MOXA            0x400
-#define MOXA_GET_IQUEUE        (MOXA + 1)      /* get input buffered count */
-#define MOXA_GET_OQUEUE        (MOXA + 2)      /* get output buffered count */
-#define MOXA_GETDATACOUNT       (MOXA + 23)
-#define MOXA_GET_IOQUEUE       (MOXA + 27)
-#define MOXA_FLUSH_QUEUE       (MOXA + 28)
-#define MOXA_GETMSTATUS         (MOXA + 65)
-
-/*
- *    System Configuration
- */
-
-#define Magic_code     0x404
-
-/*
- *    for C218 BIOS initialization
- */
-#define C218_ConfBase  0x800
-#define C218_status    (C218_ConfBase + 0)     /* BIOS running status    */
-#define C218_diag      (C218_ConfBase + 2)     /* diagnostic status      */
-#define C218_key       (C218_ConfBase + 4)     /* WORD (0x218 for C218) */
-#define C218DLoad_len  (C218_ConfBase + 6)     /* WORD           */
-#define C218check_sum  (C218_ConfBase + 8)     /* BYTE           */
-#define C218chksum_ok  (C218_ConfBase + 0x0a)  /* BYTE (1:ok)            */
-#define C218_TestRx    (C218_ConfBase + 0x10)  /* 8 bytes for 8 ports    */
-#define C218_TestTx    (C218_ConfBase + 0x18)  /* 8 bytes for 8 ports    */
-#define C218_RXerr     (C218_ConfBase + 0x20)  /* 8 bytes for 8 ports    */
-#define C218_ErrFlag   (C218_ConfBase + 0x28)  /* 8 bytes for 8 ports    */
-
-#define C218_LoadBuf   0x0F00
-#define C218_KeyCode   0x218
-#define CP204J_KeyCode 0x204
-
-/*
- *    for C320 BIOS initialization
- */
-#define C320_ConfBase  0x800
-#define C320_LoadBuf   0x0f00
-#define STS_init       0x05    /* for C320_status        */
-
-#define C320_status    C320_ConfBase + 0       /* BIOS running status    */
-#define C320_diag      C320_ConfBase + 2       /* diagnostic status      */
-#define C320_key       C320_ConfBase + 4       /* WORD (0320H for C320) */
-#define C320DLoad_len  C320_ConfBase + 6       /* WORD           */
-#define C320check_sum  C320_ConfBase + 8       /* WORD           */
-#define C320chksum_ok  C320_ConfBase + 0x0a    /* WORD (1:ok)            */
-#define C320bapi_len   C320_ConfBase + 0x0c    /* WORD           */
-#define C320UART_no    C320_ConfBase + 0x0e    /* WORD           */
-
-#define C320_KeyCode   0x320
-
-#define FixPage_addr   0x0000  /* starting addr of static page  */
-#define DynPage_addr   0x2000  /* starting addr of dynamic page */
-#define C218_start     0x3000  /* starting addr of C218 BIOS prg */
-#define Control_reg    0x1ff0  /* select page and reset control */
-#define HW_reset       0x80
-
-/*
- *    Function Codes
- */
-#define FC_CardReset   0x80
-#define FC_ChannelReset 1      /* C320 firmware not supported */
-#define FC_EnableCH    2
-#define FC_DisableCH   3
-#define FC_SetParam    4
-#define FC_SetMode     5
-#define FC_SetRate     6
-#define FC_LineControl 7
-#define FC_LineStatus  8
-#define FC_XmitControl 9
-#define FC_FlushQueue  10
-#define FC_SendBreak   11
-#define FC_StopBreak   12
-#define FC_LoopbackON  13
-#define FC_LoopbackOFF 14
-#define FC_ClrIrqTable 15
-#define FC_SendXon     16
-#define FC_SetTermIrq  17      /* C320 firmware not supported */
-#define FC_SetCntIrq   18      /* C320 firmware not supported */
-#define FC_SetBreakIrq 19
-#define FC_SetLineIrq  20
-#define FC_SetFlowCtl  21
-#define FC_GenIrq      22
-#define FC_InCD180     23
-#define FC_OutCD180    24
-#define FC_InUARTreg   23
-#define FC_OutUARTreg  24
-#define FC_SetXonXoff  25
-#define FC_OutCD180CCR 26
-#define FC_ExtIQueue   27
-#define FC_ExtOQueue   28
-#define FC_ClrLineIrq  29
-#define FC_HWFlowCtl   30
-#define FC_GetClockRate 35
-#define FC_SetBaud     36
-#define FC_SetDataMode  41
-#define FC_GetCCSR      43
-#define FC_GetDataError 45
-#define FC_RxControl   50
-#define FC_ImmSend     51
-#define FC_SetXonState 52
-#define FC_SetXoffState        53
-#define FC_SetRxFIFOTrig 54
-#define FC_SetTxFIFOCnt 55
-#define FC_UnixRate    56
-#define FC_UnixResetTimer 57
-
-#define        RxFIFOTrig1     0
-#define        RxFIFOTrig4     1
-#define        RxFIFOTrig8     2
-#define        RxFIFOTrig14    3
-
-/*
- *    Dual-Ported RAM
- */
-#define DRAM_global    0
-#define INT_data       (DRAM_global + 0)
-#define Config_base    (DRAM_global + 0x108)
-
-#define IRQindex       (INT_data + 0)
-#define IRQpending     (INT_data + 4)
-#define IRQtable       (INT_data + 8)
-
-/*
- *    Interrupt Status
- */
-#define IntrRx         0x01    /* receiver data O.K.             */
-#define IntrTx         0x02    /* transmit buffer empty  */
-#define IntrFunc       0x04    /* function complete              */
-#define IntrBreak      0x08    /* received break         */
-#define IntrLine       0x10    /* line status change
-                                  for transmitter                */
-#define IntrIntr       0x20    /* received INTR code             */
-#define IntrQuit       0x40    /* received QUIT code             */
-#define IntrEOF        0x80    /* received EOF code              */
-
-#define IntrRxTrigger  0x100   /* rx data count reach tigger value */
-#define IntrTxTrigger  0x200   /* tx data count below trigger value */
-
-#define Magic_no       (Config_base + 0)
-#define Card_model_no  (Config_base + 2)
-#define Total_ports    (Config_base + 4)
-#define Module_cnt     (Config_base + 8)
-#define Module_no      (Config_base + 10)
-#define Timer_10ms     (Config_base + 14)
-#define Disable_IRQ    (Config_base + 20)
-#define TMS320_PORT1   (Config_base + 22)
-#define TMS320_PORT2   (Config_base + 24)
-#define TMS320_CLOCK   (Config_base + 26)
-
-/*
- *    DATA BUFFER in DRAM
- */
-#define Extern_table   0x400   /* Base address of the external table
-                                  (24 words *    64) total 3K bytes
-                                  (24 words * 128) total 6K bytes */
-#define Extern_size    0x60    /* 96 bytes                       */
-#define RXrptr         0x00    /* read pointer for RX buffer     */
-#define RXwptr         0x02    /* write pointer for RX buffer    */
-#define TXrptr         0x04    /* read pointer for TX buffer     */
-#define TXwptr         0x06    /* write pointer for TX buffer    */
-#define HostStat       0x08    /* IRQ flag and general flag      */
-#define FlagStat       0x0A
-#define FlowControl    0x0C    /* B7 B6 B5 B4 B3 B2 B1 B0              */
-                               /*  x  x  x  x  |  |  |  |            */
-                               /*              |  |  |  + CTS flow   */
-                               /*              |  |  +--- RTS flow   */
-                               /*              |  +------ TX Xon/Xoff */
-                               /*              +--------- RX Xon/Xoff */
-#define Break_cnt      0x0E    /* received break count   */
-#define CD180TXirq     0x10    /* if non-0: enable TX irq        */
-#define RX_mask        0x12
-#define TX_mask        0x14
-#define Ofs_rxb        0x16
-#define Ofs_txb        0x18
-#define Page_rxb       0x1A
-#define Page_txb       0x1C
-#define EndPage_rxb    0x1E
-#define EndPage_txb    0x20
-#define Data_error     0x22
-#define RxTrigger      0x28
-#define TxTrigger      0x2a
-
-#define rRXwptr        0x34
-#define Low_water      0x36
-
-#define FuncCode       0x40
-#define FuncArg        0x42
-#define FuncArg1       0x44
-
-#define C218rx_size    0x2000  /* 8K bytes */
-#define C218tx_size    0x8000  /* 32K bytes */
-
-#define C218rx_mask    (C218rx_size - 1)
-#define C218tx_mask    (C218tx_size - 1)
-
-#define C320p8rx_size  0x2000
-#define C320p8tx_size  0x8000
-#define C320p8rx_mask  (C320p8rx_size - 1)
-#define C320p8tx_mask  (C320p8tx_size - 1)
-
-#define C320p16rx_size 0x2000
-#define C320p16tx_size 0x4000
-#define C320p16rx_mask (C320p16rx_size - 1)
-#define C320p16tx_mask (C320p16tx_size - 1)
-
-#define C320p24rx_size 0x2000
-#define C320p24tx_size 0x2000
-#define C320p24rx_mask (C320p24rx_size - 1)
-#define C320p24tx_mask (C320p24tx_size - 1)
-
-#define C320p32rx_size 0x1000
-#define C320p32tx_size 0x1000
-#define C320p32rx_mask (C320p32rx_size - 1)
-#define C320p32tx_mask (C320p32tx_size - 1)
-
-#define Page_size      0x2000U
-#define Page_mask      (Page_size - 1)
-#define C218rx_spage   3
-#define C218tx_spage   4
-#define C218rx_pageno  1
-#define C218tx_pageno  4
-#define C218buf_pageno 5
-
-#define C320p8rx_spage 3
-#define C320p8tx_spage 4
-#define C320p8rx_pgno  1
-#define C320p8tx_pgno  4
-#define C320p8buf_pgno 5
-
-#define C320p16rx_spage 3
-#define C320p16tx_spage 4
-#define C320p16rx_pgno 1
-#define C320p16tx_pgno 2
-#define C320p16buf_pgno 3
-
-#define C320p24rx_spage 3
-#define C320p24tx_spage 4
-#define C320p24rx_pgno 1
-#define C320p24tx_pgno 1
-#define C320p24buf_pgno 2
-
-#define C320p32rx_spage 3
-#define C320p32tx_ofs  C320p32rx_size
-#define C320p32tx_spage 3
-#define C320p32buf_pgno 1
-
-/*
- *    Host Status
- */
-#define WakeupRx       0x01
-#define WakeupTx       0x02
-#define WakeupBreak    0x08
-#define WakeupLine     0x10
-#define WakeupIntr     0x20
-#define WakeupQuit     0x40
-#define WakeupEOF      0x80    /* used in VTIME control */
-#define WakeupRxTrigger        0x100
-#define WakeupTxTrigger        0x200
-/*
- *    Flag status
- */
-#define Rx_over                0x01
-#define Xoff_state     0x02
-#define Tx_flowOff     0x04
-#define Tx_enable      0x08
-#define CTS_state      0x10
-#define DSR_state      0x20
-#define DCD_state      0x80
-/*
- *    FlowControl
- */
-#define CTS_FlowCtl    1
-#define RTS_FlowCtl    2
-#define Tx_FlowCtl     4
-#define Rx_FlowCtl     8
-#define IXM_IXANY      0x10
-
-#define LowWater       128
-
-#define DTR_ON         1
-#define RTS_ON         2
-#define CTS_ON         1
-#define DSR_ON         2
-#define DCD_ON         8
-
-/* mode definition */
-#define        MX_CS8          0x03
-#define        MX_CS7          0x02
-#define        MX_CS6          0x01
-#define        MX_CS5          0x00
-
-#define        MX_STOP1        0x00
-#define        MX_STOP15       0x04
-#define        MX_STOP2        0x08
-
-#define        MX_PARNONE      0x00
-#define        MX_PAREVEN      0x40
-#define        MX_PARODD       0xC0
-
-#endif
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
deleted file mode 100644 (file)
index d188f37..0000000
+++ /dev/null
@@ -1,2757 +0,0 @@
-/*
- *          mxser.c  -- MOXA Smartio/Industio family multiport serial driver.
- *
- *      Copyright (C) 1999-2006  Moxa Technologies (support@moxa.com).
- *     Copyright (C) 2006-2008  Jiri Slaby <jirislaby@gmail.com>
- *
- *      This code is loosely based on the 1.8 moxa driver which is based on
- *     Linux serial driver, written by Linus Torvalds, Theodore T'so and
- *     others.
- *
- *      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.
- *
- *     Fed through a cleanup, indent and remove of non 2.6 code by Alan Cox
- *     <alan@lxorguk.ukuu.org.uk>. The original 1.8 code is available on
- *     www.moxa.com.
- *     - Fixed x86_64 cleanness
- */
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/serial_reg.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/bitops.h>
-#include <linux/slab.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-
-#include "mxser.h"
-
-#define        MXSER_VERSION   "2.0.5"         /* 1.14 */
-#define        MXSERMAJOR       174
-
-#define MXSER_BOARDS           4       /* Max. boards */
-#define MXSER_PORTS_PER_BOARD  8       /* Max. ports per board */
-#define MXSER_PORTS            (MXSER_BOARDS * MXSER_PORTS_PER_BOARD)
-#define MXSER_ISR_PASS_LIMIT   100
-
-/*CheckIsMoxaMust return value*/
-#define MOXA_OTHER_UART                0x00
-#define MOXA_MUST_MU150_HWID   0x01
-#define MOXA_MUST_MU860_HWID   0x02
-
-#define WAKEUP_CHARS           256
-
-#define UART_MCR_AFE           0x20
-#define UART_LSR_SPECIAL       0x1E
-
-#define PCI_DEVICE_ID_POS104UL 0x1044
-#define PCI_DEVICE_ID_CB108    0x1080
-#define PCI_DEVICE_ID_CP102UF  0x1023
-#define PCI_DEVICE_ID_CP112UL  0x1120
-#define PCI_DEVICE_ID_CB114    0x1142
-#define PCI_DEVICE_ID_CP114UL  0x1143
-#define PCI_DEVICE_ID_CB134I   0x1341
-#define PCI_DEVICE_ID_CP138U   0x1380
-
-
-#define C168_ASIC_ID    1
-#define C104_ASIC_ID    2
-#define C102_ASIC_ID   0xB
-#define CI132_ASIC_ID  4
-#define CI134_ASIC_ID  3
-#define CI104J_ASIC_ID  5
-
-#define MXSER_HIGHBAUD 1
-#define MXSER_HAS2     2
-
-/* This is only for PCI */
-static const struct {
-       int type;
-       int tx_fifo;
-       int rx_fifo;
-       int xmit_fifo_size;
-       int rx_high_water;
-       int rx_trigger;
-       int rx_low_water;
-       long max_baud;
-} Gpci_uart_info[] = {
-       {MOXA_OTHER_UART, 16, 16, 16, 14, 14, 1, 921600L},
-       {MOXA_MUST_MU150_HWID, 64, 64, 64, 48, 48, 16, 230400L},
-       {MOXA_MUST_MU860_HWID, 128, 128, 128, 96, 96, 32, 921600L}
-};
-#define UART_INFO_NUM  ARRAY_SIZE(Gpci_uart_info)
-
-struct mxser_cardinfo {
-       char *name;
-       unsigned int nports;
-       unsigned int flags;
-};
-
-static const struct mxser_cardinfo mxser_cards[] = {
-/* 0*/ { "C168 series",        8, },
-       { "C104 series",        4, },
-       { "CI-104J series",     4, },
-       { "C168H/PCI series",   8, },
-       { "C104H/PCI series",   4, },
-/* 5*/ { "C102 series",        4, MXSER_HAS2 },        /* C102-ISA */
-       { "CI-132 series",      4, MXSER_HAS2 },
-       { "CI-134 series",      4, },
-       { "CP-132 series",      2, },
-       { "CP-114 series",      4, },
-/*10*/ { "CT-114 series",      4, },
-       { "CP-102 series",      2, MXSER_HIGHBAUD },
-       { "CP-104U series",     4, },
-       { "CP-168U series",     8, },
-       { "CP-132U series",     2, },
-/*15*/ { "CP-134U series",     4, },
-       { "CP-104JU series",    4, },
-       { "Moxa UC7000 Serial", 8, },           /* RC7000 */
-       { "CP-118U series",     8, },
-       { "CP-102UL series",    2, },
-/*20*/ { "CP-102U series",     2, },
-       { "CP-118EL series",    8, },
-       { "CP-168EL series",    8, },
-       { "CP-104EL series",    4, },
-       { "CB-108 series",      8, },
-/*25*/ { "CB-114 series",      4, },
-       { "CB-134I series",     4, },
-       { "CP-138U series",     8, },
-       { "POS-104UL series",   4, },
-       { "CP-114UL series",    4, },
-/*30*/ { "CP-102UF series",    2, },
-       { "CP-112UL series",    2, },
-};
-
-/* driver_data correspond to the lines in the structure above
-   see also ISA probe function before you change something */
-static struct pci_device_id mxser_pcibrds[] = {
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C168),   .driver_data = 3 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C104),   .driver_data = 4 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132),  .driver_data = 8 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114),  .driver_data = 9 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CT114),  .driver_data = 10 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102),  .driver_data = 11 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104U), .driver_data = 12 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168U), .driver_data = 13 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132U), .driver_data = 14 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134U), .driver_data = 15 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104JU),.driver_data = 16 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_RC7000), .driver_data = 17 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118U), .driver_data = 18 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102UL),.driver_data = 19 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102U), .driver_data = 20 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL),.driver_data = 21 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL),.driver_data = 22 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL),.driver_data = 23 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB108),       .driver_data = 24 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB114),       .driver_data = 25 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB134I),      .driver_data = 26 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP138U),      .driver_data = 27 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_POS104UL),    .driver_data = 28 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP114UL),     .driver_data = 29 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP102UF),     .driver_data = 30 },
-       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP112UL),     .driver_data = 31 },
-       { }
-};
-MODULE_DEVICE_TABLE(pci, mxser_pcibrds);
-
-static unsigned long ioaddr[MXSER_BOARDS];
-static int ttymajor = MXSERMAJOR;
-
-/* Variables for insmod */
-
-MODULE_AUTHOR("Casper Yang");
-MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver");
-module_param_array(ioaddr, ulong, NULL, 0);
-MODULE_PARM_DESC(ioaddr, "ISA io addresses to look for a moxa board");
-module_param(ttymajor, int, 0);
-MODULE_LICENSE("GPL");
-
-struct mxser_log {
-       int tick;
-       unsigned long rxcnt[MXSER_PORTS];
-       unsigned long txcnt[MXSER_PORTS];
-};
-
-struct mxser_mon {
-       unsigned long rxcnt;
-       unsigned long txcnt;
-       unsigned long up_rxcnt;
-       unsigned long up_txcnt;
-       int modem_status;
-       unsigned char hold_reason;
-};
-
-struct mxser_mon_ext {
-       unsigned long rx_cnt[32];
-       unsigned long tx_cnt[32];
-       unsigned long up_rxcnt[32];
-       unsigned long up_txcnt[32];
-       int modem_status[32];
-
-       long baudrate[32];
-       int databits[32];
-       int stopbits[32];
-       int parity[32];
-       int flowctrl[32];
-       int fifo[32];
-       int iftype[32];
-};
-
-struct mxser_board;
-
-struct mxser_port {
-       struct tty_port port;
-       struct mxser_board *board;
-
-       unsigned long ioaddr;
-       unsigned long opmode_ioaddr;
-       int max_baud;
-
-       int rx_high_water;
-       int rx_trigger;         /* Rx fifo trigger level */
-       int rx_low_water;
-       int baud_base;          /* max. speed */
-       int type;               /* UART type */
-
-       int x_char;             /* xon/xoff character */
-       int IER;                /* Interrupt Enable Register */
-       int MCR;                /* Modem control register */
-
-       unsigned char stop_rx;
-       unsigned char ldisc_stop_rx;
-
-       int custom_divisor;
-       unsigned char err_shadow;
-
-       struct async_icount icount; /* kernel counters for 4 input interrupts */
-       int timeout;
-
-       int read_status_mask;
-       int ignore_status_mask;
-       int xmit_fifo_size;
-       int xmit_head;
-       int xmit_tail;
-       int xmit_cnt;
-
-       struct ktermios normal_termios;
-
-       struct mxser_mon mon_data;
-
-       spinlock_t slock;
-};
-
-struct mxser_board {
-       unsigned int idx;
-       int irq;
-       const struct mxser_cardinfo *info;
-       unsigned long vector;
-       unsigned long vector_mask;
-
-       int chip_flag;
-       int uart_type;
-
-       struct mxser_port ports[MXSER_PORTS_PER_BOARD];
-};
-
-struct mxser_mstatus {
-       tcflag_t cflag;
-       int cts;
-       int dsr;
-       int ri;
-       int dcd;
-};
-
-static struct mxser_board mxser_boards[MXSER_BOARDS];
-static struct tty_driver *mxvar_sdriver;
-static struct mxser_log mxvar_log;
-static int mxser_set_baud_method[MXSER_PORTS + 1];
-
-static void mxser_enable_must_enchance_mode(unsigned long baseio)
-{
-       u8 oldlcr;
-       u8 efr;
-
-       oldlcr = inb(baseio + UART_LCR);
-       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
-
-       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
-       efr |= MOXA_MUST_EFR_EFRB_ENABLE;
-
-       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
-       outb(oldlcr, baseio + UART_LCR);
-}
-
-#ifdef CONFIG_PCI
-static void mxser_disable_must_enchance_mode(unsigned long baseio)
-{
-       u8 oldlcr;
-       u8 efr;
-
-       oldlcr = inb(baseio + UART_LCR);
-       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
-
-       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
-       efr &= ~MOXA_MUST_EFR_EFRB_ENABLE;
-
-       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
-       outb(oldlcr, baseio + UART_LCR);
-}
-#endif
-
-static void mxser_set_must_xon1_value(unsigned long baseio, u8 value)
-{
-       u8 oldlcr;
-       u8 efr;
-
-       oldlcr = inb(baseio + UART_LCR);
-       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
-
-       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
-       efr &= ~MOXA_MUST_EFR_BANK_MASK;
-       efr |= MOXA_MUST_EFR_BANK0;
-
-       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
-       outb(value, baseio + MOXA_MUST_XON1_REGISTER);
-       outb(oldlcr, baseio + UART_LCR);
-}
-
-static void mxser_set_must_xoff1_value(unsigned long baseio, u8 value)
-{
-       u8 oldlcr;
-       u8 efr;
-
-       oldlcr = inb(baseio + UART_LCR);
-       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
-
-       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
-       efr &= ~MOXA_MUST_EFR_BANK_MASK;
-       efr |= MOXA_MUST_EFR_BANK0;
-
-       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
-       outb(value, baseio + MOXA_MUST_XOFF1_REGISTER);
-       outb(oldlcr, baseio + UART_LCR);
-}
-
-static void mxser_set_must_fifo_value(struct mxser_port *info)
-{
-       u8 oldlcr;
-       u8 efr;
-
-       oldlcr = inb(info->ioaddr + UART_LCR);
-       outb(MOXA_MUST_ENTER_ENCHANCE, info->ioaddr + UART_LCR);
-
-       efr = inb(info->ioaddr + MOXA_MUST_EFR_REGISTER);
-       efr &= ~MOXA_MUST_EFR_BANK_MASK;
-       efr |= MOXA_MUST_EFR_BANK1;
-
-       outb(efr, info->ioaddr + MOXA_MUST_EFR_REGISTER);
-       outb((u8)info->rx_high_water, info->ioaddr + MOXA_MUST_RBRTH_REGISTER);
-       outb((u8)info->rx_trigger, info->ioaddr + MOXA_MUST_RBRTI_REGISTER);
-       outb((u8)info->rx_low_water, info->ioaddr + MOXA_MUST_RBRTL_REGISTER);
-       outb(oldlcr, info->ioaddr + UART_LCR);
-}
-
-static void mxser_set_must_enum_value(unsigned long baseio, u8 value)
-{
-       u8 oldlcr;
-       u8 efr;
-
-       oldlcr = inb(baseio + UART_LCR);
-       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
-
-       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
-       efr &= ~MOXA_MUST_EFR_BANK_MASK;
-       efr |= MOXA_MUST_EFR_BANK2;
-
-       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
-       outb(value, baseio + MOXA_MUST_ENUM_REGISTER);
-       outb(oldlcr, baseio + UART_LCR);
-}
-
-#ifdef CONFIG_PCI
-static void mxser_get_must_hardware_id(unsigned long baseio, u8 *pId)
-{
-       u8 oldlcr;
-       u8 efr;
-
-       oldlcr = inb(baseio + UART_LCR);
-       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
-
-       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
-       efr &= ~MOXA_MUST_EFR_BANK_MASK;
-       efr |= MOXA_MUST_EFR_BANK2;
-
-       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
-       *pId = inb(baseio + MOXA_MUST_HWID_REGISTER);
-       outb(oldlcr, baseio + UART_LCR);
-}
-#endif
-
-static void SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(unsigned long baseio)
-{
-       u8 oldlcr;
-       u8 efr;
-
-       oldlcr = inb(baseio + UART_LCR);
-       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
-
-       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
-       efr &= ~MOXA_MUST_EFR_SF_MASK;
-
-       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
-       outb(oldlcr, baseio + UART_LCR);
-}
-
-static void mxser_enable_must_tx_software_flow_control(unsigned long baseio)
-{
-       u8 oldlcr;
-       u8 efr;
-
-       oldlcr = inb(baseio + UART_LCR);
-       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
-
-       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
-       efr &= ~MOXA_MUST_EFR_SF_TX_MASK;
-       efr |= MOXA_MUST_EFR_SF_TX1;
-
-       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
-       outb(oldlcr, baseio + UART_LCR);
-}
-
-static void mxser_disable_must_tx_software_flow_control(unsigned long baseio)
-{
-       u8 oldlcr;
-       u8 efr;
-
-       oldlcr = inb(baseio + UART_LCR);
-       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
-
-       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
-       efr &= ~MOXA_MUST_EFR_SF_TX_MASK;
-
-       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
-       outb(oldlcr, baseio + UART_LCR);
-}
-
-static void mxser_enable_must_rx_software_flow_control(unsigned long baseio)
-{
-       u8 oldlcr;
-       u8 efr;
-
-       oldlcr = inb(baseio + UART_LCR);
-       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
-
-       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
-       efr &= ~MOXA_MUST_EFR_SF_RX_MASK;
-       efr |= MOXA_MUST_EFR_SF_RX1;
-
-       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
-       outb(oldlcr, baseio + UART_LCR);
-}
-
-static void mxser_disable_must_rx_software_flow_control(unsigned long baseio)
-{
-       u8 oldlcr;
-       u8 efr;
-
-       oldlcr = inb(baseio + UART_LCR);
-       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
-
-       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
-       efr &= ~MOXA_MUST_EFR_SF_RX_MASK;
-
-       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
-       outb(oldlcr, baseio + UART_LCR);
-}
-
-#ifdef CONFIG_PCI
-static int __devinit CheckIsMoxaMust(unsigned long io)
-{
-       u8 oldmcr, hwid;
-       int i;
-
-       outb(0, io + UART_LCR);
-       mxser_disable_must_enchance_mode(io);
-       oldmcr = inb(io + UART_MCR);
-       outb(0, io + UART_MCR);
-       mxser_set_must_xon1_value(io, 0x11);
-       if ((hwid = inb(io + UART_MCR)) != 0) {
-               outb(oldmcr, io + UART_MCR);
-               return MOXA_OTHER_UART;
-       }
-
-       mxser_get_must_hardware_id(io, &hwid);
-       for (i = 1; i < UART_INFO_NUM; i++) { /* 0 = OTHER_UART */
-               if (hwid == Gpci_uart_info[i].type)
-                       return (int)hwid;
-       }
-       return MOXA_OTHER_UART;
-}
-#endif
-
-static void process_txrx_fifo(struct mxser_port *info)
-{
-       int i;
-
-       if ((info->type == PORT_16450) || (info->type == PORT_8250)) {
-               info->rx_trigger = 1;
-               info->rx_high_water = 1;
-               info->rx_low_water = 1;
-               info->xmit_fifo_size = 1;
-       } else
-               for (i = 0; i < UART_INFO_NUM; i++)
-                       if (info->board->chip_flag == Gpci_uart_info[i].type) {
-                               info->rx_trigger = Gpci_uart_info[i].rx_trigger;
-                               info->rx_low_water = Gpci_uart_info[i].rx_low_water;
-                               info->rx_high_water = Gpci_uart_info[i].rx_high_water;
-                               info->xmit_fifo_size = Gpci_uart_info[i].xmit_fifo_size;
-                               break;
-                       }
-}
-
-static unsigned char mxser_get_msr(int baseaddr, int mode, int port)
-{
-       static unsigned char mxser_msr[MXSER_PORTS + 1];
-       unsigned char status = 0;
-
-       status = inb(baseaddr + UART_MSR);
-
-       mxser_msr[port] &= 0x0F;
-       mxser_msr[port] |= status;
-       status = mxser_msr[port];
-       if (mode)
-               mxser_msr[port] = 0;
-
-       return status;
-}
-
-static int mxser_carrier_raised(struct tty_port *port)
-{
-       struct mxser_port *mp = container_of(port, struct mxser_port, port);
-       return (inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD)?1:0;
-}
-
-static void mxser_dtr_rts(struct tty_port *port, int on)
-{
-       struct mxser_port *mp = container_of(port, struct mxser_port, port);
-       unsigned long flags;
-
-       spin_lock_irqsave(&mp->slock, flags);
-       if (on)
-               outb(inb(mp->ioaddr + UART_MCR) |
-                       UART_MCR_DTR | UART_MCR_RTS, mp->ioaddr + UART_MCR);
-       else
-               outb(inb(mp->ioaddr + UART_MCR)&~(UART_MCR_DTR | UART_MCR_RTS),
-                       mp->ioaddr + UART_MCR);
-       spin_unlock_irqrestore(&mp->slock, flags);
-}
-
-static int mxser_set_baud(struct tty_struct *tty, long newspd)
-{
-       struct mxser_port *info = tty->driver_data;
-       int quot = 0, baud;
-       unsigned char cval;
-
-       if (!info->ioaddr)
-               return -1;
-
-       if (newspd > info->max_baud)
-               return -1;
-
-       if (newspd == 134) {
-               quot = 2 * info->baud_base / 269;
-               tty_encode_baud_rate(tty, 134, 134);
-       } else if (newspd) {
-               quot = info->baud_base / newspd;
-               if (quot == 0)
-                       quot = 1;
-               baud = info->baud_base/quot;
-               tty_encode_baud_rate(tty, baud, baud);
-       } else {
-               quot = 0;
-       }
-
-       info->timeout = ((info->xmit_fifo_size * HZ * 10 * quot) / info->baud_base);
-       info->timeout += HZ / 50;       /* Add .02 seconds of slop */
-
-       if (quot) {
-               info->MCR |= UART_MCR_DTR;
-               outb(info->MCR, info->ioaddr + UART_MCR);
-       } else {
-               info->MCR &= ~UART_MCR_DTR;
-               outb(info->MCR, info->ioaddr + UART_MCR);
-               return 0;
-       }
-
-       cval = inb(info->ioaddr + UART_LCR);
-
-       outb(cval | UART_LCR_DLAB, info->ioaddr + UART_LCR);    /* set DLAB */
-
-       outb(quot & 0xff, info->ioaddr + UART_DLL);     /* LS of divisor */
-       outb(quot >> 8, info->ioaddr + UART_DLM);       /* MS of divisor */
-       outb(cval, info->ioaddr + UART_LCR);    /* reset DLAB */
-
-#ifdef BOTHER
-       if (C_BAUD(tty) == BOTHER) {
-               quot = info->baud_base % newspd;
-               quot *= 8;
-               if (quot % newspd > newspd / 2) {
-                       quot /= newspd;
-                       quot++;
-               } else
-                       quot /= newspd;
-
-               mxser_set_must_enum_value(info->ioaddr, quot);
-       } else
-#endif
-               mxser_set_must_enum_value(info->ioaddr, 0);
-
-       return 0;
-}
-
-/*
- * This routine is called to set the UART divisor registers to match
- * the specified baud rate for a serial port.
- */
-static int mxser_change_speed(struct tty_struct *tty,
-                                       struct ktermios *old_termios)
-{
-       struct mxser_port *info = tty->driver_data;
-       unsigned cflag, cval, fcr;
-       int ret = 0;
-       unsigned char status;
-
-       cflag = tty->termios->c_cflag;
-       if (!info->ioaddr)
-               return ret;
-
-       if (mxser_set_baud_method[tty->index] == 0)
-               mxser_set_baud(tty, tty_get_baud_rate(tty));
-
-       /* byte size and parity */
-       switch (cflag & CSIZE) {
-       case CS5:
-               cval = 0x00;
-               break;
-       case CS6:
-               cval = 0x01;
-               break;
-       case CS7:
-               cval = 0x02;
-               break;
-       case CS8:
-               cval = 0x03;
-               break;
-       default:
-               cval = 0x00;
-               break;          /* too keep GCC shut... */
-       }
-       if (cflag & CSTOPB)
-               cval |= 0x04;
-       if (cflag & PARENB)
-               cval |= UART_LCR_PARITY;
-       if (!(cflag & PARODD))
-               cval |= UART_LCR_EPAR;
-       if (cflag & CMSPAR)
-               cval |= UART_LCR_SPAR;
-
-       if ((info->type == PORT_8250) || (info->type == PORT_16450)) {
-               if (info->board->chip_flag) {
-                       fcr = UART_FCR_ENABLE_FIFO;
-                       fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE;
-                       mxser_set_must_fifo_value(info);
-               } else
-                       fcr = 0;
-       } else {
-               fcr = UART_FCR_ENABLE_FIFO;
-               if (info->board->chip_flag) {
-                       fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE;
-                       mxser_set_must_fifo_value(info);
-               } else {
-                       switch (info->rx_trigger) {
-                       case 1:
-                               fcr |= UART_FCR_TRIGGER_1;
-                               break;
-                       case 4:
-                               fcr |= UART_FCR_TRIGGER_4;
-                               break;
-                       case 8:
-                               fcr |= UART_FCR_TRIGGER_8;
-                               break;
-                       default:
-                               fcr |= UART_FCR_TRIGGER_14;
-                               break;
-                       }
-               }
-       }
-
-       /* CTS flow control flag and modem status interrupts */
-       info->IER &= ~UART_IER_MSI;
-       info->MCR &= ~UART_MCR_AFE;
-       if (cflag & CRTSCTS) {
-               info->port.flags |= ASYNC_CTS_FLOW;
-               info->IER |= UART_IER_MSI;
-               if ((info->type == PORT_16550A) || (info->board->chip_flag)) {
-                       info->MCR |= UART_MCR_AFE;
-               } else {
-                       status = inb(info->ioaddr + UART_MSR);
-                       if (tty->hw_stopped) {
-                               if (status & UART_MSR_CTS) {
-                                       tty->hw_stopped = 0;
-                                       if (info->type != PORT_16550A &&
-                                                       !info->board->chip_flag) {
-                                               outb(info->IER & ~UART_IER_THRI,
-                                                       info->ioaddr +
-                                                       UART_IER);
-                                               info->IER |= UART_IER_THRI;
-                                               outb(info->IER, info->ioaddr +
-                                                               UART_IER);
-                                       }
-                                       tty_wakeup(tty);
-                               }
-                       } else {
-                               if (!(status & UART_MSR_CTS)) {
-                                       tty->hw_stopped = 1;
-                                       if ((info->type != PORT_16550A) &&
-                                                       (!info->board->chip_flag)) {
-                                               info->IER &= ~UART_IER_THRI;
-                                               outb(info->IER, info->ioaddr +
-                                                               UART_IER);
-                                       }
-                               }
-                       }
-               }
-       } else {
-               info->port.flags &= ~ASYNC_CTS_FLOW;
-       }
-       outb(info->MCR, info->ioaddr + UART_MCR);
-       if (cflag & CLOCAL) {
-               info->port.flags &= ~ASYNC_CHECK_CD;
-       } else {
-               info->port.flags |= ASYNC_CHECK_CD;
-               info->IER |= UART_IER_MSI;
-       }
-       outb(info->IER, info->ioaddr + UART_IER);
-
-       /*
-        * Set up parity check flag
-        */
-       info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
-       if (I_INPCK(tty))
-               info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
-       if (I_BRKINT(tty) || I_PARMRK(tty))
-               info->read_status_mask |= UART_LSR_BI;
-
-       info->ignore_status_mask = 0;
-
-       if (I_IGNBRK(tty)) {
-               info->ignore_status_mask |= UART_LSR_BI;
-               info->read_status_mask |= UART_LSR_BI;
-               /*
-                * If we're ignore parity and break indicators, ignore
-                * overruns too.  (For real raw support).
-                */
-               if (I_IGNPAR(tty)) {
-                       info->ignore_status_mask |=
-                                               UART_LSR_OE |
-                                               UART_LSR_PE |
-                                               UART_LSR_FE;
-                       info->read_status_mask |=
-                                               UART_LSR_OE |
-                                               UART_LSR_PE |
-                                               UART_LSR_FE;
-               }
-       }
-       if (info->board->chip_flag) {
-               mxser_set_must_xon1_value(info->ioaddr, START_CHAR(tty));
-               mxser_set_must_xoff1_value(info->ioaddr, STOP_CHAR(tty));
-               if (I_IXON(tty)) {
-                       mxser_enable_must_rx_software_flow_control(
-                                       info->ioaddr);
-               } else {
-                       mxser_disable_must_rx_software_flow_control(
-                                       info->ioaddr);
-               }
-               if (I_IXOFF(tty)) {
-                       mxser_enable_must_tx_software_flow_control(
-                                       info->ioaddr);
-               } else {
-                       mxser_disable_must_tx_software_flow_control(
-                                       info->ioaddr);
-               }
-       }
-
-
-       outb(fcr, info->ioaddr + UART_FCR);     /* set fcr */
-       outb(cval, info->ioaddr + UART_LCR);
-
-       return ret;
-}
-
-static void mxser_check_modem_status(struct tty_struct *tty,
-                               struct mxser_port *port, int status)
-{
-       /* update input line counters */
-       if (status & UART_MSR_TERI)
-               port->icount.rng++;
-       if (status & UART_MSR_DDSR)
-               port->icount.dsr++;
-       if (status & UART_MSR_DDCD)
-               port->icount.dcd++;
-       if (status & UART_MSR_DCTS)
-               port->icount.cts++;
-       port->mon_data.modem_status = status;
-       wake_up_interruptible(&port->port.delta_msr_wait);
-
-       if ((port->port.flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
-               if (status & UART_MSR_DCD)
-                       wake_up_interruptible(&port->port.open_wait);
-       }
-
-       if (port->port.flags & ASYNC_CTS_FLOW) {
-               if (tty->hw_stopped) {
-                       if (status & UART_MSR_CTS) {
-                               tty->hw_stopped = 0;
-
-                               if ((port->type != PORT_16550A) &&
-                                               (!port->board->chip_flag)) {
-                                       outb(port->IER & ~UART_IER_THRI,
-                                               port->ioaddr + UART_IER);
-                                       port->IER |= UART_IER_THRI;
-                                       outb(port->IER, port->ioaddr +
-                                                       UART_IER);
-                               }
-                               tty_wakeup(tty);
-                       }
-               } else {
-                       if (!(status & UART_MSR_CTS)) {
-                               tty->hw_stopped = 1;
-                               if (port->type != PORT_16550A &&
-                                               !port->board->chip_flag) {
-                                       port->IER &= ~UART_IER_THRI;
-                                       outb(port->IER, port->ioaddr +
-                                                       UART_IER);
-                               }
-                       }
-               }
-       }
-}
-
-static int mxser_activate(struct tty_port *port, struct tty_struct *tty)
-{
-       struct mxser_port *info = container_of(port, struct mxser_port, port);
-       unsigned long page;
-       unsigned long flags;
-
-       page = __get_free_page(GFP_KERNEL);
-       if (!page)
-               return -ENOMEM;
-
-       spin_lock_irqsave(&info->slock, flags);
-
-       if (!info->ioaddr || !info->type) {
-               set_bit(TTY_IO_ERROR, &tty->flags);
-               free_page(page);
-               spin_unlock_irqrestore(&info->slock, flags);
-               return 0;
-       }
-       info->port.xmit_buf = (unsigned char *) page;
-
-       /*
-        * Clear the FIFO buffers and disable them
-        * (they will be reenabled in mxser_change_speed())
-        */
-       if (info->board->chip_flag)
-               outb((UART_FCR_CLEAR_RCVR |
-                       UART_FCR_CLEAR_XMIT |
-                       MOXA_MUST_FCR_GDA_MODE_ENABLE), info->ioaddr + UART_FCR);
-       else
-               outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT),
-                       info->ioaddr + UART_FCR);
-
-       /*
-        * At this point there's no way the LSR could still be 0xFF;
-        * if it is, then bail out, because there's likely no UART
-        * here.
-        */
-       if (inb(info->ioaddr + UART_LSR) == 0xff) {
-               spin_unlock_irqrestore(&info->slock, flags);
-               if (capable(CAP_SYS_ADMIN)) {
-                       set_bit(TTY_IO_ERROR, &tty->flags);
-                       return 0;
-               } else
-                       return -ENODEV;
-       }
-
-       /*
-        * Clear the interrupt registers.
-        */
-       (void) inb(info->ioaddr + UART_LSR);
-       (void) inb(info->ioaddr + UART_RX);
-       (void) inb(info->ioaddr + UART_IIR);
-       (void) inb(info->ioaddr + UART_MSR);
-
-       /*
-        * Now, initialize the UART
-        */
-       outb(UART_LCR_WLEN8, info->ioaddr + UART_LCR);  /* reset DLAB */
-       info->MCR = UART_MCR_DTR | UART_MCR_RTS;
-       outb(info->MCR, info->ioaddr + UART_MCR);
-
-       /*
-        * Finally, enable interrupts
-        */
-       info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
-
-       if (info->board->chip_flag)
-               info->IER |= MOXA_MUST_IER_EGDAI;
-       outb(info->IER, info->ioaddr + UART_IER);       /* enable interrupts */
-
-       /*
-        * And clear the interrupt registers again for luck.
-        */
-       (void) inb(info->ioaddr + UART_LSR);
-       (void) inb(info->ioaddr + UART_RX);
-       (void) inb(info->ioaddr + UART_IIR);
-       (void) inb(info->ioaddr + UART_MSR);
-
-       clear_bit(TTY_IO_ERROR, &tty->flags);
-       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-
-       /*
-        * and set the speed of the serial port
-        */
-       mxser_change_speed(tty, NULL);
-       spin_unlock_irqrestore(&info->slock, flags);
-
-       return 0;
-}
-
-/*
- * This routine will shutdown a serial port
- */
-static void mxser_shutdown_port(struct tty_port *port)
-{
-       struct mxser_port *info = container_of(port, struct mxser_port, port);
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->slock, flags);
-
-       /*
-        * 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(&info->port.delta_msr_wait);
-
-       /*
-        * Free the xmit buffer, if necessary
-        */
-       if (info->port.xmit_buf) {
-               free_page((unsigned long) info->port.xmit_buf);
-               info->port.xmit_buf = NULL;
-       }
-
-       info->IER = 0;
-       outb(0x00, info->ioaddr + UART_IER);
-
-       /* clear Rx/Tx FIFO's */
-       if (info->board->chip_flag)
-               outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT |
-                               MOXA_MUST_FCR_GDA_MODE_ENABLE,
-                               info->ioaddr + UART_FCR);
-       else
-               outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
-                       info->ioaddr + UART_FCR);
-
-       /* read data port to reset things */
-       (void) inb(info->ioaddr + UART_RX);
-
-
-       if (info->board->chip_flag)
-               SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(info->ioaddr);
-
-       spin_unlock_irqrestore(&info->slock, flags);
-}
-
-/*
- * 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 mxser_open(struct tty_struct *tty, struct file *filp)
-{
-       struct mxser_port *info;
-       int line;
-
-       line = tty->index;
-       if (line == MXSER_PORTS)
-               return 0;
-       if (line < 0 || line > MXSER_PORTS)
-               return -ENODEV;
-       info = &mxser_boards[line / MXSER_PORTS_PER_BOARD].ports[line % MXSER_PORTS_PER_BOARD];
-       if (!info->ioaddr)
-               return -ENODEV;
-
-       tty->driver_data = info;
-       return tty_port_open(&info->port, tty, filp);
-}
-
-static void mxser_flush_buffer(struct tty_struct *tty)
-{
-       struct mxser_port *info = tty->driver_data;
-       char fcr;
-       unsigned long flags;
-
-
-       spin_lock_irqsave(&info->slock, flags);
-       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-
-       fcr = inb(info->ioaddr + UART_FCR);
-       outb((fcr | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT),
-               info->ioaddr + UART_FCR);
-       outb(fcr, info->ioaddr + UART_FCR);
-
-       spin_unlock_irqrestore(&info->slock, flags);
-
-       tty_wakeup(tty);
-}
-
-
-static void mxser_close_port(struct tty_port *port)
-{
-       struct mxser_port *info = container_of(port, struct mxser_port, port);
-       unsigned long timeout;
-       /*
-        * At this point we stop accepting input.  To do this, we
-        * disable the receive line status interrupts, and tell the
-        * interrupt driver to stop checking the data ready bit in the
-        * line status register.
-        */
-       info->IER &= ~UART_IER_RLSI;
-       if (info->board->chip_flag)
-               info->IER &= ~MOXA_MUST_RECV_ISR;
-
-       outb(info->IER, info->ioaddr + UART_IER);
-       /*
-        * Before we drop DTR, make sure the UART transmitter
-        * has completely drained; this is especially
-        * important if there is a transmit FIFO!
-        */
-       timeout = jiffies + HZ;
-       while (!(inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT)) {
-               schedule_timeout_interruptible(5);
-               if (time_after(jiffies, timeout))
-                       break;
-       }
-}
-
-/*
- * 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 mxser_close(struct tty_struct *tty, struct file *filp)
-{
-       struct mxser_port *info = tty->driver_data;
-       struct tty_port *port = &info->port;
-
-       if (tty->index == MXSER_PORTS || info == NULL)
-               return;
-       if (tty_port_close_start(port, tty, filp) == 0)
-               return;
-       mutex_lock(&port->mutex);
-       mxser_close_port(port);
-       mxser_flush_buffer(tty);
-       mxser_shutdown_port(port);
-       clear_bit(ASYNCB_INITIALIZED, &port->flags);
-       mutex_unlock(&port->mutex);
-       /* Right now the tty_port set is done outside of the close_end helper
-          as we don't yet have everyone using refcounts */     
-       tty_port_close_end(port, tty);
-       tty_port_tty_set(port, NULL);
-}
-
-static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int count)
-{
-       int c, total = 0;
-       struct mxser_port *info = tty->driver_data;
-       unsigned long flags;
-
-       if (!info->port.xmit_buf)
-               return 0;
-
-       while (1) {
-               c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-                                         SERIAL_XMIT_SIZE - info->xmit_head));
-               if (c <= 0)
-                       break;
-
-               memcpy(info->port.xmit_buf + info->xmit_head, buf, c);
-               spin_lock_irqsave(&info->slock, flags);
-               info->xmit_head = (info->xmit_head + c) &
-                                 (SERIAL_XMIT_SIZE - 1);
-               info->xmit_cnt += c;
-               spin_unlock_irqrestore(&info->slock, flags);
-
-               buf += c;
-               count -= c;
-               total += c;
-       }
-
-       if (info->xmit_cnt && !tty->stopped) {
-               if (!tty->hw_stopped ||
-                               (info->type == PORT_16550A) ||
-                               (info->board->chip_flag)) {
-                       spin_lock_irqsave(&info->slock, flags);
-                       outb(info->IER & ~UART_IER_THRI, info->ioaddr +
-                                       UART_IER);
-                       info->IER |= UART_IER_THRI;
-                       outb(info->IER, info->ioaddr + UART_IER);
-                       spin_unlock_irqrestore(&info->slock, flags);
-               }
-       }
-       return total;
-}
-
-static int mxser_put_char(struct tty_struct *tty, unsigned char ch)
-{
-       struct mxser_port *info = tty->driver_data;
-       unsigned long flags;
-
-       if (!info->port.xmit_buf)
-               return 0;
-
-       if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1)
-               return 0;
-
-       spin_lock_irqsave(&info->slock, flags);
-       info->port.xmit_buf[info->xmit_head++] = ch;
-       info->xmit_head &= SERIAL_XMIT_SIZE - 1;
-       info->xmit_cnt++;
-       spin_unlock_irqrestore(&info->slock, flags);
-       if (!tty->stopped) {
-               if (!tty->hw_stopped ||
-                               (info->type == PORT_16550A) ||
-                               info->board->chip_flag) {
-                       spin_lock_irqsave(&info->slock, flags);
-                       outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER);
-                       info->IER |= UART_IER_THRI;
-                       outb(info->IER, info->ioaddr + UART_IER);
-                       spin_unlock_irqrestore(&info->slock, flags);
-               }
-       }
-       return 1;
-}
-
-
-static void mxser_flush_chars(struct tty_struct *tty)
-{
-       struct mxser_port *info = tty->driver_data;
-       unsigned long flags;
-
-       if (info->xmit_cnt <= 0 || tty->stopped || !info->port.xmit_buf ||
-                       (tty->hw_stopped && info->type != PORT_16550A &&
-                        !info->board->chip_flag))
-               return;
-
-       spin_lock_irqsave(&info->slock, flags);
-
-       outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER);
-       info->IER |= UART_IER_THRI;
-       outb(info->IER, info->ioaddr + UART_IER);
-
-       spin_unlock_irqrestore(&info->slock, flags);
-}
-
-static int mxser_write_room(struct tty_struct *tty)
-{
-       struct mxser_port *info = tty->driver_data;
-       int ret;
-
-       ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
-       return ret < 0 ? 0 : ret;
-}
-
-static int mxser_chars_in_buffer(struct tty_struct *tty)
-{
-       struct mxser_port *info = tty->driver_data;
-       return info->xmit_cnt;
-}
-
-/*
- * ------------------------------------------------------------
- * friends of mxser_ioctl()
- * ------------------------------------------------------------
- */
-static int mxser_get_serial_info(struct tty_struct *tty,
-               struct serial_struct __user *retinfo)
-{
-       struct mxser_port *info = tty->driver_data;
-       struct serial_struct tmp = {
-               .type = info->type,
-               .line = tty->index,
-               .port = info->ioaddr,
-               .irq = info->board->irq,
-               .flags = info->port.flags,
-               .baud_base = info->baud_base,
-               .close_delay = info->port.close_delay,
-               .closing_wait = info->port.closing_wait,
-               .custom_divisor = info->custom_divisor,
-               .hub6 = 0
-       };
-       if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
-               return -EFAULT;
-       return 0;
-}
-
-static int mxser_set_serial_info(struct tty_struct *tty,
-               struct serial_struct __user *new_info)
-{
-       struct mxser_port *info = tty->driver_data;
-       struct tty_port *port = &info->port;
-       struct serial_struct new_serial;
-       speed_t baud;
-       unsigned long sl_flags;
-       unsigned int flags;
-       int retval = 0;
-
-       if (!new_info || !info->ioaddr)
-               return -ENODEV;
-       if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
-               return -EFAULT;
-
-       if (new_serial.irq != info->board->irq ||
-                       new_serial.port != info->ioaddr)
-               return -EINVAL;
-
-       flags = port->flags & ASYNC_SPD_MASK;
-
-       if (!capable(CAP_SYS_ADMIN)) {
-               if ((new_serial.baud_base != info->baud_base) ||
-                               (new_serial.close_delay != info->port.close_delay) ||
-                               ((new_serial.flags & ~ASYNC_USR_MASK) != (info->port.flags & ~ASYNC_USR_MASK)))
-                       return -EPERM;
-               info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) |
-                               (new_serial.flags & ASYNC_USR_MASK));
-       } else {
-               /*
-                * OK, past this point, all the error checking has been done.
-                * At this point, we start making changes.....
-                */
-               port->flags = ((port->flags & ~ASYNC_FLAGS) |
-                               (new_serial.flags & ASYNC_FLAGS));
-               port->close_delay = new_serial.close_delay * HZ / 100;
-               port->closing_wait = new_serial.closing_wait * HZ / 100;
-               tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-               if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
-                               (new_serial.baud_base != info->baud_base ||
-                               new_serial.custom_divisor !=
-                               info->custom_divisor)) {
-                       if (new_serial.custom_divisor == 0)
-                               return -EINVAL;
-                       baud = new_serial.baud_base / new_serial.custom_divisor;
-                       tty_encode_baud_rate(tty, baud, baud);
-               }
-       }
-
-       info->type = new_serial.type;
-
-       process_txrx_fifo(info);
-
-       if (test_bit(ASYNCB_INITIALIZED, &port->flags)) {
-               if (flags != (port->flags & ASYNC_SPD_MASK)) {
-                       spin_lock_irqsave(&info->slock, sl_flags);
-                       mxser_change_speed(tty, NULL);
-                       spin_unlock_irqrestore(&info->slock, sl_flags);
-               }
-       } else {
-               retval = mxser_activate(port, tty);
-               if (retval == 0)
-                       set_bit(ASYNCB_INITIALIZED, &port->flags);
-       }
-       return retval;
-}
-
-/*
- * mxser_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 mxser_get_lsr_info(struct mxser_port *info,
-               unsigned int __user *value)
-{
-       unsigned char status;
-       unsigned int result;
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->slock, flags);
-       status = inb(info->ioaddr + UART_LSR);
-       spin_unlock_irqrestore(&info->slock, flags);
-       result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
-       return put_user(result, value);
-}
-
-static int mxser_tiocmget(struct tty_struct *tty)
-{
-       struct mxser_port *info = tty->driver_data;
-       unsigned char control, status;
-       unsigned long flags;
-
-
-       if (tty->index == MXSER_PORTS)
-               return -ENOIOCTLCMD;
-       if (test_bit(TTY_IO_ERROR, &tty->flags))
-               return -EIO;
-
-       control = info->MCR;
-
-       spin_lock_irqsave(&info->slock, flags);
-       status = inb(info->ioaddr + UART_MSR);
-       if (status & UART_MSR_ANY_DELTA)
-               mxser_check_modem_status(tty, info, status);
-       spin_unlock_irqrestore(&info->slock, flags);
-       return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) |
-                   ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) |
-                   ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) |
-                   ((status & UART_MSR_RI) ? TIOCM_RNG : 0) |
-                   ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) |
-                   ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
-}
-
-static int mxser_tiocmset(struct tty_struct *tty,
-               unsigned int set, unsigned int clear)
-{
-       struct mxser_port *info = tty->driver_data;
-       unsigned long flags;
-
-
-       if (tty->index == MXSER_PORTS)
-               return -ENOIOCTLCMD;
-       if (test_bit(TTY_IO_ERROR, &tty->flags))
-               return -EIO;
-
-       spin_lock_irqsave(&info->slock, flags);
-
-       if (set & TIOCM_RTS)
-               info->MCR |= UART_MCR_RTS;
-       if (set & TIOCM_DTR)
-               info->MCR |= UART_MCR_DTR;
-
-       if (clear & TIOCM_RTS)
-               info->MCR &= ~UART_MCR_RTS;
-       if (clear & TIOCM_DTR)
-               info->MCR &= ~UART_MCR_DTR;
-
-       outb(info->MCR, info->ioaddr + UART_MCR);
-       spin_unlock_irqrestore(&info->slock, flags);
-       return 0;
-}
-
-static int __init mxser_program_mode(int port)
-{
-       int id, i, j, n;
-
-       outb(0, port);
-       outb(0, port);
-       outb(0, port);
-       (void)inb(port);
-       (void)inb(port);
-       outb(0, port);
-       (void)inb(port);
-
-       id = inb(port + 1) & 0x1F;
-       if ((id != C168_ASIC_ID) &&
-                       (id != C104_ASIC_ID) &&
-                       (id != C102_ASIC_ID) &&
-                       (id != CI132_ASIC_ID) &&
-                       (id != CI134_ASIC_ID) &&
-                       (id != CI104J_ASIC_ID))
-               return -1;
-       for (i = 0, j = 0; i < 4; i++) {
-               n = inb(port + 2);
-               if (n == 'M') {
-                       j = 1;
-               } else if ((j == 1) && (n == 1)) {
-                       j = 2;
-                       break;
-               } else
-                       j = 0;
-       }
-       if (j != 2)
-               id = -2;
-       return id;
-}
-
-static void __init mxser_normal_mode(int port)
-{
-       int i, n;
-
-       outb(0xA5, port + 1);
-       outb(0x80, port + 3);
-       outb(12, port + 0);     /* 9600 bps */
-       outb(0, port + 1);
-       outb(0x03, port + 3);   /* 8 data bits */
-       outb(0x13, port + 4);   /* loop back mode */
-       for (i = 0; i < 16; i++) {
-               n = inb(port + 5);
-               if ((n & 0x61) == 0x60)
-                       break;
-               if ((n & 1) == 1)
-                       (void)inb(port);
-       }
-       outb(0x00, port + 4);
-}
-
-#define CHIP_SK        0x01    /* Serial Data Clock  in Eprom */
-#define CHIP_DO        0x02    /* Serial Data Output in Eprom */
-#define CHIP_CS        0x04    /* Serial Chip Select in Eprom */
-#define CHIP_DI        0x08    /* Serial Data Input  in Eprom */
-#define EN_CCMD        0x000   /* Chip's command register     */
-#define EN0_RSARLO     0x008   /* Remote start address reg 0  */
-#define EN0_RSARHI     0x009   /* Remote start address reg 1  */
-#define EN0_RCNTLO     0x00A   /* Remote byte count reg WR    */
-#define EN0_RCNTHI     0x00B   /* Remote byte count reg WR    */
-#define EN0_DCFG       0x00E   /* Data configuration reg WR   */
-#define EN0_PORT       0x010   /* Rcv missed frame error counter RD */
-#define ENC_PAGE0      0x000   /* Select page 0 of chip registers   */
-#define ENC_PAGE3      0x0C0   /* Select page 3 of chip registers   */
-static int __init mxser_read_register(int port, unsigned short *regs)
-{
-       int i, k, value, id;
-       unsigned int j;
-
-       id = mxser_program_mode(port);
-       if (id < 0)
-               return id;
-       for (i = 0; i < 14; i++) {
-               k = (i & 0x3F) | 0x180;
-               for (j = 0x100; j > 0; j >>= 1) {
-                       outb(CHIP_CS, port);
-                       if (k & j) {
-                               outb(CHIP_CS | CHIP_DO, port);
-                               outb(CHIP_CS | CHIP_DO | CHIP_SK, port);        /* A? bit of read */
-                       } else {
-                               outb(CHIP_CS, port);
-                               outb(CHIP_CS | CHIP_SK, port);  /* A? bit of read */
-                       }
-               }
-               (void)inb(port);
-               value = 0;
-               for (k = 0, j = 0x8000; k < 16; k++, j >>= 1) {
-                       outb(CHIP_CS, port);
-                       outb(CHIP_CS | CHIP_SK, port);
-                       if (inb(port) & CHIP_DI)
-                               value |= j;
-               }
-               regs[i] = value;
-               outb(0, port);
-       }
-       mxser_normal_mode(port);
-       return id;
-}
-
-static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
-{
-       struct mxser_port *ip;
-       struct tty_port *port;
-       struct tty_struct *tty;
-       int result, status;
-       unsigned int i, j;
-       int ret = 0;
-
-       switch (cmd) {
-       case MOXA_GET_MAJOR:
-               if (printk_ratelimit())
-                       printk(KERN_WARNING "mxser: '%s' uses deprecated ioctl "
-                                       "%x (GET_MAJOR), fix your userspace\n",
-                                       current->comm, cmd);
-               return put_user(ttymajor, (int __user *)argp);
-
-       case MOXA_CHKPORTENABLE:
-               result = 0;
-               for (i = 0; i < MXSER_BOARDS; i++)
-                       for (j = 0; j < MXSER_PORTS_PER_BOARD; j++)
-                               if (mxser_boards[i].ports[j].ioaddr)
-                                       result |= (1 << i);
-               return put_user(result, (unsigned long __user *)argp);
-       case MOXA_GETDATACOUNT:
-               /* The receive side is locked by port->slock but it isn't
-                  clear that an exact snapshot is worth copying here */
-               if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log)))
-                       ret = -EFAULT;
-               return ret;
-       case MOXA_GETMSTATUS: {
-               struct mxser_mstatus ms, __user *msu = argp;
-               for (i = 0; i < MXSER_BOARDS; i++)
-                       for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) {
-                               ip = &mxser_boards[i].ports[j];
-                               port = &ip->port;
-                               memset(&ms, 0, sizeof(ms));
-
-                               mutex_lock(&port->mutex);
-                               if (!ip->ioaddr)
-                                       goto copy;
-                               
-                               tty = tty_port_tty_get(port);
-
-                               if (!tty || !tty->termios)
-                                       ms.cflag = ip->normal_termios.c_cflag;
-                               else
-                                       ms.cflag = tty->termios->c_cflag;
-                               tty_kref_put(tty);
-                               spin_lock_irq(&ip->slock);
-                               status = inb(ip->ioaddr + UART_MSR);
-                               spin_unlock_irq(&ip->slock);
-                               if (status & UART_MSR_DCD)
-                                       ms.dcd = 1;
-                               if (status & UART_MSR_DSR)
-                                       ms.dsr = 1;
-                               if (status & UART_MSR_CTS)
-                                       ms.cts = 1;
-                       copy:
-                               mutex_unlock(&port->mutex);
-                               if (copy_to_user(msu, &ms, sizeof(ms)))
-                                       return -EFAULT;
-                               msu++;
-                       }
-               return 0;
-       }
-       case MOXA_ASPP_MON_EXT: {
-               struct mxser_mon_ext *me; /* it's 2k, stack unfriendly */
-               unsigned int cflag, iflag, p;
-               u8 opmode;
-
-               me = kzalloc(sizeof(*me), GFP_KERNEL);
-               if (!me)
-                       return -ENOMEM;
-
-               for (i = 0, p = 0; i < MXSER_BOARDS; i++) {
-                       for (j = 0; j < MXSER_PORTS_PER_BOARD; j++, p++) {
-                               if (p >= ARRAY_SIZE(me->rx_cnt)) {
-                                       i = MXSER_BOARDS;
-                                       break;
-                               }
-                               ip = &mxser_boards[i].ports[j];
-                               port = &ip->port;
-
-                               mutex_lock(&port->mutex);
-                               if (!ip->ioaddr) {
-                                       mutex_unlock(&port->mutex);
-                                       continue;
-                               }
-
-                               spin_lock_irq(&ip->slock);
-                               status = mxser_get_msr(ip->ioaddr, 0, p);
-
-                               if (status & UART_MSR_TERI)
-                                       ip->icount.rng++;
-                               if (status & UART_MSR_DDSR)
-                                       ip->icount.dsr++;
-                               if (status & UART_MSR_DDCD)
-                                       ip->icount.dcd++;
-                               if (status & UART_MSR_DCTS)
-                                       ip->icount.cts++;
-
-                               ip->mon_data.modem_status = status;
-                               me->rx_cnt[p] = ip->mon_data.rxcnt;
-                               me->tx_cnt[p] = ip->mon_data.txcnt;
-                               me->up_rxcnt[p] = ip->mon_data.up_rxcnt;
-                               me->up_txcnt[p] = ip->mon_data.up_txcnt;
-                               me->modem_status[p] =
-                                       ip->mon_data.modem_status;
-                               spin_unlock_irq(&ip->slock);
-
-                               tty = tty_port_tty_get(&ip->port);
-
-                               if (!tty || !tty->termios) {
-                                       cflag = ip->normal_termios.c_cflag;
-                                       iflag = ip->normal_termios.c_iflag;
-                                       me->baudrate[p] = tty_termios_baud_rate(&ip->normal_termios);
-                               } else {
-                                       cflag = tty->termios->c_cflag;
-                                       iflag = tty->termios->c_iflag;
-                                       me->baudrate[p] = tty_get_baud_rate(tty);
-                               }
-                               tty_kref_put(tty);
-
-                               me->databits[p] = cflag & CSIZE;
-                               me->stopbits[p] = cflag & CSTOPB;
-                               me->parity[p] = cflag & (PARENB | PARODD |
-                                               CMSPAR);
-
-                               if (cflag & CRTSCTS)
-                                       me->flowctrl[p] |= 0x03;
-
-                               if (iflag & (IXON | IXOFF))
-                                       me->flowctrl[p] |= 0x0C;
-
-                               if (ip->type == PORT_16550A)
-                                       me->fifo[p] = 1;
-
-                               opmode = inb(ip->opmode_ioaddr)>>((p % 4) * 2);
-                               opmode &= OP_MODE_MASK;
-                               me->iftype[p] = opmode;
-                               mutex_unlock(&port->mutex);
-                       }
-               }
-               if (copy_to_user(argp, me, sizeof(*me)))
-                       ret = -EFAULT;
-               kfree(me);
-               return ret;
-       }
-       default:
-               return -ENOIOCTLCMD;
-       }
-       return 0;
-}
-
-static int mxser_cflags_changed(struct mxser_port *info, unsigned long arg,
-               struct async_icount *cprev)
-{
-       struct async_icount cnow;
-       unsigned long flags;
-       int ret;
-
-       spin_lock_irqsave(&info->slock, flags);
-       cnow = info->icount;    /* atomic copy */
-       spin_unlock_irqrestore(&info->slock, flags);
-
-       ret =   ((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));
-
-       *cprev = cnow;
-
-       return ret;
-}
-
-static int mxser_ioctl(struct tty_struct *tty,
-               unsigned int cmd, unsigned long arg)
-{
-       struct mxser_port *info = tty->driver_data;
-       struct tty_port *port = &info->port;
-       struct async_icount cnow;
-       unsigned long flags;
-       void __user *argp = (void __user *)arg;
-       int retval;
-
-       if (tty->index == MXSER_PORTS)
-               return mxser_ioctl_special(cmd, argp);
-
-       if (cmd == MOXA_SET_OP_MODE || cmd == MOXA_GET_OP_MODE) {
-               int p;
-               unsigned long opmode;
-               static unsigned char ModeMask[] = { 0xfc, 0xf3, 0xcf, 0x3f };
-               int shiftbit;
-               unsigned char val, mask;
-
-               p = tty->index % 4;
-               if (cmd == MOXA_SET_OP_MODE) {
-                       if (get_user(opmode, (int __user *) argp))
-                               return -EFAULT;
-                       if (opmode != RS232_MODE &&
-                                       opmode != RS485_2WIRE_MODE &&
-                                       opmode != RS422_MODE &&
-                                       opmode != RS485_4WIRE_MODE)
-                               return -EFAULT;
-                       mask = ModeMask[p];
-                       shiftbit = p * 2;
-                       spin_lock_irq(&info->slock);
-                       val = inb(info->opmode_ioaddr);
-                       val &= mask;
-                       val |= (opmode << shiftbit);
-                       outb(val, info->opmode_ioaddr);
-                       spin_unlock_irq(&info->slock);
-               } else {
-                       shiftbit = p * 2;
-                       spin_lock_irq(&info->slock);
-                       opmode = inb(info->opmode_ioaddr) >> shiftbit;
-                       spin_unlock_irq(&info->slock);
-                       opmode &= OP_MODE_MASK;
-                       if (put_user(opmode, (int __user *)argp))
-                               return -EFAULT;
-               }
-               return 0;
-       }
-
-       if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT &&
-                       test_bit(TTY_IO_ERROR, &tty->flags))
-               return -EIO;
-
-       switch (cmd) {
-       case TIOCGSERIAL:
-               mutex_lock(&port->mutex);
-               retval = mxser_get_serial_info(tty, argp);
-               mutex_unlock(&port->mutex);
-               return retval;
-       case TIOCSSERIAL:
-               mutex_lock(&port->mutex);
-               retval = mxser_set_serial_info(tty, argp);
-               mutex_unlock(&port->mutex);
-               return retval;
-       case TIOCSERGETLSR:     /* Get line status register */
-               return  mxser_get_lsr_info(info, argp);
-               /*
-                * 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:
-               spin_lock_irqsave(&info->slock, flags);
-               cnow = info->icount;    /* note the counters on entry */
-               spin_unlock_irqrestore(&info->slock, flags);
-
-               return wait_event_interruptible(info->port.delta_msr_wait,
-                               mxser_cflags_changed(info, arg, &cnow));
-       case MOXA_HighSpeedOn:
-               return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp);
-       case MOXA_SDS_RSTICOUNTER:
-               spin_lock_irq(&info->slock);
-               info->mon_data.rxcnt = 0;
-               info->mon_data.txcnt = 0;
-               spin_unlock_irq(&info->slock);
-               return 0;
-
-       case MOXA_ASPP_OQUEUE:{
-               int len, lsr;
-
-               len = mxser_chars_in_buffer(tty);
-               spin_lock_irq(&info->slock);
-               lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_THRE;
-               spin_unlock_irq(&info->slock);
-               len += (lsr ? 0 : 1);
-
-               return put_user(len, (int __user *)argp);
-       }
-       case MOXA_ASPP_MON: {
-               int mcr, status;
-
-               spin_lock_irq(&info->slock);
-               status = mxser_get_msr(info->ioaddr, 1, tty->index);
-               mxser_check_modem_status(tty, info, status);
-
-               mcr = inb(info->ioaddr + UART_MCR);
-               spin_unlock_irq(&info->slock);
-
-               if (mcr & MOXA_MUST_MCR_XON_FLAG)
-                       info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFHOLD;
-               else
-                       info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFHOLD;
-
-               if (mcr & MOXA_MUST_MCR_TX_XON)
-                       info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFXENT;
-               else
-                       info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFXENT;
-
-               if (tty->hw_stopped)
-                       info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD;
-               else
-                       info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD;
-
-               if (copy_to_user(argp, &info->mon_data,
-                               sizeof(struct mxser_mon)))
-                       return -EFAULT;
-
-               return 0;
-       }
-       case MOXA_ASPP_LSTATUS: {
-               if (put_user(info->err_shadow, (unsigned char __user *)argp))
-                       return -EFAULT;
-
-               info->err_shadow = 0;
-               return 0;
-       }
-       case MOXA_SET_BAUD_METHOD: {
-               int method;
-
-               if (get_user(method, (int __user *)argp))
-                       return -EFAULT;
-               mxser_set_baud_method[tty->index] = method;
-               return put_user(method, (int __user *)argp);
-       }
-       default:
-               return -ENOIOCTLCMD;
-       }
-       return 0;
-}
-
-       /*
-        * 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.
-        */
-
-static int mxser_get_icount(struct tty_struct *tty,
-               struct serial_icounter_struct *icount)
-
-{
-       struct mxser_port *info = tty->driver_data;
-       struct async_icount cnow;
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->slock, flags);
-       cnow = info->icount;
-       spin_unlock_irqrestore(&info->slock, flags);
-
-       icount->frame = cnow.frame;
-       icount->brk = cnow.brk;
-       icount->overrun = cnow.overrun;
-       icount->buf_overrun = cnow.buf_overrun;
-       icount->parity = cnow.parity;
-       icount->rx = cnow.rx;
-       icount->tx = cnow.tx;
-       icount->cts = cnow.cts;
-       icount->dsr = cnow.dsr;
-       icount->rng = cnow.rng;
-       icount->dcd = cnow.dcd;
-       return 0;
-}
-
-static void mxser_stoprx(struct tty_struct *tty)
-{
-       struct mxser_port *info = tty->driver_data;
-
-       info->ldisc_stop_rx = 1;
-       if (I_IXOFF(tty)) {
-               if (info->board->chip_flag) {
-                       info->IER &= ~MOXA_MUST_RECV_ISR;
-                       outb(info->IER, info->ioaddr + UART_IER);
-               } else {
-                       info->x_char = STOP_CHAR(tty);
-                       outb(0, info->ioaddr + UART_IER);
-                       info->IER |= UART_IER_THRI;
-                       outb(info->IER, info->ioaddr + UART_IER);
-               }
-       }
-
-       if (tty->termios->c_cflag & CRTSCTS) {
-               info->MCR &= ~UART_MCR_RTS;
-               outb(info->MCR, info->ioaddr + UART_MCR);
-       }
-}
-
-/*
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- */
-static void mxser_throttle(struct tty_struct *tty)
-{
-       mxser_stoprx(tty);
-}
-
-static void mxser_unthrottle(struct tty_struct *tty)
-{
-       struct mxser_port *info = tty->driver_data;
-
-       /* startrx */
-       info->ldisc_stop_rx = 0;
-       if (I_IXOFF(tty)) {
-               if (info->x_char)
-                       info->x_char = 0;
-               else {
-                       if (info->board->chip_flag) {
-                               info->IER |= MOXA_MUST_RECV_ISR;
-                               outb(info->IER, info->ioaddr + UART_IER);
-                       } else {
-                               info->x_char = START_CHAR(tty);
-                               outb(0, info->ioaddr + UART_IER);
-                               info->IER |= UART_IER_THRI;
-                               outb(info->IER, info->ioaddr + UART_IER);
-                       }
-               }
-       }
-
-       if (tty->termios->c_cflag & CRTSCTS) {
-               info->MCR |= UART_MCR_RTS;
-               outb(info->MCR, info->ioaddr + UART_MCR);
-       }
-}
-
-/*
- * mxser_stop() and mxser_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter interrupts, as necessary.
- */
-static void mxser_stop(struct tty_struct *tty)
-{
-       struct mxser_port *info = tty->driver_data;
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->slock, flags);
-       if (info->IER & UART_IER_THRI) {
-               info->IER &= ~UART_IER_THRI;
-               outb(info->IER, info->ioaddr + UART_IER);
-       }
-       spin_unlock_irqrestore(&info->slock, flags);
-}
-
-static void mxser_start(struct tty_struct *tty)
-{
-       struct mxser_port *info = tty->driver_data;
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->slock, flags);
-       if (info->xmit_cnt && info->port.xmit_buf) {
-               outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER);
-               info->IER |= UART_IER_THRI;
-               outb(info->IER, info->ioaddr + UART_IER);
-       }
-       spin_unlock_irqrestore(&info->slock, flags);
-}
-
-static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
-{
-       struct mxser_port *info = tty->driver_data;
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->slock, flags);
-       mxser_change_speed(tty, old_termios);
-       spin_unlock_irqrestore(&info->slock, flags);
-
-       if ((old_termios->c_cflag & CRTSCTS) &&
-                       !(tty->termios->c_cflag & CRTSCTS)) {
-               tty->hw_stopped = 0;
-               mxser_start(tty);
-       }
-
-       /* Handle sw stopped */
-       if ((old_termios->c_iflag & IXON) &&
-                       !(tty->termios->c_iflag & IXON)) {
-               tty->stopped = 0;
-
-               if (info->board->chip_flag) {
-                       spin_lock_irqsave(&info->slock, flags);
-                       mxser_disable_must_rx_software_flow_control(
-                                       info->ioaddr);
-                       spin_unlock_irqrestore(&info->slock, flags);
-               }
-
-               mxser_start(tty);
-       }
-}
-
-/*
- * mxser_wait_until_sent() --- wait until the transmitter is empty
- */
-static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
-{
-       struct mxser_port *info = tty->driver_data;
-       unsigned long orig_jiffies, char_time;
-       unsigned long flags;
-       int lsr;
-
-       if (info->type == PORT_UNKNOWN)
-               return;
-
-       if (info->xmit_fifo_size == 0)
-               return;         /* Just in case.... */
-
-       orig_jiffies = jiffies;
-       /*
-        * Set the check interval to be 1/5 of the estimated time to
-        * send a single character, and make it at least 1.  The check
-        * interval should also be less than the timeout.
-        *
-        * Note: we have to use pretty tight timings here to satisfy
-        * the NIST-PCTS.
-        */
-       char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size;
-       char_time = char_time / 5;
-       if (char_time == 0)
-               char_time = 1;
-       if (timeout && timeout < char_time)
-               char_time = timeout;
-       /*
-        * If the transmitter hasn't cleared in twice the approximate
-        * amount of time to send the entire FIFO, it probably won't
-        * ever clear.  This assumes the UART isn't doing flow
-        * control, which is currently the case.  Hence, if it ever
-        * takes longer than info->timeout, this is probably due to a
-        * UART bug of some kind.  So, we clamp the timeout parameter at
-        * 2*info->timeout.
-        */
-       if (!timeout || timeout > 2 * info->timeout)
-               timeout = 2 * info->timeout;
-#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
-       printk(KERN_DEBUG "In rs_wait_until_sent(%d) check=%lu...",
-               timeout, char_time);
-       printk("jiff=%lu...", jiffies);
-#endif
-       spin_lock_irqsave(&info->slock, flags);
-       while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) {
-#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
-               printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
-#endif
-               spin_unlock_irqrestore(&info->slock, flags);
-               schedule_timeout_interruptible(char_time);
-               spin_lock_irqsave(&info->slock, flags);
-               if (signal_pending(current))
-                       break;
-               if (timeout && time_after(jiffies, orig_jiffies + timeout))
-                       break;
-       }
-       spin_unlock_irqrestore(&info->slock, flags);
-       set_current_state(TASK_RUNNING);
-
-#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
-       printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
-#endif
-}
-
-/*
- * This routine is called by tty_hangup() when a hangup is signaled.
- */
-static void mxser_hangup(struct tty_struct *tty)
-{
-       struct mxser_port *info = tty->driver_data;
-
-       mxser_flush_buffer(tty);
-       tty_port_hangup(&info->port);
-}
-
-/*
- * mxser_rs_break() --- routine which turns the break handling on or off
- */
-static int mxser_rs_break(struct tty_struct *tty, int break_state)
-{
-       struct mxser_port *info = tty->driver_data;
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->slock, flags);
-       if (break_state == -1)
-               outb(inb(info->ioaddr + UART_LCR) | UART_LCR_SBC,
-                       info->ioaddr + UART_LCR);
-       else
-               outb(inb(info->ioaddr + UART_LCR) & ~UART_LCR_SBC,
-                       info->ioaddr + UART_LCR);
-       spin_unlock_irqrestore(&info->slock, flags);
-       return 0;
-}
-
-static void mxser_receive_chars(struct tty_struct *tty,
-                               struct mxser_port *port, int *status)
-{
-       unsigned char ch, gdl;
-       int ignored = 0;
-       int cnt = 0;
-       int recv_room;
-       int max = 256;
-
-       recv_room = tty->receive_room;
-       if (recv_room == 0 && !port->ldisc_stop_rx)
-               mxser_stoprx(tty);
-       if (port->board->chip_flag != MOXA_OTHER_UART) {
-
-               if (*status & UART_LSR_SPECIAL)
-                       goto intr_old;
-               if (port->board->chip_flag == MOXA_MUST_MU860_HWID &&
-                               (*status & MOXA_MUST_LSR_RERR))
-                       goto intr_old;
-               if (*status & MOXA_MUST_LSR_RERR)
-                       goto intr_old;
-
-               gdl = inb(port->ioaddr + MOXA_MUST_GDL_REGISTER);
-
-               if (port->board->chip_flag == MOXA_MUST_MU150_HWID)
-                       gdl &= MOXA_MUST_GDL_MASK;
-               if (gdl >= recv_room) {
-                       if (!port->ldisc_stop_rx)
-                               mxser_stoprx(tty);
-               }
-               while (gdl--) {
-                       ch = inb(port->ioaddr + UART_RX);
-                       tty_insert_flip_char(tty, ch, 0);
-                       cnt++;
-               }
-               goto end_intr;
-       }
-intr_old:
-
-       do {
-               if (max-- < 0)
-                       break;
-
-               ch = inb(port->ioaddr + UART_RX);
-               if (port->board->chip_flag && (*status & UART_LSR_OE))
-                       outb(0x23, port->ioaddr + UART_FCR);
-               *status &= port->read_status_mask;
-               if (*status & port->ignore_status_mask) {
-                       if (++ignored > 100)
-                               break;
-               } else {
-                       char flag = 0;
-                       if (*status & UART_LSR_SPECIAL) {
-                               if (*status & UART_LSR_BI) {
-                                       flag = TTY_BREAK;
-                                       port->icount.brk++;
-
-                                       if (port->port.flags & ASYNC_SAK)
-                                               do_SAK(tty);
-                               } else if (*status & UART_LSR_PE) {
-                                       flag = TTY_PARITY;
-                                       port->icount.parity++;
-                               } else if (*status & UART_LSR_FE) {
-                                       flag = TTY_FRAME;
-                                       port->icount.frame++;
-                               } else if (*status & UART_LSR_OE) {
-                                       flag = TTY_OVERRUN;
-                                       port->icount.overrun++;
-                               } else
-                                       flag = TTY_BREAK;
-                       }
-                       tty_insert_flip_char(tty, ch, flag);
-                       cnt++;
-                       if (cnt >= recv_room) {
-                               if (!port->ldisc_stop_rx)
-                                       mxser_stoprx(tty);
-                               break;
-                       }
-
-               }
-
-               if (port->board->chip_flag)
-                       break;
-
-               *status = inb(port->ioaddr + UART_LSR);
-       } while (*status & UART_LSR_DR);
-
-end_intr:
-       mxvar_log.rxcnt[tty->index] += cnt;
-       port->mon_data.rxcnt += cnt;
-       port->mon_data.up_rxcnt += cnt;
-
-       /*
-        * We are called from an interrupt context with &port->slock
-        * being held. Drop it temporarily in order to prevent
-        * recursive locking.
-        */
-       spin_unlock(&port->slock);
-       tty_flip_buffer_push(tty);
-       spin_lock(&port->slock);
-}
-
-static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port)
-{
-       int count, cnt;
-
-       if (port->x_char) {
-               outb(port->x_char, port->ioaddr + UART_TX);
-               port->x_char = 0;
-               mxvar_log.txcnt[tty->index]++;
-               port->mon_data.txcnt++;
-               port->mon_data.up_txcnt++;
-               port->icount.tx++;
-               return;
-       }
-
-       if (port->port.xmit_buf == NULL)
-               return;
-
-       if (port->xmit_cnt <= 0 || tty->stopped ||
-                       (tty->hw_stopped &&
-                       (port->type != PORT_16550A) &&
-                       (!port->board->chip_flag))) {
-               port->IER &= ~UART_IER_THRI;
-               outb(port->IER, port->ioaddr + UART_IER);
-               return;
-       }
-
-       cnt = port->xmit_cnt;
-       count = port->xmit_fifo_size;
-       do {
-               outb(port->port.xmit_buf[port->xmit_tail++],
-                       port->ioaddr + UART_TX);
-               port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1);
-               if (--port->xmit_cnt <= 0)
-                       break;
-       } while (--count > 0);
-       mxvar_log.txcnt[tty->index] += (cnt - port->xmit_cnt);
-
-       port->mon_data.txcnt += (cnt - port->xmit_cnt);
-       port->mon_data.up_txcnt += (cnt - port->xmit_cnt);
-       port->icount.tx += (cnt - port->xmit_cnt);
-
-       if (port->xmit_cnt < WAKEUP_CHARS)
-               tty_wakeup(tty);
-
-       if (port->xmit_cnt <= 0) {
-               port->IER &= ~UART_IER_THRI;
-               outb(port->IER, port->ioaddr + UART_IER);
-       }
-}
-
-/*
- * This is the serial driver's generic interrupt routine
- */
-static irqreturn_t mxser_interrupt(int irq, void *dev_id)
-{
-       int status, iir, i;
-       struct mxser_board *brd = NULL;
-       struct mxser_port *port;
-       int max, irqbits, bits, msr;
-       unsigned int int_cnt, pass_counter = 0;
-       int handled = IRQ_NONE;
-       struct tty_struct *tty;
-
-       for (i = 0; i < MXSER_BOARDS; i++)
-               if (dev_id == &mxser_boards[i]) {
-                       brd = dev_id;
-                       break;
-               }
-
-       if (i == MXSER_BOARDS)
-               goto irq_stop;
-       if (brd == NULL)
-               goto irq_stop;
-       max = brd->info->nports;
-       while (pass_counter++ < MXSER_ISR_PASS_LIMIT) {
-               irqbits = inb(brd->vector) & brd->vector_mask;
-               if (irqbits == brd->vector_mask)
-                       break;
-
-               handled = IRQ_HANDLED;
-               for (i = 0, bits = 1; i < max; i++, irqbits |= bits, bits <<= 1) {
-                       if (irqbits == brd->vector_mask)
-                               break;
-                       if (bits & irqbits)
-                               continue;
-                       port = &brd->ports[i];
-
-                       int_cnt = 0;
-                       spin_lock(&port->slock);
-                       do {
-                               iir = inb(port->ioaddr + UART_IIR);
-                               if (iir & UART_IIR_NO_INT)
-                                       break;
-                               iir &= MOXA_MUST_IIR_MASK;
-                               tty = tty_port_tty_get(&port->port);
-                               if (!tty ||
-                                               (port->port.flags & ASYNC_CLOSING) ||
-                                               !(port->port.flags &
-                                                       ASYNC_INITIALIZED)) {
-                                       status = inb(port->ioaddr + UART_LSR);
-                                       outb(0x27, port->ioaddr + UART_FCR);
-                                       inb(port->ioaddr + UART_MSR);
-                                       tty_kref_put(tty);
-                                       break;
-                               }
-
-                               status = inb(port->ioaddr + UART_LSR);
-
-                               if (status & UART_LSR_PE)
-                                       port->err_shadow |= NPPI_NOTIFY_PARITY;
-                               if (status & UART_LSR_FE)
-                                       port->err_shadow |= NPPI_NOTIFY_FRAMING;
-                               if (status & UART_LSR_OE)
-                                       port->err_shadow |=
-                                               NPPI_NOTIFY_HW_OVERRUN;
-                               if (status & UART_LSR_BI)
-                                       port->err_shadow |= NPPI_NOTIFY_BREAK;
-
-                               if (port->board->chip_flag) {
-                                       if (iir == MOXA_MUST_IIR_GDA ||
-                                           iir == MOXA_MUST_IIR_RDA ||
-                                           iir == MOXA_MUST_IIR_RTO ||
-                                           iir == MOXA_MUST_IIR_LSR)
-                                               mxser_receive_chars(tty, port,
-                                                               &status);
-
-                               } else {
-                                       status &= port->read_status_mask;
-                                       if (status & UART_LSR_DR)
-                                               mxser_receive_chars(tty, port,
-                                                               &status);
-                               }
-                               msr = inb(port->ioaddr + UART_MSR);
-                               if (msr & UART_MSR_ANY_DELTA)
-                                       mxser_check_modem_status(tty, port, msr);
-
-                               if (port->board->chip_flag) {
-                                       if (iir == 0x02 && (status &
-                                                               UART_LSR_THRE))
-                                               mxser_transmit_chars(tty, port);
-                               } else {
-                                       if (status & UART_LSR_THRE)
-                                               mxser_transmit_chars(tty, port);
-                               }
-                               tty_kref_put(tty);
-                       } while (int_cnt++ < MXSER_ISR_PASS_LIMIT);
-                       spin_unlock(&port->slock);
-               }
-       }
-
-irq_stop:
-       return handled;
-}
-
-static const struct tty_operations mxser_ops = {
-       .open = mxser_open,
-       .close = mxser_close,
-       .write = mxser_write,
-       .put_char = mxser_put_char,
-       .flush_chars = mxser_flush_chars,
-       .write_room = mxser_write_room,
-       .chars_in_buffer = mxser_chars_in_buffer,
-       .flush_buffer = mxser_flush_buffer,
-       .ioctl = mxser_ioctl,
-       .throttle = mxser_throttle,
-       .unthrottle = mxser_unthrottle,
-       .set_termios = mxser_set_termios,
-       .stop = mxser_stop,
-       .start = mxser_start,
-       .hangup = mxser_hangup,
-       .break_ctl = mxser_rs_break,
-       .wait_until_sent = mxser_wait_until_sent,
-       .tiocmget = mxser_tiocmget,
-       .tiocmset = mxser_tiocmset,
-       .get_icount = mxser_get_icount,
-};
-
-struct tty_port_operations mxser_port_ops = {
-       .carrier_raised = mxser_carrier_raised,
-       .dtr_rts = mxser_dtr_rts,
-       .activate = mxser_activate,
-       .shutdown = mxser_shutdown_port,
-};
-
-/*
- * The MOXA Smartio/Industio serial driver boot-time initialization code!
- */
-
-static void mxser_release_ISA_res(struct mxser_board *brd)
-{
-       free_irq(brd->irq, brd);
-       release_region(brd->ports[0].ioaddr, 8 * brd->info->nports);
-       release_region(brd->vector, 1);
-}
-
-static int __devinit mxser_initbrd(struct mxser_board *brd,
-               struct pci_dev *pdev)
-{
-       struct mxser_port *info;
-       unsigned int i;
-       int retval;
-
-       printk(KERN_INFO "mxser: max. baud rate = %d bps\n",
-                       brd->ports[0].max_baud);
-
-       for (i = 0; i < brd->info->nports; i++) {
-               info = &brd->ports[i];
-               tty_port_init(&info->port);
-               info->port.ops = &mxser_port_ops;
-               info->board = brd;
-               info->stop_rx = 0;
-               info->ldisc_stop_rx = 0;
-
-               /* Enhance mode enabled here */
-               if (brd->chip_flag != MOXA_OTHER_UART)
-                       mxser_enable_must_enchance_mode(info->ioaddr);
-
-               info->port.flags = ASYNC_SHARE_IRQ;
-               info->type = brd->uart_type;
-
-               process_txrx_fifo(info);
-
-               info->custom_divisor = info->baud_base * 16;
-               info->port.close_delay = 5 * HZ / 10;
-               info->port.closing_wait = 30 * HZ;
-               info->normal_termios = mxvar_sdriver->init_termios;
-               memset(&info->mon_data, 0, sizeof(struct mxser_mon));
-               info->err_shadow = 0;
-               spin_lock_init(&info->slock);
-
-               /* before set INT ISR, disable all int */
-               outb(inb(info->ioaddr + UART_IER) & 0xf0,
-                       info->ioaddr + UART_IER);
-       }
-
-       retval = request_irq(brd->irq, mxser_interrupt, IRQF_SHARED, "mxser",
-                       brd);
-       if (retval)
-               printk(KERN_ERR "Board %s: Request irq failed, IRQ (%d) may "
-                       "conflict with another device.\n",
-                       brd->info->name, brd->irq);
-
-       return retval;
-}
-
-static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd)
-{
-       int id, i, bits;
-       unsigned short regs[16], irq;
-       unsigned char scratch, scratch2;
-
-       brd->chip_flag = MOXA_OTHER_UART;
-
-       id = mxser_read_register(cap, regs);
-       switch (id) {
-       case C168_ASIC_ID:
-               brd->info = &mxser_cards[0];
-               break;
-       case C104_ASIC_ID:
-               brd->info = &mxser_cards[1];
-               break;
-       case CI104J_ASIC_ID:
-               brd->info = &mxser_cards[2];
-               break;
-       case C102_ASIC_ID:
-               brd->info = &mxser_cards[5];
-               break;
-       case CI132_ASIC_ID:
-               brd->info = &mxser_cards[6];
-               break;
-       case CI134_ASIC_ID:
-               brd->info = &mxser_cards[7];
-               break;
-       default:
-               return 0;
-       }
-
-       irq = 0;
-       /* some ISA cards have 2 ports, but we want to see them as 4-port (why?)
-          Flag-hack checks if configuration should be read as 2-port here. */
-       if (brd->info->nports == 2 || (brd->info->flags & MXSER_HAS2)) {
-               irq = regs[9] & 0xF000;
-               irq = irq | (irq >> 4);
-               if (irq != (regs[9] & 0xFF00))
-                       goto err_irqconflict;
-       } else if (brd->info->nports == 4) {
-               irq = regs[9] & 0xF000;
-               irq = irq | (irq >> 4);
-               irq = irq | (irq >> 8);
-               if (irq != regs[9])
-                       goto err_irqconflict;
-       } else if (brd->info->nports == 8) {
-               irq = regs[9] & 0xF000;
-               irq = irq | (irq >> 4);
-               irq = irq | (irq >> 8);
-               if ((irq != regs[9]) || (irq != regs[10]))
-                       goto err_irqconflict;
-       }
-
-       if (!irq) {
-               printk(KERN_ERR "mxser: interrupt number unset\n");
-               return -EIO;
-       }
-       brd->irq = ((int)(irq & 0xF000) >> 12);
-       for (i = 0; i < 8; i++)
-               brd->ports[i].ioaddr = (int) regs[i + 1] & 0xFFF8;
-       if ((regs[12] & 0x80) == 0) {
-               printk(KERN_ERR "mxser: invalid interrupt vector\n");
-               return -EIO;
-       }
-       brd->vector = (int)regs[11];    /* interrupt vector */
-       if (id == 1)
-               brd->vector_mask = 0x00FF;
-       else
-               brd->vector_mask = 0x000F;
-       for (i = 7, bits = 0x0100; i >= 0; i--, bits <<= 1) {
-               if (regs[12] & bits) {
-                       brd->ports[i].baud_base = 921600;
-                       brd->ports[i].max_baud = 921600;
-               } else {
-                       brd->ports[i].baud_base = 115200;
-                       brd->ports[i].max_baud = 115200;
-               }
-       }
-       scratch2 = inb(cap + UART_LCR) & (~UART_LCR_DLAB);
-       outb(scratch2 | UART_LCR_DLAB, cap + UART_LCR);
-       outb(0, cap + UART_EFR);        /* EFR is the same as FCR */
-       outb(scratch2, cap + UART_LCR);
-       outb(UART_FCR_ENABLE_FIFO, cap + UART_FCR);
-       scratch = inb(cap + UART_IIR);
-
-       if (scratch & 0xC0)
-               brd->uart_type = PORT_16550A;
-       else
-               brd->uart_type = PORT_16450;
-       if (!request_region(brd->ports[0].ioaddr, 8 * brd->info->nports,
-                       "mxser(IO)")) {
-               printk(KERN_ERR "mxser: can't request ports I/O region: "
-                               "0x%.8lx-0x%.8lx\n",
-                               brd->ports[0].ioaddr, brd->ports[0].ioaddr +
-                               8 * brd->info->nports - 1);
-               return -EIO;
-       }
-       if (!request_region(brd->vector, 1, "mxser(vector)")) {
-               release_region(brd->ports[0].ioaddr, 8 * brd->info->nports);
-               printk(KERN_ERR "mxser: can't request interrupt vector region: "
-                               "0x%.8lx-0x%.8lx\n",
-                               brd->ports[0].ioaddr, brd->ports[0].ioaddr +
-                               8 * brd->info->nports - 1);
-               return -EIO;
-       }
-       return brd->info->nports;
-
-err_irqconflict:
-       printk(KERN_ERR "mxser: invalid interrupt number\n");
-       return -EIO;
-}
-
-static int __devinit mxser_probe(struct pci_dev *pdev,
-               const struct pci_device_id *ent)
-{
-#ifdef CONFIG_PCI
-       struct mxser_board *brd;
-       unsigned int i, j;
-       unsigned long ioaddress;
-       int retval = -EINVAL;
-
-       for (i = 0; i < MXSER_BOARDS; i++)
-               if (mxser_boards[i].info == NULL)
-                       break;
-
-       if (i >= MXSER_BOARDS) {
-               dev_err(&pdev->dev, "too many boards found (maximum %d), board "
-                               "not configured\n", MXSER_BOARDS);
-               goto err;
-       }
-
-       brd = &mxser_boards[i];
-       brd->idx = i * MXSER_PORTS_PER_BOARD;
-       dev_info(&pdev->dev, "found MOXA %s board (BusNo=%d, DevNo=%d)\n",
-               mxser_cards[ent->driver_data].name,
-               pdev->bus->number, PCI_SLOT(pdev->devfn));
-
-       retval = pci_enable_device(pdev);
-       if (retval) {
-               dev_err(&pdev->dev, "PCI enable failed\n");
-               goto err;
-       }
-
-       /* io address */
-       ioaddress = pci_resource_start(pdev, 2);
-       retval = pci_request_region(pdev, 2, "mxser(IO)");
-       if (retval)
-               goto err_dis;
-
-       brd->info = &mxser_cards[ent->driver_data];
-       for (i = 0; i < brd->info->nports; i++)
-               brd->ports[i].ioaddr = ioaddress + 8 * i;
-
-       /* vector */
-       ioaddress = pci_resource_start(pdev, 3);
-       retval = pci_request_region(pdev, 3, "mxser(vector)");
-       if (retval)
-               goto err_zero;
-       brd->vector = ioaddress;
-
-       /* irq */
-       brd->irq = pdev->irq;
-
-       brd->chip_flag = CheckIsMoxaMust(brd->ports[0].ioaddr);
-       brd->uart_type = PORT_16550A;
-       brd->vector_mask = 0;
-
-       for (i = 0; i < brd->info->nports; i++) {
-               for (j = 0; j < UART_INFO_NUM; j++) {
-                       if (Gpci_uart_info[j].type == brd->chip_flag) {
-                               brd->ports[i].max_baud =
-                                       Gpci_uart_info[j].max_baud;
-
-                               /* exception....CP-102 */
-                               if (brd->info->flags & MXSER_HIGHBAUD)
-                                       brd->ports[i].max_baud = 921600;
-                               break;
-                       }
-               }
-       }
-
-       if (brd->chip_flag == MOXA_MUST_MU860_HWID) {
-               for (i = 0; i < brd->info->nports; i++) {
-                       if (i < 4)
-                               brd->ports[i].opmode_ioaddr = ioaddress + 4;
-                       else
-                               brd->ports[i].opmode_ioaddr = ioaddress + 0x0c;
-               }
-               outb(0, ioaddress + 4); /* default set to RS232 mode */
-               outb(0, ioaddress + 0x0c);      /* default set to RS232 mode */
-       }
-
-       for (i = 0; i < brd->info->nports; i++) {
-               brd->vector_mask |= (1 << i);
-               brd->ports[i].baud_base = 921600;
-       }
-
-       /* mxser_initbrd will hook ISR. */
-       retval = mxser_initbrd(brd, pdev);
-       if (retval)
-               goto err_rel3;
-
-       for (i = 0; i < brd->info->nports; i++)
-               tty_register_device(mxvar_sdriver, brd->idx + i, &pdev->dev);
-
-       pci_set_drvdata(pdev, brd);
-
-       return 0;
-err_rel3:
-       pci_release_region(pdev, 3);
-err_zero:
-       brd->info = NULL;
-       pci_release_region(pdev, 2);
-err_dis:
-       pci_disable_device(pdev);
-err:
-       return retval;
-#else
-       return -ENODEV;
-#endif
-}
-
-static void __devexit mxser_remove(struct pci_dev *pdev)
-{
-#ifdef CONFIG_PCI
-       struct mxser_board *brd = pci_get_drvdata(pdev);
-       unsigned int i;
-
-       for (i = 0; i < brd->info->nports; i++)
-               tty_unregister_device(mxvar_sdriver, brd->idx + i);
-
-       free_irq(pdev->irq, brd);
-       pci_release_region(pdev, 2);
-       pci_release_region(pdev, 3);
-       pci_disable_device(pdev);
-       brd->info = NULL;
-#endif
-}
-
-static struct pci_driver mxser_driver = {
-       .name = "mxser",
-       .id_table = mxser_pcibrds,
-       .probe = mxser_probe,
-       .remove = __devexit_p(mxser_remove)
-};
-
-static int __init mxser_module_init(void)
-{
-       struct mxser_board *brd;
-       unsigned int b, i, m;
-       int retval;
-
-       mxvar_sdriver = alloc_tty_driver(MXSER_PORTS + 1);
-       if (!mxvar_sdriver)
-               return -ENOMEM;
-
-       printk(KERN_INFO "MOXA Smartio/Industio family driver version %s\n",
-               MXSER_VERSION);
-
-       /* Initialize the tty_driver structure */
-       mxvar_sdriver->owner = THIS_MODULE;
-       mxvar_sdriver->magic = TTY_DRIVER_MAGIC;
-       mxvar_sdriver->name = "ttyMI";
-       mxvar_sdriver->major = ttymajor;
-       mxvar_sdriver->minor_start = 0;
-       mxvar_sdriver->num = MXSER_PORTS + 1;
-       mxvar_sdriver->type = TTY_DRIVER_TYPE_SERIAL;
-       mxvar_sdriver->subtype = SERIAL_TYPE_NORMAL;
-       mxvar_sdriver->init_termios = tty_std_termios;
-       mxvar_sdriver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL;
-       mxvar_sdriver->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_DYNAMIC_DEV;
-       tty_set_operations(mxvar_sdriver, &mxser_ops);
-
-       retval = tty_register_driver(mxvar_sdriver);
-       if (retval) {
-               printk(KERN_ERR "Couldn't install MOXA Smartio/Industio family "
-                               "tty driver !\n");
-               goto err_put;
-       }
-
-       /* Start finding ISA boards here */
-       for (m = 0, b = 0; b < MXSER_BOARDS; b++) {
-               if (!ioaddr[b])
-                       continue;
-
-               brd = &mxser_boards[m];
-               retval = mxser_get_ISA_conf(ioaddr[b], brd);
-               if (retval <= 0) {
-                       brd->info = NULL;
-                       continue;
-               }
-
-               printk(KERN_INFO "mxser: found MOXA %s board (CAP=0x%lx)\n",
-                               brd->info->name, ioaddr[b]);
-
-               /* mxser_initbrd will hook ISR. */
-               if (mxser_initbrd(brd, NULL) < 0) {
-                       brd->info = NULL;
-                       continue;
-               }
-
-               brd->idx = m * MXSER_PORTS_PER_BOARD;
-               for (i = 0; i < brd->info->nports; i++)
-                       tty_register_device(mxvar_sdriver, brd->idx + i, NULL);
-
-               m++;
-       }
-
-       retval = pci_register_driver(&mxser_driver);
-       if (retval) {
-               printk(KERN_ERR "mxser: can't register pci driver\n");
-               if (!m) {
-                       retval = -ENODEV;
-                       goto err_unr;
-               } /* else: we have some ISA cards under control */
-       }
-
-       return 0;
-err_unr:
-       tty_unregister_driver(mxvar_sdriver);
-err_put:
-       put_tty_driver(mxvar_sdriver);
-       return retval;
-}
-
-static void __exit mxser_module_exit(void)
-{
-       unsigned int i, j;
-
-       pci_unregister_driver(&mxser_driver);
-
-       for (i = 0; i < MXSER_BOARDS; i++) /* ISA remains */
-               if (mxser_boards[i].info != NULL)
-                       for (j = 0; j < mxser_boards[i].info->nports; j++)
-                               tty_unregister_device(mxvar_sdriver,
-                                               mxser_boards[i].idx + j);
-       tty_unregister_driver(mxvar_sdriver);
-       put_tty_driver(mxvar_sdriver);
-
-       for (i = 0; i < MXSER_BOARDS; i++)
-               if (mxser_boards[i].info != NULL)
-                       mxser_release_ISA_res(&mxser_boards[i]);
-}
-
-module_init(mxser_module_init);
-module_exit(mxser_module_exit);
diff --git a/drivers/char/mxser.h b/drivers/char/mxser.h
deleted file mode 100644 (file)
index 41878a6..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-#ifndef _MXSER_H
-#define _MXSER_H
-
-/*
- *     Semi-public control interfaces
- */
-
-/*
- *     MOXA ioctls
- */
-
-#define MOXA                   0x400
-#define MOXA_GETDATACOUNT      (MOXA + 23)
-#define MOXA_DIAGNOSE          (MOXA + 50)
-#define MOXA_CHKPORTENABLE     (MOXA + 60)
-#define MOXA_HighSpeedOn       (MOXA + 61)
-#define MOXA_GET_MAJOR         (MOXA + 63)
-#define MOXA_GETMSTATUS                (MOXA + 65)
-#define MOXA_SET_OP_MODE       (MOXA + 66)
-#define MOXA_GET_OP_MODE       (MOXA + 67)
-
-#define RS232_MODE             0
-#define RS485_2WIRE_MODE       1
-#define RS422_MODE             2
-#define RS485_4WIRE_MODE       3
-#define OP_MODE_MASK           3
-
-#define MOXA_SDS_RSTICOUNTER   (MOXA + 69)
-#define MOXA_ASPP_OQUEUE       (MOXA + 70)
-#define MOXA_ASPP_MON          (MOXA + 73)
-#define MOXA_ASPP_LSTATUS      (MOXA + 74)
-#define MOXA_ASPP_MON_EXT      (MOXA + 75)
-#define MOXA_SET_BAUD_METHOD   (MOXA + 76)
-
-/* --------------------------------------------------- */
-
-#define NPPI_NOTIFY_PARITY     0x01
-#define NPPI_NOTIFY_FRAMING    0x02
-#define NPPI_NOTIFY_HW_OVERRUN 0x04
-#define NPPI_NOTIFY_SW_OVERRUN 0x08
-#define NPPI_NOTIFY_BREAK      0x10
-
-#define NPPI_NOTIFY_CTSHOLD         0x01       /* Tx hold by CTS low */
-#define NPPI_NOTIFY_DSRHOLD         0x02       /* Tx hold by DSR low */
-#define NPPI_NOTIFY_XOFFHOLD        0x08       /* Tx hold by Xoff received */
-#define NPPI_NOTIFY_XOFFXENT        0x10       /* Xoff Sent */
-
-/* follow just for Moxa Must chip define. */
-/* */
-/* when LCR register (offset 0x03) write following value, */
-/* the Must chip will enter enchance mode. And write value */
-/* on EFR (offset 0x02) bit 6,7 to change bank. */
-#define MOXA_MUST_ENTER_ENCHANCE       0xBF
-
-/* when enhance mode enable, access on general bank register */
-#define MOXA_MUST_GDL_REGISTER         0x07
-#define MOXA_MUST_GDL_MASK             0x7F
-#define MOXA_MUST_GDL_HAS_BAD_DATA     0x80
-
-#define MOXA_MUST_LSR_RERR             0x80    /* error in receive FIFO */
-/* enchance register bank select and enchance mode setting register */
-/* when LCR register equal to 0xBF */
-#define MOXA_MUST_EFR_REGISTER         0x02
-/* enchance mode enable */
-#define MOXA_MUST_EFR_EFRB_ENABLE      0x10
-/* enchance reister bank set 0, 1, 2 */
-#define MOXA_MUST_EFR_BANK0            0x00
-#define MOXA_MUST_EFR_BANK1            0x40
-#define MOXA_MUST_EFR_BANK2            0x80
-#define MOXA_MUST_EFR_BANK3            0xC0
-#define MOXA_MUST_EFR_BANK_MASK                0xC0
-
-/* set XON1 value register, when LCR=0xBF and change to bank0 */
-#define MOXA_MUST_XON1_REGISTER                0x04
-
-/* set XON2 value register, when LCR=0xBF and change to bank0 */
-#define MOXA_MUST_XON2_REGISTER                0x05
-
-/* set XOFF1 value register, when LCR=0xBF and change to bank0 */
-#define MOXA_MUST_XOFF1_REGISTER       0x06
-
-/* set XOFF2 value register, when LCR=0xBF and change to bank0 */
-#define MOXA_MUST_XOFF2_REGISTER       0x07
-
-#define MOXA_MUST_RBRTL_REGISTER       0x04
-#define MOXA_MUST_RBRTH_REGISTER       0x05
-#define MOXA_MUST_RBRTI_REGISTER       0x06
-#define MOXA_MUST_THRTL_REGISTER       0x07
-#define MOXA_MUST_ENUM_REGISTER                0x04
-#define MOXA_MUST_HWID_REGISTER                0x05
-#define MOXA_MUST_ECR_REGISTER         0x06
-#define MOXA_MUST_CSR_REGISTER         0x07
-
-/* good data mode enable */
-#define MOXA_MUST_FCR_GDA_MODE_ENABLE  0x20
-/* only good data put into RxFIFO */
-#define MOXA_MUST_FCR_GDA_ONLY_ENABLE  0x10
-
-/* enable CTS interrupt */
-#define MOXA_MUST_IER_ECTSI            0x80
-/* enable RTS interrupt */
-#define MOXA_MUST_IER_ERTSI            0x40
-/* enable Xon/Xoff interrupt */
-#define MOXA_MUST_IER_XINT             0x20
-/* enable GDA interrupt */
-#define MOXA_MUST_IER_EGDAI            0x10
-
-#define MOXA_MUST_RECV_ISR             (UART_IER_RDI | MOXA_MUST_IER_EGDAI)
-
-/* GDA interrupt pending */
-#define MOXA_MUST_IIR_GDA              0x1C
-#define MOXA_MUST_IIR_RDA              0x04
-#define MOXA_MUST_IIR_RTO              0x0C
-#define MOXA_MUST_IIR_LSR              0x06
-
-/* recieved Xon/Xoff or specical interrupt pending */
-#define MOXA_MUST_IIR_XSC              0x10
-
-/* RTS/CTS change state interrupt pending */
-#define MOXA_MUST_IIR_RTSCTS           0x20
-#define MOXA_MUST_IIR_MASK             0x3E
-
-#define MOXA_MUST_MCR_XON_FLAG         0x40
-#define MOXA_MUST_MCR_XON_ANY          0x80
-#define MOXA_MUST_MCR_TX_XON           0x08
-
-/* software flow control on chip mask value */
-#define MOXA_MUST_EFR_SF_MASK          0x0F
-/* send Xon1/Xoff1 */
-#define MOXA_MUST_EFR_SF_TX1           0x08
-/* send Xon2/Xoff2 */
-#define MOXA_MUST_EFR_SF_TX2           0x04
-/* send Xon1,Xon2/Xoff1,Xoff2 */
-#define MOXA_MUST_EFR_SF_TX12          0x0C
-/* don't send Xon/Xoff */
-#define MOXA_MUST_EFR_SF_TX_NO         0x00
-/* Tx software flow control mask */
-#define MOXA_MUST_EFR_SF_TX_MASK       0x0C
-/* don't receive Xon/Xoff */
-#define MOXA_MUST_EFR_SF_RX_NO         0x00
-/* receive Xon1/Xoff1 */
-#define MOXA_MUST_EFR_SF_RX1           0x02
-/* receive Xon2/Xoff2 */
-#define MOXA_MUST_EFR_SF_RX2           0x01
-/* receive Xon1,Xon2/Xoff1,Xoff2 */
-#define MOXA_MUST_EFR_SF_RX12          0x03
-/* Rx software flow control mask */
-#define MOXA_MUST_EFR_SF_RX_MASK       0x03
-
-#endif
diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c
deleted file mode 100644 (file)
index 513ba12..0000000
+++ /dev/null
@@ -1,1993 +0,0 @@
-/*
- * nozomi.c  -- HSDPA driver Broadband Wireless Data Card - Globe Trotter
- *
- * Written by: Ulf Jakobsson,
- *             Jan Ã…kerfeldt,
- *             Stefan Thomasson,
- *
- * Maintained by: Paul Hardwick (p.hardwick@option.com)
- *
- * Patches:
- *          Locking code changes for Vodafone by Sphere Systems Ltd,
- *                              Andrew Bird (ajb@spheresystems.co.uk )
- *                              & Phil Sanderson
- *
- * Source has been ported from an implementation made by Filip Aben @ Option
- *
- * --------------------------------------------------------------------------
- *
- * Copyright (c) 2005,2006 Option Wireless Sweden AB
- * Copyright (c) 2006 Sphere Systems Ltd
- * Copyright (c) 2006 Option Wireless n/v
- * All rights Reserved.
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * --------------------------------------------------------------------------
- */
-
-/* Enable this to have a lot of debug printouts */
-#define DEBUG
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/ioport.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-#include <linux/sched.h>
-#include <linux/serial.h>
-#include <linux/interrupt.h>
-#include <linux/kmod.h>
-#include <linux/init.h>
-#include <linux/kfifo.h>
-#include <linux/uaccess.h>
-#include <linux/slab.h>
-#include <asm/byteorder.h>
-
-#include <linux/delay.h>
-
-
-#define VERSION_STRING DRIVER_DESC " 2.1d (build date: " \
-                                       __DATE__ " " __TIME__ ")"
-
-/*    Macros definitions */
-
-/* Default debug printout level */
-#define NOZOMI_DEBUG_LEVEL 0x00
-
-#define P_BUF_SIZE 128
-#define NFO(_err_flag_, args...)                               \
-do {                                                           \
-       char tmp[P_BUF_SIZE];                                   \
-       snprintf(tmp, sizeof(tmp), ##args);                     \
-       printk(_err_flag_ "[%d] %s(): %s\n", __LINE__,          \
-               __func__, tmp);                         \
-} while (0)
-
-#define DBG1(args...) D_(0x01, ##args)
-#define DBG2(args...) D_(0x02, ##args)
-#define DBG3(args...) D_(0x04, ##args)
-#define DBG4(args...) D_(0x08, ##args)
-#define DBG5(args...) D_(0x10, ##args)
-#define DBG6(args...) D_(0x20, ##args)
-#define DBG7(args...) D_(0x40, ##args)
-#define DBG8(args...) D_(0x80, ##args)
-
-#ifdef DEBUG
-/* Do we need this settable at runtime? */
-static int debug = NOZOMI_DEBUG_LEVEL;
-
-#define D(lvl, args...)  do \
-                       {if (lvl & debug) NFO(KERN_DEBUG, ##args); } \
-                       while (0)
-#define D_(lvl, args...) D(lvl, ##args)
-
-/* These printouts are always printed */
-
-#else
-static int debug;
-#define D_(lvl, args...)
-#endif
-
-/* TODO: rewrite to optimize macros... */
-
-#define TMP_BUF_MAX 256
-
-#define DUMP(buf__,len__) \
-  do {  \
-    char tbuf[TMP_BUF_MAX] = {0};\
-    if (len__ > 1) {\
-       snprintf(tbuf, len__ > TMP_BUF_MAX ? TMP_BUF_MAX : len__, "%s", buf__);\
-       if (tbuf[len__-2] == '\r') {\
-               tbuf[len__-2] = 'r';\
-       } \
-       DBG1("SENDING: '%s' (%d+n)", tbuf, len__);\
-    } else {\
-       DBG1("SENDING: '%s' (%d)", tbuf, len__);\
-    } \
-} while (0)
-
-/*    Defines */
-#define NOZOMI_NAME            "nozomi"
-#define NOZOMI_NAME_TTY                "nozomi_tty"
-#define DRIVER_DESC            "Nozomi driver"
-
-#define NTTY_TTY_MAXMINORS     256
-#define NTTY_FIFO_BUFFER_SIZE  8192
-
-/* Must be power of 2 */
-#define FIFO_BUFFER_SIZE_UL    8192
-
-/* Size of tmp send buffer to card */
-#define SEND_BUF_MAX           1024
-#define RECEIVE_BUF_MAX                4
-
-
-#define R_IIR          0x0000  /* Interrupt Identity Register */
-#define R_FCR          0x0000  /* Flow Control Register */
-#define R_IER          0x0004  /* Interrupt Enable Register */
-
-#define CONFIG_MAGIC   0xEFEFFEFE
-#define TOGGLE_VALID   0x0000
-
-/* Definition of interrupt tokens */
-#define MDM_DL1                0x0001
-#define MDM_UL1                0x0002
-#define MDM_DL2                0x0004
-#define MDM_UL2                0x0008
-#define DIAG_DL1       0x0010
-#define DIAG_DL2       0x0020
-#define DIAG_UL                0x0040
-#define APP1_DL                0x0080
-#define APP1_UL                0x0100
-#define APP2_DL                0x0200
-#define APP2_UL                0x0400
-#define CTRL_DL                0x0800
-#define CTRL_UL                0x1000
-#define RESET          0x8000
-
-#define MDM_DL         (MDM_DL1  | MDM_DL2)
-#define MDM_UL         (MDM_UL1  | MDM_UL2)
-#define DIAG_DL                (DIAG_DL1 | DIAG_DL2)
-
-/* modem signal definition */
-#define CTRL_DSR       0x0001
-#define CTRL_DCD       0x0002
-#define CTRL_RI                0x0004
-#define CTRL_CTS       0x0008
-
-#define CTRL_DTR       0x0001
-#define CTRL_RTS       0x0002
-
-#define MAX_PORT               4
-#define NOZOMI_MAX_PORTS       5
-#define NOZOMI_MAX_CARDS       (NTTY_TTY_MAXMINORS / MAX_PORT)
-
-/*    Type definitions */
-
-/*
- * There are two types of nozomi cards,
- * one with 2048 memory and with 8192 memory
- */
-enum card_type {
-       F32_2 = 2048,   /* 512 bytes downlink + uplink * 2 -> 2048 */
-       F32_8 = 8192,   /* 3072 bytes downl. + 1024 bytes uplink * 2 -> 8192 */
-};
-
-/* Initialization states a card can be in */
-enum card_state {
-       NOZOMI_STATE_UKNOWN     = 0,
-       NOZOMI_STATE_ENABLED    = 1,    /* pci device enabled */
-       NOZOMI_STATE_ALLOCATED  = 2,    /* config setup done */
-       NOZOMI_STATE_READY      = 3,    /* flowcontrols received */
-};
-
-/* Two different toggle channels exist */
-enum channel_type {
-       CH_A = 0,
-       CH_B = 1,
-};
-
-/* Port definition for the card regarding flow control */
-enum ctrl_port_type {
-       CTRL_CMD        = 0,
-       CTRL_MDM        = 1,
-       CTRL_DIAG       = 2,
-       CTRL_APP1       = 3,
-       CTRL_APP2       = 4,
-       CTRL_ERROR      = -1,
-};
-
-/* Ports that the nozomi has */
-enum port_type {
-       PORT_MDM        = 0,
-       PORT_DIAG       = 1,
-       PORT_APP1       = 2,
-       PORT_APP2       = 3,
-       PORT_CTRL       = 4,
-       PORT_ERROR      = -1,
-};
-
-#ifdef __BIG_ENDIAN
-/* Big endian */
-
-struct toggles {
-       unsigned int enabled:5; /*
-                                * Toggle fields are valid if enabled is 0,
-                                * else A-channels must always be used.
-                                */
-       unsigned int diag_dl:1;
-       unsigned int mdm_dl:1;
-       unsigned int mdm_ul:1;
-} __attribute__ ((packed));
-
-/* Configuration table to read at startup of card */
-/* Is for now only needed during initialization phase */
-struct config_table {
-       u32 signature;
-       u16 product_information;
-       u16 version;
-       u8 pad3[3];
-       struct toggles toggle;
-       u8 pad1[4];
-       u16 dl_mdm_len1;        /*
-                                * If this is 64, it can hold
-                                * 60 bytes + 4 that is length field
-                                */
-       u16 dl_start;
-
-       u16 dl_diag_len1;
-       u16 dl_mdm_len2;        /*
-                                * If this is 64, it can hold
-                                * 60 bytes + 4 that is length field
-                                */
-       u16 dl_app1_len;
-
-       u16 dl_diag_len2;
-       u16 dl_ctrl_len;
-       u16 dl_app2_len;
-       u8 pad2[16];
-       u16 ul_mdm_len1;
-       u16 ul_start;
-       u16 ul_diag_len;
-       u16 ul_mdm_len2;
-       u16 ul_app1_len;
-       u16 ul_app2_len;
-       u16 ul_ctrl_len;
-} __attribute__ ((packed));
-
-/* This stores all control downlink flags */
-struct ctrl_dl {
-       u8 port;
-       unsigned int reserved:4;
-       unsigned int CTS:1;
-       unsigned int RI:1;
-       unsigned int DCD:1;
-       unsigned int DSR:1;
-} __attribute__ ((packed));
-
-/* This stores all control uplink flags */
-struct ctrl_ul {
-       u8 port;
-       unsigned int reserved:6;
-       unsigned int RTS:1;
-       unsigned int DTR:1;
-} __attribute__ ((packed));
-
-#else
-/* Little endian */
-
-/* This represents the toggle information */
-struct toggles {
-       unsigned int mdm_ul:1;
-       unsigned int mdm_dl:1;
-       unsigned int diag_dl:1;
-       unsigned int enabled:5; /*
-                                * Toggle fields are valid if enabled is 0,
-                                * else A-channels must always be used.
-                                */
-} __attribute__ ((packed));
-
-/* Configuration table to read at startup of card */
-struct config_table {
-       u32 signature;
-       u16 version;
-       u16 product_information;
-       struct toggles toggle;
-       u8 pad1[7];
-       u16 dl_start;
-       u16 dl_mdm_len1;        /*
-                                * If this is 64, it can hold
-                                * 60 bytes + 4 that is length field
-                                */
-       u16 dl_mdm_len2;
-       u16 dl_diag_len1;
-       u16 dl_diag_len2;
-       u16 dl_app1_len;
-       u16 dl_app2_len;
-       u16 dl_ctrl_len;
-       u8 pad2[16];
-       u16 ul_start;
-       u16 ul_mdm_len2;
-       u16 ul_mdm_len1;
-       u16 ul_diag_len;
-       u16 ul_app1_len;
-       u16 ul_app2_len;
-       u16 ul_ctrl_len;
-} __attribute__ ((packed));
-
-/* This stores all control downlink flags */
-struct ctrl_dl {
-       unsigned int DSR:1;
-       unsigned int DCD:1;
-       unsigned int RI:1;
-       unsigned int CTS:1;
-       unsigned int reserverd:4;
-       u8 port;
-} __attribute__ ((packed));
-
-/* This stores all control uplink flags */
-struct ctrl_ul {
-       unsigned int DTR:1;
-       unsigned int RTS:1;
-       unsigned int reserved:6;
-       u8 port;
-} __attribute__ ((packed));
-#endif
-
-/* This holds all information that is needed regarding a port */
-struct port {
-       struct tty_port port;
-       u8 update_flow_control;
-       struct ctrl_ul ctrl_ul;
-       struct ctrl_dl ctrl_dl;
-       struct kfifo fifo_ul;
-       void __iomem *dl_addr[2];
-       u32 dl_size[2];
-       u8 toggle_dl;
-       void __iomem *ul_addr[2];
-       u32 ul_size[2];
-       u8 toggle_ul;
-       u16 token_dl;
-
-       /* mutex to ensure one access patch to this port */
-       struct mutex tty_sem;
-       wait_queue_head_t tty_wait;
-       struct async_icount tty_icount;
-
-       struct nozomi *dc;
-};
-
-/* Private data one for each card in the system */
-struct nozomi {
-       void __iomem *base_addr;
-       unsigned long flip;
-
-       /* Pointers to registers */
-       void __iomem *reg_iir;
-       void __iomem *reg_fcr;
-       void __iomem *reg_ier;
-
-       u16 last_ier;
-       enum card_type card_type;
-       struct config_table config_table;       /* Configuration table */
-       struct pci_dev *pdev;
-       struct port port[NOZOMI_MAX_PORTS];
-       u8 *send_buf;
-
-       spinlock_t spin_mutex;  /* secures access to registers and tty */
-
-       unsigned int index_start;
-       enum card_state state;
-       u32 open_ttys;
-};
-
-/* This is a data packet that is read or written to/from card */
-struct buffer {
-       u32 size;               /* size is the length of the data buffer */
-       u8 *data;
-} __attribute__ ((packed));
-
-/*    Global variables */
-static const struct pci_device_id nozomi_pci_tbl[] __devinitconst = {
-       {PCI_DEVICE(0x1931, 0x000c)},   /* Nozomi HSDPA */
-       {},
-};
-
-MODULE_DEVICE_TABLE(pci, nozomi_pci_tbl);
-
-static struct nozomi *ndevs[NOZOMI_MAX_CARDS];
-static struct tty_driver *ntty_driver;
-
-static const struct tty_port_operations noz_tty_port_ops;
-
-/*
- * find card by tty_index
- */
-static inline struct nozomi *get_dc_by_tty(const struct tty_struct *tty)
-{
-       return tty ? ndevs[tty->index / MAX_PORT] : NULL;
-}
-
-static inline struct port *get_port_by_tty(const struct tty_struct *tty)
-{
-       struct nozomi *ndev = get_dc_by_tty(tty);
-       return ndev ? &ndev->port[tty->index % MAX_PORT] : NULL;
-}
-
-/*
- * TODO:
- * -Optimize
- * -Rewrite cleaner
- */
-
-static void read_mem32(u32 *buf, const void __iomem *mem_addr_start,
-                       u32 size_bytes)
-{
-       u32 i = 0;
-       const u32 __iomem *ptr = mem_addr_start;
-       u16 *buf16;
-
-       if (unlikely(!ptr || !buf))
-               goto out;
-
-       /* shortcut for extremely often used cases */
-       switch (size_bytes) {
-       case 2: /* 2 bytes */
-               buf16 = (u16 *) buf;
-               *buf16 = __le16_to_cpu(readw(ptr));
-               goto out;
-               break;
-       case 4: /* 4 bytes */
-               *(buf) = __le32_to_cpu(readl(ptr));
-               goto out;
-               break;
-       }
-
-       while (i < size_bytes) {
-               if (size_bytes - i == 2) {
-                       /* Handle 2 bytes in the end */
-                       buf16 = (u16 *) buf;
-                       *(buf16) = __le16_to_cpu(readw(ptr));
-                       i += 2;
-               } else {
-                       /* Read 4 bytes */
-                       *(buf) = __le32_to_cpu(readl(ptr));
-                       i += 4;
-               }
-               buf++;
-               ptr++;
-       }
-out:
-       return;
-}
-
-/*
- * TODO:
- * -Optimize
- * -Rewrite cleaner
- */
-static u32 write_mem32(void __iomem *mem_addr_start, const u32 *buf,
-                       u32 size_bytes)
-{
-       u32 i = 0;
-       u32 __iomem *ptr = mem_addr_start;
-       const u16 *buf16;
-
-       if (unlikely(!ptr || !buf))
-               return 0;
-
-       /* shortcut for extremely often used cases */
-       switch (size_bytes) {
-       case 2: /* 2 bytes */
-               buf16 = (const u16 *)buf;
-               writew(__cpu_to_le16(*buf16), ptr);
-               return 2;
-               break;
-       case 1: /*
-                * also needs to write 4 bytes in this case
-                * so falling through..
-                */
-       case 4: /* 4 bytes */
-               writel(__cpu_to_le32(*buf), ptr);
-               return 4;
-               break;
-       }
-
-       while (i < size_bytes) {
-               if (size_bytes - i == 2) {
-                       /* 2 bytes */
-                       buf16 = (const u16 *)buf;
-                       writew(__cpu_to_le16(*buf16), ptr);
-                       i += 2;
-               } else {
-                       /* 4 bytes */
-                       writel(__cpu_to_le32(*buf), ptr);
-                       i += 4;
-               }
-               buf++;
-               ptr++;
-       }
-       return i;
-}
-
-/* Setup pointers to different channels and also setup buffer sizes. */
-static void setup_memory(struct nozomi *dc)
-{
-       void __iomem *offset = dc->base_addr + dc->config_table.dl_start;
-       /* The length reported is including the length field of 4 bytes,
-        * hence subtract with 4.
-        */
-       const u16 buff_offset = 4;
-
-       /* Modem port dl configuration */
-       dc->port[PORT_MDM].dl_addr[CH_A] = offset;
-       dc->port[PORT_MDM].dl_addr[CH_B] =
-                               (offset += dc->config_table.dl_mdm_len1);
-       dc->port[PORT_MDM].dl_size[CH_A] =
-                               dc->config_table.dl_mdm_len1 - buff_offset;
-       dc->port[PORT_MDM].dl_size[CH_B] =
-                               dc->config_table.dl_mdm_len2 - buff_offset;
-
-       /* Diag port dl configuration */
-       dc->port[PORT_DIAG].dl_addr[CH_A] =
-                               (offset += dc->config_table.dl_mdm_len2);
-       dc->port[PORT_DIAG].dl_size[CH_A] =
-                               dc->config_table.dl_diag_len1 - buff_offset;
-       dc->port[PORT_DIAG].dl_addr[CH_B] =
-                               (offset += dc->config_table.dl_diag_len1);
-       dc->port[PORT_DIAG].dl_size[CH_B] =
-                               dc->config_table.dl_diag_len2 - buff_offset;
-
-       /* App1 port dl configuration */
-       dc->port[PORT_APP1].dl_addr[CH_A] =
-                               (offset += dc->config_table.dl_diag_len2);
-       dc->port[PORT_APP1].dl_size[CH_A] =
-                               dc->config_table.dl_app1_len - buff_offset;
-
-       /* App2 port dl configuration */
-       dc->port[PORT_APP2].dl_addr[CH_A] =
-                               (offset += dc->config_table.dl_app1_len);
-       dc->port[PORT_APP2].dl_size[CH_A] =
-                               dc->config_table.dl_app2_len - buff_offset;
-
-       /* Ctrl dl configuration */
-       dc->port[PORT_CTRL].dl_addr[CH_A] =
-                               (offset += dc->config_table.dl_app2_len);
-       dc->port[PORT_CTRL].dl_size[CH_A] =
-                               dc->config_table.dl_ctrl_len - buff_offset;
-
-       offset = dc->base_addr + dc->config_table.ul_start;
-
-       /* Modem Port ul configuration */
-       dc->port[PORT_MDM].ul_addr[CH_A] = offset;
-       dc->port[PORT_MDM].ul_size[CH_A] =
-                               dc->config_table.ul_mdm_len1 - buff_offset;
-       dc->port[PORT_MDM].ul_addr[CH_B] =
-                               (offset += dc->config_table.ul_mdm_len1);
-       dc->port[PORT_MDM].ul_size[CH_B] =
-                               dc->config_table.ul_mdm_len2 - buff_offset;
-
-       /* Diag port ul configuration */
-       dc->port[PORT_DIAG].ul_addr[CH_A] =
-                               (offset += dc->config_table.ul_mdm_len2);
-       dc->port[PORT_DIAG].ul_size[CH_A] =
-                               dc->config_table.ul_diag_len - buff_offset;
-
-       /* App1 port ul configuration */
-       dc->port[PORT_APP1].ul_addr[CH_A] =
-                               (offset += dc->config_table.ul_diag_len);
-       dc->port[PORT_APP1].ul_size[CH_A] =
-                               dc->config_table.ul_app1_len - buff_offset;
-
-       /* App2 port ul configuration */
-       dc->port[PORT_APP2].ul_addr[CH_A] =
-                               (offset += dc->config_table.ul_app1_len);
-       dc->port[PORT_APP2].ul_size[CH_A] =
-                               dc->config_table.ul_app2_len - buff_offset;
-
-       /* Ctrl ul configuration */
-       dc->port[PORT_CTRL].ul_addr[CH_A] =
-                               (offset += dc->config_table.ul_app2_len);
-       dc->port[PORT_CTRL].ul_size[CH_A] =
-                               dc->config_table.ul_ctrl_len - buff_offset;
-}
-
-/* Dump config table under initalization phase */
-#ifdef DEBUG
-static void dump_table(const struct nozomi *dc)
-{
-       DBG3("signature: 0x%08X", dc->config_table.signature);
-       DBG3("version: 0x%04X", dc->config_table.version);
-       DBG3("product_information: 0x%04X", \
-                               dc->config_table.product_information);
-       DBG3("toggle enabled: %d", dc->config_table.toggle.enabled);
-       DBG3("toggle up_mdm: %d", dc->config_table.toggle.mdm_ul);
-       DBG3("toggle dl_mdm: %d", dc->config_table.toggle.mdm_dl);
-       DBG3("toggle dl_dbg: %d", dc->config_table.toggle.diag_dl);
-
-       DBG3("dl_start: 0x%04X", dc->config_table.dl_start);
-       DBG3("dl_mdm_len0: 0x%04X, %d", dc->config_table.dl_mdm_len1,
-          dc->config_table.dl_mdm_len1);
-       DBG3("dl_mdm_len1: 0x%04X, %d", dc->config_table.dl_mdm_len2,
-          dc->config_table.dl_mdm_len2);
-       DBG3("dl_diag_len0: 0x%04X, %d", dc->config_table.dl_diag_len1,
-          dc->config_table.dl_diag_len1);
-       DBG3("dl_diag_len1: 0x%04X, %d", dc->config_table.dl_diag_len2,
-          dc->config_table.dl_diag_len2);
-       DBG3("dl_app1_len: 0x%04X, %d", dc->config_table.dl_app1_len,
-          dc->config_table.dl_app1_len);
-       DBG3("dl_app2_len: 0x%04X, %d", dc->config_table.dl_app2_len,
-          dc->config_table.dl_app2_len);
-       DBG3("dl_ctrl_len: 0x%04X, %d", dc->config_table.dl_ctrl_len,
-          dc->config_table.dl_ctrl_len);
-       DBG3("ul_start: 0x%04X, %d", dc->config_table.ul_start,
-          dc->config_table.ul_start);
-       DBG3("ul_mdm_len[0]: 0x%04X, %d", dc->config_table.ul_mdm_len1,
-          dc->config_table.ul_mdm_len1);
-       DBG3("ul_mdm_len[1]: 0x%04X, %d", dc->config_table.ul_mdm_len2,
-          dc->config_table.ul_mdm_len2);
-       DBG3("ul_diag_len: 0x%04X, %d", dc->config_table.ul_diag_len,
-          dc->config_table.ul_diag_len);
-       DBG3("ul_app1_len: 0x%04X, %d", dc->config_table.ul_app1_len,
-          dc->config_table.ul_app1_len);
-       DBG3("ul_app2_len: 0x%04X, %d", dc->config_table.ul_app2_len,
-          dc->config_table.ul_app2_len);
-       DBG3("ul_ctrl_len: 0x%04X, %d", dc->config_table.ul_ctrl_len,
-          dc->config_table.ul_ctrl_len);
-}
-#else
-static inline void dump_table(const struct nozomi *dc) { }
-#endif
-
-/*
- * Read configuration table from card under intalization phase
- * Returns 1 if ok, else 0
- */
-static int nozomi_read_config_table(struct nozomi *dc)
-{
-       read_mem32((u32 *) &dc->config_table, dc->base_addr + 0,
-                                               sizeof(struct config_table));
-
-       if (dc->config_table.signature != CONFIG_MAGIC) {
-               dev_err(&dc->pdev->dev, "ConfigTable Bad! 0x%08X != 0x%08X\n",
-                       dc->config_table.signature, CONFIG_MAGIC);
-               return 0;
-       }
-
-       if ((dc->config_table.version == 0)
-           || (dc->config_table.toggle.enabled == TOGGLE_VALID)) {
-               int i;
-               DBG1("Second phase, configuring card");
-
-               setup_memory(dc);
-
-               dc->port[PORT_MDM].toggle_ul = dc->config_table.toggle.mdm_ul;
-               dc->port[PORT_MDM].toggle_dl = dc->config_table.toggle.mdm_dl;
-               dc->port[PORT_DIAG].toggle_dl = dc->config_table.toggle.diag_dl;
-               DBG1("toggle ports: MDM UL:%d MDM DL:%d, DIAG DL:%d",
-                  dc->port[PORT_MDM].toggle_ul,
-                  dc->port[PORT_MDM].toggle_dl, dc->port[PORT_DIAG].toggle_dl);
-
-               dump_table(dc);
-
-               for (i = PORT_MDM; i < MAX_PORT; i++) {
-                       memset(&dc->port[i].ctrl_dl, 0, sizeof(struct ctrl_dl));
-                       memset(&dc->port[i].ctrl_ul, 0, sizeof(struct ctrl_ul));
-               }
-
-               /* Enable control channel */
-               dc->last_ier = dc->last_ier | CTRL_DL;
-               writew(dc->last_ier, dc->reg_ier);
-
-               dc->state = NOZOMI_STATE_ALLOCATED;
-               dev_info(&dc->pdev->dev, "Initialization OK!\n");
-               return 1;
-       }
-
-       if ((dc->config_table.version > 0)
-           && (dc->config_table.toggle.enabled != TOGGLE_VALID)) {
-               u32 offset = 0;
-               DBG1("First phase: pushing upload buffers, clearing download");
-
-               dev_info(&dc->pdev->dev, "Version of card: %d\n",
-                        dc->config_table.version);
-
-               /* Here we should disable all I/O over F32. */
-               setup_memory(dc);
-
-               /*
-                * We should send ALL channel pair tokens back along
-                * with reset token
-                */
-
-               /* push upload modem buffers */
-               write_mem32(dc->port[PORT_MDM].ul_addr[CH_A],
-                       (u32 *) &offset, 4);
-               write_mem32(dc->port[PORT_MDM].ul_addr[CH_B],
-                       (u32 *) &offset, 4);
-
-               writew(MDM_UL | DIAG_DL | MDM_DL, dc->reg_fcr);
-
-               DBG1("First phase done");
-       }
-
-       return 1;
-}
-
-/* Enable uplink interrupts  */
-static void enable_transmit_ul(enum port_type port, struct nozomi *dc)
-{
-       static const u16 mask[] = {MDM_UL, DIAG_UL, APP1_UL, APP2_UL, CTRL_UL};
-
-       if (port < NOZOMI_MAX_PORTS) {
-               dc->last_ier |= mask[port];
-               writew(dc->last_ier, dc->reg_ier);
-       } else {
-               dev_err(&dc->pdev->dev, "Called with wrong port?\n");
-       }
-}
-
-/* Disable uplink interrupts  */
-static void disable_transmit_ul(enum port_type port, struct nozomi *dc)
-{
-       static const u16 mask[] =
-               {~MDM_UL, ~DIAG_UL, ~APP1_UL, ~APP2_UL, ~CTRL_UL};
-
-       if (port < NOZOMI_MAX_PORTS) {
-               dc->last_ier &= mask[port];
-               writew(dc->last_ier, dc->reg_ier);
-       } else {
-               dev_err(&dc->pdev->dev, "Called with wrong port?\n");
-       }
-}
-
-/* Enable downlink interrupts */
-static void enable_transmit_dl(enum port_type port, struct nozomi *dc)
-{
-       static const u16 mask[] = {MDM_DL, DIAG_DL, APP1_DL, APP2_DL, CTRL_DL};
-
-       if (port < NOZOMI_MAX_PORTS) {
-               dc->last_ier |= mask[port];
-               writew(dc->last_ier, dc->reg_ier);
-       } else {
-               dev_err(&dc->pdev->dev, "Called with wrong port?\n");
-       }
-}
-
-/* Disable downlink interrupts */
-static void disable_transmit_dl(enum port_type port, struct nozomi *dc)
-{
-       static const u16 mask[] =
-               {~MDM_DL, ~DIAG_DL, ~APP1_DL, ~APP2_DL, ~CTRL_DL};
-
-       if (port < NOZOMI_MAX_PORTS) {
-               dc->last_ier &= mask[port];
-               writew(dc->last_ier, dc->reg_ier);
-       } else {
-               dev_err(&dc->pdev->dev, "Called with wrong port?\n");
-       }
-}
-
-/*
- * Return 1 - send buffer to card and ack.
- * Return 0 - don't ack, don't send buffer to card.
- */
-static int send_data(enum port_type index, struct nozomi *dc)
-{
-       u32 size = 0;
-       struct port *port = &dc->port[index];
-       const u8 toggle = port->toggle_ul;
-       void __iomem *addr = port->ul_addr[toggle];
-       const u32 ul_size = port->ul_size[toggle];
-       struct tty_struct *tty = tty_port_tty_get(&port->port);
-
-       /* Get data from tty and place in buf for now */
-       size = kfifo_out(&port->fifo_ul, dc->send_buf,
-                          ul_size < SEND_BUF_MAX ? ul_size : SEND_BUF_MAX);
-
-       if (size == 0) {
-               DBG4("No more data to send, disable link:");
-               tty_kref_put(tty);
-               return 0;
-       }
-
-       /* DUMP(buf, size); */
-
-       /* Write length + data */
-       write_mem32(addr, (u32 *) &size, 4);
-       write_mem32(addr + 4, (u32 *) dc->send_buf, size);
-
-       if (tty)
-               tty_wakeup(tty);
-
-       tty_kref_put(tty);
-       return 1;
-}
-
-/* If all data has been read, return 1, else 0 */
-static int receive_data(enum port_type index, struct nozomi *dc)
-{
-       u8 buf[RECEIVE_BUF_MAX] = { 0 };
-       int size;
-       u32 offset = 4;
-       struct port *port = &dc->port[index];
-       void __iomem *addr = port->dl_addr[port->toggle_dl];
-       struct tty_struct *tty = tty_port_tty_get(&port->port);
-       int i, ret;
-
-       if (unlikely(!tty)) {
-               DBG1("tty not open for port: %d?", index);
-               return 1;
-       }
-
-       read_mem32((u32 *) &size, addr, 4);
-       /*  DBG1( "%d bytes port: %d", size, index); */
-
-       if (test_bit(TTY_THROTTLED, &tty->flags)) {
-               DBG1("No room in tty, don't read data, don't ack interrupt, "
-                       "disable interrupt");
-
-               /* disable interrupt in downlink... */
-               disable_transmit_dl(index, dc);
-               ret = 0;
-               goto put;
-       }
-
-       if (unlikely(size == 0)) {
-               dev_err(&dc->pdev->dev, "size == 0?\n");
-               ret = 1;
-               goto put;
-       }
-
-       while (size > 0) {
-               read_mem32((u32 *) buf, addr + offset, RECEIVE_BUF_MAX);
-
-               if (size == 1) {
-                       tty_insert_flip_char(tty, buf[0], TTY_NORMAL);
-                       size = 0;
-               } else if (size < RECEIVE_BUF_MAX) {
-                       size -= tty_insert_flip_string(tty, (char *) buf, size);
-               } else {
-                       i = tty_insert_flip_string(tty, \
-                                               (char *) buf, RECEIVE_BUF_MAX);
-                       size -= i;
-                       offset += i;
-               }
-       }
-
-       set_bit(index, &dc->flip);
-       ret = 1;
-put:
-       tty_kref_put(tty);
-       return ret;
-}
-
-/* Debug for interrupts */
-#ifdef DEBUG
-static char *interrupt2str(u16 interrupt)
-{
-       static char buf[TMP_BUF_MAX];
-       char *p = buf;
-
-       interrupt & MDM_DL1 ? p += snprintf(p, TMP_BUF_MAX, "MDM_DL1 ") : NULL;
-       interrupt & MDM_DL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
-                                       "MDM_DL2 ") : NULL;
-
-       interrupt & MDM_UL1 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
-                                       "MDM_UL1 ") : NULL;
-       interrupt & MDM_UL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
-                                       "MDM_UL2 ") : NULL;
-
-       interrupt & DIAG_DL1 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
-                                       "DIAG_DL1 ") : NULL;
-       interrupt & DIAG_DL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
-                                       "DIAG_DL2 ") : NULL;
-
-       interrupt & DIAG_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
-                                       "DIAG_UL ") : NULL;
-
-       interrupt & APP1_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
-                                       "APP1_DL ") : NULL;
-       interrupt & APP2_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
-                                       "APP2_DL ") : NULL;
-
-       interrupt & APP1_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
-                                       "APP1_UL ") : NULL;
-       interrupt & APP2_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
-                                       "APP2_UL ") : NULL;
-
-       interrupt & CTRL_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
-                                       "CTRL_DL ") : NULL;
-       interrupt & CTRL_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
-                                       "CTRL_UL ") : NULL;
-
-       interrupt & RESET ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
-                                       "RESET ") : NULL;
-
-       return buf;
-}
-#endif
-
-/*
- * Receive flow control
- * Return 1 - If ok, else 0
- */
-static int receive_flow_control(struct nozomi *dc)
-{
-       enum port_type port = PORT_MDM;
-       struct ctrl_dl ctrl_dl;
-       struct ctrl_dl old_ctrl;
-       u16 enable_ier = 0;
-
-       read_mem32((u32 *) &ctrl_dl, dc->port[PORT_CTRL].dl_addr[CH_A], 2);
-
-       switch (ctrl_dl.port) {
-       case CTRL_CMD:
-               DBG1("The Base Band sends this value as a response to a "
-                       "request for IMSI detach sent over the control "
-                       "channel uplink (see section 7.6.1).");
-               break;
-       case CTRL_MDM:
-               port = PORT_MDM;
-               enable_ier = MDM_DL;
-               break;
-       case CTRL_DIAG:
-               port = PORT_DIAG;
-               enable_ier = DIAG_DL;
-               break;
-       case CTRL_APP1:
-               port = PORT_APP1;
-               enable_ier = APP1_DL;
-               break;
-       case CTRL_APP2:
-               port = PORT_APP2;
-               enable_ier = APP2_DL;
-               if (dc->state == NOZOMI_STATE_ALLOCATED) {
-                       /*
-                        * After card initialization the flow control
-                        * received for APP2 is always the last
-                        */
-                       dc->state = NOZOMI_STATE_READY;
-                       dev_info(&dc->pdev->dev, "Device READY!\n");
-               }
-               break;
-       default:
-               dev_err(&dc->pdev->dev,
-                       "ERROR: flow control received for non-existing port\n");
-               return 0;
-       };
-
-       DBG1("0x%04X->0x%04X", *((u16 *)&dc->port[port].ctrl_dl),
-          *((u16 *)&ctrl_dl));
-
-       old_ctrl = dc->port[port].ctrl_dl;
-       dc->port[port].ctrl_dl = ctrl_dl;
-
-       if (old_ctrl.CTS == 1 && ctrl_dl.CTS == 0) {
-               DBG1("Disable interrupt (0x%04X) on port: %d",
-                       enable_ier, port);
-               disable_transmit_ul(port, dc);
-
-       } else if (old_ctrl.CTS == 0 && ctrl_dl.CTS == 1) {
-
-               if (kfifo_len(&dc->port[port].fifo_ul)) {
-                       DBG1("Enable interrupt (0x%04X) on port: %d",
-                               enable_ier, port);
-                       DBG1("Data in buffer [%d], enable transmit! ",
-                               kfifo_len(&dc->port[port].fifo_ul));
-                       enable_transmit_ul(port, dc);
-               } else {
-                       DBG1("No data in buffer...");
-               }
-       }
-
-       if (*(u16 *)&old_ctrl == *(u16 *)&ctrl_dl) {
-               DBG1(" No change in mctrl");
-               return 1;
-       }
-       /* Update statistics */
-       if (old_ctrl.CTS != ctrl_dl.CTS)
-               dc->port[port].tty_icount.cts++;
-       if (old_ctrl.DSR != ctrl_dl.DSR)
-               dc->port[port].tty_icount.dsr++;
-       if (old_ctrl.RI != ctrl_dl.RI)
-               dc->port[port].tty_icount.rng++;
-       if (old_ctrl.DCD != ctrl_dl.DCD)
-               dc->port[port].tty_icount.dcd++;
-
-       wake_up_interruptible(&dc->port[port].tty_wait);
-
-       DBG1("port: %d DCD(%d), CTS(%d), RI(%d), DSR(%d)",
-          port,
-          dc->port[port].tty_icount.dcd, dc->port[port].tty_icount.cts,
-          dc->port[port].tty_icount.rng, dc->port[port].tty_icount.dsr);
-
-       return 1;
-}
-
-static enum ctrl_port_type port2ctrl(enum port_type port,
-                                       const struct nozomi *dc)
-{
-       switch (port) {
-       case PORT_MDM:
-               return CTRL_MDM;
-       case PORT_DIAG:
-               return CTRL_DIAG;
-       case PORT_APP1:
-               return CTRL_APP1;
-       case PORT_APP2:
-               return CTRL_APP2;
-       default:
-               dev_err(&dc->pdev->dev,
-                       "ERROR: send flow control " \
-                       "received for non-existing port\n");
-       };
-       return CTRL_ERROR;
-}
-
-/*
- * Send flow control, can only update one channel at a time
- * Return 0 - If we have updated all flow control
- * Return 1 - If we need to update more flow control, ack current enable more
- */
-static int send_flow_control(struct nozomi *dc)
-{
-       u32 i, more_flow_control_to_be_updated = 0;
-       u16 *ctrl;
-
-       for (i = PORT_MDM; i < MAX_PORT; i++) {
-               if (dc->port[i].update_flow_control) {
-                       if (more_flow_control_to_be_updated) {
-                               /* We have more flow control to be updated */
-                               return 1;
-                       }
-                       dc->port[i].ctrl_ul.port = port2ctrl(i, dc);
-                       ctrl = (u16 *)&dc->port[i].ctrl_ul;
-                       write_mem32(dc->port[PORT_CTRL].ul_addr[0], \
-                               (u32 *) ctrl, 2);
-                       dc->port[i].update_flow_control = 0;
-                       more_flow_control_to_be_updated = 1;
-               }
-       }
-       return 0;
-}
-
-/*
- * Handle downlink data, ports that are handled are modem and diagnostics
- * Return 1 - ok
- * Return 0 - toggle fields are out of sync
- */
-static int handle_data_dl(struct nozomi *dc, enum port_type port, u8 *toggle,
-                       u16 read_iir, u16 mask1, u16 mask2)
-{
-       if (*toggle == 0 && read_iir & mask1) {
-               if (receive_data(port, dc)) {
-                       writew(mask1, dc->reg_fcr);
-                       *toggle = !(*toggle);
-               }
-
-               if (read_iir & mask2) {
-                       if (receive_data(port, dc)) {
-                               writew(mask2, dc->reg_fcr);
-                               *toggle = !(*toggle);
-                       }
-               }
-       } else if (*toggle == 1 && read_iir & mask2) {
-               if (receive_data(port, dc)) {
-                       writew(mask2, dc->reg_fcr);
-                       *toggle = !(*toggle);
-               }
-
-               if (read_iir & mask1) {
-                       if (receive_data(port, dc)) {
-                               writew(mask1, dc->reg_fcr);
-                               *toggle = !(*toggle);
-                       }
-               }
-       } else {
-               dev_err(&dc->pdev->dev, "port out of sync!, toggle:%d\n",
-                       *toggle);
-               return 0;
-       }
-       return 1;
-}
-
-/*
- * Handle uplink data, this is currently for the modem port
- * Return 1 - ok
- * Return 0 - toggle field are out of sync
- */
-static int handle_data_ul(struct nozomi *dc, enum port_type port, u16 read_iir)
-{
-       u8 *toggle = &(dc->port[port].toggle_ul);
-
-       if (*toggle == 0 && read_iir & MDM_UL1) {
-               dc->last_ier &= ~MDM_UL;
-               writew(dc->last_ier, dc->reg_ier);
-               if (send_data(port, dc)) {
-                       writew(MDM_UL1, dc->reg_fcr);
-                       dc->last_ier = dc->last_ier | MDM_UL;
-                       writew(dc->last_ier, dc->reg_ier);
-                       *toggle = !*toggle;
-               }
-
-               if (read_iir & MDM_UL2) {
-                       dc->last_ier &= ~MDM_UL;
-                       writew(dc->last_ier, dc->reg_ier);
-                       if (send_data(port, dc)) {
-                               writew(MDM_UL2, dc->reg_fcr);
-                               dc->last_ier = dc->last_ier | MDM_UL;
-                               writew(dc->last_ier, dc->reg_ier);
-                               *toggle = !*toggle;
-                       }
-               }
-
-       } else if (*toggle == 1 && read_iir & MDM_UL2) {
-               dc->last_ier &= ~MDM_UL;
-               writew(dc->last_ier, dc->reg_ier);
-               if (send_data(port, dc)) {
-                       writew(MDM_UL2, dc->reg_fcr);
-                       dc->last_ier = dc->last_ier | MDM_UL;
-                       writew(dc->last_ier, dc->reg_ier);
-                       *toggle = !*toggle;
-               }
-
-               if (read_iir & MDM_UL1) {
-                       dc->last_ier &= ~MDM_UL;
-                       writew(dc->last_ier, dc->reg_ier);
-                       if (send_data(port, dc)) {
-                               writew(MDM_UL1, dc->reg_fcr);
-                               dc->last_ier = dc->last_ier | MDM_UL;
-                               writew(dc->last_ier, dc->reg_ier);
-                               *toggle = !*toggle;
-                       }
-               }
-       } else {
-               writew(read_iir & MDM_UL, dc->reg_fcr);
-               dev_err(&dc->pdev->dev, "port out of sync!\n");
-               return 0;
-       }
-       return 1;
-}
-
-static irqreturn_t interrupt_handler(int irq, void *dev_id)
-{
-       struct nozomi *dc = dev_id;
-       unsigned int a;
-       u16 read_iir;
-
-       if (!dc)
-               return IRQ_NONE;
-
-       spin_lock(&dc->spin_mutex);
-       read_iir = readw(dc->reg_iir);
-
-       /* Card removed */
-       if (read_iir == (u16)-1)
-               goto none;
-       /*
-        * Just handle interrupt enabled in IER
-        * (by masking with dc->last_ier)
-        */
-       read_iir &= dc->last_ier;
-
-       if (read_iir == 0)
-               goto none;
-
-
-       DBG4("%s irq:0x%04X, prev:0x%04X", interrupt2str(read_iir), read_iir,
-               dc->last_ier);
-
-       if (read_iir & RESET) {
-               if (unlikely(!nozomi_read_config_table(dc))) {
-                       dc->last_ier = 0x0;
-                       writew(dc->last_ier, dc->reg_ier);
-                       dev_err(&dc->pdev->dev, "Could not read status from "
-                               "card, we should disable interface\n");
-               } else {
-                       writew(RESET, dc->reg_fcr);
-               }
-               /* No more useful info if this was the reset interrupt. */
-               goto exit_handler;
-       }
-       if (read_iir & CTRL_UL) {
-               DBG1("CTRL_UL");
-               dc->last_ier &= ~CTRL_UL;
-               writew(dc->last_ier, dc->reg_ier);
-               if (send_flow_control(dc)) {
-                       writew(CTRL_UL, dc->reg_fcr);
-                       dc->last_ier = dc->last_ier | CTRL_UL;
-                       writew(dc->last_ier, dc->reg_ier);
-               }
-       }
-       if (read_iir & CTRL_DL) {
-               receive_flow_control(dc);
-               writew(CTRL_DL, dc->reg_fcr);
-       }
-       if (read_iir & MDM_DL) {
-               if (!handle_data_dl(dc, PORT_MDM,
-                               &(dc->port[PORT_MDM].toggle_dl), read_iir,
-                               MDM_DL1, MDM_DL2)) {
-                       dev_err(&dc->pdev->dev, "MDM_DL out of sync!\n");
-                       goto exit_handler;
-               }
-       }
-       if (read_iir & MDM_UL) {
-               if (!handle_data_ul(dc, PORT_MDM, read_iir)) {
-                       dev_err(&dc->pdev->dev, "MDM_UL out of sync!\n");
-                       goto exit_handler;
-               }
-       }
-       if (read_iir & DIAG_DL) {
-               if (!handle_data_dl(dc, PORT_DIAG,
-                               &(dc->port[PORT_DIAG].toggle_dl), read_iir,
-                               DIAG_DL1, DIAG_DL2)) {
-                       dev_err(&dc->pdev->dev, "DIAG_DL out of sync!\n");
-                       goto exit_handler;
-               }
-       }
-       if (read_iir & DIAG_UL) {
-               dc->last_ier &= ~DIAG_UL;
-               writew(dc->last_ier, dc->reg_ier);
-               if (send_data(PORT_DIAG, dc)) {
-                       writew(DIAG_UL, dc->reg_fcr);
-                       dc->last_ier = dc->last_ier | DIAG_UL;
-                       writew(dc->last_ier, dc->reg_ier);
-               }
-       }
-       if (read_iir & APP1_DL) {
-               if (receive_data(PORT_APP1, dc))
-                       writew(APP1_DL, dc->reg_fcr);
-       }
-       if (read_iir & APP1_UL) {
-               dc->last_ier &= ~APP1_UL;
-               writew(dc->last_ier, dc->reg_ier);
-               if (send_data(PORT_APP1, dc)) {
-                       writew(APP1_UL, dc->reg_fcr);
-                       dc->last_ier = dc->last_ier | APP1_UL;
-                       writew(dc->last_ier, dc->reg_ier);
-               }
-       }
-       if (read_iir & APP2_DL) {
-               if (receive_data(PORT_APP2, dc))
-                       writew(APP2_DL, dc->reg_fcr);
-       }
-       if (read_iir & APP2_UL) {
-               dc->last_ier &= ~APP2_UL;
-               writew(dc->last_ier, dc->reg_ier);
-               if (send_data(PORT_APP2, dc)) {
-                       writew(APP2_UL, dc->reg_fcr);
-                       dc->last_ier = dc->last_ier | APP2_UL;
-                       writew(dc->last_ier, dc->reg_ier);
-               }
-       }
-
-exit_handler:
-       spin_unlock(&dc->spin_mutex);
-       for (a = 0; a < NOZOMI_MAX_PORTS; a++) {
-               struct tty_struct *tty;
-               if (test_and_clear_bit(a, &dc->flip)) {
-                       tty = tty_port_tty_get(&dc->port[a].port);
-                       if (tty)
-                               tty_flip_buffer_push(tty);
-                       tty_kref_put(tty);
-               }
-       }
-       return IRQ_HANDLED;
-none:
-       spin_unlock(&dc->spin_mutex);
-       return IRQ_NONE;
-}
-
-static void nozomi_get_card_type(struct nozomi *dc)
-{
-       int i;
-       u32 size = 0;
-
-       for (i = 0; i < 6; i++)
-               size += pci_resource_len(dc->pdev, i);
-
-       /* Assume card type F32_8 if no match */
-       dc->card_type = size == 2048 ? F32_2 : F32_8;
-
-       dev_info(&dc->pdev->dev, "Card type is: %d\n", dc->card_type);
-}
-
-static void nozomi_setup_private_data(struct nozomi *dc)
-{
-       void __iomem *offset = dc->base_addr + dc->card_type / 2;
-       unsigned int i;
-
-       dc->reg_fcr = (void __iomem *)(offset + R_FCR);
-       dc->reg_iir = (void __iomem *)(offset + R_IIR);
-       dc->reg_ier = (void __iomem *)(offset + R_IER);
-       dc->last_ier = 0;
-       dc->flip = 0;
-
-       dc->port[PORT_MDM].token_dl = MDM_DL;
-       dc->port[PORT_DIAG].token_dl = DIAG_DL;
-       dc->port[PORT_APP1].token_dl = APP1_DL;
-       dc->port[PORT_APP2].token_dl = APP2_DL;
-
-       for (i = 0; i < MAX_PORT; i++)
-               init_waitqueue_head(&dc->port[i].tty_wait);
-}
-
-static ssize_t card_type_show(struct device *dev, struct device_attribute *attr,
-                         char *buf)
-{
-       const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
-
-       return sprintf(buf, "%d\n", dc->card_type);
-}
-static DEVICE_ATTR(card_type, S_IRUGO, card_type_show, NULL);
-
-static ssize_t open_ttys_show(struct device *dev, struct device_attribute *attr,
-                         char *buf)
-{
-       const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
-
-       return sprintf(buf, "%u\n", dc->open_ttys);
-}
-static DEVICE_ATTR(open_ttys, S_IRUGO, open_ttys_show, NULL);
-
-static void make_sysfs_files(struct nozomi *dc)
-{
-       if (device_create_file(&dc->pdev->dev, &dev_attr_card_type))
-               dev_err(&dc->pdev->dev,
-                       "Could not create sysfs file for card_type\n");
-       if (device_create_file(&dc->pdev->dev, &dev_attr_open_ttys))
-               dev_err(&dc->pdev->dev,
-                       "Could not create sysfs file for open_ttys\n");
-}
-
-static void remove_sysfs_files(struct nozomi *dc)
-{
-       device_remove_file(&dc->pdev->dev, &dev_attr_card_type);
-       device_remove_file(&dc->pdev->dev, &dev_attr_open_ttys);
-}
-
-/* Allocate memory for one device */
-static int __devinit nozomi_card_init(struct pci_dev *pdev,
-                                     const struct pci_device_id *ent)
-{
-       resource_size_t start;
-       int ret;
-       struct nozomi *dc = NULL;
-       int ndev_idx;
-       int i;
-
-       dev_dbg(&pdev->dev, "Init, new card found\n");
-
-       for (ndev_idx = 0; ndev_idx < ARRAY_SIZE(ndevs); ndev_idx++)
-               if (!ndevs[ndev_idx])
-                       break;
-
-       if (ndev_idx >= ARRAY_SIZE(ndevs)) {
-               dev_err(&pdev->dev, "no free tty range for this card left\n");
-               ret = -EIO;
-               goto err;
-       }
-
-       dc = kzalloc(sizeof(struct nozomi), GFP_KERNEL);
-       if (unlikely(!dc)) {
-               dev_err(&pdev->dev, "Could not allocate memory\n");
-               ret = -ENOMEM;
-               goto err_free;
-       }
-
-       dc->pdev = pdev;
-
-       ret = pci_enable_device(dc->pdev);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to enable PCI Device\n");
-               goto err_free;
-       }
-
-       ret = pci_request_regions(dc->pdev, NOZOMI_NAME);
-       if (ret) {
-               dev_err(&pdev->dev, "I/O address 0x%04x already in use\n",
-                       (int) /* nozomi_private.io_addr */ 0);
-               goto err_disable_device;
-       }
-
-       start = pci_resource_start(dc->pdev, 0);
-       if (start == 0) {
-               dev_err(&pdev->dev, "No I/O address for card detected\n");
-               ret = -ENODEV;
-               goto err_rel_regs;
-       }
-
-       /* Find out what card type it is */
-       nozomi_get_card_type(dc);
-
-       dc->base_addr = ioremap_nocache(start, dc->card_type);
-       if (!dc->base_addr) {
-               dev_err(&pdev->dev, "Unable to map card MMIO\n");
-               ret = -ENODEV;
-               goto err_rel_regs;
-       }
-
-       dc->send_buf = kmalloc(SEND_BUF_MAX, GFP_KERNEL);
-       if (!dc->send_buf) {
-               dev_err(&pdev->dev, "Could not allocate send buffer?\n");
-               ret = -ENOMEM;
-               goto err_free_sbuf;
-       }
-
-       for (i = PORT_MDM; i < MAX_PORT; i++) {
-               if (kfifo_alloc(&dc->port[i].fifo_ul,
-                     FIFO_BUFFER_SIZE_UL, GFP_ATOMIC)) {
-                       dev_err(&pdev->dev,
-                                       "Could not allocate kfifo buffer\n");
-                       ret = -ENOMEM;
-                       goto err_free_kfifo;
-               }
-       }
-
-       spin_lock_init(&dc->spin_mutex);
-
-       nozomi_setup_private_data(dc);
-
-       /* Disable all interrupts */
-       dc->last_ier = 0;
-       writew(dc->last_ier, dc->reg_ier);
-
-       ret = request_irq(pdev->irq, &interrupt_handler, IRQF_SHARED,
-                       NOZOMI_NAME, dc);
-       if (unlikely(ret)) {
-               dev_err(&pdev->dev, "can't request irq %d\n", pdev->irq);
-               goto err_free_kfifo;
-       }
-
-       DBG1("base_addr: %p", dc->base_addr);
-
-       make_sysfs_files(dc);
-
-       dc->index_start = ndev_idx * MAX_PORT;
-       ndevs[ndev_idx] = dc;
-
-       pci_set_drvdata(pdev, dc);
-
-       /* Enable RESET interrupt */
-       dc->last_ier = RESET;
-       iowrite16(dc->last_ier, dc->reg_ier);
-
-       dc->state = NOZOMI_STATE_ENABLED;
-
-       for (i = 0; i < MAX_PORT; i++) {
-               struct device *tty_dev;
-               struct port *port = &dc->port[i];
-               port->dc = dc;
-               mutex_init(&port->tty_sem);
-               tty_port_init(&port->port);
-               port->port.ops = &noz_tty_port_ops;
-               tty_dev = tty_register_device(ntty_driver, dc->index_start + i,
-                                                       &pdev->dev);
-
-               if (IS_ERR(tty_dev)) {
-                       ret = PTR_ERR(tty_dev);
-                       dev_err(&pdev->dev, "Could not allocate tty?\n");
-                       goto err_free_tty;
-               }
-       }
-
-       return 0;
-
-err_free_tty:
-       for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i)
-               tty_unregister_device(ntty_driver, i);
-err_free_kfifo:
-       for (i = 0; i < MAX_PORT; i++)
-               kfifo_free(&dc->port[i].fifo_ul);
-err_free_sbuf:
-       kfree(dc->send_buf);
-       iounmap(dc->base_addr);
-err_rel_regs:
-       pci_release_regions(pdev);
-err_disable_device:
-       pci_disable_device(pdev);
-err_free:
-       kfree(dc);
-err:
-       return ret;
-}
-
-static void __devexit tty_exit(struct nozomi *dc)
-{
-       unsigned int i;
-
-       DBG1(" ");
-
-       flush_scheduled_work();
-
-       for (i = 0; i < MAX_PORT; ++i) {
-               struct tty_struct *tty = tty_port_tty_get(&dc->port[i].port);
-               if (tty && list_empty(&tty->hangup_work.entry))
-                       tty_hangup(tty);
-               tty_kref_put(tty);
-       }
-       /* Racy below - surely should wait for scheduled work to be done or
-          complete off a hangup method ? */
-       while (dc->open_ttys)
-               msleep(1);
-       for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i)
-               tty_unregister_device(ntty_driver, i);
-}
-
-/* Deallocate memory for one device */
-static void __devexit nozomi_card_exit(struct pci_dev *pdev)
-{
-       int i;
-       struct ctrl_ul ctrl;
-       struct nozomi *dc = pci_get_drvdata(pdev);
-
-       /* Disable all interrupts */
-       dc->last_ier = 0;
-       writew(dc->last_ier, dc->reg_ier);
-
-       tty_exit(dc);
-
-       /* Send 0x0001, command card to resend the reset token.  */
-       /* This is to get the reset when the module is reloaded. */
-       ctrl.port = 0x00;
-       ctrl.reserved = 0;
-       ctrl.RTS = 0;
-       ctrl.DTR = 1;
-       DBG1("sending flow control 0x%04X", *((u16 *)&ctrl));
-
-       /* Setup dc->reg addresses to we can use defines here */
-       write_mem32(dc->port[PORT_CTRL].ul_addr[0], (u32 *)&ctrl, 2);
-       writew(CTRL_UL, dc->reg_fcr);   /* push the token to the card. */
-
-       remove_sysfs_files(dc);
-
-       free_irq(pdev->irq, dc);
-
-       for (i = 0; i < MAX_PORT; i++)
-               kfifo_free(&dc->port[i].fifo_ul);
-
-       kfree(dc->send_buf);
-
-       iounmap(dc->base_addr);
-
-       pci_release_regions(pdev);
-
-       pci_disable_device(pdev);
-
-       ndevs[dc->index_start / MAX_PORT] = NULL;
-
-       kfree(dc);
-}
-
-static void set_rts(const struct tty_struct *tty, int rts)
-{
-       struct port *port = get_port_by_tty(tty);
-
-       port->ctrl_ul.RTS = rts;
-       port->update_flow_control = 1;
-       enable_transmit_ul(PORT_CTRL, get_dc_by_tty(tty));
-}
-
-static void set_dtr(const struct tty_struct *tty, int dtr)
-{
-       struct port *port = get_port_by_tty(tty);
-
-       DBG1("SETTING DTR index: %d, dtr: %d", tty->index, dtr);
-
-       port->ctrl_ul.DTR = dtr;
-       port->update_flow_control = 1;
-       enable_transmit_ul(PORT_CTRL, get_dc_by_tty(tty));
-}
-
-/*
- * ----------------------------------------------------------------------------
- * TTY code
- * ----------------------------------------------------------------------------
- */
-
-static int ntty_install(struct tty_driver *driver, struct tty_struct *tty)
-{
-       struct port *port = get_port_by_tty(tty);
-       struct nozomi *dc = get_dc_by_tty(tty);
-       int ret;
-       if (!port || !dc || dc->state != NOZOMI_STATE_READY)
-               return -ENODEV;
-       ret = tty_init_termios(tty);
-       if (ret == 0) {
-               tty_driver_kref_get(driver);
-               tty->count++;
-               tty->driver_data = port;
-               driver->ttys[tty->index] = tty;
-       }
-       return ret;
-}
-
-static void ntty_cleanup(struct tty_struct *tty)
-{
-       tty->driver_data = NULL;
-}
-
-static int ntty_activate(struct tty_port *tport, struct tty_struct *tty)
-{
-       struct port *port = container_of(tport, struct port, port);
-       struct nozomi *dc = port->dc;
-       unsigned long flags;
-
-       DBG1("open: %d", port->token_dl);
-       spin_lock_irqsave(&dc->spin_mutex, flags);
-       dc->last_ier = dc->last_ier | port->token_dl;
-       writew(dc->last_ier, dc->reg_ier);
-       dc->open_ttys++;
-       spin_unlock_irqrestore(&dc->spin_mutex, flags);
-       printk("noz: activated %d: %p\n", tty->index, tport);
-       return 0;
-}
-
-static int ntty_open(struct tty_struct *tty, struct file *filp)
-{
-       struct port *port = tty->driver_data;
-       return tty_port_open(&port->port, tty, filp);
-}
-
-static void ntty_shutdown(struct tty_port *tport)
-{
-       struct port *port = container_of(tport, struct port, port);
-       struct nozomi *dc = port->dc;
-       unsigned long flags;
-
-       DBG1("close: %d", port->token_dl);
-       spin_lock_irqsave(&dc->spin_mutex, flags);
-       dc->last_ier &= ~(port->token_dl);
-       writew(dc->last_ier, dc->reg_ier);
-       dc->open_ttys--;
-       spin_unlock_irqrestore(&dc->spin_mutex, flags);
-       printk("noz: shutdown %p\n", tport);
-}
-
-static void ntty_close(struct tty_struct *tty, struct file *filp)
-{
-       struct port *port = tty->driver_data;
-       if (port)
-               tty_port_close(&port->port, tty, filp);
-}
-
-static void ntty_hangup(struct tty_struct *tty)
-{
-       struct port *port = tty->driver_data;
-       tty_port_hangup(&port->port);
-}
-
-/*
- * called when the userspace process writes to the tty (/dev/noz*).
- * Data is inserted into a fifo, which is then read and transfered to the modem.
- */
-static int ntty_write(struct tty_struct *tty, const unsigned char *buffer,
-                     int count)
-{
-       int rval = -EINVAL;
-       struct nozomi *dc = get_dc_by_tty(tty);
-       struct port *port = tty->driver_data;
-       unsigned long flags;
-
-       /* DBG1( "WRITEx: %d, index = %d", count, index); */
-
-       if (!dc || !port)
-               return -ENODEV;
-
-       mutex_lock(&port->tty_sem);
-
-       if (unlikely(!port->port.count)) {
-               DBG1(" ");
-               goto exit;
-       }
-
-       rval = kfifo_in(&port->fifo_ul, (unsigned char *)buffer, count);
-
-       /* notify card */
-       if (unlikely(dc == NULL)) {
-               DBG1("No device context?");
-               goto exit;
-       }
-
-       spin_lock_irqsave(&dc->spin_mutex, flags);
-       /* CTS is only valid on the modem channel */
-       if (port == &(dc->port[PORT_MDM])) {
-               if (port->ctrl_dl.CTS) {
-                       DBG4("Enable interrupt");
-                       enable_transmit_ul(tty->index % MAX_PORT, dc);
-               } else {
-                       dev_err(&dc->pdev->dev,
-                               "CTS not active on modem port?\n");
-               }
-       } else {
-               enable_transmit_ul(tty->index % MAX_PORT, dc);
-       }
-       spin_unlock_irqrestore(&dc->spin_mutex, flags);
-
-exit:
-       mutex_unlock(&port->tty_sem);
-       return rval;
-}
-
-/*
- * Calculate how much is left in device
- * This method is called by the upper tty layer.
- *   #according to sources N_TTY.c it expects a value >= 0 and
- *    does not check for negative values.
- *
- * If the port is unplugged report lots of room and let the bits
- * dribble away so we don't block anything.
- */
-static int ntty_write_room(struct tty_struct *tty)
-{
-       struct port *port = tty->driver_data;
-       int room = 4096;
-       const struct nozomi *dc = get_dc_by_tty(tty);
-
-       if (dc) {
-               mutex_lock(&port->tty_sem);
-               if (port->port.count)
-                       room = kfifo_avail(&port->fifo_ul);
-               mutex_unlock(&port->tty_sem);
-       }
-       return room;
-}
-
-/* Gets io control parameters */
-static int ntty_tiocmget(struct tty_struct *tty)
-{
-       const struct port *port = tty->driver_data;
-       const struct ctrl_dl *ctrl_dl = &port->ctrl_dl;
-       const struct ctrl_ul *ctrl_ul = &port->ctrl_ul;
-
-       /* Note: these could change under us but it is not clear this
-          matters if so */
-       return  (ctrl_ul->RTS ? TIOCM_RTS : 0) |
-               (ctrl_ul->DTR ? TIOCM_DTR : 0) |
-               (ctrl_dl->DCD ? TIOCM_CAR : 0) |
-               (ctrl_dl->RI  ? TIOCM_RNG : 0) |
-               (ctrl_dl->DSR ? TIOCM_DSR : 0) |
-               (ctrl_dl->CTS ? TIOCM_CTS : 0);
-}
-
-/* Sets io controls parameters */
-static int ntty_tiocmset(struct tty_struct *tty,
-                                       unsigned int set, unsigned int clear)
-{
-       struct nozomi *dc = get_dc_by_tty(tty);
-       unsigned long flags;
-
-       spin_lock_irqsave(&dc->spin_mutex, flags);
-       if (set & TIOCM_RTS)
-               set_rts(tty, 1);
-       else if (clear & TIOCM_RTS)
-               set_rts(tty, 0);
-
-       if (set & TIOCM_DTR)
-               set_dtr(tty, 1);
-       else if (clear & TIOCM_DTR)
-               set_dtr(tty, 0);
-       spin_unlock_irqrestore(&dc->spin_mutex, flags);
-
-       return 0;
-}
-
-static int ntty_cflags_changed(struct port *port, unsigned long flags,
-               struct async_icount *cprev)
-{
-       const struct async_icount cnow = port->tty_icount;
-       int ret;
-
-       ret =   ((flags & TIOCM_RNG) && (cnow.rng != cprev->rng)) ||
-               ((flags & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) ||
-               ((flags & TIOCM_CD)  && (cnow.dcd != cprev->dcd)) ||
-               ((flags & TIOCM_CTS) && (cnow.cts != cprev->cts));
-
-       *cprev = cnow;
-
-       return ret;
-}
-
-static int ntty_tiocgicount(struct tty_struct *tty,
-                               struct serial_icounter_struct *icount)
-{
-       struct port *port = tty->driver_data;
-       const struct async_icount cnow = port->tty_icount;
-
-       icount->cts = cnow.cts;
-       icount->dsr = cnow.dsr;
-       icount->rng = cnow.rng;
-       icount->dcd = cnow.dcd;
-       icount->rx = cnow.rx;
-       icount->tx = cnow.tx;
-       icount->frame = cnow.frame;
-       icount->overrun = cnow.overrun;
-       icount->parity = cnow.parity;
-       icount->brk = cnow.brk;
-       icount->buf_overrun = cnow.buf_overrun;
-       return 0;
-}
-
-static int ntty_ioctl(struct tty_struct *tty,
-                     unsigned int cmd, unsigned long arg)
-{
-       struct port *port = tty->driver_data;
-       int rval = -ENOIOCTLCMD;
-
-       DBG1("******** IOCTL, cmd: %d", cmd);
-
-       switch (cmd) {
-       case TIOCMIWAIT: {
-               struct async_icount cprev = port->tty_icount;
-
-               rval = wait_event_interruptible(port->tty_wait,
-                               ntty_cflags_changed(port, arg, &cprev));
-               break;
-       }
-       default:
-               DBG1("ERR: 0x%08X, %d", cmd, cmd);
-               break;
-       };
-
-       return rval;
-}
-
-/*
- * Called by the upper tty layer when tty buffers are ready
- * to receive data again after a call to throttle.
- */
-static void ntty_unthrottle(struct tty_struct *tty)
-{
-       struct nozomi *dc = get_dc_by_tty(tty);
-       unsigned long flags;
-
-       DBG1("UNTHROTTLE");
-       spin_lock_irqsave(&dc->spin_mutex, flags);
-       enable_transmit_dl(tty->index % MAX_PORT, dc);
-       set_rts(tty, 1);
-
-       spin_unlock_irqrestore(&dc->spin_mutex, flags);
-}
-
-/*
- * Called by the upper tty layer when the tty buffers are almost full.
- * The driver should stop send more data.
- */
-static void ntty_throttle(struct tty_struct *tty)
-{
-       struct nozomi *dc = get_dc_by_tty(tty);
-       unsigned long flags;
-
-       DBG1("THROTTLE");
-       spin_lock_irqsave(&dc->spin_mutex, flags);
-       set_rts(tty, 0);
-       spin_unlock_irqrestore(&dc->spin_mutex, flags);
-}
-
-/* Returns number of chars in buffer, called by tty layer */
-static s32 ntty_chars_in_buffer(struct tty_struct *tty)
-{
-       struct port *port = tty->driver_data;
-       struct nozomi *dc = get_dc_by_tty(tty);
-       s32 rval = 0;
-
-       if (unlikely(!dc || !port)) {
-               goto exit_in_buffer;
-       }
-
-       if (unlikely(!port->port.count)) {
-               dev_err(&dc->pdev->dev, "No tty open?\n");
-               goto exit_in_buffer;
-       }
-
-       rval = kfifo_len(&port->fifo_ul);
-
-exit_in_buffer:
-       return rval;
-}
-
-static const struct tty_port_operations noz_tty_port_ops = {
-       .activate = ntty_activate,
-       .shutdown = ntty_shutdown,
-};
-
-static const struct tty_operations tty_ops = {
-       .ioctl = ntty_ioctl,
-       .open = ntty_open,
-       .close = ntty_close,
-       .hangup = ntty_hangup,
-       .write = ntty_write,
-       .write_room = ntty_write_room,
-       .unthrottle = ntty_unthrottle,
-       .throttle = ntty_throttle,
-       .chars_in_buffer = ntty_chars_in_buffer,
-       .tiocmget = ntty_tiocmget,
-       .tiocmset = ntty_tiocmset,
-       .get_icount = ntty_tiocgicount,
-       .install = ntty_install,
-       .cleanup = ntty_cleanup,
-};
-
-/* Module initialization */
-static struct pci_driver nozomi_driver = {
-       .name = NOZOMI_NAME,
-       .id_table = nozomi_pci_tbl,
-       .probe = nozomi_card_init,
-       .remove = __devexit_p(nozomi_card_exit),
-};
-
-static __init int nozomi_init(void)
-{
-       int ret;
-
-       printk(KERN_INFO "Initializing %s\n", VERSION_STRING);
-
-       ntty_driver = alloc_tty_driver(NTTY_TTY_MAXMINORS);
-       if (!ntty_driver)
-               return -ENOMEM;
-
-       ntty_driver->owner = THIS_MODULE;
-       ntty_driver->driver_name = NOZOMI_NAME_TTY;
-       ntty_driver->name = "noz";
-       ntty_driver->major = 0;
-       ntty_driver->type = TTY_DRIVER_TYPE_SERIAL;
-       ntty_driver->subtype = SERIAL_TYPE_NORMAL;
-       ntty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
-       ntty_driver->init_termios = tty_std_termios;
-       ntty_driver->init_termios.c_cflag = B115200 | CS8 | CREAD | \
-                                               HUPCL | CLOCAL;
-       ntty_driver->init_termios.c_ispeed = 115200;
-       ntty_driver->init_termios.c_ospeed = 115200;
-       tty_set_operations(ntty_driver, &tty_ops);
-
-       ret = tty_register_driver(ntty_driver);
-       if (ret) {
-               printk(KERN_ERR "Nozomi: failed to register ntty driver\n");
-               goto free_tty;
-       }
-
-       ret = pci_register_driver(&nozomi_driver);
-       if (ret) {
-               printk(KERN_ERR "Nozomi: can't register pci driver\n");
-               goto unr_tty;
-       }
-
-       return 0;
-unr_tty:
-       tty_unregister_driver(ntty_driver);
-free_tty:
-       put_tty_driver(ntty_driver);
-       return ret;
-}
-
-static __exit void nozomi_exit(void)
-{
-       printk(KERN_INFO "Unloading %s\n", DRIVER_DESC);
-       pci_unregister_driver(&nozomi_driver);
-       tty_unregister_driver(ntty_driver);
-       put_tty_driver(ntty_driver);
-}
-
-module_init(nozomi_init);
-module_exit(nozomi_exit);
-
-module_param(debug, int, S_IRUGO | S_IWUSR);
-
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c
deleted file mode 100644 (file)
index 3780da8..0000000
+++ /dev/null
@@ -1,3199 +0,0 @@
-/*
- * RocketPort device driver for Linux
- *
- * Written by Theodore Ts'o, 1995, 1996, 1997, 1998, 1999, 2000.
- * 
- * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2003 by Comtrol, 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.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/*
- * Kernel Synchronization:
- *
- * This driver has 2 kernel control paths - exception handlers (calls into the driver
- * from user mode) and the timer bottom half (tasklet).  This is a polled driver, interrupts
- * are not used.
- *
- * Critical data: 
- * -  rp_table[], accessed through passed "info" pointers, is a global (static) array of 
- *    serial port state information and the xmit_buf circular buffer.  Protected by 
- *    a per port spinlock.
- * -  xmit_flags[], an array of ints indexed by line (port) number, indicating that there
- *    is data to be transmitted.  Protected by atomic bit operations.
- * -  rp_num_ports, int indicating number of open ports, protected by atomic operations.
- * 
- * rp_write() and rp_write_char() functions use a per port semaphore to protect against
- * simultaneous access to the same port by more than one process.
- */
-
-/****** Defines ******/
-#define ROCKET_PARANOIA_CHECK
-#define ROCKET_DISABLE_SIMUSAGE
-
-#undef ROCKET_SOFT_FLOW
-#undef ROCKET_DEBUG_OPEN
-#undef ROCKET_DEBUG_INTR
-#undef ROCKET_DEBUG_WRITE
-#undef ROCKET_DEBUG_FLOW
-#undef ROCKET_DEBUG_THROTTLE
-#undef ROCKET_DEBUG_WAIT_UNTIL_SENT
-#undef ROCKET_DEBUG_RECEIVE
-#undef ROCKET_DEBUG_HANGUP
-#undef REV_PCI_ORDER
-#undef ROCKET_DEBUG_IO
-
-#define POLL_PERIOD HZ/100     /*  Polling period .01 seconds (10ms) */
-
-/****** Kernel includes ******/
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/major.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/mutex.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/completion.h>
-#include <linux/wait.h>
-#include <linux/pci.h>
-#include <linux/uaccess.h>
-#include <asm/atomic.h>
-#include <asm/unaligned.h>
-#include <linux/bitops.h>
-#include <linux/spinlock.h>
-#include <linux/init.h>
-
-/****** RocketPort includes ******/
-
-#include "rocket_int.h"
-#include "rocket.h"
-
-#define ROCKET_VERSION "2.09"
-#define ROCKET_DATE "12-June-2003"
-
-/****** RocketPort Local Variables ******/
-
-static void rp_do_poll(unsigned long dummy);
-
-static struct tty_driver *rocket_driver;
-
-static struct rocket_version driver_version = {        
-       ROCKET_VERSION, ROCKET_DATE
-};
-
-static struct r_port *rp_table[MAX_RP_PORTS];         /*  The main repository of serial port state information. */
-static unsigned int xmit_flags[NUM_BOARDS];           /*  Bit significant, indicates port had data to transmit. */
-                                                      /*  eg.  Bit 0 indicates port 0 has xmit data, ...        */
-static atomic_t rp_num_ports_open;                    /*  Number of serial ports open                           */
-static DEFINE_TIMER(rocket_timer, rp_do_poll, 0, 0);
-
-static unsigned long board1;                          /* ISA addresses, retrieved from rocketport.conf          */
-static unsigned long board2;
-static unsigned long board3;
-static unsigned long board4;
-static unsigned long controller;
-static int support_low_speed;
-static unsigned long modem1;
-static unsigned long modem2;
-static unsigned long modem3;
-static unsigned long modem4;
-static unsigned long pc104_1[8];
-static unsigned long pc104_2[8];
-static unsigned long pc104_3[8];
-static unsigned long pc104_4[8];
-static unsigned long *pc104[4] = { pc104_1, pc104_2, pc104_3, pc104_4 };
-
-static int rp_baud_base[NUM_BOARDS];                  /*  Board config info (Someday make a per-board structure)  */
-static unsigned long rcktpt_io_addr[NUM_BOARDS];
-static int rcktpt_type[NUM_BOARDS];
-static int is_PCI[NUM_BOARDS];
-static rocketModel_t rocketModel[NUM_BOARDS];
-static int max_board;
-static const struct tty_port_operations rocket_port_ops;
-
-/*
- * The following arrays define the interrupt bits corresponding to each AIOP.
- * These bits are different between the ISA and regular PCI boards and the
- * Universal PCI boards.
- */
-
-static Word_t aiop_intr_bits[AIOP_CTL_SIZE] = {
-       AIOP_INTR_BIT_0,
-       AIOP_INTR_BIT_1,
-       AIOP_INTR_BIT_2,
-       AIOP_INTR_BIT_3
-};
-
-static Word_t upci_aiop_intr_bits[AIOP_CTL_SIZE] = {
-       UPCI_AIOP_INTR_BIT_0,
-       UPCI_AIOP_INTR_BIT_1,
-       UPCI_AIOP_INTR_BIT_2,
-       UPCI_AIOP_INTR_BIT_3
-};
-
-static Byte_t RData[RDATASIZE] = {
-       0x00, 0x09, 0xf6, 0x82,
-       0x02, 0x09, 0x86, 0xfb,
-       0x04, 0x09, 0x00, 0x0a,
-       0x06, 0x09, 0x01, 0x0a,
-       0x08, 0x09, 0x8a, 0x13,
-       0x0a, 0x09, 0xc5, 0x11,
-       0x0c, 0x09, 0x86, 0x85,
-       0x0e, 0x09, 0x20, 0x0a,
-       0x10, 0x09, 0x21, 0x0a,
-       0x12, 0x09, 0x41, 0xff,
-       0x14, 0x09, 0x82, 0x00,
-       0x16, 0x09, 0x82, 0x7b,
-       0x18, 0x09, 0x8a, 0x7d,
-       0x1a, 0x09, 0x88, 0x81,
-       0x1c, 0x09, 0x86, 0x7a,
-       0x1e, 0x09, 0x84, 0x81,
-       0x20, 0x09, 0x82, 0x7c,
-       0x22, 0x09, 0x0a, 0x0a
-};
-
-static Byte_t RRegData[RREGDATASIZE] = {
-       0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */
-       0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */
-       0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */
-       0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */
-       0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */
-       0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */
-       0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */
-       0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */
-       0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */
-       0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */
-       0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */
-       0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */
-       0x22, 0x09, 0x0a, 0x0a  /* 30: Rx FIFO Enable */
-};
-
-static CONTROLLER_T sController[CTL_SIZE] = {
-       {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0},
-        {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}},
-       {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0},
-        {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}},
-       {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0},
-        {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}},
-       {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0},
-        {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}
-};
-
-static Byte_t sBitMapClrTbl[8] = {
-       0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f
-};
-
-static Byte_t sBitMapSetTbl[8] = {
-       0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
-};
-
-static int sClockPrescale = 0x14;
-
-/*
- *  Line number is the ttySIx number (x), the Minor number.  We 
- *  assign them sequentially, starting at zero.  The following 
- *  array keeps track of the line number assigned to a given board/aiop/channel.
- */
-static unsigned char lineNumbers[MAX_RP_PORTS];
-static unsigned long nextLineNumber;
-
-/*****  RocketPort Static Prototypes   *********/
-static int __init init_ISA(int i);
-static void rp_wait_until_sent(struct tty_struct *tty, int timeout);
-static void rp_flush_buffer(struct tty_struct *tty);
-static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model);
-static unsigned char GetLineNumber(int ctrl, int aiop, int ch);
-static unsigned char SetLineNumber(int ctrl, int aiop, int ch);
-static void rp_start(struct tty_struct *tty);
-static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum,
-                    int ChanNum);
-static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode);
-static void sFlushRxFIFO(CHANNEL_T * ChP);
-static void sFlushTxFIFO(CHANNEL_T * ChP);
-static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags);
-static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags);
-static void sModemReset(CONTROLLER_T * CtlP, int chan, int on);
-static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on);
-static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data);
-static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum,
-                             ByteIO_t * AiopIOList, int AiopIOListSize,
-                             WordIO_t ConfigIO, int IRQNum, Byte_t Frequency,
-                             int PeriodicOnly, int altChanRingIndicator,
-                             int UPCIRingInd);
-static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO,
-                          ByteIO_t * AiopIOList, int AiopIOListSize,
-                          int IRQNum, Byte_t Frequency, int PeriodicOnly);
-static int sReadAiopID(ByteIO_t io);
-static int sReadAiopNumChan(WordIO_t io);
-
-MODULE_AUTHOR("Theodore Ts'o");
-MODULE_DESCRIPTION("Comtrol RocketPort driver");
-module_param(board1, ulong, 0);
-MODULE_PARM_DESC(board1, "I/O port for (ISA) board #1");
-module_param(board2, ulong, 0);
-MODULE_PARM_DESC(board2, "I/O port for (ISA) board #2");
-module_param(board3, ulong, 0);
-MODULE_PARM_DESC(board3, "I/O port for (ISA) board #3");
-module_param(board4, ulong, 0);
-MODULE_PARM_DESC(board4, "I/O port for (ISA) board #4");
-module_param(controller, ulong, 0);
-MODULE_PARM_DESC(controller, "I/O port for (ISA) rocketport controller");
-module_param(support_low_speed, bool, 0);
-MODULE_PARM_DESC(support_low_speed, "1 means support 50 baud, 0 means support 460400 baud");
-module_param(modem1, ulong, 0);
-MODULE_PARM_DESC(modem1, "1 means (ISA) board #1 is a RocketModem");
-module_param(modem2, ulong, 0);
-MODULE_PARM_DESC(modem2, "1 means (ISA) board #2 is a RocketModem");
-module_param(modem3, ulong, 0);
-MODULE_PARM_DESC(modem3, "1 means (ISA) board #3 is a RocketModem");
-module_param(modem4, ulong, 0);
-MODULE_PARM_DESC(modem4, "1 means (ISA) board #4 is a RocketModem");
-module_param_array(pc104_1, ulong, NULL, 0);
-MODULE_PARM_DESC(pc104_1, "set interface types for ISA(PC104) board #1 (e.g. pc104_1=232,232,485,485,...");
-module_param_array(pc104_2, ulong, NULL, 0);
-MODULE_PARM_DESC(pc104_2, "set interface types for ISA(PC104) board #2 (e.g. pc104_2=232,232,485,485,...");
-module_param_array(pc104_3, ulong, NULL, 0);
-MODULE_PARM_DESC(pc104_3, "set interface types for ISA(PC104) board #3 (e.g. pc104_3=232,232,485,485,...");
-module_param_array(pc104_4, ulong, NULL, 0);
-MODULE_PARM_DESC(pc104_4, "set interface types for ISA(PC104) board #4 (e.g. pc104_4=232,232,485,485,...");
-
-static int rp_init(void);
-static void rp_cleanup_module(void);
-
-module_init(rp_init);
-module_exit(rp_cleanup_module);
-
-
-MODULE_LICENSE("Dual BSD/GPL");
-
-/*************************************************************************/
-/*                     Module code starts here                           */
-
-static inline int rocket_paranoia_check(struct r_port *info,
-                                       const char *routine)
-{
-#ifdef ROCKET_PARANOIA_CHECK
-       if (!info)
-               return 1;
-       if (info->magic != RPORT_MAGIC) {
-               printk(KERN_WARNING "Warning: bad magic number for rocketport "
-                               "struct in %s\n", routine);
-               return 1;
-       }
-#endif
-       return 0;
-}
-
-
-/*  Serial port receive data function.  Called (from timer poll) when an AIOPIC signals 
- *  that receive data is present on a serial port.  Pulls data from FIFO, moves it into the 
- *  tty layer.  
- */
-static void rp_do_receive(struct r_port *info,
-                         struct tty_struct *tty,
-                         CHANNEL_t * cp, unsigned int ChanStatus)
-{
-       unsigned int CharNStat;
-       int ToRecv, wRecv, space;
-       unsigned char *cbuf;
-
-       ToRecv = sGetRxCnt(cp);
-#ifdef ROCKET_DEBUG_INTR
-       printk(KERN_INFO "rp_do_receive(%d)...\n", ToRecv);
-#endif
-       if (ToRecv == 0)
-               return;
-
-       /*
-        * if status indicates there are errored characters in the
-        * FIFO, then enter status mode (a word in FIFO holds
-        * character and status).
-        */
-       if (ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) {
-               if (!(ChanStatus & STATMODE)) {
-#ifdef ROCKET_DEBUG_RECEIVE
-                       printk(KERN_INFO "Entering STATMODE...\n");
-#endif
-                       ChanStatus |= STATMODE;
-                       sEnRxStatusMode(cp);
-               }
-       }
-
-       /* 
-        * if we previously entered status mode, then read down the
-        * FIFO one word at a time, pulling apart the character and
-        * the status.  Update error counters depending on status
-        */
-       if (ChanStatus & STATMODE) {
-#ifdef ROCKET_DEBUG_RECEIVE
-               printk(KERN_INFO "Ignore %x, read %x...\n",
-                       info->ignore_status_mask, info->read_status_mask);
-#endif
-               while (ToRecv) {
-                       char flag;
-
-                       CharNStat = sInW(sGetTxRxDataIO(cp));
-#ifdef ROCKET_DEBUG_RECEIVE
-                       printk(KERN_INFO "%x...\n", CharNStat);
-#endif
-                       if (CharNStat & STMBREAKH)
-                               CharNStat &= ~(STMFRAMEH | STMPARITYH);
-                       if (CharNStat & info->ignore_status_mask) {
-                               ToRecv--;
-                               continue;
-                       }
-                       CharNStat &= info->read_status_mask;
-                       if (CharNStat & STMBREAKH)
-                               flag = TTY_BREAK;
-                       else if (CharNStat & STMPARITYH)
-                               flag = TTY_PARITY;
-                       else if (CharNStat & STMFRAMEH)
-                               flag = TTY_FRAME;
-                       else if (CharNStat & STMRCVROVRH)
-                               flag = TTY_OVERRUN;
-                       else
-                               flag = TTY_NORMAL;
-                       tty_insert_flip_char(tty, CharNStat & 0xff, flag);
-                       ToRecv--;
-               }
-
-               /*
-                * after we've emptied the FIFO in status mode, turn
-                * status mode back off
-                */
-               if (sGetRxCnt(cp) == 0) {
-#ifdef ROCKET_DEBUG_RECEIVE
-                       printk(KERN_INFO "Status mode off.\n");
-#endif
-                       sDisRxStatusMode(cp);
-               }
-       } else {
-               /*
-                * we aren't in status mode, so read down the FIFO two
-                * characters at time by doing repeated word IO
-                * transfer.
-                */
-               space = tty_prepare_flip_string(tty, &cbuf, ToRecv);
-               if (space < ToRecv) {
-#ifdef ROCKET_DEBUG_RECEIVE
-                       printk(KERN_INFO "rp_do_receive:insufficient space ToRecv=%d space=%d\n", ToRecv, space);
-#endif
-                       if (space <= 0)
-                               return;
-                       ToRecv = space;
-               }
-               wRecv = ToRecv >> 1;
-               if (wRecv)
-                       sInStrW(sGetTxRxDataIO(cp), (unsigned short *) cbuf, wRecv);
-               if (ToRecv & 1)
-                       cbuf[ToRecv - 1] = sInB(sGetTxRxDataIO(cp));
-       }
-       /*  Push the data up to the tty layer */
-       tty_flip_buffer_push(tty);
-}
-
-/*
- *  Serial port transmit data function.  Called from the timer polling loop as a 
- *  result of a bit set in xmit_flags[], indicating data (from the tty layer) is ready
- *  to be sent out the serial port.  Data is buffered in rp_table[line].xmit_buf, it is 
- *  moved to the port's xmit FIFO.  *info is critical data, protected by spinlocks.
- */
-static void rp_do_transmit(struct r_port *info)
-{
-       int c;
-       CHANNEL_t *cp = &info->channel;
-       struct tty_struct *tty;
-       unsigned long flags;
-
-#ifdef ROCKET_DEBUG_INTR
-       printk(KERN_DEBUG "%s\n", __func__);
-#endif
-       if (!info)
-               return;
-       tty = tty_port_tty_get(&info->port);
-
-       if (tty == NULL) {
-               printk(KERN_WARNING "rp: WARNING %s called with tty==NULL\n", __func__);
-               clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
-               return;
-       }
-
-       spin_lock_irqsave(&info->slock, flags);
-       info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp);
-
-       /*  Loop sending data to FIFO until done or FIFO full */
-       while (1) {
-               if (tty->stopped || tty->hw_stopped)
-                       break;
-               c = min(info->xmit_fifo_room, info->xmit_cnt);
-               c = min(c, XMIT_BUF_SIZE - info->xmit_tail);
-               if (c <= 0 || info->xmit_fifo_room <= 0)
-                       break;
-               sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) (info->xmit_buf + info->xmit_tail), c / 2);
-               if (c & 1)
-                       sOutB(sGetTxRxDataIO(cp), info->xmit_buf[info->xmit_tail + c - 1]);
-               info->xmit_tail += c;
-               info->xmit_tail &= XMIT_BUF_SIZE - 1;
-               info->xmit_cnt -= c;
-               info->xmit_fifo_room -= c;
-#ifdef ROCKET_DEBUG_INTR
-               printk(KERN_INFO "tx %d chars...\n", c);
-#endif
-       }
-
-       if (info->xmit_cnt == 0)
-               clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
-
-       if (info->xmit_cnt < WAKEUP_CHARS) {
-               tty_wakeup(tty);
-#ifdef ROCKETPORT_HAVE_POLL_WAIT
-               wake_up_interruptible(&tty->poll_wait);
-#endif
-       }
-
-       spin_unlock_irqrestore(&info->slock, flags);
-       tty_kref_put(tty);
-
-#ifdef ROCKET_DEBUG_INTR
-       printk(KERN_DEBUG "(%d,%d,%d,%d)...\n", info->xmit_cnt, info->xmit_head,
-              info->xmit_tail, info->xmit_fifo_room);
-#endif
-}
-
-/*
- *  Called when a serial port signals it has read data in it's RX FIFO.
- *  It checks what interrupts are pending and services them, including
- *  receiving serial data.  
- */
-static void rp_handle_port(struct r_port *info)
-{
-       CHANNEL_t *cp;
-       struct tty_struct *tty;
-       unsigned int IntMask, ChanStatus;
-
-       if (!info)
-               return;
-
-       if ((info->port.flags & ASYNC_INITIALIZED) == 0) {
-               printk(KERN_WARNING "rp: WARNING: rp_handle_port called with "
-                               "info->flags & NOT_INIT\n");
-               return;
-       }
-       tty = tty_port_tty_get(&info->port);
-       if (!tty) {
-               printk(KERN_WARNING "rp: WARNING: rp_handle_port called with "
-                               "tty==NULL\n");
-               return;
-       }
-       cp = &info->channel;
-
-       IntMask = sGetChanIntID(cp) & info->intmask;
-#ifdef ROCKET_DEBUG_INTR
-       printk(KERN_INFO "rp_interrupt %02x...\n", IntMask);
-#endif
-       ChanStatus = sGetChanStatus(cp);
-       if (IntMask & RXF_TRIG) {       /* Rx FIFO trigger level */
-               rp_do_receive(info, tty, cp, ChanStatus);
-       }
-       if (IntMask & DELTA_CD) {       /* CD change  */
-#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_INTR) || defined(ROCKET_DEBUG_HANGUP))
-               printk(KERN_INFO "ttyR%d CD now %s...\n", info->line,
-                      (ChanStatus & CD_ACT) ? "on" : "off");
-#endif
-               if (!(ChanStatus & CD_ACT) && info->cd_status) {
-#ifdef ROCKET_DEBUG_HANGUP
-                       printk(KERN_INFO "CD drop, calling hangup.\n");
-#endif
-                       tty_hangup(tty);
-               }
-               info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0;
-               wake_up_interruptible(&info->port.open_wait);
-       }
-#ifdef ROCKET_DEBUG_INTR
-       if (IntMask & DELTA_CTS) {      /* CTS change */
-               printk(KERN_INFO "CTS change...\n");
-       }
-       if (IntMask & DELTA_DSR) {      /* DSR change */
-               printk(KERN_INFO "DSR change...\n");
-       }
-#endif
-       tty_kref_put(tty);
-}
-
-/*
- *  The top level polling routine.  Repeats every 1/100 HZ (10ms).
- */
-static void rp_do_poll(unsigned long dummy)
-{
-       CONTROLLER_t *ctlp;
-       int ctrl, aiop, ch, line;
-       unsigned int xmitmask, i;
-       unsigned int CtlMask;
-       unsigned char AiopMask;
-       Word_t bit;
-
-       /*  Walk through all the boards (ctrl's) */
-       for (ctrl = 0; ctrl < max_board; ctrl++) {
-               if (rcktpt_io_addr[ctrl] <= 0)
-                       continue;
-
-               /*  Get a ptr to the board's control struct */
-               ctlp = sCtlNumToCtlPtr(ctrl);
-
-               /*  Get the interrupt status from the board */
-#ifdef CONFIG_PCI
-               if (ctlp->BusType == isPCI)
-                       CtlMask = sPCIGetControllerIntStatus(ctlp);
-               else
-#endif
-                       CtlMask = sGetControllerIntStatus(ctlp);
-
-               /*  Check if any AIOP read bits are set */
-               for (aiop = 0; CtlMask; aiop++) {
-                       bit = ctlp->AiopIntrBits[aiop];
-                       if (CtlMask & bit) {
-                               CtlMask &= ~bit;
-                               AiopMask = sGetAiopIntStatus(ctlp, aiop);
-
-                               /*  Check if any port read bits are set */
-                               for (ch = 0; AiopMask;  AiopMask >>= 1, ch++) {
-                                       if (AiopMask & 1) {
-
-                                               /*  Get the line number (/dev/ttyRx number). */
-                                               /*  Read the data from the port. */
-                                               line = GetLineNumber(ctrl, aiop, ch);
-                                               rp_handle_port(rp_table[line]);
-                                       }
-                               }
-                       }
-               }
-
-               xmitmask = xmit_flags[ctrl];
-
-               /*
-                *  xmit_flags contains bit-significant flags, indicating there is data
-                *  to xmit on the port. Bit 0 is port 0 on this board, bit 1 is port 
-                *  1, ... (32 total possible).  The variable i has the aiop and ch 
-                *  numbers encoded in it (port 0-7 are aiop0, 8-15 are aiop1, etc).
-                */
-               if (xmitmask) {
-                       for (i = 0; i < rocketModel[ctrl].numPorts; i++) {
-                               if (xmitmask & (1 << i)) {
-                                       aiop = (i & 0x18) >> 3;
-                                       ch = i & 0x07;
-                                       line = GetLineNumber(ctrl, aiop, ch);
-                                       rp_do_transmit(rp_table[line]);
-                               }
-                       }
-               }
-       }
-
-       /*
-        * Reset the timer so we get called at the next clock tick (10ms).
-        */
-       if (atomic_read(&rp_num_ports_open))
-               mod_timer(&rocket_timer, jiffies + POLL_PERIOD);
-}
-
-/*
- *  Initializes the r_port structure for a port, as well as enabling the port on 
- *  the board.  
- *  Inputs:  board, aiop, chan numbers
- */
-static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev)
-{
-       unsigned rocketMode;
-       struct r_port *info;
-       int line;
-       CONTROLLER_T *ctlp;
-
-       /*  Get the next available line number */
-       line = SetLineNumber(board, aiop, chan);
-
-       ctlp = sCtlNumToCtlPtr(board);
-
-       /*  Get a r_port struct for the port, fill it in and save it globally, indexed by line number */
-       info = kzalloc(sizeof (struct r_port), GFP_KERNEL);
-       if (!info) {
-               printk(KERN_ERR "Couldn't allocate info struct for line #%d\n",
-                               line);
-               return;
-       }
-
-       info->magic = RPORT_MAGIC;
-       info->line = line;
-       info->ctlp = ctlp;
-       info->board = board;
-       info->aiop = aiop;
-       info->chan = chan;
-       tty_port_init(&info->port);
-       info->port.ops = &rocket_port_ops;
-       init_completion(&info->close_wait);
-       info->flags &= ~ROCKET_MODE_MASK;
-       switch (pc104[board][line]) {
-       case 422:
-               info->flags |= ROCKET_MODE_RS422;
-               break;
-       case 485:
-               info->flags |= ROCKET_MODE_RS485;
-               break;
-       case 232:
-       default:
-               info->flags |= ROCKET_MODE_RS232;
-               break;
-       }
-
-       info->intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR;
-       if (sInitChan(ctlp, &info->channel, aiop, chan) == 0) {
-               printk(KERN_ERR "RocketPort sInitChan(%d, %d, %d) failed!\n",
-                               board, aiop, chan);
-               kfree(info);
-               return;
-       }
-
-       rocketMode = info->flags & ROCKET_MODE_MASK;
-
-       if ((info->flags & ROCKET_RTS_TOGGLE) || (rocketMode == ROCKET_MODE_RS485))
-               sEnRTSToggle(&info->channel);
-       else
-               sDisRTSToggle(&info->channel);
-
-       if (ctlp->boardType == ROCKET_TYPE_PC104) {
-               switch (rocketMode) {
-               case ROCKET_MODE_RS485:
-                       sSetInterfaceMode(&info->channel, InterfaceModeRS485);
-                       break;
-               case ROCKET_MODE_RS422:
-                       sSetInterfaceMode(&info->channel, InterfaceModeRS422);
-                       break;
-               case ROCKET_MODE_RS232:
-               default:
-                       if (info->flags & ROCKET_RTS_TOGGLE)
-                               sSetInterfaceMode(&info->channel, InterfaceModeRS232T);
-                       else
-                               sSetInterfaceMode(&info->channel, InterfaceModeRS232);
-                       break;
-               }
-       }
-       spin_lock_init(&info->slock);
-       mutex_init(&info->write_mtx);
-       rp_table[line] = info;
-       tty_register_device(rocket_driver, line, pci_dev ? &pci_dev->dev :
-                       NULL);
-}
-
-/*
- *  Configures a rocketport port according to its termio settings.  Called from 
- *  user mode into the driver (exception handler).  *info CD manipulation is spinlock protected.
- */
-static void configure_r_port(struct tty_struct *tty, struct r_port *info,
-                            struct ktermios *old_termios)
-{
-       unsigned cflag;
-       unsigned long flags;
-       unsigned rocketMode;
-       int bits, baud, divisor;
-       CHANNEL_t *cp;
-       struct ktermios *t = tty->termios;
-
-       cp = &info->channel;
-       cflag = t->c_cflag;
-
-       /* Byte size and parity */
-       if ((cflag & CSIZE) == CS8) {
-               sSetData8(cp);
-               bits = 10;
-       } else {
-               sSetData7(cp);
-               bits = 9;
-       }
-       if (cflag & CSTOPB) {
-               sSetStop2(cp);
-               bits++;
-       } else {
-               sSetStop1(cp);
-       }
-
-       if (cflag & PARENB) {
-               sEnParity(cp);
-               bits++;
-               if (cflag & PARODD) {
-                       sSetOddParity(cp);
-               } else {
-                       sSetEvenParity(cp);
-               }
-       } else {
-               sDisParity(cp);
-       }
-
-       /* baud rate */
-       baud = tty_get_baud_rate(tty);
-       if (!baud)
-               baud = 9600;
-       divisor = ((rp_baud_base[info->board] + (baud >> 1)) / baud) - 1;
-       if ((divisor >= 8192 || divisor < 0) && old_termios) {
-               baud = tty_termios_baud_rate(old_termios);
-               if (!baud)
-                       baud = 9600;
-               divisor = (rp_baud_base[info->board] / baud) - 1;
-       }
-       if (divisor >= 8192 || divisor < 0) {
-               baud = 9600;
-               divisor = (rp_baud_base[info->board] / baud) - 1;
-       }
-       info->cps = baud / bits;
-       sSetBaud(cp, divisor);
-
-       /* FIXME: Should really back compute a baud rate from the divisor */
-       tty_encode_baud_rate(tty, baud, baud);
-
-       if (cflag & CRTSCTS) {
-               info->intmask |= DELTA_CTS;
-               sEnCTSFlowCtl(cp);
-       } else {
-               info->intmask &= ~DELTA_CTS;
-               sDisCTSFlowCtl(cp);
-       }
-       if (cflag & CLOCAL) {
-               info->intmask &= ~DELTA_CD;
-       } else {
-               spin_lock_irqsave(&info->slock, flags);
-               if (sGetChanStatus(cp) & CD_ACT)
-                       info->cd_status = 1;
-               else
-                       info->cd_status = 0;
-               info->intmask |= DELTA_CD;
-               spin_unlock_irqrestore(&info->slock, flags);
-       }
-
-       /*
-        * Handle software flow control in the board
-        */
-#ifdef ROCKET_SOFT_FLOW
-       if (I_IXON(tty)) {
-               sEnTxSoftFlowCtl(cp);
-               if (I_IXANY(tty)) {
-                       sEnIXANY(cp);
-               } else {
-                       sDisIXANY(cp);
-               }
-               sSetTxXONChar(cp, START_CHAR(tty));
-               sSetTxXOFFChar(cp, STOP_CHAR(tty));
-       } else {
-               sDisTxSoftFlowCtl(cp);
-               sDisIXANY(cp);
-               sClrTxXOFF(cp);
-       }
-#endif
-
-       /*
-        * Set up ignore/read mask words
-        */
-       info->read_status_mask = STMRCVROVRH | 0xFF;
-       if (I_INPCK(tty))
-               info->read_status_mask |= STMFRAMEH | STMPARITYH;
-       if (I_BRKINT(tty) || I_PARMRK(tty))
-               info->read_status_mask |= STMBREAKH;
-
-       /*
-        * Characters to ignore
-        */
-       info->ignore_status_mask = 0;
-       if (I_IGNPAR(tty))
-               info->ignore_status_mask |= STMFRAMEH | STMPARITYH;
-       if (I_IGNBRK(tty)) {
-               info->ignore_status_mask |= STMBREAKH;
-               /*
-                * If we're ignoring parity and break indicators,
-                * ignore overruns too.  (For real raw support).
-                */
-               if (I_IGNPAR(tty))
-                       info->ignore_status_mask |= STMRCVROVRH;
-       }
-
-       rocketMode = info->flags & ROCKET_MODE_MASK;
-
-       if ((info->flags & ROCKET_RTS_TOGGLE)
-           || (rocketMode == ROCKET_MODE_RS485))
-               sEnRTSToggle(cp);
-       else
-               sDisRTSToggle(cp);
-
-       sSetRTS(&info->channel);
-
-       if (cp->CtlP->boardType == ROCKET_TYPE_PC104) {
-               switch (rocketMode) {
-               case ROCKET_MODE_RS485:
-                       sSetInterfaceMode(cp, InterfaceModeRS485);
-                       break;
-               case ROCKET_MODE_RS422:
-                       sSetInterfaceMode(cp, InterfaceModeRS422);
-                       break;
-               case ROCKET_MODE_RS232:
-               default:
-                       if (info->flags & ROCKET_RTS_TOGGLE)
-                               sSetInterfaceMode(cp, InterfaceModeRS232T);
-                       else
-                               sSetInterfaceMode(cp, InterfaceModeRS232);
-                       break;
-               }
-       }
-}
-
-static int carrier_raised(struct tty_port *port)
-{
-       struct r_port *info = container_of(port, struct r_port, port);
-       return (sGetChanStatusLo(&info->channel) & CD_ACT) ? 1 : 0;
-}
-
-static void dtr_rts(struct tty_port *port, int on)
-{
-       struct r_port *info = container_of(port, struct r_port, port);
-       if (on) {
-               sSetDTR(&info->channel);
-               sSetRTS(&info->channel);
-       } else {
-               sClrDTR(&info->channel);
-               sClrRTS(&info->channel);
-       }
-}
-
-/*
- *  Exception handler that opens a serial port.  Creates xmit_buf storage, fills in 
- *  port's r_port struct.  Initializes the port hardware.  
- */
-static int rp_open(struct tty_struct *tty, struct file *filp)
-{
-       struct r_port *info;
-       struct tty_port *port;
-       int line = 0, retval;
-       CHANNEL_t *cp;
-       unsigned long page;
-
-       line = tty->index;
-       if (line < 0 || line >= MAX_RP_PORTS || ((info = rp_table[line]) == NULL))
-               return -ENXIO;
-       port = &info->port;
-       
-       page = __get_free_page(GFP_KERNEL);
-       if (!page)
-               return -ENOMEM;
-
-       if (port->flags & ASYNC_CLOSING) {
-               retval = wait_for_completion_interruptible(&info->close_wait);
-               free_page(page);
-               if (retval)
-                       return retval;
-               return ((port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS);
-       }
-
-       /*
-        * We must not sleep from here until the port is marked fully in use.
-        */
-       if (info->xmit_buf)
-               free_page(page);
-       else
-               info->xmit_buf = (unsigned char *) page;
-
-       tty->driver_data = info;
-       tty_port_tty_set(port, tty);
-
-       if (port->count++ == 0) {
-               atomic_inc(&rp_num_ports_open);
-
-#ifdef ROCKET_DEBUG_OPEN
-               printk(KERN_INFO "rocket mod++ = %d...\n",
-                               atomic_read(&rp_num_ports_open));
-#endif
-       }
-#ifdef ROCKET_DEBUG_OPEN
-       printk(KERN_INFO "rp_open ttyR%d, count=%d\n", info->line, info->port.count);
-#endif
-
-       /*
-        * Info->count is now 1; so it's safe to sleep now.
-        */
-       if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) {
-               cp = &info->channel;
-               sSetRxTrigger(cp, TRIG_1);
-               if (sGetChanStatus(cp) & CD_ACT)
-                       info->cd_status = 1;
-               else
-                       info->cd_status = 0;
-               sDisRxStatusMode(cp);
-               sFlushRxFIFO(cp);
-               sFlushTxFIFO(cp);
-
-               sEnInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN));
-               sSetRxTrigger(cp, TRIG_1);
-
-               sGetChanStatus(cp);
-               sDisRxStatusMode(cp);
-               sClrTxXOFF(cp);
-
-               sDisCTSFlowCtl(cp);
-               sDisTxSoftFlowCtl(cp);
-
-               sEnRxFIFO(cp);
-               sEnTransmit(cp);
-
-               set_bit(ASYNCB_INITIALIZED, &info->port.flags);
-
-               /*
-                * Set up the tty->alt_speed kludge
-                */
-               if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
-                       tty->alt_speed = 57600;
-               if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
-                       tty->alt_speed = 115200;
-               if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
-                       tty->alt_speed = 230400;
-               if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
-                       tty->alt_speed = 460800;
-
-               configure_r_port(tty, info, NULL);
-               if (tty->termios->c_cflag & CBAUD) {
-                       sSetDTR(cp);
-                       sSetRTS(cp);
-               }
-       }
-       /*  Starts (or resets) the maint polling loop */
-       mod_timer(&rocket_timer, jiffies + POLL_PERIOD);
-
-       retval = tty_port_block_til_ready(port, tty, filp);
-       if (retval) {
-#ifdef ROCKET_DEBUG_OPEN
-               printk(KERN_INFO "rp_open returning after block_til_ready with %d\n", retval);
-#endif
-               return retval;
-       }
-       return 0;
-}
-
-/*
- *  Exception handler that closes a serial port. info->port.count is considered critical.
- */
-static void rp_close(struct tty_struct *tty, struct file *filp)
-{
-       struct r_port *info = tty->driver_data;
-       struct tty_port *port = &info->port;
-       int timeout;
-       CHANNEL_t *cp;
-       
-       if (rocket_paranoia_check(info, "rp_close"))
-               return;
-
-#ifdef ROCKET_DEBUG_OPEN
-       printk(KERN_INFO "rp_close ttyR%d, count = %d\n", info->line, info->port.count);
-#endif
-
-       if (tty_port_close_start(port, tty, filp) == 0)
-               return;
-
-       mutex_lock(&port->mutex);
-       cp = &info->channel;
-       /*
-        * Before we drop DTR, make sure the UART transmitter
-        * has completely drained; this is especially
-        * important if there is a transmit FIFO!
-        */
-       timeout = (sGetTxCnt(cp) + 1) * HZ / info->cps;
-       if (timeout == 0)
-               timeout = 1;
-       rp_wait_until_sent(tty, timeout);
-       clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
-
-       sDisTransmit(cp);
-       sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN));
-       sDisCTSFlowCtl(cp);
-       sDisTxSoftFlowCtl(cp);
-       sClrTxXOFF(cp);
-       sFlushRxFIFO(cp);
-       sFlushTxFIFO(cp);
-       sClrRTS(cp);
-       if (C_HUPCL(tty))
-               sClrDTR(cp);
-
-       rp_flush_buffer(tty);
-               
-       tty_ldisc_flush(tty);
-
-       clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
-
-       /* We can't yet use tty_port_close_end as the buffer handling in this
-          driver is a bit different to the usual */
-
-       if (port->blocked_open) {
-               if (port->close_delay) {
-                       msleep_interruptible(jiffies_to_msecs(port->close_delay));
-               }
-               wake_up_interruptible(&port->open_wait);
-       } else {
-               if (info->xmit_buf) {
-                       free_page((unsigned long) info->xmit_buf);
-                       info->xmit_buf = NULL;
-               }
-       }
-       spin_lock_irq(&port->lock);
-       info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING | ASYNC_NORMAL_ACTIVE);
-       tty->closing = 0;
-       spin_unlock_irq(&port->lock);
-       mutex_unlock(&port->mutex);
-       tty_port_tty_set(port, NULL);
-
-       wake_up_interruptible(&port->close_wait);
-       complete_all(&info->close_wait);
-       atomic_dec(&rp_num_ports_open);
-
-#ifdef ROCKET_DEBUG_OPEN
-       printk(KERN_INFO "rocket mod-- = %d...\n",
-                       atomic_read(&rp_num_ports_open));
-       printk(KERN_INFO "rp_close ttyR%d complete shutdown\n", info->line);
-#endif
-
-}
-
-static void rp_set_termios(struct tty_struct *tty,
-                          struct ktermios *old_termios)
-{
-       struct r_port *info = tty->driver_data;
-       CHANNEL_t *cp;
-       unsigned cflag;
-
-       if (rocket_paranoia_check(info, "rp_set_termios"))
-               return;
-
-       cflag = tty->termios->c_cflag;
-
-       /*
-        * This driver doesn't support CS5 or CS6
-        */
-       if (((cflag & CSIZE) == CS5) || ((cflag & CSIZE) == CS6))
-               tty->termios->c_cflag =
-                   ((cflag & ~CSIZE) | (old_termios->c_cflag & CSIZE));
-       /* Or CMSPAR */
-       tty->termios->c_cflag &= ~CMSPAR;
-
-       configure_r_port(tty, info, old_termios);
-
-       cp = &info->channel;
-
-       /* Handle transition to B0 status */
-       if ((old_termios->c_cflag & CBAUD) && !(tty->termios->c_cflag & CBAUD)) {
-               sClrDTR(cp);
-               sClrRTS(cp);
-       }
-
-       /* Handle transition away from B0 status */
-       if (!(old_termios->c_cflag & CBAUD) && (tty->termios->c_cflag & CBAUD)) {
-               if (!tty->hw_stopped || !(tty->termios->c_cflag & CRTSCTS))
-                       sSetRTS(cp);
-               sSetDTR(cp);
-       }
-
-       if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) {
-               tty->hw_stopped = 0;
-               rp_start(tty);
-       }
-}
-
-static int rp_break(struct tty_struct *tty, int break_state)
-{
-       struct r_port *info = tty->driver_data;
-       unsigned long flags;
-
-       if (rocket_paranoia_check(info, "rp_break"))
-               return -EINVAL;
-
-       spin_lock_irqsave(&info->slock, flags);
-       if (break_state == -1)
-               sSendBreak(&info->channel);
-       else
-               sClrBreak(&info->channel);
-       spin_unlock_irqrestore(&info->slock, flags);
-       return 0;
-}
-
-/*
- * sGetChanRI used to be a macro in rocket_int.h. When the functionality for
- * the UPCI boards was added, it was decided to make this a function because
- * the macro was getting too complicated. All cases except the first one
- * (UPCIRingInd) are taken directly from the original macro.
- */
-static int sGetChanRI(CHANNEL_T * ChP)
-{
-       CONTROLLER_t *CtlP = ChP->CtlP;
-       int ChanNum = ChP->ChanNum;
-       int RingInd = 0;
-
-       if (CtlP->UPCIRingInd)
-               RingInd = !(sInB(CtlP->UPCIRingInd) & sBitMapSetTbl[ChanNum]);
-       else if (CtlP->AltChanRingIndicator)
-               RingInd = sInB((ByteIO_t) (ChP->ChanStat + 8)) & DSR_ACT;
-       else if (CtlP->boardType == ROCKET_TYPE_PC104)
-               RingInd = !(sInB(CtlP->AiopIO[3]) & sBitMapSetTbl[ChanNum]);
-
-       return RingInd;
-}
-
-/********************************************************************************************/
-/*  Here are the routines used by rp_ioctl.  These are all called from exception handlers.  */
-
-/*
- *  Returns the state of the serial modem control lines.  These next 2 functions 
- *  are the way kernel versions > 2.5 handle modem control lines rather than IOCTLs.
- */
-static int rp_tiocmget(struct tty_struct *tty)
-{
-       struct r_port *info = tty->driver_data;
-       unsigned int control, result, ChanStatus;
-
-       ChanStatus = sGetChanStatusLo(&info->channel);
-       control = info->channel.TxControl[3];
-       result = ((control & SET_RTS) ? TIOCM_RTS : 0) | 
-               ((control & SET_DTR) ?  TIOCM_DTR : 0) |
-               ((ChanStatus & CD_ACT) ? TIOCM_CAR : 0) |
-               (sGetChanRI(&info->channel) ? TIOCM_RNG : 0) |
-               ((ChanStatus & DSR_ACT) ? TIOCM_DSR : 0) |
-               ((ChanStatus & CTS_ACT) ? TIOCM_CTS : 0);
-
-       return result;
-}
-
-/* 
- *  Sets the modem control lines
- */
-static int rp_tiocmset(struct tty_struct *tty,
-                               unsigned int set, unsigned int clear)
-{
-       struct r_port *info = tty->driver_data;
-
-       if (set & TIOCM_RTS)
-               info->channel.TxControl[3] |= SET_RTS;
-       if (set & TIOCM_DTR)
-               info->channel.TxControl[3] |= SET_DTR;
-       if (clear & TIOCM_RTS)
-               info->channel.TxControl[3] &= ~SET_RTS;
-       if (clear & TIOCM_DTR)
-               info->channel.TxControl[3] &= ~SET_DTR;
-
-       out32(info->channel.IndexAddr, info->channel.TxControl);
-       return 0;
-}
-
-static int get_config(struct r_port *info, struct rocket_config __user *retinfo)
-{
-       struct rocket_config tmp;
-
-       if (!retinfo)
-               return -EFAULT;
-       memset(&tmp, 0, sizeof (tmp));
-       mutex_lock(&info->port.mutex);
-       tmp.line = info->line;
-       tmp.flags = info->flags;
-       tmp.close_delay = info->port.close_delay;
-       tmp.closing_wait = info->port.closing_wait;
-       tmp.port = rcktpt_io_addr[(info->line >> 5) & 3];
-       mutex_unlock(&info->port.mutex);
-
-       if (copy_to_user(retinfo, &tmp, sizeof (*retinfo)))
-               return -EFAULT;
-       return 0;
-}
-
-static int set_config(struct tty_struct *tty, struct r_port *info,
-                                       struct rocket_config __user *new_info)
-{
-       struct rocket_config new_serial;
-
-       if (copy_from_user(&new_serial, new_info, sizeof (new_serial)))
-               return -EFAULT;
-
-       mutex_lock(&info->port.mutex);
-       if (!capable(CAP_SYS_ADMIN))
-       {
-               if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK)) {
-                       mutex_unlock(&info->port.mutex);
-                       return -EPERM;
-               }
-               info->flags = ((info->flags & ~ROCKET_USR_MASK) | (new_serial.flags & ROCKET_USR_MASK));
-               configure_r_port(tty, info, NULL);
-               mutex_unlock(&info->port.mutex);
-               return 0;
-       }
-
-       info->flags = ((info->flags & ~ROCKET_FLAGS) | (new_serial.flags & ROCKET_FLAGS));
-       info->port.close_delay = new_serial.close_delay;
-       info->port.closing_wait = new_serial.closing_wait;
-
-       if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
-               tty->alt_speed = 57600;
-       if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
-               tty->alt_speed = 115200;
-       if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
-               tty->alt_speed = 230400;
-       if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
-               tty->alt_speed = 460800;
-       mutex_unlock(&info->port.mutex);
-
-       configure_r_port(tty, info, NULL);
-       return 0;
-}
-
-/*
- *  This function fills in a rocket_ports struct with information
- *  about what boards/ports are in the system.  This info is passed
- *  to user space.  See setrocket.c where the info is used to create
- *  the /dev/ttyRx ports.
- */
-static int get_ports(struct r_port *info, struct rocket_ports __user *retports)
-{
-       struct rocket_ports tmp;
-       int board;
-
-       if (!retports)
-               return -EFAULT;
-       memset(&tmp, 0, sizeof (tmp));
-       tmp.tty_major = rocket_driver->major;
-
-       for (board = 0; board < 4; board++) {
-               tmp.rocketModel[board].model = rocketModel[board].model;
-               strcpy(tmp.rocketModel[board].modelString, rocketModel[board].modelString);
-               tmp.rocketModel[board].numPorts = rocketModel[board].numPorts;
-               tmp.rocketModel[board].loadrm2 = rocketModel[board].loadrm2;
-               tmp.rocketModel[board].startingPortNumber = rocketModel[board].startingPortNumber;
-       }
-       if (copy_to_user(retports, &tmp, sizeof (*retports)))
-               return -EFAULT;
-       return 0;
-}
-
-static int reset_rm2(struct r_port *info, void __user *arg)
-{
-       int reset;
-
-       if (!capable(CAP_SYS_ADMIN))
-               return -EPERM;
-
-       if (copy_from_user(&reset, arg, sizeof (int)))
-               return -EFAULT;
-       if (reset)
-               reset = 1;
-
-       if (rcktpt_type[info->board] != ROCKET_TYPE_MODEMII &&
-            rcktpt_type[info->board] != ROCKET_TYPE_MODEMIII)
-               return -EINVAL;
-
-       if (info->ctlp->BusType == isISA)
-               sModemReset(info->ctlp, info->chan, reset);
-       else
-               sPCIModemReset(info->ctlp, info->chan, reset);
-
-       return 0;
-}
-
-static int get_version(struct r_port *info, struct rocket_version __user *retvers)
-{
-       if (copy_to_user(retvers, &driver_version, sizeof (*retvers)))
-               return -EFAULT;
-       return 0;
-}
-
-/*  IOCTL call handler into the driver */
-static int rp_ioctl(struct tty_struct *tty,
-                   unsigned int cmd, unsigned long arg)
-{
-       struct r_port *info = tty->driver_data;
-       void __user *argp = (void __user *)arg;
-       int ret = 0;
-
-       if (cmd != RCKP_GET_PORTS && rocket_paranoia_check(info, "rp_ioctl"))
-               return -ENXIO;
-
-       switch (cmd) {
-       case RCKP_GET_STRUCT:
-               if (copy_to_user(argp, info, sizeof (struct r_port)))
-                       ret = -EFAULT;
-               break;
-       case RCKP_GET_CONFIG:
-               ret = get_config(info, argp);
-               break;
-       case RCKP_SET_CONFIG:
-               ret = set_config(tty, info, argp);
-               break;
-       case RCKP_GET_PORTS:
-               ret = get_ports(info, argp);
-               break;
-       case RCKP_RESET_RM2:
-               ret = reset_rm2(info, argp);
-               break;
-       case RCKP_GET_VERSION:
-               ret = get_version(info, argp);
-               break;
-       default:
-               ret = -ENOIOCTLCMD;
-       }
-       return ret;
-}
-
-static void rp_send_xchar(struct tty_struct *tty, char ch)
-{
-       struct r_port *info = tty->driver_data;
-       CHANNEL_t *cp;
-
-       if (rocket_paranoia_check(info, "rp_send_xchar"))
-               return;
-
-       cp = &info->channel;
-       if (sGetTxCnt(cp))
-               sWriteTxPrioByte(cp, ch);
-       else
-               sWriteTxByte(sGetTxRxDataIO(cp), ch);
-}
-
-static void rp_throttle(struct tty_struct *tty)
-{
-       struct r_port *info = tty->driver_data;
-       CHANNEL_t *cp;
-
-#ifdef ROCKET_DEBUG_THROTTLE
-       printk(KERN_INFO "throttle %s: %d....\n", tty->name,
-              tty->ldisc.chars_in_buffer(tty));
-#endif
-
-       if (rocket_paranoia_check(info, "rp_throttle"))
-               return;
-
-       cp = &info->channel;
-       if (I_IXOFF(tty))
-               rp_send_xchar(tty, STOP_CHAR(tty));
-
-       sClrRTS(&info->channel);
-}
-
-static void rp_unthrottle(struct tty_struct *tty)
-{
-       struct r_port *info = tty->driver_data;
-       CHANNEL_t *cp;
-#ifdef ROCKET_DEBUG_THROTTLE
-       printk(KERN_INFO "unthrottle %s: %d....\n", tty->name,
-              tty->ldisc.chars_in_buffer(tty));
-#endif
-
-       if (rocket_paranoia_check(info, "rp_throttle"))
-               return;
-
-       cp = &info->channel;
-       if (I_IXOFF(tty))
-               rp_send_xchar(tty, START_CHAR(tty));
-
-       sSetRTS(&info->channel);
-}
-
-/*
- * ------------------------------------------------------------
- * rp_stop() and rp_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter interrupts, as necessary.
- * ------------------------------------------------------------
- */
-static void rp_stop(struct tty_struct *tty)
-{
-       struct r_port *info = tty->driver_data;
-
-#ifdef ROCKET_DEBUG_FLOW
-       printk(KERN_INFO "stop %s: %d %d....\n", tty->name,
-              info->xmit_cnt, info->xmit_fifo_room);
-#endif
-
-       if (rocket_paranoia_check(info, "rp_stop"))
-               return;
-
-       if (sGetTxCnt(&info->channel))
-               sDisTransmit(&info->channel);
-}
-
-static void rp_start(struct tty_struct *tty)
-{
-       struct r_port *info = tty->driver_data;
-
-#ifdef ROCKET_DEBUG_FLOW
-       printk(KERN_INFO "start %s: %d %d....\n", tty->name,
-              info->xmit_cnt, info->xmit_fifo_room);
-#endif
-
-       if (rocket_paranoia_check(info, "rp_stop"))
-               return;
-
-       sEnTransmit(&info->channel);
-       set_bit((info->aiop * 8) + info->chan,
-               (void *) &xmit_flags[info->board]);
-}
-
-/*
- * rp_wait_until_sent() --- wait until the transmitter is empty
- */
-static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
-{
-       struct r_port *info = tty->driver_data;
-       CHANNEL_t *cp;
-       unsigned long orig_jiffies;
-       int check_time, exit_time;
-       int txcnt;
-
-       if (rocket_paranoia_check(info, "rp_wait_until_sent"))
-               return;
-
-       cp = &info->channel;
-
-       orig_jiffies = jiffies;
-#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
-       printk(KERN_INFO "In RP_wait_until_sent(%d) (jiff=%lu)...\n", timeout,
-              jiffies);
-       printk(KERN_INFO "cps=%d...\n", info->cps);
-#endif
-       while (1) {
-               txcnt = sGetTxCnt(cp);
-               if (!txcnt) {
-                       if (sGetChanStatusLo(cp) & TXSHRMT)
-                               break;
-                       check_time = (HZ / info->cps) / 5;
-               } else {
-                       check_time = HZ * txcnt / info->cps;
-               }
-               if (timeout) {
-                       exit_time = orig_jiffies + timeout - jiffies;
-                       if (exit_time <= 0)
-                               break;
-                       if (exit_time < check_time)
-                               check_time = exit_time;
-               }
-               if (check_time == 0)
-                       check_time = 1;
-#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
-               printk(KERN_INFO "txcnt = %d (jiff=%lu,check=%d)...\n", txcnt,
-                               jiffies, check_time);
-#endif
-               msleep_interruptible(jiffies_to_msecs(check_time));
-               if (signal_pending(current))
-                       break;
-       }
-       __set_current_state(TASK_RUNNING);
-#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
-       printk(KERN_INFO "txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies);
-#endif
-}
-
-/*
- * rp_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-static void rp_hangup(struct tty_struct *tty)
-{
-       CHANNEL_t *cp;
-       struct r_port *info = tty->driver_data;
-       unsigned long flags;
-
-       if (rocket_paranoia_check(info, "rp_hangup"))
-               return;
-
-#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_HANGUP))
-       printk(KERN_INFO "rp_hangup of ttyR%d...\n", info->line);
-#endif
-       rp_flush_buffer(tty);
-       spin_lock_irqsave(&info->port.lock, flags);
-       if (info->port.flags & ASYNC_CLOSING) {
-               spin_unlock_irqrestore(&info->port.lock, flags);
-               return;
-       }
-       if (info->port.count)
-               atomic_dec(&rp_num_ports_open);
-       clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
-       spin_unlock_irqrestore(&info->port.lock, flags);
-
-       tty_port_hangup(&info->port);
-
-       cp = &info->channel;
-       sDisRxFIFO(cp);
-       sDisTransmit(cp);
-       sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN));
-       sDisCTSFlowCtl(cp);
-       sDisTxSoftFlowCtl(cp);
-       sClrTxXOFF(cp);
-       clear_bit(ASYNCB_INITIALIZED, &info->port.flags);
-
-       wake_up_interruptible(&info->port.open_wait);
-}
-
-/*
- *  Exception handler - write char routine.  The RocketPort driver uses a
- *  double-buffering strategy, with the twist that if the in-memory CPU
- *  buffer is empty, and there's space in the transmit FIFO, the
- *  writing routines will write directly to transmit FIFO.
- *  Write buffer and counters protected by spinlocks
- */
-static int rp_put_char(struct tty_struct *tty, unsigned char ch)
-{
-       struct r_port *info = tty->driver_data;
-       CHANNEL_t *cp;
-       unsigned long flags;
-
-       if (rocket_paranoia_check(info, "rp_put_char"))
-               return 0;
-
-       /*
-        * Grab the port write mutex, locking out other processes that try to
-        * write to this port
-        */
-       mutex_lock(&info->write_mtx);
-
-#ifdef ROCKET_DEBUG_WRITE
-       printk(KERN_INFO "rp_put_char %c...\n", ch);
-#endif
-
-       spin_lock_irqsave(&info->slock, flags);
-       cp = &info->channel;
-
-       if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room == 0)
-               info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp);
-
-       if (tty->stopped || tty->hw_stopped || info->xmit_fifo_room == 0 || info->xmit_cnt != 0) {
-               info->xmit_buf[info->xmit_head++] = ch;
-               info->xmit_head &= XMIT_BUF_SIZE - 1;
-               info->xmit_cnt++;
-               set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
-       } else {
-               sOutB(sGetTxRxDataIO(cp), ch);
-               info->xmit_fifo_room--;
-       }
-       spin_unlock_irqrestore(&info->slock, flags);
-       mutex_unlock(&info->write_mtx);
-       return 1;
-}
-
-/*
- *  Exception handler - write routine, called when user app writes to the device.
- *  A per port write mutex is used to protect from another process writing to
- *  this port at the same time.  This other process could be running on the other CPU
- *  or get control of the CPU if the copy_from_user() blocks due to a page fault (swapped out). 
- *  Spinlocks protect the info xmit members.
- */
-static int rp_write(struct tty_struct *tty,
-                   const unsigned char *buf, int count)
-{
-       struct r_port *info = tty->driver_data;
-       CHANNEL_t *cp;
-       const unsigned char *b;
-       int c, retval = 0;
-       unsigned long flags;
-
-       if (count <= 0 || rocket_paranoia_check(info, "rp_write"))
-               return 0;
-
-       if (mutex_lock_interruptible(&info->write_mtx))
-               return -ERESTARTSYS;
-
-#ifdef ROCKET_DEBUG_WRITE
-       printk(KERN_INFO "rp_write %d chars...\n", count);
-#endif
-       cp = &info->channel;
-
-       if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room < count)
-               info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp);
-
-        /*
-        *  If the write queue for the port is empty, and there is FIFO space, stuff bytes 
-        *  into FIFO.  Use the write queue for temp storage.
-         */
-       if (!tty->stopped && !tty->hw_stopped && info->xmit_cnt == 0 && info->xmit_fifo_room > 0) {
-               c = min(count, info->xmit_fifo_room);
-               b = buf;
-
-               /*  Push data into FIFO, 2 bytes at a time */
-               sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) b, c / 2);
-
-               /*  If there is a byte remaining, write it */
-               if (c & 1)
-                       sOutB(sGetTxRxDataIO(cp), b[c - 1]);
-
-               retval += c;
-               buf += c;
-               count -= c;
-
-               spin_lock_irqsave(&info->slock, flags);
-               info->xmit_fifo_room -= c;
-               spin_unlock_irqrestore(&info->slock, flags);
-       }
-
-       /* If count is zero, we wrote it all and are done */
-       if (!count)
-               goto end;
-
-       /*  Write remaining data into the port's xmit_buf */
-       while (1) {
-               /* Hung up ? */
-               if (!test_bit(ASYNCB_NORMAL_ACTIVE, &info->port.flags))
-                       goto end;
-               c = min(count, XMIT_BUF_SIZE - info->xmit_cnt - 1);
-               c = min(c, XMIT_BUF_SIZE - info->xmit_head);
-               if (c <= 0)
-                       break;
-
-               b = buf;
-               memcpy(info->xmit_buf + info->xmit_head, b, c);
-
-               spin_lock_irqsave(&info->slock, flags);
-               info->xmit_head =
-                   (info->xmit_head + c) & (XMIT_BUF_SIZE - 1);
-               info->xmit_cnt += c;
-               spin_unlock_irqrestore(&info->slock, flags);
-
-               buf += c;
-               count -= c;
-               retval += c;
-       }
-
-       if ((retval > 0) && !tty->stopped && !tty->hw_stopped)
-               set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
-       
-end:
-       if (info->xmit_cnt < WAKEUP_CHARS) {
-               tty_wakeup(tty);
-#ifdef ROCKETPORT_HAVE_POLL_WAIT
-               wake_up_interruptible(&tty->poll_wait);
-#endif
-       }
-       mutex_unlock(&info->write_mtx);
-       return retval;
-}
-
-/*
- * Return the number of characters that can be sent.  We estimate
- * only using the in-memory transmit buffer only, and ignore the
- * potential space in the transmit FIFO.
- */
-static int rp_write_room(struct tty_struct *tty)
-{
-       struct r_port *info = tty->driver_data;
-       int ret;
-
-       if (rocket_paranoia_check(info, "rp_write_room"))
-               return 0;
-
-       ret = XMIT_BUF_SIZE - info->xmit_cnt - 1;
-       if (ret < 0)
-               ret = 0;
-#ifdef ROCKET_DEBUG_WRITE
-       printk(KERN_INFO "rp_write_room returns %d...\n", ret);
-#endif
-       return ret;
-}
-
-/*
- * Return the number of characters in the buffer.  Again, this only
- * counts those characters in the in-memory transmit buffer.
- */
-static int rp_chars_in_buffer(struct tty_struct *tty)
-{
-       struct r_port *info = tty->driver_data;
-       CHANNEL_t *cp;
-
-       if (rocket_paranoia_check(info, "rp_chars_in_buffer"))
-               return 0;
-
-       cp = &info->channel;
-
-#ifdef ROCKET_DEBUG_WRITE
-       printk(KERN_INFO "rp_chars_in_buffer returns %d...\n", info->xmit_cnt);
-#endif
-       return info->xmit_cnt;
-}
-
-/*
- *  Flushes the TX fifo for a port, deletes data in the xmit_buf stored in the
- *  r_port struct for the port.  Note that spinlock are used to protect info members,
- *  do not call this function if the spinlock is already held.
- */
-static void rp_flush_buffer(struct tty_struct *tty)
-{
-       struct r_port *info = tty->driver_data;
-       CHANNEL_t *cp;
-       unsigned long flags;
-
-       if (rocket_paranoia_check(info, "rp_flush_buffer"))
-               return;
-
-       spin_lock_irqsave(&info->slock, flags);
-       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-       spin_unlock_irqrestore(&info->slock, flags);
-
-#ifdef ROCKETPORT_HAVE_POLL_WAIT
-       wake_up_interruptible(&tty->poll_wait);
-#endif
-       tty_wakeup(tty);
-
-       cp = &info->channel;
-       sFlushTxFIFO(cp);
-}
-
-#ifdef CONFIG_PCI
-
-static struct pci_device_id __devinitdata __used rocket_pci_ids[] = {
-       { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_ANY_ID) },
-       { }
-};
-MODULE_DEVICE_TABLE(pci, rocket_pci_ids);
-
-/*
- *  Called when a PCI card is found.  Retrieves and stores model information,
- *  init's aiopic and serial port hardware.
- *  Inputs:  i is the board number (0-n)
- */
-static __init int register_PCI(int i, struct pci_dev *dev)
-{
-       int num_aiops, aiop, max_num_aiops, num_chan, chan;
-       unsigned int aiopio[MAX_AIOPS_PER_BOARD];
-       char *str, *board_type;
-       CONTROLLER_t *ctlp;
-
-       int fast_clock = 0;
-       int altChanRingIndicator = 0;
-       int ports_per_aiop = 8;
-       WordIO_t ConfigIO = 0;
-       ByteIO_t UPCIRingInd = 0;
-
-       if (!dev || pci_enable_device(dev))
-               return 0;
-
-       rcktpt_io_addr[i] = pci_resource_start(dev, 0);
-
-       rcktpt_type[i] = ROCKET_TYPE_NORMAL;
-       rocketModel[i].loadrm2 = 0;
-       rocketModel[i].startingPortNumber = nextLineNumber;
-
-       /*  Depending on the model, set up some config variables */
-       switch (dev->device) {
-       case PCI_DEVICE_ID_RP4QUAD:
-               str = "Quadcable";
-               max_num_aiops = 1;
-               ports_per_aiop = 4;
-               rocketModel[i].model = MODEL_RP4QUAD;
-               strcpy(rocketModel[i].modelString, "RocketPort 4 port w/quad cable");
-               rocketModel[i].numPorts = 4;
-               break;
-       case PCI_DEVICE_ID_RP8OCTA:
-               str = "Octacable";
-               max_num_aiops = 1;
-               rocketModel[i].model = MODEL_RP8OCTA;
-               strcpy(rocketModel[i].modelString, "RocketPort 8 port w/octa cable");
-               rocketModel[i].numPorts = 8;
-               break;
-       case PCI_DEVICE_ID_URP8OCTA:
-               str = "Octacable";
-               max_num_aiops = 1;
-               rocketModel[i].model = MODEL_UPCI_RP8OCTA;
-               strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/octa cable");
-               rocketModel[i].numPorts = 8;
-               break;
-       case PCI_DEVICE_ID_RP8INTF:
-               str = "8";
-               max_num_aiops = 1;
-               rocketModel[i].model = MODEL_RP8INTF;
-               strcpy(rocketModel[i].modelString, "RocketPort 8 port w/external I/F");
-               rocketModel[i].numPorts = 8;
-               break;
-       case PCI_DEVICE_ID_URP8INTF:
-               str = "8";
-               max_num_aiops = 1;
-               rocketModel[i].model = MODEL_UPCI_RP8INTF;
-               strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/external I/F");
-               rocketModel[i].numPorts = 8;
-               break;
-       case PCI_DEVICE_ID_RP8J:
-               str = "8J";
-               max_num_aiops = 1;
-               rocketModel[i].model = MODEL_RP8J;
-               strcpy(rocketModel[i].modelString, "RocketPort 8 port w/RJ11 connectors");
-               rocketModel[i].numPorts = 8;
-               break;
-       case PCI_DEVICE_ID_RP4J:
-               str = "4J";
-               max_num_aiops = 1;
-               ports_per_aiop = 4;
-               rocketModel[i].model = MODEL_RP4J;
-               strcpy(rocketModel[i].modelString, "RocketPort 4 port w/RJ45 connectors");
-               rocketModel[i].numPorts = 4;
-               break;
-       case PCI_DEVICE_ID_RP8SNI:
-               str = "8 (DB78 Custom)";
-               max_num_aiops = 1;
-               rocketModel[i].model = MODEL_RP8SNI;
-               strcpy(rocketModel[i].modelString, "RocketPort 8 port w/ custom DB78");
-               rocketModel[i].numPorts = 8;
-               break;
-       case PCI_DEVICE_ID_RP16SNI:
-               str = "16 (DB78 Custom)";
-               max_num_aiops = 2;
-               rocketModel[i].model = MODEL_RP16SNI;
-               strcpy(rocketModel[i].modelString, "RocketPort 16 port w/ custom DB78");
-               rocketModel[i].numPorts = 16;
-               break;
-       case PCI_DEVICE_ID_RP16INTF:
-               str = "16";
-               max_num_aiops = 2;
-               rocketModel[i].model = MODEL_RP16INTF;
-               strcpy(rocketModel[i].modelString, "RocketPort 16 port w/external I/F");
-               rocketModel[i].numPorts = 16;
-               break;
-       case PCI_DEVICE_ID_URP16INTF:
-               str = "16";
-               max_num_aiops = 2;
-               rocketModel[i].model = MODEL_UPCI_RP16INTF;
-               strcpy(rocketModel[i].modelString, "RocketPort UPCI 16 port w/external I/F");
-               rocketModel[i].numPorts = 16;
-               break;
-       case PCI_DEVICE_ID_CRP16INTF:
-               str = "16";
-               max_num_aiops = 2;
-               rocketModel[i].model = MODEL_CPCI_RP16INTF;
-               strcpy(rocketModel[i].modelString, "RocketPort Compact PCI 16 port w/external I/F");
-               rocketModel[i].numPorts = 16;
-               break;
-       case PCI_DEVICE_ID_RP32INTF:
-               str = "32";
-               max_num_aiops = 4;
-               rocketModel[i].model = MODEL_RP32INTF;
-               strcpy(rocketModel[i].modelString, "RocketPort 32 port w/external I/F");
-               rocketModel[i].numPorts = 32;
-               break;
-       case PCI_DEVICE_ID_URP32INTF:
-               str = "32";
-               max_num_aiops = 4;
-               rocketModel[i].model = MODEL_UPCI_RP32INTF;
-               strcpy(rocketModel[i].modelString, "RocketPort UPCI 32 port w/external I/F");
-               rocketModel[i].numPorts = 32;
-               break;
-       case PCI_DEVICE_ID_RPP4:
-               str = "Plus Quadcable";
-               max_num_aiops = 1;
-               ports_per_aiop = 4;
-               altChanRingIndicator++;
-               fast_clock++;
-               rocketModel[i].model = MODEL_RPP4;
-               strcpy(rocketModel[i].modelString, "RocketPort Plus 4 port");
-               rocketModel[i].numPorts = 4;
-               break;
-       case PCI_DEVICE_ID_RPP8:
-               str = "Plus Octacable";
-               max_num_aiops = 2;
-               ports_per_aiop = 4;
-               altChanRingIndicator++;
-               fast_clock++;
-               rocketModel[i].model = MODEL_RPP8;
-               strcpy(rocketModel[i].modelString, "RocketPort Plus 8 port");
-               rocketModel[i].numPorts = 8;
-               break;
-       case PCI_DEVICE_ID_RP2_232:
-               str = "Plus 2 (RS-232)";
-               max_num_aiops = 1;
-               ports_per_aiop = 2;
-               altChanRingIndicator++;
-               fast_clock++;
-               rocketModel[i].model = MODEL_RP2_232;
-               strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS232");
-               rocketModel[i].numPorts = 2;
-               break;
-       case PCI_DEVICE_ID_RP2_422:
-               str = "Plus 2 (RS-422)";
-               max_num_aiops = 1;
-               ports_per_aiop = 2;
-               altChanRingIndicator++;
-               fast_clock++;
-               rocketModel[i].model = MODEL_RP2_422;
-               strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS422");
-               rocketModel[i].numPorts = 2;
-               break;
-       case PCI_DEVICE_ID_RP6M:
-
-               max_num_aiops = 1;
-               ports_per_aiop = 6;
-               str = "6-port";
-
-               /*  If revision is 1, the rocketmodem flash must be loaded.
-                *  If it is 2 it is a "socketed" version. */
-               if (dev->revision == 1) {
-                       rcktpt_type[i] = ROCKET_TYPE_MODEMII;
-                       rocketModel[i].loadrm2 = 1;
-               } else {
-                       rcktpt_type[i] = ROCKET_TYPE_MODEM;
-               }
-
-               rocketModel[i].model = MODEL_RP6M;
-               strcpy(rocketModel[i].modelString, "RocketModem 6 port");
-               rocketModel[i].numPorts = 6;
-               break;
-       case PCI_DEVICE_ID_RP4M:
-               max_num_aiops = 1;
-               ports_per_aiop = 4;
-               str = "4-port";
-               if (dev->revision == 1) {
-                       rcktpt_type[i] = ROCKET_TYPE_MODEMII;
-                       rocketModel[i].loadrm2 = 1;
-               } else {
-                       rcktpt_type[i] = ROCKET_TYPE_MODEM;
-               }
-
-               rocketModel[i].model = MODEL_RP4M;
-               strcpy(rocketModel[i].modelString, "RocketModem 4 port");
-               rocketModel[i].numPorts = 4;
-               break;
-       default:
-               str = "(unknown/unsupported)";
-               max_num_aiops = 0;
-               break;
-       }
-
-       /*
-        * Check for UPCI boards.
-        */
-
-       switch (dev->device) {
-       case PCI_DEVICE_ID_URP32INTF:
-       case PCI_DEVICE_ID_URP8INTF:
-       case PCI_DEVICE_ID_URP16INTF:
-       case PCI_DEVICE_ID_CRP16INTF:
-       case PCI_DEVICE_ID_URP8OCTA:
-               rcktpt_io_addr[i] = pci_resource_start(dev, 2);
-               ConfigIO = pci_resource_start(dev, 1);
-               if (dev->device == PCI_DEVICE_ID_URP8OCTA) {
-                       UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND;
-
-                       /*
-                        * Check for octa or quad cable.
-                        */
-                       if (!
-                           (sInW(ConfigIO + _PCI_9030_GPIO_CTRL) &
-                            PCI_GPIO_CTRL_8PORT)) {
-                               str = "Quadcable";
-                               ports_per_aiop = 4;
-                               rocketModel[i].numPorts = 4;
-                       }
-               }
-               break;
-       case PCI_DEVICE_ID_UPCI_RM3_8PORT:
-               str = "8 ports";
-               max_num_aiops = 1;
-               rocketModel[i].model = MODEL_UPCI_RM3_8PORT;
-               strcpy(rocketModel[i].modelString, "RocketModem III 8 port");
-               rocketModel[i].numPorts = 8;
-               rcktpt_io_addr[i] = pci_resource_start(dev, 2);
-               UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND;
-               ConfigIO = pci_resource_start(dev, 1);
-               rcktpt_type[i] = ROCKET_TYPE_MODEMIII;
-               break;
-       case PCI_DEVICE_ID_UPCI_RM3_4PORT:
-               str = "4 ports";
-               max_num_aiops = 1;
-               rocketModel[i].model = MODEL_UPCI_RM3_4PORT;
-               strcpy(rocketModel[i].modelString, "RocketModem III 4 port");
-               rocketModel[i].numPorts = 4;
-               rcktpt_io_addr[i] = pci_resource_start(dev, 2);
-               UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND;
-               ConfigIO = pci_resource_start(dev, 1);
-               rcktpt_type[i] = ROCKET_TYPE_MODEMIII;
-               break;
-       default:
-               break;
-       }
-
-       switch (rcktpt_type[i]) {
-       case ROCKET_TYPE_MODEM:
-               board_type = "RocketModem";
-               break;
-       case ROCKET_TYPE_MODEMII:
-               board_type = "RocketModem II";
-               break;
-       case ROCKET_TYPE_MODEMIII:
-               board_type = "RocketModem III";
-               break;
-       default:
-               board_type = "RocketPort";
-               break;
-       }
-
-       if (fast_clock) {
-               sClockPrescale = 0x12;  /* mod 2 (divide by 3) */
-               rp_baud_base[i] = 921600;
-       } else {
-               /*
-                * If support_low_speed is set, use the slow clock
-                * prescale, which supports 50 bps
-                */
-               if (support_low_speed) {
-                       /* mod 9 (divide by 10) prescale */
-                       sClockPrescale = 0x19;
-                       rp_baud_base[i] = 230400;
-               } else {
-                       /* mod 4 (devide by 5) prescale */
-                       sClockPrescale = 0x14;
-                       rp_baud_base[i] = 460800;
-               }
-       }
-
-       for (aiop = 0; aiop < max_num_aiops; aiop++)
-               aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x40);
-       ctlp = sCtlNumToCtlPtr(i);
-       num_aiops = sPCIInitController(ctlp, i, aiopio, max_num_aiops, ConfigIO, 0, FREQ_DIS, 0, altChanRingIndicator, UPCIRingInd);
-       for (aiop = 0; aiop < max_num_aiops; aiop++)
-               ctlp->AiopNumChan[aiop] = ports_per_aiop;
-
-       dev_info(&dev->dev, "comtrol PCI controller #%d found at "
-               "address %04lx, %d AIOP(s) (%s), creating ttyR%d - %ld\n",
-               i, rcktpt_io_addr[i], num_aiops, rocketModel[i].modelString,
-               rocketModel[i].startingPortNumber,
-               rocketModel[i].startingPortNumber + rocketModel[i].numPorts-1);
-
-       if (num_aiops <= 0) {
-               rcktpt_io_addr[i] = 0;
-               return (0);
-       }
-       is_PCI[i] = 1;
-
-       /*  Reset the AIOPIC, init the serial ports */
-       for (aiop = 0; aiop < num_aiops; aiop++) {
-               sResetAiopByNum(ctlp, aiop);
-               num_chan = ports_per_aiop;
-               for (chan = 0; chan < num_chan; chan++)
-                       init_r_port(i, aiop, chan, dev);
-       }
-
-       /*  Rocket modems must be reset */
-       if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) ||
-           (rcktpt_type[i] == ROCKET_TYPE_MODEMII) ||
-           (rcktpt_type[i] == ROCKET_TYPE_MODEMIII)) {
-               num_chan = ports_per_aiop;
-               for (chan = 0; chan < num_chan; chan++)
-                       sPCIModemReset(ctlp, chan, 1);
-               msleep(500);
-               for (chan = 0; chan < num_chan; chan++)
-                       sPCIModemReset(ctlp, chan, 0);
-               msleep(500);
-               rmSpeakerReset(ctlp, rocketModel[i].model);
-       }
-       return (1);
-}
-
-/*
- *  Probes for PCI cards, inits them if found
- *  Input:   board_found = number of ISA boards already found, or the
- *           starting board number
- *  Returns: Number of PCI boards found
- */
-static int __init init_PCI(int boards_found)
-{
-       struct pci_dev *dev = NULL;
-       int count = 0;
-
-       /*  Work through the PCI device list, pulling out ours */
-       while ((dev = pci_get_device(PCI_VENDOR_ID_RP, PCI_ANY_ID, dev))) {
-               if (register_PCI(count + boards_found, dev))
-                       count++;
-       }
-       return (count);
-}
-
-#endif                         /* CONFIG_PCI */
-
-/*
- *  Probes for ISA cards
- *  Input:   i = the board number to look for
- *  Returns: 1 if board found, 0 else
- */
-static int __init init_ISA(int i)
-{
-       int num_aiops, num_chan = 0, total_num_chan = 0;
-       int aiop, chan;
-       unsigned int aiopio[MAX_AIOPS_PER_BOARD];
-       CONTROLLER_t *ctlp;
-       char *type_string;
-
-       /*  If io_addr is zero, no board configured */
-       if (rcktpt_io_addr[i] == 0)
-               return (0);
-
-       /*  Reserve the IO region */
-       if (!request_region(rcktpt_io_addr[i], 64, "Comtrol RocketPort")) {
-               printk(KERN_ERR "Unable to reserve IO region for configured "
-                               "ISA RocketPort at address 0x%lx, board not "
-                               "installed...\n", rcktpt_io_addr[i]);
-               rcktpt_io_addr[i] = 0;
-               return (0);
-       }
-
-       ctlp = sCtlNumToCtlPtr(i);
-
-       ctlp->boardType = rcktpt_type[i];
-
-       switch (rcktpt_type[i]) {
-       case ROCKET_TYPE_PC104:
-               type_string = "(PC104)";
-               break;
-       case ROCKET_TYPE_MODEM:
-               type_string = "(RocketModem)";
-               break;
-       case ROCKET_TYPE_MODEMII:
-               type_string = "(RocketModem II)";
-               break;
-       default:
-               type_string = "";
-               break;
-       }
-
-       /*
-        * If support_low_speed is set, use the slow clock prescale,
-        * which supports 50 bps
-        */
-       if (support_low_speed) {
-               sClockPrescale = 0x19;  /* mod 9 (divide by 10) prescale */
-               rp_baud_base[i] = 230400;
-       } else {
-               sClockPrescale = 0x14;  /* mod 4 (devide by 5) prescale */
-               rp_baud_base[i] = 460800;
-       }
-
-       for (aiop = 0; aiop < MAX_AIOPS_PER_BOARD; aiop++)
-               aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x400);
-
-       num_aiops = sInitController(ctlp, i, controller + (i * 0x400), aiopio,  MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0);
-
-       if (ctlp->boardType == ROCKET_TYPE_PC104) {
-               sEnAiop(ctlp, 2);       /* only one AIOPIC, but these */
-               sEnAiop(ctlp, 3);       /* CSels used for other stuff */
-       }
-
-       /*  If something went wrong initing the AIOP's release the ISA IO memory */
-       if (num_aiops <= 0) {
-               release_region(rcktpt_io_addr[i], 64);
-               rcktpt_io_addr[i] = 0;
-               return (0);
-       }
-  
-       rocketModel[i].startingPortNumber = nextLineNumber;
-
-       for (aiop = 0; aiop < num_aiops; aiop++) {
-               sResetAiopByNum(ctlp, aiop);
-               sEnAiop(ctlp, aiop);
-               num_chan = sGetAiopNumChan(ctlp, aiop);
-               total_num_chan += num_chan;
-               for (chan = 0; chan < num_chan; chan++)
-                       init_r_port(i, aiop, chan, NULL);
-       }
-       is_PCI[i] = 0;
-       if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) || (rcktpt_type[i] == ROCKET_TYPE_MODEMII)) {
-               num_chan = sGetAiopNumChan(ctlp, 0);
-               total_num_chan = num_chan;
-               for (chan = 0; chan < num_chan; chan++)
-                       sModemReset(ctlp, chan, 1);
-               msleep(500);
-               for (chan = 0; chan < num_chan; chan++)
-                       sModemReset(ctlp, chan, 0);
-               msleep(500);
-               strcpy(rocketModel[i].modelString, "RocketModem ISA");
-       } else {
-               strcpy(rocketModel[i].modelString, "RocketPort ISA");
-       }
-       rocketModel[i].numPorts = total_num_chan;
-       rocketModel[i].model = MODEL_ISA;
-
-       printk(KERN_INFO "RocketPort ISA card #%d found at 0x%lx - %d AIOPs %s\n", 
-              i, rcktpt_io_addr[i], num_aiops, type_string);
-
-       printk(KERN_INFO "Installing %s, creating /dev/ttyR%d - %ld\n",
-              rocketModel[i].modelString,
-              rocketModel[i].startingPortNumber,
-              rocketModel[i].startingPortNumber +
-              rocketModel[i].numPorts - 1);
-
-       return (1);
-}
-
-static const struct tty_operations rocket_ops = {
-       .open = rp_open,
-       .close = rp_close,
-       .write = rp_write,
-       .put_char = rp_put_char,
-       .write_room = rp_write_room,
-       .chars_in_buffer = rp_chars_in_buffer,
-       .flush_buffer = rp_flush_buffer,
-       .ioctl = rp_ioctl,
-       .throttle = rp_throttle,
-       .unthrottle = rp_unthrottle,
-       .set_termios = rp_set_termios,
-       .stop = rp_stop,
-       .start = rp_start,
-       .hangup = rp_hangup,
-       .break_ctl = rp_break,
-       .send_xchar = rp_send_xchar,
-       .wait_until_sent = rp_wait_until_sent,
-       .tiocmget = rp_tiocmget,
-       .tiocmset = rp_tiocmset,
-};
-
-static const struct tty_port_operations rocket_port_ops = {
-       .carrier_raised = carrier_raised,
-       .dtr_rts = dtr_rts,
-};
-
-/*
- * The module "startup" routine; it's run when the module is loaded.
- */
-static int __init rp_init(void)
-{
-       int ret = -ENOMEM, pci_boards_found, isa_boards_found, i;
-
-       printk(KERN_INFO "RocketPort device driver module, version %s, %s\n",
-              ROCKET_VERSION, ROCKET_DATE);
-
-       rocket_driver = alloc_tty_driver(MAX_RP_PORTS);
-       if (!rocket_driver)
-               goto err;
-
-       /*
-        *  If board 1 is non-zero, there is at least one ISA configured.  If controller is 
-        *  zero, use the default controller IO address of board1 + 0x40.
-        */
-       if (board1) {
-               if (controller == 0)
-                       controller = board1 + 0x40;
-       } else {
-               controller = 0;  /*  Used as a flag, meaning no ISA boards */
-       }
-
-       /*  If an ISA card is configured, reserve the 4 byte IO space for the Mudbac controller */
-       if (controller && (!request_region(controller, 4, "Comtrol RocketPort"))) {
-               printk(KERN_ERR "Unable to reserve IO region for first "
-                       "configured ISA RocketPort controller 0x%lx.  "
-                       "Driver exiting\n", controller);
-               ret = -EBUSY;
-               goto err_tty;
-       }
-
-       /*  Store ISA variable retrieved from command line or .conf file. */
-       rcktpt_io_addr[0] = board1;
-       rcktpt_io_addr[1] = board2;
-       rcktpt_io_addr[2] = board3;
-       rcktpt_io_addr[3] = board4;
-
-       rcktpt_type[0] = modem1 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL;
-       rcktpt_type[0] = pc104_1[0] ? ROCKET_TYPE_PC104 : rcktpt_type[0];
-       rcktpt_type[1] = modem2 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL;
-       rcktpt_type[1] = pc104_2[0] ? ROCKET_TYPE_PC104 : rcktpt_type[1];
-       rcktpt_type[2] = modem3 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL;
-       rcktpt_type[2] = pc104_3[0] ? ROCKET_TYPE_PC104 : rcktpt_type[2];
-       rcktpt_type[3] = modem4 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL;
-       rcktpt_type[3] = pc104_4[0] ? ROCKET_TYPE_PC104 : rcktpt_type[3];
-
-       /*
-        * Set up the tty driver structure and then register this
-        * driver with the tty layer.
-        */
-
-       rocket_driver->owner = THIS_MODULE;
-       rocket_driver->flags = TTY_DRIVER_DYNAMIC_DEV;
-       rocket_driver->name = "ttyR";
-       rocket_driver->driver_name = "Comtrol RocketPort";
-       rocket_driver->major = TTY_ROCKET_MAJOR;
-       rocket_driver->minor_start = 0;
-       rocket_driver->type = TTY_DRIVER_TYPE_SERIAL;
-       rocket_driver->subtype = SERIAL_TYPE_NORMAL;
-       rocket_driver->init_termios = tty_std_termios;
-       rocket_driver->init_termios.c_cflag =
-           B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-       rocket_driver->init_termios.c_ispeed = 9600;
-       rocket_driver->init_termios.c_ospeed = 9600;
-#ifdef ROCKET_SOFT_FLOW
-       rocket_driver->flags |= TTY_DRIVER_REAL_RAW;
-#endif
-       tty_set_operations(rocket_driver, &rocket_ops);
-
-       ret = tty_register_driver(rocket_driver);
-       if (ret < 0) {
-               printk(KERN_ERR "Couldn't install tty RocketPort driver\n");
-               goto err_controller;
-       }
-
-#ifdef ROCKET_DEBUG_OPEN
-       printk(KERN_INFO "RocketPort driver is major %d\n", rocket_driver.major);
-#endif
-
-       /*
-        *  OK, let's probe each of the controllers looking for boards.  Any boards found
-         *  will be initialized here.
-        */
-       isa_boards_found = 0;
-       pci_boards_found = 0;
-
-       for (i = 0; i < NUM_BOARDS; i++) {
-               if (init_ISA(i))
-                       isa_boards_found++;
-       }
-
-#ifdef CONFIG_PCI
-       if (isa_boards_found < NUM_BOARDS)
-               pci_boards_found = init_PCI(isa_boards_found);
-#endif
-
-       max_board = pci_boards_found + isa_boards_found;
-
-       if (max_board == 0) {
-               printk(KERN_ERR "No rocketport ports found; unloading driver\n");
-               ret = -ENXIO;
-               goto err_ttyu;
-       }
-
-       return 0;
-err_ttyu:
-       tty_unregister_driver(rocket_driver);
-err_controller:
-       if (controller)
-               release_region(controller, 4);
-err_tty:
-       put_tty_driver(rocket_driver);
-err:
-       return ret;
-}
-
-
-static void rp_cleanup_module(void)
-{
-       int retval;
-       int i;
-
-       del_timer_sync(&rocket_timer);
-
-       retval = tty_unregister_driver(rocket_driver);
-       if (retval)
-               printk(KERN_ERR "Error %d while trying to unregister "
-                      "rocketport driver\n", -retval);
-
-       for (i = 0; i < MAX_RP_PORTS; i++)
-               if (rp_table[i]) {
-                       tty_unregister_device(rocket_driver, i);
-                       kfree(rp_table[i]);
-               }
-
-       put_tty_driver(rocket_driver);
-
-       for (i = 0; i < NUM_BOARDS; i++) {
-               if (rcktpt_io_addr[i] <= 0 || is_PCI[i])
-                       continue;
-               release_region(rcktpt_io_addr[i], 64);
-       }
-       if (controller)
-               release_region(controller, 4);
-}
-
-/***************************************************************************
-Function: sInitController
-Purpose:  Initialization of controller global registers and controller
-          structure.
-Call:     sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize,
-                          IRQNum,Frequency,PeriodicOnly)
-          CONTROLLER_T *CtlP; Ptr to controller structure
-          int CtlNum; Controller number
-          ByteIO_t MudbacIO; Mudbac base I/O address.
-          ByteIO_t *AiopIOList; List of I/O addresses for each AIOP.
-             This list must be in the order the AIOPs will be found on the
-             controller.  Once an AIOP in the list is not found, it is
-             assumed that there are no more AIOPs on the controller.
-          int AiopIOListSize; Number of addresses in AiopIOList
-          int IRQNum; Interrupt Request number.  Can be any of the following:
-                         0: Disable global interrupts
-                         3: IRQ 3
-                         4: IRQ 4
-                         5: IRQ 5
-                         9: IRQ 9
-                         10: IRQ 10
-                         11: IRQ 11
-                         12: IRQ 12
-                         15: IRQ 15
-          Byte_t Frequency: A flag identifying the frequency
-                   of the periodic interrupt, can be any one of the following:
-                      FREQ_DIS - periodic interrupt disabled
-                      FREQ_137HZ - 137 Hertz
-                      FREQ_69HZ - 69 Hertz
-                      FREQ_34HZ - 34 Hertz
-                      FREQ_17HZ - 17 Hertz
-                      FREQ_9HZ - 9 Hertz
-                      FREQ_4HZ - 4 Hertz
-                   If IRQNum is set to 0 the Frequency parameter is
-                   overidden, it is forced to a value of FREQ_DIS.
-          int PeriodicOnly: 1 if all interrupts except the periodic
-                               interrupt are to be blocked.
-                            0 is both the periodic interrupt and
-                               other channel interrupts are allowed.
-                            If IRQNum is set to 0 the PeriodicOnly parameter is
-                               overidden, it is forced to a value of 0.
-Return:   int: Number of AIOPs on the controller, or CTLID_NULL if controller
-               initialization failed.
-
-Comments:
-          If periodic interrupts are to be disabled but AIOP interrupts
-          are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0.
-
-          If interrupts are to be completely disabled set IRQNum to 0.
-
-          Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an
-          invalid combination.
-
-          This function performs initialization of global interrupt modes,
-          but it does not actually enable global interrupts.  To enable
-          and disable global interrupts use functions sEnGlobalInt() and
-          sDisGlobalInt().  Enabling of global interrupts is normally not
-          done until all other initializations are complete.
-
-          Even if interrupts are globally enabled, they must also be
-          individually enabled for each channel that is to generate
-          interrupts.
-
-Warnings: No range checking on any of the parameters is done.
-
-          No context switches are allowed while executing this function.
-
-          After this function all AIOPs on the controller are disabled,
-          they can be enabled with sEnAiop().
-*/
-static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO,
-                          ByteIO_t * AiopIOList, int AiopIOListSize,
-                          int IRQNum, Byte_t Frequency, int PeriodicOnly)
-{
-       int i;
-       ByteIO_t io;
-       int done;
-
-       CtlP->AiopIntrBits = aiop_intr_bits;
-       CtlP->AltChanRingIndicator = 0;
-       CtlP->CtlNum = CtlNum;
-       CtlP->CtlID = CTLID_0001;       /* controller release 1 */
-       CtlP->BusType = isISA;
-       CtlP->MBaseIO = MudbacIO;
-       CtlP->MReg1IO = MudbacIO + 1;
-       CtlP->MReg2IO = MudbacIO + 2;
-       CtlP->MReg3IO = MudbacIO + 3;
-#if 1
-       CtlP->MReg2 = 0;        /* interrupt disable */
-       CtlP->MReg3 = 0;        /* no periodic interrupts */
-#else
-       if (sIRQMap[IRQNum] == 0) {     /* interrupts globally disabled */
-               CtlP->MReg2 = 0;        /* interrupt disable */
-               CtlP->MReg3 = 0;        /* no periodic interrupts */
-       } else {
-               CtlP->MReg2 = sIRQMap[IRQNum];  /* set IRQ number */
-               CtlP->MReg3 = Frequency;        /* set frequency */
-               if (PeriodicOnly) {     /* periodic interrupt only */
-                       CtlP->MReg3 |= PERIODIC_ONLY;
-               }
-       }
-#endif
-       sOutB(CtlP->MReg2IO, CtlP->MReg2);
-       sOutB(CtlP->MReg3IO, CtlP->MReg3);
-       sControllerEOI(CtlP);   /* clear EOI if warm init */
-       /* Init AIOPs */
-       CtlP->NumAiop = 0;
-       for (i = done = 0; i < AiopIOListSize; i++) {
-               io = AiopIOList[i];
-               CtlP->AiopIO[i] = (WordIO_t) io;
-               CtlP->AiopIntChanIO[i] = io + _INT_CHAN;
-               sOutB(CtlP->MReg2IO, CtlP->MReg2 | (i & 0x03)); /* AIOP index */
-               sOutB(MudbacIO, (Byte_t) (io >> 6));    /* set up AIOP I/O in MUDBAC */
-               if (done)
-                       continue;
-               sEnAiop(CtlP, i);       /* enable the AIOP */
-               CtlP->AiopID[i] = sReadAiopID(io);      /* read AIOP ID */
-               if (CtlP->AiopID[i] == AIOPID_NULL)     /* if AIOP does not exist */
-                       done = 1;       /* done looking for AIOPs */
-               else {
-                       CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */
-                       sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE);    /* clock prescaler */
-                       sOutB(io + _INDX_DATA, sClockPrescale);
-                       CtlP->NumAiop++;        /* bump count of AIOPs */
-               }
-               sDisAiop(CtlP, i);      /* disable AIOP */
-       }
-
-       if (CtlP->NumAiop == 0)
-               return (-1);
-       else
-               return (CtlP->NumAiop);
-}
-
-/***************************************************************************
-Function: sPCIInitController
-Purpose:  Initialization of controller global registers and controller
-          structure.
-Call:     sPCIInitController(CtlP,CtlNum,AiopIOList,AiopIOListSize,
-                          IRQNum,Frequency,PeriodicOnly)
-          CONTROLLER_T *CtlP; Ptr to controller structure
-          int CtlNum; Controller number
-          ByteIO_t *AiopIOList; List of I/O addresses for each AIOP.
-             This list must be in the order the AIOPs will be found on the
-             controller.  Once an AIOP in the list is not found, it is
-             assumed that there are no more AIOPs on the controller.
-          int AiopIOListSize; Number of addresses in AiopIOList
-          int IRQNum; Interrupt Request number.  Can be any of the following:
-                         0: Disable global interrupts
-                         3: IRQ 3
-                         4: IRQ 4
-                         5: IRQ 5
-                         9: IRQ 9
-                         10: IRQ 10
-                         11: IRQ 11
-                         12: IRQ 12
-                         15: IRQ 15
-          Byte_t Frequency: A flag identifying the frequency
-                   of the periodic interrupt, can be any one of the following:
-                      FREQ_DIS - periodic interrupt disabled
-                      FREQ_137HZ - 137 Hertz
-                      FREQ_69HZ - 69 Hertz
-                      FREQ_34HZ - 34 Hertz
-                      FREQ_17HZ - 17 Hertz
-                      FREQ_9HZ - 9 Hertz
-                      FREQ_4HZ - 4 Hertz
-                   If IRQNum is set to 0 the Frequency parameter is
-                   overidden, it is forced to a value of FREQ_DIS.
-          int PeriodicOnly: 1 if all interrupts except the periodic
-                               interrupt are to be blocked.
-                            0 is both the periodic interrupt and
-                               other channel interrupts are allowed.
-                            If IRQNum is set to 0 the PeriodicOnly parameter is
-                               overidden, it is forced to a value of 0.
-Return:   int: Number of AIOPs on the controller, or CTLID_NULL if controller
-               initialization failed.
-
-Comments:
-          If periodic interrupts are to be disabled but AIOP interrupts
-          are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0.
-
-          If interrupts are to be completely disabled set IRQNum to 0.
-
-          Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an
-          invalid combination.
-
-          This function performs initialization of global interrupt modes,
-          but it does not actually enable global interrupts.  To enable
-          and disable global interrupts use functions sEnGlobalInt() and
-          sDisGlobalInt().  Enabling of global interrupts is normally not
-          done until all other initializations are complete.
-
-          Even if interrupts are globally enabled, they must also be
-          individually enabled for each channel that is to generate
-          interrupts.
-
-Warnings: No range checking on any of the parameters is done.
-
-          No context switches are allowed while executing this function.
-
-          After this function all AIOPs on the controller are disabled,
-          they can be enabled with sEnAiop().
-*/
-static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum,
-                             ByteIO_t * AiopIOList, int AiopIOListSize,
-                             WordIO_t ConfigIO, int IRQNum, Byte_t Frequency,
-                             int PeriodicOnly, int altChanRingIndicator,
-                             int UPCIRingInd)
-{
-       int i;
-       ByteIO_t io;
-
-       CtlP->AltChanRingIndicator = altChanRingIndicator;
-       CtlP->UPCIRingInd = UPCIRingInd;
-       CtlP->CtlNum = CtlNum;
-       CtlP->CtlID = CTLID_0001;       /* controller release 1 */
-       CtlP->BusType = isPCI;  /* controller release 1 */
-
-       if (ConfigIO) {
-               CtlP->isUPCI = 1;
-               CtlP->PCIIO = ConfigIO + _PCI_9030_INT_CTRL;
-               CtlP->PCIIO2 = ConfigIO + _PCI_9030_GPIO_CTRL;
-               CtlP->AiopIntrBits = upci_aiop_intr_bits;
-       } else {
-               CtlP->isUPCI = 0;
-               CtlP->PCIIO =
-                   (WordIO_t) ((ByteIO_t) AiopIOList[0] + _PCI_INT_FUNC);
-               CtlP->AiopIntrBits = aiop_intr_bits;
-       }
-
-       sPCIControllerEOI(CtlP);        /* clear EOI if warm init */
-       /* Init AIOPs */
-       CtlP->NumAiop = 0;
-       for (i = 0; i < AiopIOListSize; i++) {
-               io = AiopIOList[i];
-               CtlP->AiopIO[i] = (WordIO_t) io;
-               CtlP->AiopIntChanIO[i] = io + _INT_CHAN;
-
-               CtlP->AiopID[i] = sReadAiopID(io);      /* read AIOP ID */
-               if (CtlP->AiopID[i] == AIOPID_NULL)     /* if AIOP does not exist */
-                       break;  /* done looking for AIOPs */
-
-               CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */
-               sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE);    /* clock prescaler */
-               sOutB(io + _INDX_DATA, sClockPrescale);
-               CtlP->NumAiop++;        /* bump count of AIOPs */
-       }
-
-       if (CtlP->NumAiop == 0)
-               return (-1);
-       else
-               return (CtlP->NumAiop);
-}
-
-/***************************************************************************
-Function: sReadAiopID
-Purpose:  Read the AIOP idenfication number directly from an AIOP.
-Call:     sReadAiopID(io)
-          ByteIO_t io: AIOP base I/O address
-Return:   int: Flag AIOPID_XXXX if a valid AIOP is found, where X
-                 is replace by an identifying number.
-          Flag AIOPID_NULL if no valid AIOP is found
-Warnings: No context switches are allowed while executing this function.
-
-*/
-static int sReadAiopID(ByteIO_t io)
-{
-       Byte_t AiopID;          /* ID byte from AIOP */
-
-       sOutB(io + _CMD_REG, RESET_ALL);        /* reset AIOP */
-       sOutB(io + _CMD_REG, 0x0);
-       AiopID = sInW(io + _CHN_STAT0) & 0x07;
-       if (AiopID == 0x06)
-               return (1);
-       else                    /* AIOP does not exist */
-               return (-1);
-}
-
-/***************************************************************************
-Function: sReadAiopNumChan
-Purpose:  Read the number of channels available in an AIOP directly from
-          an AIOP.
-Call:     sReadAiopNumChan(io)
-          WordIO_t io: AIOP base I/O address
-Return:   int: The number of channels available
-Comments: The number of channels is determined by write/reads from identical
-          offsets within the SRAM address spaces for channels 0 and 4.
-          If the channel 4 space is mirrored to channel 0 it is a 4 channel
-          AIOP, otherwise it is an 8 channel.
-Warnings: No context switches are allowed while executing this function.
-*/
-static int sReadAiopNumChan(WordIO_t io)
-{
-       Word_t x;
-       static Byte_t R[4] = { 0x00, 0x00, 0x34, 0x12 };
-
-       /* write to chan 0 SRAM */
-       out32((DWordIO_t) io + _INDX_ADDR, R);
-       sOutW(io + _INDX_ADDR, 0);      /* read from SRAM, chan 0 */
-       x = sInW(io + _INDX_DATA);
-       sOutW(io + _INDX_ADDR, 0x4000); /* read from SRAM, chan 4 */
-       if (x != sInW(io + _INDX_DATA)) /* if different must be 8 chan */
-               return (8);
-       else
-               return (4);
-}
-
-/***************************************************************************
-Function: sInitChan
-Purpose:  Initialization of a channel and channel structure
-Call:     sInitChan(CtlP,ChP,AiopNum,ChanNum)
-          CONTROLLER_T *CtlP; Ptr to controller structure
-          CHANNEL_T *ChP; Ptr to channel structure
-          int AiopNum; AIOP number within controller
-          int ChanNum; Channel number within AIOP
-Return:   int: 1 if initialization succeeded, 0 if it fails because channel
-               number exceeds number of channels available in AIOP.
-Comments: This function must be called before a channel can be used.
-Warnings: No range checking on any of the parameters is done.
-
-          No context switches are allowed while executing this function.
-*/
-static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum,
-                    int ChanNum)
-{
-       int i;
-       WordIO_t AiopIO;
-       WordIO_t ChIOOff;
-       Byte_t *ChR;
-       Word_t ChOff;
-       static Byte_t R[4];
-       int brd9600;
-
-       if (ChanNum >= CtlP->AiopNumChan[AiopNum])
-               return 0;       /* exceeds num chans in AIOP */
-
-       /* Channel, AIOP, and controller identifiers */
-       ChP->CtlP = CtlP;
-       ChP->ChanID = CtlP->AiopID[AiopNum];
-       ChP->AiopNum = AiopNum;
-       ChP->ChanNum = ChanNum;
-
-       /* Global direct addresses */
-       AiopIO = CtlP->AiopIO[AiopNum];
-       ChP->Cmd = (ByteIO_t) AiopIO + _CMD_REG;
-       ChP->IntChan = (ByteIO_t) AiopIO + _INT_CHAN;
-       ChP->IntMask = (ByteIO_t) AiopIO + _INT_MASK;
-       ChP->IndexAddr = (DWordIO_t) AiopIO + _INDX_ADDR;
-       ChP->IndexData = AiopIO + _INDX_DATA;
-
-       /* Channel direct addresses */
-       ChIOOff = AiopIO + ChP->ChanNum * 2;
-       ChP->TxRxData = ChIOOff + _TD0;
-       ChP->ChanStat = ChIOOff + _CHN_STAT0;
-       ChP->TxRxCount = ChIOOff + _FIFO_CNT0;
-       ChP->IntID = (ByteIO_t) AiopIO + ChP->ChanNum + _INT_ID0;
-
-       /* Initialize the channel from the RData array */
-       for (i = 0; i < RDATASIZE; i += 4) {
-               R[0] = RData[i];
-               R[1] = RData[i + 1] + 0x10 * ChanNum;
-               R[2] = RData[i + 2];
-               R[3] = RData[i + 3];
-               out32(ChP->IndexAddr, R);
-       }
-
-       ChR = ChP->R;
-       for (i = 0; i < RREGDATASIZE; i += 4) {
-               ChR[i] = RRegData[i];
-               ChR[i + 1] = RRegData[i + 1] + 0x10 * ChanNum;
-               ChR[i + 2] = RRegData[i + 2];
-               ChR[i + 3] = RRegData[i + 3];
-       }
-
-       /* Indexed registers */
-       ChOff = (Word_t) ChanNum *0x1000;
-
-       if (sClockPrescale == 0x14)
-               brd9600 = 47;
-       else
-               brd9600 = 23;
-
-       ChP->BaudDiv[0] = (Byte_t) (ChOff + _BAUD);
-       ChP->BaudDiv[1] = (Byte_t) ((ChOff + _BAUD) >> 8);
-       ChP->BaudDiv[2] = (Byte_t) brd9600;
-       ChP->BaudDiv[3] = (Byte_t) (brd9600 >> 8);
-       out32(ChP->IndexAddr, ChP->BaudDiv);
-
-       ChP->TxControl[0] = (Byte_t) (ChOff + _TX_CTRL);
-       ChP->TxControl[1] = (Byte_t) ((ChOff + _TX_CTRL) >> 8);
-       ChP->TxControl[2] = 0;
-       ChP->TxControl[3] = 0;
-       out32(ChP->IndexAddr, ChP->TxControl);
-
-       ChP->RxControl[0] = (Byte_t) (ChOff + _RX_CTRL);
-       ChP->RxControl[1] = (Byte_t) ((ChOff + _RX_CTRL) >> 8);
-       ChP->RxControl[2] = 0;
-       ChP->RxControl[3] = 0;
-       out32(ChP->IndexAddr, ChP->RxControl);
-
-       ChP->TxEnables[0] = (Byte_t) (ChOff + _TX_ENBLS);
-       ChP->TxEnables[1] = (Byte_t) ((ChOff + _TX_ENBLS) >> 8);
-       ChP->TxEnables[2] = 0;
-       ChP->TxEnables[3] = 0;
-       out32(ChP->IndexAddr, ChP->TxEnables);
-
-       ChP->TxCompare[0] = (Byte_t) (ChOff + _TXCMP1);
-       ChP->TxCompare[1] = (Byte_t) ((ChOff + _TXCMP1) >> 8);
-       ChP->TxCompare[2] = 0;
-       ChP->TxCompare[3] = 0;
-       out32(ChP->IndexAddr, ChP->TxCompare);
-
-       ChP->TxReplace1[0] = (Byte_t) (ChOff + _TXREP1B1);
-       ChP->TxReplace1[1] = (Byte_t) ((ChOff + _TXREP1B1) >> 8);
-       ChP->TxReplace1[2] = 0;
-       ChP->TxReplace1[3] = 0;
-       out32(ChP->IndexAddr, ChP->TxReplace1);
-
-       ChP->TxReplace2[0] = (Byte_t) (ChOff + _TXREP2);
-       ChP->TxReplace2[1] = (Byte_t) ((ChOff + _TXREP2) >> 8);
-       ChP->TxReplace2[2] = 0;
-       ChP->TxReplace2[3] = 0;
-       out32(ChP->IndexAddr, ChP->TxReplace2);
-
-       ChP->TxFIFOPtrs = ChOff + _TXF_OUTP;
-       ChP->TxFIFO = ChOff + _TX_FIFO;
-
-       sOutB(ChP->Cmd, (Byte_t) ChanNum | RESTXFCNT);  /* apply reset Tx FIFO count */
-       sOutB(ChP->Cmd, (Byte_t) ChanNum);      /* remove reset Tx FIFO count */
-       sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs);      /* clear Tx in/out ptrs */
-       sOutW(ChP->IndexData, 0);
-       ChP->RxFIFOPtrs = ChOff + _RXF_OUTP;
-       ChP->RxFIFO = ChOff + _RX_FIFO;
-
-       sOutB(ChP->Cmd, (Byte_t) ChanNum | RESRXFCNT);  /* apply reset Rx FIFO count */
-       sOutB(ChP->Cmd, (Byte_t) ChanNum);      /* remove reset Rx FIFO count */
-       sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs);      /* clear Rx out ptr */
-       sOutW(ChP->IndexData, 0);
-       sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2);  /* clear Rx in ptr */
-       sOutW(ChP->IndexData, 0);
-       ChP->TxPrioCnt = ChOff + _TXP_CNT;
-       sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioCnt);
-       sOutB(ChP->IndexData, 0);
-       ChP->TxPrioPtr = ChOff + _TXP_PNTR;
-       sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioPtr);
-       sOutB(ChP->IndexData, 0);
-       ChP->TxPrioBuf = ChOff + _TXP_BUF;
-       sEnRxProcessor(ChP);    /* start the Rx processor */
-
-       return 1;
-}
-
-/***************************************************************************
-Function: sStopRxProcessor
-Purpose:  Stop the receive processor from processing a channel.
-Call:     sStopRxProcessor(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-
-Comments: The receive processor can be started again with sStartRxProcessor().
-          This function causes the receive processor to skip over the
-          stopped channel.  It does not stop it from processing other channels.
-
-Warnings: No context switches are allowed while executing this function.
-
-          Do not leave the receive processor stopped for more than one
-          character time.
-
-          After calling this function a delay of 4 uS is required to ensure
-          that the receive processor is no longer processing this channel.
-*/
-static void sStopRxProcessor(CHANNEL_T * ChP)
-{
-       Byte_t R[4];
-
-       R[0] = ChP->R[0];
-       R[1] = ChP->R[1];
-       R[2] = 0x0a;
-       R[3] = ChP->R[3];
-       out32(ChP->IndexAddr, R);
-}
-
-/***************************************************************************
-Function: sFlushRxFIFO
-Purpose:  Flush the Rx FIFO
-Call:     sFlushRxFIFO(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Return:   void
-Comments: To prevent data from being enqueued or dequeued in the Tx FIFO
-          while it is being flushed the receive processor is stopped
-          and the transmitter is disabled.  After these operations a
-          4 uS delay is done before clearing the pointers to allow
-          the receive processor to stop.  These items are handled inside
-          this function.
-Warnings: No context switches are allowed while executing this function.
-*/
-static void sFlushRxFIFO(CHANNEL_T * ChP)
-{
-       int i;
-       Byte_t Ch;              /* channel number within AIOP */
-       int RxFIFOEnabled;      /* 1 if Rx FIFO enabled */
-
-       if (sGetRxCnt(ChP) == 0)        /* Rx FIFO empty */
-               return;         /* don't need to flush */
-
-       RxFIFOEnabled = 0;
-       if (ChP->R[0x32] == 0x08) {     /* Rx FIFO is enabled */
-               RxFIFOEnabled = 1;
-               sDisRxFIFO(ChP);        /* disable it */
-               for (i = 0; i < 2000 / 200; i++)        /* delay 2 uS to allow proc to disable FIFO */
-                       sInB(ChP->IntChan);     /* depends on bus i/o timing */
-       }
-       sGetChanStatus(ChP);    /* clear any pending Rx errors in chan stat */
-       Ch = (Byte_t) sGetChanNum(ChP);
-       sOutB(ChP->Cmd, Ch | RESRXFCNT);        /* apply reset Rx FIFO count */
-       sOutB(ChP->Cmd, Ch);    /* remove reset Rx FIFO count */
-       sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs);      /* clear Rx out ptr */
-       sOutW(ChP->IndexData, 0);
-       sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2);  /* clear Rx in ptr */
-       sOutW(ChP->IndexData, 0);
-       if (RxFIFOEnabled)
-               sEnRxFIFO(ChP); /* enable Rx FIFO */
-}
-
-/***************************************************************************
-Function: sFlushTxFIFO
-Purpose:  Flush the Tx FIFO
-Call:     sFlushTxFIFO(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Return:   void
-Comments: To prevent data from being enqueued or dequeued in the Tx FIFO
-          while it is being flushed the receive processor is stopped
-          and the transmitter is disabled.  After these operations a
-          4 uS delay is done before clearing the pointers to allow
-          the receive processor to stop.  These items are handled inside
-          this function.
-Warnings: No context switches are allowed while executing this function.
-*/
-static void sFlushTxFIFO(CHANNEL_T * ChP)
-{
-       int i;
-       Byte_t Ch;              /* channel number within AIOP */
-       int TxEnabled;          /* 1 if transmitter enabled */
-
-       if (sGetTxCnt(ChP) == 0)        /* Tx FIFO empty */
-               return;         /* don't need to flush */
-
-       TxEnabled = 0;
-       if (ChP->TxControl[3] & TX_ENABLE) {
-               TxEnabled = 1;
-               sDisTransmit(ChP);      /* disable transmitter */
-       }
-       sStopRxProcessor(ChP);  /* stop Rx processor */
-       for (i = 0; i < 4000 / 200; i++)        /* delay 4 uS to allow proc to stop */
-               sInB(ChP->IntChan);     /* depends on bus i/o timing */
-       Ch = (Byte_t) sGetChanNum(ChP);
-       sOutB(ChP->Cmd, Ch | RESTXFCNT);        /* apply reset Tx FIFO count */
-       sOutB(ChP->Cmd, Ch);    /* remove reset Tx FIFO count */
-       sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs);      /* clear Tx in/out ptrs */
-       sOutW(ChP->IndexData, 0);
-       if (TxEnabled)
-               sEnTransmit(ChP);       /* enable transmitter */
-       sStartRxProcessor(ChP); /* restart Rx processor */
-}
-
-/***************************************************************************
-Function: sWriteTxPrioByte
-Purpose:  Write a byte of priority transmit data to a channel
-Call:     sWriteTxPrioByte(ChP,Data)
-          CHANNEL_T *ChP; Ptr to channel structure
-          Byte_t Data; The transmit data byte
-
-Return:   int: 1 if the bytes is successfully written, otherwise 0.
-
-Comments: The priority byte is transmitted before any data in the Tx FIFO.
-
-Warnings: No context switches are allowed while executing this function.
-*/
-static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data)
-{
-       Byte_t DWBuf[4];        /* buffer for double word writes */
-       Word_t *WordPtr;        /* must be far because Win SS != DS */
-       register DWordIO_t IndexAddr;
-
-       if (sGetTxCnt(ChP) > 1) {       /* write it to Tx priority buffer */
-               IndexAddr = ChP->IndexAddr;
-               sOutW((WordIO_t) IndexAddr, ChP->TxPrioCnt);    /* get priority buffer status */
-               if (sInB((ByteIO_t) ChP->IndexData) & PRI_PEND) /* priority buffer busy */
-                       return (0);     /* nothing sent */
-
-               WordPtr = (Word_t *) (&DWBuf[0]);
-               *WordPtr = ChP->TxPrioBuf;      /* data byte address */
-
-               DWBuf[2] = Data;        /* data byte value */
-               out32(IndexAddr, DWBuf);        /* write it out */
-
-               *WordPtr = ChP->TxPrioCnt;      /* Tx priority count address */
-
-               DWBuf[2] = PRI_PEND + 1;        /* indicate 1 byte pending */
-               DWBuf[3] = 0;   /* priority buffer pointer */
-               out32(IndexAddr, DWBuf);        /* write it out */
-       } else {                /* write it to Tx FIFO */
-
-               sWriteTxByte(sGetTxRxDataIO(ChP), Data);
-       }
-       return (1);             /* 1 byte sent */
-}
-
-/***************************************************************************
-Function: sEnInterrupts
-Purpose:  Enable one or more interrupts for a channel
-Call:     sEnInterrupts(ChP,Flags)
-          CHANNEL_T *ChP; Ptr to channel structure
-          Word_t Flags: Interrupt enable flags, can be any combination
-             of the following flags:
-                TXINT_EN:   Interrupt on Tx FIFO empty
-                RXINT_EN:   Interrupt on Rx FIFO at trigger level (see
-                            sSetRxTrigger())
-                SRCINT_EN:  Interrupt on SRC (Special Rx Condition)
-                MCINT_EN:   Interrupt on modem input change
-                CHANINT_EN: Allow channel interrupt signal to the AIOP's
-                            Interrupt Channel Register.
-Return:   void
-Comments: If an interrupt enable flag is set in Flags, that interrupt will be
-          enabled.  If an interrupt enable flag is not set in Flags, that
-          interrupt will not be changed.  Interrupts can be disabled with
-          function sDisInterrupts().
-
-          This function sets the appropriate bit for the channel in the AIOP's
-          Interrupt Mask Register if the CHANINT_EN flag is set.  This allows
-          this channel's bit to be set in the AIOP's Interrupt Channel Register.
-
-          Interrupts must also be globally enabled before channel interrupts
-          will be passed on to the host.  This is done with function
-          sEnGlobalInt().
-
-          In some cases it may be desirable to disable interrupts globally but
-          enable channel interrupts.  This would allow the global interrupt
-          status register to be used to determine which AIOPs need service.
-*/
-static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags)
-{
-       Byte_t Mask;            /* Interrupt Mask Register */
-
-       ChP->RxControl[2] |=
-           ((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN));
-
-       out32(ChP->IndexAddr, ChP->RxControl);
-
-       ChP->TxControl[2] |= ((Byte_t) Flags & TXINT_EN);
-
-       out32(ChP->IndexAddr, ChP->TxControl);
-
-       if (Flags & CHANINT_EN) {
-               Mask = sInB(ChP->IntMask) | sBitMapSetTbl[ChP->ChanNum];
-               sOutB(ChP->IntMask, Mask);
-       }
-}
-
-/***************************************************************************
-Function: sDisInterrupts
-Purpose:  Disable one or more interrupts for a channel
-Call:     sDisInterrupts(ChP,Flags)
-          CHANNEL_T *ChP; Ptr to channel structure
-          Word_t Flags: Interrupt flags, can be any combination
-             of the following flags:
-                TXINT_EN:   Interrupt on Tx FIFO empty
-                RXINT_EN:   Interrupt on Rx FIFO at trigger level (see
-                            sSetRxTrigger())
-                SRCINT_EN:  Interrupt on SRC (Special Rx Condition)
-                MCINT_EN:   Interrupt on modem input change
-                CHANINT_EN: Disable channel interrupt signal to the
-                            AIOP's Interrupt Channel Register.
-Return:   void
-Comments: If an interrupt flag is set in Flags, that interrupt will be
-          disabled.  If an interrupt flag is not set in Flags, that
-          interrupt will not be changed.  Interrupts can be enabled with
-          function sEnInterrupts().
-
-          This function clears the appropriate bit for the channel in the AIOP's
-          Interrupt Mask Register if the CHANINT_EN flag is set.  This blocks
-          this channel's bit from being set in the AIOP's Interrupt Channel
-          Register.
-*/
-static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags)
-{
-       Byte_t Mask;            /* Interrupt Mask Register */
-
-       ChP->RxControl[2] &=
-           ~((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN));
-       out32(ChP->IndexAddr, ChP->RxControl);
-       ChP->TxControl[2] &= ~((Byte_t) Flags & TXINT_EN);
-       out32(ChP->IndexAddr, ChP->TxControl);
-
-       if (Flags & CHANINT_EN) {
-               Mask = sInB(ChP->IntMask) & sBitMapClrTbl[ChP->ChanNum];
-               sOutB(ChP->IntMask, Mask);
-       }
-}
-
-static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode)
-{
-       sOutB(ChP->CtlP->AiopIO[2], (mode & 0x18) | ChP->ChanNum);
-}
-
-/*
- *  Not an official SSCI function, but how to reset RocketModems.
- *  ISA bus version
- */
-static void sModemReset(CONTROLLER_T * CtlP, int chan, int on)
-{
-       ByteIO_t addr;
-       Byte_t val;
-
-       addr = CtlP->AiopIO[0] + 0x400;
-       val = sInB(CtlP->MReg3IO);
-       /* if AIOP[1] is not enabled, enable it */
-       if ((val & 2) == 0) {
-               val = sInB(CtlP->MReg2IO);
-               sOutB(CtlP->MReg2IO, (val & 0xfc) | (1 & 0x03));
-               sOutB(CtlP->MBaseIO, (unsigned char) (addr >> 6));
-       }
-
-       sEnAiop(CtlP, 1);
-       if (!on)
-               addr += 8;
-       sOutB(addr + chan, 0);  /* apply or remove reset */
-       sDisAiop(CtlP, 1);
-}
-
-/*
- *  Not an official SSCI function, but how to reset RocketModems.
- *  PCI bus version
- */
-static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on)
-{
-       ByteIO_t addr;
-
-       addr = CtlP->AiopIO[0] + 0x40;  /* 2nd AIOP */
-       if (!on)
-               addr += 8;
-       sOutB(addr + chan, 0);  /* apply or remove reset */
-}
-
-/*  Resets the speaker controller on RocketModem II and III devices */
-static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model)
-{
-       ByteIO_t addr;
-
-       /* RocketModem II speaker control is at the 8th port location of offset 0x40 */
-       if ((model == MODEL_RP4M) || (model == MODEL_RP6M)) {
-               addr = CtlP->AiopIO[0] + 0x4F;
-               sOutB(addr, 0);
-       }
-
-       /* RocketModem III speaker control is at the 1st port location of offset 0x80 */
-       if ((model == MODEL_UPCI_RM3_8PORT)
-           || (model == MODEL_UPCI_RM3_4PORT)) {
-               addr = CtlP->AiopIO[0] + 0x88;
-               sOutB(addr, 0);
-       }
-}
-
-/*  Returns the line number given the controller (board), aiop and channel number */
-static unsigned char GetLineNumber(int ctrl, int aiop, int ch)
-{
-       return lineNumbers[(ctrl << 5) | (aiop << 3) | ch];
-}
-
-/*
- *  Stores the line number associated with a given controller (board), aiop
- *  and channel number.  
- *  Returns:  The line number assigned 
- */
-static unsigned char SetLineNumber(int ctrl, int aiop, int ch)
-{
-       lineNumbers[(ctrl << 5) | (aiop << 3) | ch] = nextLineNumber++;
-       return (nextLineNumber - 1);
-}
diff --git a/drivers/char/rocket.h b/drivers/char/rocket.h
deleted file mode 100644 (file)
index ec863f3..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * rocket.h --- the exported interface of the rocket driver to its configuration program.
- *
- * Written by Theodore Ts'o, Copyright 1997.
- * Copyright 1997 Comtrol Corporation. 
- *
- */
-
-/*  Model Information Struct */
-typedef struct {
-       unsigned long model;
-       char modelString[80];
-       unsigned long numPorts;
-       int loadrm2;
-       int startingPortNumber;
-} rocketModel_t;
-
-struct rocket_config {
-       int line;
-       int flags;
-       int closing_wait;
-       int close_delay;
-       int port;
-       int reserved[32];
-};
-
-struct rocket_ports {
-       int tty_major;
-       int callout_major;
-       rocketModel_t rocketModel[8];
-};
-
-struct rocket_version {
-       char rocket_version[32];
-       char rocket_date[32];
-       char reserved[64];
-};
-
-/*
- * Rocketport flags
- */
-/*#define ROCKET_CALLOUT_NOHUP    0x00000001 */
-#define ROCKET_FORCE_CD                0x00000002
-#define ROCKET_HUP_NOTIFY      0x00000004
-#define ROCKET_SPLIT_TERMIOS   0x00000008
-#define ROCKET_SPD_MASK                0x00000070
-#define ROCKET_SPD_HI          0x00000010      /* Use 56000 instead of 38400 bps */
-#define ROCKET_SPD_VHI         0x00000020      /* Use 115200 instead of 38400 bps */
-#define ROCKET_SPD_SHI         0x00000030      /* Use 230400 instead of 38400 bps */
-#define ROCKET_SPD_WARP                0x00000040      /* Use 460800 instead of 38400 bps */
-#define ROCKET_SAK             0x00000080
-#define ROCKET_SESSION_LOCKOUT 0x00000100
-#define ROCKET_PGRP_LOCKOUT    0x00000200
-#define ROCKET_RTS_TOGGLE      0x00000400
-#define ROCKET_MODE_MASK        0x00003000
-#define ROCKET_MODE_RS232       0x00000000
-#define ROCKET_MODE_RS485       0x00001000
-#define ROCKET_MODE_RS422       0x00002000
-#define ROCKET_FLAGS           0x00003FFF
-
-#define ROCKET_USR_MASK 0x0071 /* Legal flags that non-privileged
-                                * users can set or reset */
-
-/*
- * For closing_wait and closing_wait2
- */
-#define ROCKET_CLOSING_WAIT_NONE       ASYNC_CLOSING_WAIT_NONE
-#define ROCKET_CLOSING_WAIT_INF                ASYNC_CLOSING_WAIT_INF
-
-/*
- * Rocketport ioctls -- "RP"
- */
-#define RCKP_GET_STRUCT                0x00525001
-#define RCKP_GET_CONFIG                0x00525002
-#define RCKP_SET_CONFIG                0x00525003
-#define RCKP_GET_PORTS         0x00525004
-#define RCKP_RESET_RM2         0x00525005
-#define RCKP_GET_VERSION       0x00525006
-
-/*  Rocketport Models */
-#define MODEL_RP32INTF        0x0001   /* RP 32 port w/external I/F   */
-#define MODEL_RP8INTF         0x0002   /* RP 8 port w/external I/F    */
-#define MODEL_RP16INTF        0x0003   /* RP 16 port w/external I/F   */
-#define MODEL_RP8OCTA         0x0005   /* RP 8 port w/octa cable      */
-#define MODEL_RP4QUAD         0x0004   /* RP 4 port w/quad cable      */
-#define MODEL_RP8J            0x0006   /* RP 8 port w/RJ11 connectors */
-#define MODEL_RP4J            0x0007   /* RP 4 port w/RJ45 connectors */
-#define MODEL_RP8SNI          0x0008   /* RP 8 port w/ DB78 SNI connector */
-#define MODEL_RP16SNI         0x0009   /* RP 16 port w/ DB78 SNI connector */
-#define MODEL_RPP4            0x000A   /* RP Plus 4 port              */
-#define MODEL_RPP8            0x000B   /* RP Plus 8 port              */
-#define MODEL_RP2_232         0x000E   /* RP Plus 2 port RS232        */
-#define MODEL_RP2_422         0x000F   /* RP Plus 2 port RS232        */
-
-/*  Rocketmodem II Models */
-#define MODEL_RP6M            0x000C   /* RM 6 port                   */
-#define MODEL_RP4M            0x000D   /* RM 4 port                   */
-
-/* Universal PCI boards */
-#define MODEL_UPCI_RP32INTF   0x0801   /* RP UPCI 32 port w/external I/F     */
-#define MODEL_UPCI_RP8INTF    0x0802   /* RP UPCI 8 port w/external I/F      */
-#define MODEL_UPCI_RP16INTF   0x0803   /* RP UPCI 16 port w/external I/F     */
-#define MODEL_UPCI_RP8OCTA    0x0805   /* RP UPCI 8 port w/octa cable        */ 
-#define MODEL_UPCI_RM3_8PORT  0x080C   /* RP UPCI Rocketmodem III 8 port     */
-#define MODEL_UPCI_RM3_4PORT  0x080C   /* RP UPCI Rocketmodem III 4 port     */
-
-/*  Compact PCI 16 port  */
-#define MODEL_CPCI_RP16INTF   0x0903   /* RP Compact PCI 16 port w/external I/F */
-
-/* All ISA boards */
-#define MODEL_ISA             0x1000
diff --git a/drivers/char/rocket_int.h b/drivers/char/rocket_int.h
deleted file mode 100644 (file)
index 67e0f1e..0000000
+++ /dev/null
@@ -1,1214 +0,0 @@
-/*
- * rocket_int.h --- internal header file for rocket.c
- *
- * Written by Theodore Ts'o, Copyright 1997.
- * Copyright 1997 Comtrol Corporation.  
- * 
- */
-
-/*
- * Definition of the types in rcktpt_type
- */
-#define ROCKET_TYPE_NORMAL     0
-#define ROCKET_TYPE_MODEM      1
-#define ROCKET_TYPE_MODEMII    2
-#define ROCKET_TYPE_MODEMIII   3
-#define ROCKET_TYPE_PC104       4
-
-#include <linux/mutex.h>
-
-#include <asm/io.h>
-#include <asm/byteorder.h>
-
-typedef unsigned char Byte_t;
-typedef unsigned int ByteIO_t;
-
-typedef unsigned int Word_t;
-typedef unsigned int WordIO_t;
-
-typedef unsigned int DWordIO_t;
-
-/*
- * Note!  Normally the Linux I/O macros already take care of
- * byte-swapping the I/O instructions.  However, all accesses using
- * sOutDW aren't really 32-bit accesses, but should be handled in byte
- * order.  Hence the use of the cpu_to_le32() macro to byte-swap
- * things to no-op the byte swapping done by the big-endian outl()
- * instruction.
- */
-
-static inline void sOutB(unsigned short port, unsigned char value)
-{
-#ifdef ROCKET_DEBUG_IO
-       printk(KERN_DEBUG "sOutB(%x, %x)...\n", port, value);
-#endif
-       outb_p(value, port);
-}
-
-static inline void sOutW(unsigned short port, unsigned short value)
-{
-#ifdef ROCKET_DEBUG_IO
-       printk(KERN_DEBUG "sOutW(%x, %x)...\n", port, value);
-#endif
-       outw_p(value, port);
-}
-
-static inline void out32(unsigned short port, Byte_t *p)
-{
-       u32 value = get_unaligned_le32(p);
-#ifdef ROCKET_DEBUG_IO
-       printk(KERN_DEBUG "out32(%x, %lx)...\n", port, value);
-#endif
-       outl_p(value, port);
-}
-
-static inline unsigned char sInB(unsigned short port)
-{
-       return inb_p(port);
-}
-
-static inline unsigned short sInW(unsigned short port)
-{
-       return inw_p(port);
-}
-
-/* This is used to move arrays of bytes so byte swapping isn't appropriate. */
-#define sOutStrW(port, addr, count) if (count) outsw(port, addr, count)
-#define sInStrW(port, addr, count) if (count) insw(port, addr, count)
-
-#define CTL_SIZE 8
-#define AIOP_CTL_SIZE 4
-#define CHAN_AIOP_SIZE 8
-#define MAX_PORTS_PER_AIOP 8
-#define MAX_AIOPS_PER_BOARD 4
-#define MAX_PORTS_PER_BOARD 32
-
-/* Bus type ID */
-#define        isISA   0
-#define        isPCI   1
-#define        isMC    2
-
-/* Controller ID numbers */
-#define CTLID_NULL  -1         /* no controller exists */
-#define CTLID_0001  0x0001     /* controller release 1 */
-
-/* AIOP ID numbers, identifies AIOP type implementing channel */
-#define AIOPID_NULL -1         /* no AIOP or channel exists */
-#define AIOPID_0001 0x0001     /* AIOP release 1 */
-
-/************************************************************************
- Global Register Offsets - Direct Access - Fixed values
-************************************************************************/
-
-#define _CMD_REG   0x38                /* Command Register            8    Write */
-#define _INT_CHAN  0x39                /* Interrupt Channel Register  8    Read */
-#define _INT_MASK  0x3A                /* Interrupt Mask Register     8    Read / Write */
-#define _UNUSED    0x3B                /* Unused                      8 */
-#define _INDX_ADDR 0x3C                /* Index Register Address      16   Write */
-#define _INDX_DATA 0x3E                /* Index Register Data         8/16 Read / Write */
-
-/************************************************************************
- Channel Register Offsets for 1st channel in AIOP - Direct Access
-************************************************************************/
-#define _TD0       0x00                /* Transmit Data               16   Write */
-#define _RD0       0x00                /* Receive Data                16   Read */
-#define _CHN_STAT0 0x20                /* Channel Status              8/16 Read / Write */
-#define _FIFO_CNT0 0x10                /* Transmit/Receive FIFO Count 16   Read */
-#define _INT_ID0   0x30                /* Interrupt Identification    8    Read */
-
-/************************************************************************
- Tx Control Register Offsets - Indexed - External - Fixed
-************************************************************************/
-#define _TX_ENBLS  0x980       /* Tx Processor Enables Register 8 Read / Write */
-#define _TXCMP1    0x988       /* Transmit Compare Value #1     8 Read / Write */
-#define _TXCMP2    0x989       /* Transmit Compare Value #2     8 Read / Write */
-#define _TXREP1B1  0x98A       /* Tx Replace Value #1 - Byte 1  8 Read / Write */
-#define _TXREP1B2  0x98B       /* Tx Replace Value #1 - Byte 2  8 Read / Write */
-#define _TXREP2    0x98C       /* Transmit Replace Value #2     8 Read / Write */
-
-/************************************************************************
-Memory Controller Register Offsets - Indexed - External - Fixed
-************************************************************************/
-#define _RX_FIFO    0x000      /* Rx FIFO */
-#define _TX_FIFO    0x800      /* Tx FIFO */
-#define _RXF_OUTP   0x990      /* Rx FIFO OUT pointer        16 Read / Write */
-#define _RXF_INP    0x992      /* Rx FIFO IN pointer         16 Read / Write */
-#define _TXF_OUTP   0x994      /* Tx FIFO OUT pointer        8  Read / Write */
-#define _TXF_INP    0x995      /* Tx FIFO IN pointer         8  Read / Write */
-#define _TXP_CNT    0x996      /* Tx Priority Count          8  Read / Write */
-#define _TXP_PNTR   0x997      /* Tx Priority Pointer        8  Read / Write */
-
-#define PRI_PEND    0x80       /* Priority data pending (bit7, Tx pri cnt) */
-#define TXFIFO_SIZE 255                /* size of Tx FIFO */
-#define RXFIFO_SIZE 1023       /* size of Rx FIFO */
-
-/************************************************************************
-Tx Priority Buffer - Indexed - External - Fixed
-************************************************************************/
-#define _TXP_BUF    0x9C0      /* Tx Priority Buffer  32  Bytes   Read / Write */
-#define TXP_SIZE    0x20       /* 32 bytes */
-
-/************************************************************************
-Channel Register Offsets - Indexed - Internal - Fixed
-************************************************************************/
-
-#define _TX_CTRL    0xFF0      /* Transmit Control               16  Write */
-#define _RX_CTRL    0xFF2      /* Receive Control                 8  Write */
-#define _BAUD       0xFF4      /* Baud Rate                      16  Write */
-#define _CLK_PRE    0xFF6      /* Clock Prescaler                 8  Write */
-
-#define STMBREAK   0x08                /* BREAK */
-#define STMFRAME   0x04                /* framing error */
-#define STMRCVROVR 0x02                /* receiver over run error */
-#define STMPARITY  0x01                /* parity error */
-#define STMERROR   (STMBREAK | STMFRAME | STMPARITY)
-#define STMBREAKH   0x800      /* BREAK */
-#define STMFRAMEH   0x400      /* framing error */
-#define STMRCVROVRH 0x200      /* receiver over run error */
-#define STMPARITYH  0x100      /* parity error */
-#define STMERRORH   (STMBREAKH | STMFRAMEH | STMPARITYH)
-
-#define CTS_ACT   0x20         /* CTS input asserted */
-#define DSR_ACT   0x10         /* DSR input asserted */
-#define CD_ACT    0x08         /* CD input asserted */
-#define TXFIFOMT  0x04         /* Tx FIFO is empty */
-#define TXSHRMT   0x02         /* Tx shift register is empty */
-#define RDA       0x01         /* Rx data available */
-#define DRAINED (TXFIFOMT | TXSHRMT)   /* indicates Tx is drained */
-
-#define STATMODE  0x8000       /* status mode enable bit */
-#define RXFOVERFL 0x2000       /* receive FIFO overflow */
-#define RX2MATCH  0x1000       /* receive compare byte 2 match */
-#define RX1MATCH  0x0800       /* receive compare byte 1 match */
-#define RXBREAK   0x0400       /* received BREAK */
-#define RXFRAME   0x0200       /* received framing error */
-#define RXPARITY  0x0100       /* received parity error */
-#define STATERROR (RXBREAK | RXFRAME | RXPARITY)
-
-#define CTSFC_EN  0x80         /* CTS flow control enable bit */
-#define RTSTOG_EN 0x40         /* RTS toggle enable bit */
-#define TXINT_EN  0x10         /* transmit interrupt enable */
-#define STOP2     0x08         /* enable 2 stop bits (0 = 1 stop) */
-#define PARITY_EN 0x04         /* enable parity (0 = no parity) */
-#define EVEN_PAR  0x02         /* even parity (0 = odd parity) */
-#define DATA8BIT  0x01         /* 8 bit data (0 = 7 bit data) */
-
-#define SETBREAK  0x10         /* send break condition (must clear) */
-#define LOCALLOOP 0x08         /* local loopback set for test */
-#define SET_DTR   0x04         /* assert DTR */
-#define SET_RTS   0x02         /* assert RTS */
-#define TX_ENABLE 0x01         /* enable transmitter */
-
-#define RTSFC_EN  0x40         /* RTS flow control enable */
-#define RXPROC_EN 0x20         /* receive processor enable */
-#define TRIG_NO   0x00         /* Rx FIFO trigger level 0 (no trigger) */
-#define TRIG_1    0x08         /* trigger level 1 char */
-#define TRIG_1_2  0x10         /* trigger level 1/2 */
-#define TRIG_7_8  0x18         /* trigger level 7/8 */
-#define TRIG_MASK 0x18         /* trigger level mask */
-#define SRCINT_EN 0x04         /* special Rx condition interrupt enable */
-#define RXINT_EN  0x02         /* Rx interrupt enable */
-#define MCINT_EN  0x01         /* modem change interrupt enable */
-
-#define RXF_TRIG  0x20         /* Rx FIFO trigger level interrupt */
-#define TXFIFO_MT 0x10         /* Tx FIFO empty interrupt */
-#define SRC_INT   0x08         /* special receive condition interrupt */
-#define DELTA_CD  0x04         /* CD change interrupt */
-#define DELTA_CTS 0x02         /* CTS change interrupt */
-#define DELTA_DSR 0x01         /* DSR change interrupt */
-
-#define REP1W2_EN 0x10         /* replace byte 1 with 2 bytes enable */
-#define IGN2_EN   0x08         /* ignore byte 2 enable */
-#define IGN1_EN   0x04         /* ignore byte 1 enable */
-#define COMP2_EN  0x02         /* compare byte 2 enable */
-#define COMP1_EN  0x01         /* compare byte 1 enable */
-
-#define RESET_ALL 0x80         /* reset AIOP (all channels) */
-#define TXOVERIDE 0x40         /* Transmit software off override */
-#define RESETUART 0x20         /* reset channel's UART */
-#define RESTXFCNT 0x10         /* reset channel's Tx FIFO count register */
-#define RESRXFCNT 0x08         /* reset channel's Rx FIFO count register */
-
-#define INTSTAT0  0x01         /* AIOP 0 interrupt status */
-#define INTSTAT1  0x02         /* AIOP 1 interrupt status */
-#define INTSTAT2  0x04         /* AIOP 2 interrupt status */
-#define INTSTAT3  0x08         /* AIOP 3 interrupt status */
-
-#define INTR_EN   0x08         /* allow interrupts to host */
-#define INT_STROB 0x04         /* strobe and clear interrupt line (EOI) */
-
-/**************************************************************************
- MUDBAC remapped for PCI
-**************************************************************************/
-
-#define _CFG_INT_PCI  0x40
-#define _PCI_INT_FUNC 0x3A
-
-#define PCI_STROB 0x2000       /* bit 13 of int aiop register */
-#define INTR_EN_PCI   0x0010   /* allow interrupts to host */
-
-/*
- * Definitions for Universal PCI board registers
- */
-#define _PCI_9030_INT_CTRL     0x4c          /* Offsets from BAR1 */
-#define _PCI_9030_GPIO_CTRL    0x54
-#define PCI_INT_CTRL_AIOP      0x0001
-#define PCI_GPIO_CTRL_8PORT    0x4000
-#define _PCI_9030_RING_IND     0xc0          /* Offsets from BAR1 */
-
-#define CHAN3_EN  0x08         /* enable AIOP 3 */
-#define CHAN2_EN  0x04         /* enable AIOP 2 */
-#define CHAN1_EN  0x02         /* enable AIOP 1 */
-#define CHAN0_EN  0x01         /* enable AIOP 0 */
-#define FREQ_DIS  0x00
-#define FREQ_274HZ 0x60
-#define FREQ_137HZ 0x50
-#define FREQ_69HZ  0x40
-#define FREQ_34HZ  0x30
-#define FREQ_17HZ  0x20
-#define FREQ_9HZ   0x10
-#define PERIODIC_ONLY 0x80     /* only PERIODIC interrupt */
-
-#define CHANINT_EN 0x0100      /* flags to enable/disable channel ints */
-
-#define RDATASIZE 72
-#define RREGDATASIZE 52
-
-/*
- * AIOP interrupt bits for ISA/PCI boards and UPCI boards.
- */
-#define AIOP_INTR_BIT_0                0x0001
-#define AIOP_INTR_BIT_1                0x0002
-#define AIOP_INTR_BIT_2                0x0004
-#define AIOP_INTR_BIT_3                0x0008
-
-#define AIOP_INTR_BITS ( \
-       AIOP_INTR_BIT_0 \
-       | AIOP_INTR_BIT_1 \
-       | AIOP_INTR_BIT_2 \
-       | AIOP_INTR_BIT_3)
-
-#define UPCI_AIOP_INTR_BIT_0   0x0004
-#define UPCI_AIOP_INTR_BIT_1   0x0020
-#define UPCI_AIOP_INTR_BIT_2   0x0100
-#define UPCI_AIOP_INTR_BIT_3   0x0800
-
-#define UPCI_AIOP_INTR_BITS ( \
-       UPCI_AIOP_INTR_BIT_0 \
-       | UPCI_AIOP_INTR_BIT_1 \
-       | UPCI_AIOP_INTR_BIT_2 \
-       | UPCI_AIOP_INTR_BIT_3)
-
-/* Controller level information structure */
-typedef struct {
-       int CtlID;
-       int CtlNum;
-       int BusType;
-       int boardType;
-       int isUPCI;
-       WordIO_t PCIIO;
-       WordIO_t PCIIO2;
-       ByteIO_t MBaseIO;
-       ByteIO_t MReg1IO;
-       ByteIO_t MReg2IO;
-       ByteIO_t MReg3IO;
-       Byte_t MReg2;
-       Byte_t MReg3;
-       int NumAiop;
-       int AltChanRingIndicator;
-       ByteIO_t UPCIRingInd;
-       WordIO_t AiopIO[AIOP_CTL_SIZE];
-       ByteIO_t AiopIntChanIO[AIOP_CTL_SIZE];
-       int AiopID[AIOP_CTL_SIZE];
-       int AiopNumChan[AIOP_CTL_SIZE];
-       Word_t *AiopIntrBits;
-} CONTROLLER_T;
-
-typedef CONTROLLER_T CONTROLLER_t;
-
-/* Channel level information structure */
-typedef struct {
-       CONTROLLER_T *CtlP;
-       int AiopNum;
-       int ChanID;
-       int ChanNum;
-       int rtsToggle;
-
-       ByteIO_t Cmd;
-       ByteIO_t IntChan;
-       ByteIO_t IntMask;
-       DWordIO_t IndexAddr;
-       WordIO_t IndexData;
-
-       WordIO_t TxRxData;
-       WordIO_t ChanStat;
-       WordIO_t TxRxCount;
-       ByteIO_t IntID;
-
-       Word_t TxFIFO;
-       Word_t TxFIFOPtrs;
-       Word_t RxFIFO;
-       Word_t RxFIFOPtrs;
-       Word_t TxPrioCnt;
-       Word_t TxPrioPtr;
-       Word_t TxPrioBuf;
-
-       Byte_t R[RREGDATASIZE];
-
-       Byte_t BaudDiv[4];
-       Byte_t TxControl[4];
-       Byte_t RxControl[4];
-       Byte_t TxEnables[4];
-       Byte_t TxCompare[4];
-       Byte_t TxReplace1[4];
-       Byte_t TxReplace2[4];
-} CHANNEL_T;
-
-typedef CHANNEL_T CHANNEL_t;
-typedef CHANNEL_T *CHANPTR_T;
-
-#define InterfaceModeRS232  0x00
-#define InterfaceModeRS422  0x08
-#define InterfaceModeRS485  0x10
-#define InterfaceModeRS232T 0x18
-
-/***************************************************************************
-Function: sClrBreak
-Purpose:  Stop sending a transmit BREAK signal
-Call:     sClrBreak(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sClrBreak(ChP) \
-do { \
-   (ChP)->TxControl[3] &= ~SETBREAK; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sClrDTR
-Purpose:  Clr the DTR output
-Call:     sClrDTR(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sClrDTR(ChP) \
-do { \
-   (ChP)->TxControl[3] &= ~SET_DTR; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sClrRTS
-Purpose:  Clr the RTS output
-Call:     sClrRTS(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sClrRTS(ChP) \
-do { \
-   if ((ChP)->rtsToggle) break; \
-   (ChP)->TxControl[3] &= ~SET_RTS; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sClrTxXOFF
-Purpose:  Clear any existing transmit software flow control off condition
-Call:     sClrTxXOFF(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sClrTxXOFF(ChP) \
-do { \
-   sOutB((ChP)->Cmd,TXOVERIDE | (Byte_t)(ChP)->ChanNum); \
-   sOutB((ChP)->Cmd,(Byte_t)(ChP)->ChanNum); \
-} while (0)
-
-/***************************************************************************
-Function: sCtlNumToCtlPtr
-Purpose:  Convert a controller number to controller structure pointer
-Call:     sCtlNumToCtlPtr(CtlNum)
-          int CtlNum; Controller number
-Return:   CONTROLLER_T *: Ptr to controller structure
-*/
-#define sCtlNumToCtlPtr(CTLNUM) &sController[CTLNUM]
-
-/***************************************************************************
-Function: sControllerEOI
-Purpose:  Strobe the MUDBAC's End Of Interrupt bit.
-Call:     sControllerEOI(CtlP)
-          CONTROLLER_T *CtlP; Ptr to controller structure
-*/
-#define sControllerEOI(CTLP) sOutB((CTLP)->MReg2IO,(CTLP)->MReg2 | INT_STROB)
-
-/***************************************************************************
-Function: sPCIControllerEOI
-Purpose:  Strobe the PCI End Of Interrupt bit.
-          For the UPCI boards, toggle the AIOP interrupt enable bit
-         (this was taken from the Windows driver).
-Call:     sPCIControllerEOI(CtlP)
-          CONTROLLER_T *CtlP; Ptr to controller structure
-*/
-#define sPCIControllerEOI(CTLP) \
-do { \
-    if ((CTLP)->isUPCI) { \
-       Word_t w = sInW((CTLP)->PCIIO); \
-       sOutW((CTLP)->PCIIO, (w ^ PCI_INT_CTRL_AIOP)); \
-       sOutW((CTLP)->PCIIO, w); \
-    } \
-    else { \
-       sOutW((CTLP)->PCIIO, PCI_STROB); \
-    } \
-} while (0)
-
-/***************************************************************************
-Function: sDisAiop
-Purpose:  Disable I/O access to an AIOP
-Call:     sDisAiop(CltP)
-          CONTROLLER_T *CtlP; Ptr to controller structure
-          int AiopNum; Number of AIOP on controller
-*/
-#define sDisAiop(CTLP,AIOPNUM) \
-do { \
-   (CTLP)->MReg3 &= sBitMapClrTbl[AIOPNUM]; \
-   sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \
-} while (0)
-
-/***************************************************************************
-Function: sDisCTSFlowCtl
-Purpose:  Disable output flow control using CTS
-Call:     sDisCTSFlowCtl(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sDisCTSFlowCtl(ChP) \
-do { \
-   (ChP)->TxControl[2] &= ~CTSFC_EN; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sDisIXANY
-Purpose:  Disable IXANY Software Flow Control
-Call:     sDisIXANY(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sDisIXANY(ChP) \
-do { \
-   (ChP)->R[0x0e] = 0x86; \
-   out32((ChP)->IndexAddr,&(ChP)->R[0x0c]); \
-} while (0)
-
-/***************************************************************************
-Function: DisParity
-Purpose:  Disable parity
-Call:     sDisParity(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Comments: Function sSetParity() can be used in place of functions sEnParity(),
-          sDisParity(), sSetOddParity(), and sSetEvenParity().
-*/
-#define sDisParity(ChP) \
-do { \
-   (ChP)->TxControl[2] &= ~PARITY_EN; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sDisRTSToggle
-Purpose:  Disable RTS toggle
-Call:     sDisRTSToggle(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sDisRTSToggle(ChP) \
-do { \
-   (ChP)->TxControl[2] &= ~RTSTOG_EN; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-   (ChP)->rtsToggle = 0; \
-} while (0)
-
-/***************************************************************************
-Function: sDisRxFIFO
-Purpose:  Disable Rx FIFO
-Call:     sDisRxFIFO(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sDisRxFIFO(ChP) \
-do { \
-   (ChP)->R[0x32] = 0x0a; \
-   out32((ChP)->IndexAddr,&(ChP)->R[0x30]); \
-} while (0)
-
-/***************************************************************************
-Function: sDisRxStatusMode
-Purpose:  Disable the Rx status mode
-Call:     sDisRxStatusMode(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Comments: This takes the channel out of the receive status mode.  All
-          subsequent reads of receive data using sReadRxWord() will return
-          two data bytes.
-*/
-#define sDisRxStatusMode(ChP) sOutW((ChP)->ChanStat,0)
-
-/***************************************************************************
-Function: sDisTransmit
-Purpose:  Disable transmit
-Call:     sDisTransmit(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-          This disables movement of Tx data from the Tx FIFO into the 1 byte
-          Tx buffer.  Therefore there could be up to a 2 byte latency
-          between the time sDisTransmit() is called and the transmit buffer
-          and transmit shift register going completely empty.
-*/
-#define sDisTransmit(ChP) \
-do { \
-   (ChP)->TxControl[3] &= ~TX_ENABLE; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sDisTxSoftFlowCtl
-Purpose:  Disable Tx Software Flow Control
-Call:     sDisTxSoftFlowCtl(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sDisTxSoftFlowCtl(ChP) \
-do { \
-   (ChP)->R[0x06] = 0x8a; \
-   out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \
-} while (0)
-
-/***************************************************************************
-Function: sEnAiop
-Purpose:  Enable I/O access to an AIOP
-Call:     sEnAiop(CltP)
-          CONTROLLER_T *CtlP; Ptr to controller structure
-          int AiopNum; Number of AIOP on controller
-*/
-#define sEnAiop(CTLP,AIOPNUM) \
-do { \
-   (CTLP)->MReg3 |= sBitMapSetTbl[AIOPNUM]; \
-   sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \
-} while (0)
-
-/***************************************************************************
-Function: sEnCTSFlowCtl
-Purpose:  Enable output flow control using CTS
-Call:     sEnCTSFlowCtl(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sEnCTSFlowCtl(ChP) \
-do { \
-   (ChP)->TxControl[2] |= CTSFC_EN; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sEnIXANY
-Purpose:  Enable IXANY Software Flow Control
-Call:     sEnIXANY(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sEnIXANY(ChP) \
-do { \
-   (ChP)->R[0x0e] = 0x21; \
-   out32((ChP)->IndexAddr,&(ChP)->R[0x0c]); \
-} while (0)
-
-/***************************************************************************
-Function: EnParity
-Purpose:  Enable parity
-Call:     sEnParity(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Comments: Function sSetParity() can be used in place of functions sEnParity(),
-          sDisParity(), sSetOddParity(), and sSetEvenParity().
-
-Warnings: Before enabling parity odd or even parity should be chosen using
-          functions sSetOddParity() or sSetEvenParity().
-*/
-#define sEnParity(ChP) \
-do { \
-   (ChP)->TxControl[2] |= PARITY_EN; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sEnRTSToggle
-Purpose:  Enable RTS toggle
-Call:     sEnRTSToggle(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Comments: This function will disable RTS flow control and clear the RTS
-          line to allow operation of RTS toggle.
-*/
-#define sEnRTSToggle(ChP) \
-do { \
-   (ChP)->RxControl[2] &= ~RTSFC_EN; \
-   out32((ChP)->IndexAddr,(ChP)->RxControl); \
-   (ChP)->TxControl[2] |= RTSTOG_EN; \
-   (ChP)->TxControl[3] &= ~SET_RTS; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-   (ChP)->rtsToggle = 1; \
-} while (0)
-
-/***************************************************************************
-Function: sEnRxFIFO
-Purpose:  Enable Rx FIFO
-Call:     sEnRxFIFO(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sEnRxFIFO(ChP) \
-do { \
-   (ChP)->R[0x32] = 0x08; \
-   out32((ChP)->IndexAddr,&(ChP)->R[0x30]); \
-} while (0)
-
-/***************************************************************************
-Function: sEnRxProcessor
-Purpose:  Enable the receive processor
-Call:     sEnRxProcessor(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Comments: This function is used to start the receive processor.  When
-          the channel is in the reset state the receive processor is not
-          running.  This is done to prevent the receive processor from
-          executing invalid microcode instructions prior to the
-          downloading of the microcode.
-
-Warnings: This function must be called after valid microcode has been
-          downloaded to the AIOP, and it must not be called before the
-          microcode has been downloaded.
-*/
-#define sEnRxProcessor(ChP) \
-do { \
-   (ChP)->RxControl[2] |= RXPROC_EN; \
-   out32((ChP)->IndexAddr,(ChP)->RxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sEnRxStatusMode
-Purpose:  Enable the Rx status mode
-Call:     sEnRxStatusMode(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Comments: This places the channel in the receive status mode.  All subsequent
-          reads of receive data using sReadRxWord() will return a data byte
-          in the low word and a status byte in the high word.
-
-*/
-#define sEnRxStatusMode(ChP) sOutW((ChP)->ChanStat,STATMODE)
-
-/***************************************************************************
-Function: sEnTransmit
-Purpose:  Enable transmit
-Call:     sEnTransmit(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sEnTransmit(ChP) \
-do { \
-   (ChP)->TxControl[3] |= TX_ENABLE; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sEnTxSoftFlowCtl
-Purpose:  Enable Tx Software Flow Control
-Call:     sEnTxSoftFlowCtl(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sEnTxSoftFlowCtl(ChP) \
-do { \
-   (ChP)->R[0x06] = 0xc5; \
-   out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \
-} while (0)
-
-/***************************************************************************
-Function: sGetAiopIntStatus
-Purpose:  Get the AIOP interrupt status
-Call:     sGetAiopIntStatus(CtlP,AiopNum)
-          CONTROLLER_T *CtlP; Ptr to controller structure
-          int AiopNum; AIOP number
-Return:   Byte_t: The AIOP interrupt status.  Bits 0 through 7
-                         represent channels 0 through 7 respectively.  If a
-                         bit is set that channel is interrupting.
-*/
-#define sGetAiopIntStatus(CTLP,AIOPNUM) sInB((CTLP)->AiopIntChanIO[AIOPNUM])
-
-/***************************************************************************
-Function: sGetAiopNumChan
-Purpose:  Get the number of channels supported by an AIOP
-Call:     sGetAiopNumChan(CtlP,AiopNum)
-          CONTROLLER_T *CtlP; Ptr to controller structure
-          int AiopNum; AIOP number
-Return:   int: The number of channels supported by the AIOP
-*/
-#define sGetAiopNumChan(CTLP,AIOPNUM) (CTLP)->AiopNumChan[AIOPNUM]
-
-/***************************************************************************
-Function: sGetChanIntID
-Purpose:  Get a channel's interrupt identification byte
-Call:     sGetChanIntID(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Return:   Byte_t: The channel interrupt ID.  Can be any
-             combination of the following flags:
-                RXF_TRIG:     Rx FIFO trigger level interrupt
-                TXFIFO_MT:    Tx FIFO empty interrupt
-                SRC_INT:      Special receive condition interrupt
-                DELTA_CD:     CD change interrupt
-                DELTA_CTS:    CTS change interrupt
-                DELTA_DSR:    DSR change interrupt
-*/
-#define sGetChanIntID(ChP) (sInB((ChP)->IntID) & (RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR))
-
-/***************************************************************************
-Function: sGetChanNum
-Purpose:  Get the number of a channel within an AIOP
-Call:     sGetChanNum(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Return:   int: Channel number within AIOP, or NULLCHAN if channel does
-               not exist.
-*/
-#define sGetChanNum(ChP) (ChP)->ChanNum
-
-/***************************************************************************
-Function: sGetChanStatus
-Purpose:  Get the channel status
-Call:     sGetChanStatus(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Return:   Word_t: The channel status.  Can be any combination of
-             the following flags:
-                LOW BYTE FLAGS
-                CTS_ACT:      CTS input asserted
-                DSR_ACT:      DSR input asserted
-                CD_ACT:       CD input asserted
-                TXFIFOMT:     Tx FIFO is empty
-                TXSHRMT:      Tx shift register is empty
-                RDA:          Rx data available
-
-                HIGH BYTE FLAGS
-                STATMODE:     status mode enable bit
-                RXFOVERFL:    receive FIFO overflow
-                RX2MATCH:     receive compare byte 2 match
-                RX1MATCH:     receive compare byte 1 match
-                RXBREAK:      received BREAK
-                RXFRAME:      received framing error
-                RXPARITY:     received parity error
-Warnings: This function will clear the high byte flags in the Channel
-          Status Register.
-*/
-#define sGetChanStatus(ChP) sInW((ChP)->ChanStat)
-
-/***************************************************************************
-Function: sGetChanStatusLo
-Purpose:  Get the low byte only of the channel status
-Call:     sGetChanStatusLo(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Return:   Byte_t: The channel status low byte.  Can be any combination
-             of the following flags:
-                CTS_ACT:      CTS input asserted
-                DSR_ACT:      DSR input asserted
-                CD_ACT:       CD input asserted
-                TXFIFOMT:     Tx FIFO is empty
-                TXSHRMT:      Tx shift register is empty
-                RDA:          Rx data available
-*/
-#define sGetChanStatusLo(ChP) sInB((ByteIO_t)(ChP)->ChanStat)
-
-/**********************************************************************
- * Get RI status of channel
- * Defined as a function in rocket.c   -aes
- */
-#if 0
-#define sGetChanRI(ChP) ((ChP)->CtlP->AltChanRingIndicator ? \
-                          (sInB((ByteIO_t)((ChP)->ChanStat+8)) & DSR_ACT) : \
-                            (((ChP)->CtlP->boardType == ROCKET_TYPE_PC104) ? \
-                               (!(sInB((ChP)->CtlP->AiopIO[3]) & sBitMapSetTbl[(ChP)->ChanNum])) : \
-                             0))
-#endif
-
-/***************************************************************************
-Function: sGetControllerIntStatus
-Purpose:  Get the controller interrupt status
-Call:     sGetControllerIntStatus(CtlP)
-          CONTROLLER_T *CtlP; Ptr to controller structure
-Return:   Byte_t: The controller interrupt status in the lower 4
-                         bits.  Bits 0 through 3 represent AIOP's 0
-                         through 3 respectively.  If a bit is set that
-                         AIOP is interrupting.  Bits 4 through 7 will
-                         always be cleared.
-*/
-#define sGetControllerIntStatus(CTLP) (sInB((CTLP)->MReg1IO) & 0x0f)
-
-/***************************************************************************
-Function: sPCIGetControllerIntStatus
-Purpose:  Get the controller interrupt status
-Call:     sPCIGetControllerIntStatus(CtlP)
-          CONTROLLER_T *CtlP; Ptr to controller structure
-Return:   unsigned char: The controller interrupt status in the lower 4
-                         bits and bit 4.  Bits 0 through 3 represent AIOP's 0
-                         through 3 respectively. Bit 4 is set if the int 
-                        was generated from periodic. If a bit is set the
-                        AIOP is interrupting.
-*/
-#define sPCIGetControllerIntStatus(CTLP) \
-       ((CTLP)->isUPCI ? \
-         (sInW((CTLP)->PCIIO2) & UPCI_AIOP_INTR_BITS) : \
-         ((sInW((CTLP)->PCIIO) >> 8) & AIOP_INTR_BITS))
-
-/***************************************************************************
-
-Function: sGetRxCnt
-Purpose:  Get the number of data bytes in the Rx FIFO
-Call:     sGetRxCnt(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Return:   int: The number of data bytes in the Rx FIFO.
-Comments: Byte read of count register is required to obtain Rx count.
-
-*/
-#define sGetRxCnt(ChP) sInW((ChP)->TxRxCount)
-
-/***************************************************************************
-Function: sGetTxCnt
-Purpose:  Get the number of data bytes in the Tx FIFO
-Call:     sGetTxCnt(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Return:   Byte_t: The number of data bytes in the Tx FIFO.
-Comments: Byte read of count register is required to obtain Tx count.
-
-*/
-#define sGetTxCnt(ChP) sInB((ByteIO_t)(ChP)->TxRxCount)
-
-/*****************************************************************************
-Function: sGetTxRxDataIO
-Purpose:  Get the I/O address of a channel's TxRx Data register
-Call:     sGetTxRxDataIO(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Return:   WordIO_t: I/O address of a channel's TxRx Data register
-*/
-#define sGetTxRxDataIO(ChP) (ChP)->TxRxData
-
-/***************************************************************************
-Function: sInitChanDefaults
-Purpose:  Initialize a channel structure to it's default state.
-Call:     sInitChanDefaults(ChP)
-          CHANNEL_T *ChP; Ptr to the channel structure
-Comments: This function must be called once for every channel structure
-          that exists before any other SSCI calls can be made.
-
-*/
-#define sInitChanDefaults(ChP) \
-do { \
-   (ChP)->CtlP = NULLCTLPTR; \
-   (ChP)->AiopNum = NULLAIOP; \
-   (ChP)->ChanID = AIOPID_NULL; \
-   (ChP)->ChanNum = NULLCHAN; \
-} while (0)
-
-/***************************************************************************
-Function: sResetAiopByNum
-Purpose:  Reset the AIOP by number
-Call:     sResetAiopByNum(CTLP,AIOPNUM)
-       CONTROLLER_T CTLP; Ptr to controller structure
-       AIOPNUM; AIOP index 
-*/
-#define sResetAiopByNum(CTLP,AIOPNUM) \
-do { \
-   sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,RESET_ALL); \
-   sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,0x0); \
-} while (0)
-
-/***************************************************************************
-Function: sSendBreak
-Purpose:  Send a transmit BREAK signal
-Call:     sSendBreak(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sSendBreak(ChP) \
-do { \
-   (ChP)->TxControl[3] |= SETBREAK; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sSetBaud
-Purpose:  Set baud rate
-Call:     sSetBaud(ChP,Divisor)
-          CHANNEL_T *ChP; Ptr to channel structure
-          Word_t Divisor; 16 bit baud rate divisor for channel
-*/
-#define sSetBaud(ChP,DIVISOR) \
-do { \
-   (ChP)->BaudDiv[2] = (Byte_t)(DIVISOR); \
-   (ChP)->BaudDiv[3] = (Byte_t)((DIVISOR) >> 8); \
-   out32((ChP)->IndexAddr,(ChP)->BaudDiv); \
-} while (0)
-
-/***************************************************************************
-Function: sSetData7
-Purpose:  Set data bits to 7
-Call:     sSetData7(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sSetData7(ChP) \
-do { \
-   (ChP)->TxControl[2] &= ~DATA8BIT; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sSetData8
-Purpose:  Set data bits to 8
-Call:     sSetData8(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sSetData8(ChP) \
-do { \
-   (ChP)->TxControl[2] |= DATA8BIT; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sSetDTR
-Purpose:  Set the DTR output
-Call:     sSetDTR(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sSetDTR(ChP) \
-do { \
-   (ChP)->TxControl[3] |= SET_DTR; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sSetEvenParity
-Purpose:  Set even parity
-Call:     sSetEvenParity(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Comments: Function sSetParity() can be used in place of functions sEnParity(),
-          sDisParity(), sSetOddParity(), and sSetEvenParity().
-
-Warnings: This function has no effect unless parity is enabled with function
-          sEnParity().
-*/
-#define sSetEvenParity(ChP) \
-do { \
-   (ChP)->TxControl[2] |= EVEN_PAR; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sSetOddParity
-Purpose:  Set odd parity
-Call:     sSetOddParity(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Comments: Function sSetParity() can be used in place of functions sEnParity(),
-          sDisParity(), sSetOddParity(), and sSetEvenParity().
-
-Warnings: This function has no effect unless parity is enabled with function
-          sEnParity().
-*/
-#define sSetOddParity(ChP) \
-do { \
-   (ChP)->TxControl[2] &= ~EVEN_PAR; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sSetRTS
-Purpose:  Set the RTS output
-Call:     sSetRTS(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sSetRTS(ChP) \
-do { \
-   if ((ChP)->rtsToggle) break; \
-   (ChP)->TxControl[3] |= SET_RTS; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sSetRxTrigger
-Purpose:  Set the Rx FIFO trigger level
-Call:     sSetRxProcessor(ChP,Level)
-          CHANNEL_T *ChP; Ptr to channel structure
-          Byte_t Level; Number of characters in Rx FIFO at which the
-             interrupt will be generated.  Can be any of the following flags:
-
-             TRIG_NO:   no trigger
-             TRIG_1:    1 character in FIFO
-             TRIG_1_2:  FIFO 1/2 full
-             TRIG_7_8:  FIFO 7/8 full
-Comments: An interrupt will be generated when the trigger level is reached
-          only if function sEnInterrupt() has been called with flag
-          RXINT_EN set.  The RXF_TRIG flag in the Interrupt Idenfification
-          register will be set whenever the trigger level is reached
-          regardless of the setting of RXINT_EN.
-
-*/
-#define sSetRxTrigger(ChP,LEVEL) \
-do { \
-   (ChP)->RxControl[2] &= ~TRIG_MASK; \
-   (ChP)->RxControl[2] |= LEVEL; \
-   out32((ChP)->IndexAddr,(ChP)->RxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sSetStop1
-Purpose:  Set stop bits to 1
-Call:     sSetStop1(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sSetStop1(ChP) \
-do { \
-   (ChP)->TxControl[2] &= ~STOP2; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sSetStop2
-Purpose:  Set stop bits to 2
-Call:     sSetStop2(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-*/
-#define sSetStop2(ChP) \
-do { \
-   (ChP)->TxControl[2] |= STOP2; \
-   out32((ChP)->IndexAddr,(ChP)->TxControl); \
-} while (0)
-
-/***************************************************************************
-Function: sSetTxXOFFChar
-Purpose:  Set the Tx XOFF flow control character
-Call:     sSetTxXOFFChar(ChP,Ch)
-          CHANNEL_T *ChP; Ptr to channel structure
-          Byte_t Ch; The value to set the Tx XOFF character to
-*/
-#define sSetTxXOFFChar(ChP,CH) \
-do { \
-   (ChP)->R[0x07] = (CH); \
-   out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \
-} while (0)
-
-/***************************************************************************
-Function: sSetTxXONChar
-Purpose:  Set the Tx XON flow control character
-Call:     sSetTxXONChar(ChP,Ch)
-          CHANNEL_T *ChP; Ptr to channel structure
-          Byte_t Ch; The value to set the Tx XON character to
-*/
-#define sSetTxXONChar(ChP,CH) \
-do { \
-   (ChP)->R[0x0b] = (CH); \
-   out32((ChP)->IndexAddr,&(ChP)->R[0x08]); \
-} while (0)
-
-/***************************************************************************
-Function: sStartRxProcessor
-Purpose:  Start a channel's receive processor
-Call:     sStartRxProcessor(ChP)
-          CHANNEL_T *ChP; Ptr to channel structure
-Comments: This function is used to start a Rx processor after it was
-          stopped with sStopRxProcessor() or sStopSWInFlowCtl().  It
-          will restart both the Rx processor and software input flow control.
-
-*/
-#define sStartRxProcessor(ChP) out32((ChP)->IndexAddr,&(ChP)->R[0])
-
-/***************************************************************************
-Function: sWriteTxByte
-Purpose:  Write a transmit data byte to a channel.
-          ByteIO_t io: Channel transmit register I/O address.  This can
-                           be obtained with sGetTxRxDataIO().
-          Byte_t Data; The transmit data byte.
-Warnings: This function writes the data byte without checking to see if
-          sMaxTxSize is exceeded in the Tx FIFO.
-*/
-#define sWriteTxByte(IO,DATA) sOutB(IO,DATA)
-
-/*
- * Begin Linux specific definitions for the Rocketport driver
- *
- * This code is Copyright Theodore Ts'o, 1995-1997
- */
-
-struct r_port {
-       int magic;
-       struct tty_port port;
-       int line;
-       int flags;              /* Don't yet match the ASY_ flags!! */
-       unsigned int board:3;
-       unsigned int aiop:2;
-       unsigned int chan:3;
-       CONTROLLER_t *ctlp;
-       CHANNEL_t channel;
-       int intmask;
-       int xmit_fifo_room;     /* room in xmit fifo */
-       unsigned char *xmit_buf;
-       int xmit_head;
-       int xmit_tail;
-       int xmit_cnt;
-       int cd_status;
-       int ignore_status_mask;
-       int read_status_mask;
-       int cps;
-
-       struct completion close_wait;   /* Not yet matching the core */
-       spinlock_t slock;
-       struct mutex write_mtx;
-};
-
-#define RPORT_MAGIC 0x525001
-
-#define NUM_BOARDS 8
-#define MAX_RP_PORTS (32*NUM_BOARDS)
-
-/*
- * The size of the xmit buffer is 1 page, or 4096 bytes
- */
-#define XMIT_BUF_SIZE 4096
-
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
-/*
- * Assigned major numbers for the Comtrol Rocketport
- */
-#define TTY_ROCKET_MAJOR       46
-#define CUA_ROCKET_MAJOR       47
-
-#ifdef PCI_VENDOR_ID_RP
-#undef PCI_VENDOR_ID_RP
-#undef PCI_DEVICE_ID_RP8OCTA
-#undef PCI_DEVICE_ID_RP8INTF
-#undef PCI_DEVICE_ID_RP16INTF
-#undef PCI_DEVICE_ID_RP32INTF
-#undef PCI_DEVICE_ID_URP8OCTA
-#undef PCI_DEVICE_ID_URP8INTF
-#undef PCI_DEVICE_ID_URP16INTF
-#undef PCI_DEVICE_ID_CRP16INTF
-#undef PCI_DEVICE_ID_URP32INTF
-#endif
-
-/*  Comtrol PCI Vendor ID */
-#define PCI_VENDOR_ID_RP               0x11fe
-
-/*  Comtrol Device ID's */
-#define PCI_DEVICE_ID_RP32INTF         0x0001  /* Rocketport 32 port w/external I/F     */
-#define PCI_DEVICE_ID_RP8INTF          0x0002  /* Rocketport 8 port w/external I/F      */
-#define PCI_DEVICE_ID_RP16INTF         0x0003  /* Rocketport 16 port w/external I/F     */
-#define PCI_DEVICE_ID_RP4QUAD          0x0004  /* Rocketport 4 port w/quad cable        */
-#define PCI_DEVICE_ID_RP8OCTA          0x0005  /* Rocketport 8 port w/octa cable        */
-#define PCI_DEVICE_ID_RP8J             0x0006  /* Rocketport 8 port w/RJ11 connectors   */
-#define PCI_DEVICE_ID_RP4J             0x0007  /* Rocketport 4 port w/RJ11 connectors   */
-#define PCI_DEVICE_ID_RP8SNI           0x0008  /* Rocketport 8 port w/ DB78 SNI (Siemens) connector */
-#define PCI_DEVICE_ID_RP16SNI          0x0009  /* Rocketport 16 port w/ DB78 SNI (Siemens) connector   */
-#define PCI_DEVICE_ID_RPP4             0x000A  /* Rocketport Plus 4 port                */
-#define PCI_DEVICE_ID_RPP8             0x000B  /* Rocketport Plus 8 port                */
-#define PCI_DEVICE_ID_RP6M             0x000C  /* RocketModem 6 port                    */
-#define PCI_DEVICE_ID_RP4M             0x000D  /* RocketModem 4 port                    */
-#define PCI_DEVICE_ID_RP2_232           0x000E /* Rocketport Plus 2 port RS232          */
-#define PCI_DEVICE_ID_RP2_422           0x000F /* Rocketport Plus 2 port RS422          */ 
-
-/* Universal PCI boards  */
-#define PCI_DEVICE_ID_URP32INTF                0x0801  /* Rocketport UPCI 32 port w/external I/F */ 
-#define PCI_DEVICE_ID_URP8INTF         0x0802  /* Rocketport UPCI 8 port w/external I/F  */
-#define PCI_DEVICE_ID_URP16INTF                0x0803  /* Rocketport UPCI 16 port w/external I/F */
-#define PCI_DEVICE_ID_URP8OCTA         0x0805  /* Rocketport UPCI 8 port w/octa cable    */
-#define PCI_DEVICE_ID_UPCI_RM3_8PORT    0x080C /* Rocketmodem III 8 port                 */
-#define PCI_DEVICE_ID_UPCI_RM3_4PORT    0x080D /* Rocketmodem III 4 port                 */
-
-/* Compact PCI device */ 
-#define PCI_DEVICE_ID_CRP16INTF                0x0903  /* Rocketport Compact PCI 16 port w/external I/F */
-
diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c
deleted file mode 100644 (file)
index 18888d0..0000000
+++ /dev/null
@@ -1,8119 +0,0 @@
-/*
- * linux/drivers/char/synclink.c
- *
- * $Id: synclink.c,v 4.38 2005/11/07 16:30:34 paulkf Exp $
- *
- * Device driver for Microgate SyncLink ISA and PCI
- * high speed multiprotocol serial adapters.
- *
- * written by Paul Fulghum for Microgate Corporation
- * paulkf@microgate.com
- *
- * Microgate and SyncLink are trademarks of Microgate Corporation
- *
- * Derived from serial.c written by Theodore Ts'o and Linus Torvalds
- *
- * Original release 01/11/99
- *
- * This code is released under the GNU General Public License (GPL)
- *
- * This driver is primarily intended for use in synchronous
- * HDLC mode. Asynchronous mode is also provided.
- *
- * When operating in synchronous mode, each call to mgsl_write()
- * contains exactly one complete HDLC frame. Calling mgsl_put_char
- * will start assembling an HDLC frame that will not be sent until
- * mgsl_flush_chars or mgsl_write is called.
- * 
- * Synchronous receive data is reported as complete frames. To accomplish
- * this, the TTY flip buffer is bypassed (too small to hold largest
- * frame and may fragment frames) and the line discipline
- * receive entry point is called directly.
- *
- * This driver has been tested with a slightly modified ppp.c driver
- * for synchronous PPP.
- *
- * 2000/02/16
- * Added interface for syncppp.c driver (an alternate synchronous PPP
- * implementation that also supports Cisco HDLC). Each device instance
- * registers as a tty device AND a network device (if dosyncppp option
- * is set for the device). The functionality is determined by which
- * device interface is opened.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#if defined(__i386__)
-#  define BREAKPOINT() asm("   int $3");
-#else
-#  define BREAKPOINT() { }
-#endif
-
-#define MAX_ISA_DEVICES 10
-#define MAX_PCI_DEVICES 10
-#define MAX_TOTAL_DEVICES 20
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
-#include <linux/ioctl.h>
-#include <linux/synclink.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <linux/bitops.h>
-#include <asm/types.h>
-#include <linux/termios.h>
-#include <linux/workqueue.h>
-#include <linux/hdlc.h>
-#include <linux/dma-mapping.h>
-
-#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_MODULE))
-#define SYNCLINK_GENERIC_HDLC 1
-#else
-#define SYNCLINK_GENERIC_HDLC 0
-#endif
-
-#define GET_USER(error,value,addr) error = get_user(value,addr)
-#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0
-#define PUT_USER(error,value,addr) error = put_user(value,addr)
-#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0
-
-#include <asm/uaccess.h>
-
-#define RCLRVALUE 0xffff
-
-static MGSL_PARAMS default_params = {
-       MGSL_MODE_HDLC,                 /* unsigned long mode */
-       0,                              /* unsigned char loopback; */
-       HDLC_FLAG_UNDERRUN_ABORT15,     /* unsigned short flags; */
-       HDLC_ENCODING_NRZI_SPACE,       /* unsigned char encoding; */
-       0,                              /* unsigned long clock_speed; */
-       0xff,                           /* unsigned char addr_filter; */
-       HDLC_CRC_16_CCITT,              /* unsigned short crc_type; */
-       HDLC_PREAMBLE_LENGTH_8BITS,     /* unsigned char preamble_length; */
-       HDLC_PREAMBLE_PATTERN_NONE,     /* unsigned char preamble; */
-       9600,                           /* unsigned long data_rate; */
-       8,                              /* unsigned char data_bits; */
-       1,                              /* unsigned char stop_bits; */
-       ASYNC_PARITY_NONE               /* unsigned char parity; */
-};
-
-#define SHARED_MEM_ADDRESS_SIZE 0x40000
-#define BUFFERLISTSIZE 4096
-#define DMABUFFERSIZE 4096
-#define MAXRXFRAMES 7
-
-typedef struct _DMABUFFERENTRY
-{
-       u32 phys_addr;  /* 32-bit flat physical address of data buffer */
-       volatile u16 count;     /* buffer size/data count */
-       volatile u16 status;    /* Control/status field */
-       volatile u16 rcc;       /* character count field */
-       u16 reserved;   /* padding required by 16C32 */
-       u32 link;       /* 32-bit flat link to next buffer entry */
-       char *virt_addr;        /* virtual address of data buffer */
-       u32 phys_entry; /* physical address of this buffer entry */
-       dma_addr_t dma_addr;
-} DMABUFFERENTRY, *DMAPBUFFERENTRY;
-
-/* The queue of BH actions to be performed */
-
-#define BH_RECEIVE  1
-#define BH_TRANSMIT 2
-#define BH_STATUS   4
-
-#define IO_PIN_SHUTDOWN_LIMIT 100
-
-struct _input_signal_events {
-       int     ri_up;  
-       int     ri_down;
-       int     dsr_up;
-       int     dsr_down;
-       int     dcd_up;
-       int     dcd_down;
-       int     cts_up;
-       int     cts_down;
-};
-
-/* transmit holding buffer definitions*/
-#define MAX_TX_HOLDING_BUFFERS 5
-struct tx_holding_buffer {
-       int     buffer_size;
-       unsigned char * buffer;
-};
-
-
-/*
- * Device instance data structure
- */
-struct mgsl_struct {
-       int                     magic;
-       struct tty_port         port;
-       int                     line;
-       int                     hw_version;
-       
-       struct mgsl_icount      icount;
-       
-       int                     timeout;
-       int                     x_char;         /* xon/xoff character */
-       u16                     read_status_mask;
-       u16                     ignore_status_mask;     
-       unsigned char           *xmit_buf;
-       int                     xmit_head;
-       int                     xmit_tail;
-       int                     xmit_cnt;
-       
-       wait_queue_head_t       status_event_wait_q;
-       wait_queue_head_t       event_wait_q;
-       struct timer_list       tx_timer;       /* HDLC transmit timeout timer */
-       struct mgsl_struct      *next_device;   /* device list link */
-       
-       spinlock_t irq_spinlock;                /* spinlock for synchronizing with ISR */
-       struct work_struct task;                /* task structure for scheduling bh */
-
-       u32 EventMask;                  /* event trigger mask */
-       u32 RecordedEvents;             /* pending events */
-
-       u32 max_frame_size;             /* as set by device config */
-
-       u32 pending_bh;
-
-       bool bh_running;                /* Protection from multiple */
-       int isr_overflow;
-       bool bh_requested;
-       
-       int dcd_chkcount;               /* check counts to prevent */
-       int cts_chkcount;               /* too many IRQs if a signal */
-       int dsr_chkcount;               /* is floating */
-       int ri_chkcount;
-
-       char *buffer_list;              /* virtual address of Rx & Tx buffer lists */
-       u32 buffer_list_phys;
-       dma_addr_t buffer_list_dma_addr;
-
-       unsigned int rx_buffer_count;   /* count of total allocated Rx buffers */
-       DMABUFFERENTRY *rx_buffer_list; /* list of receive buffer entries */
-       unsigned int current_rx_buffer;
-
-       int num_tx_dma_buffers;         /* number of tx dma frames required */
-       int tx_dma_buffers_used;
-       unsigned int tx_buffer_count;   /* count of total allocated Tx buffers */
-       DMABUFFERENTRY *tx_buffer_list; /* list of transmit buffer entries */
-       int start_tx_dma_buffer;        /* tx dma buffer to start tx dma operation */
-       int current_tx_buffer;          /* next tx dma buffer to be loaded */
-       
-       unsigned char *intermediate_rxbuffer;
-
-       int num_tx_holding_buffers;     /* number of tx holding buffer allocated */
-       int get_tx_holding_index;       /* next tx holding buffer for adapter to load */
-       int put_tx_holding_index;       /* next tx holding buffer to store user request */
-       int tx_holding_count;           /* number of tx holding buffers waiting */
-       struct tx_holding_buffer tx_holding_buffers[MAX_TX_HOLDING_BUFFERS];
-
-       bool rx_enabled;
-       bool rx_overflow;
-       bool rx_rcc_underrun;
-
-       bool tx_enabled;
-       bool tx_active;
-       u32 idle_mode;
-
-       u16 cmr_value;
-       u16 tcsr_value;
-
-       char device_name[25];           /* device instance name */
-
-       unsigned int bus_type;  /* expansion bus type (ISA,EISA,PCI) */
-       unsigned char bus;              /* expansion bus number (zero based) */
-       unsigned char function;         /* PCI device number */
-
-       unsigned int io_base;           /* base I/O address of adapter */
-       unsigned int io_addr_size;      /* size of the I/O address range */
-       bool io_addr_requested;         /* true if I/O address requested */
-       
-       unsigned int irq_level;         /* interrupt level */
-       unsigned long irq_flags;
-       bool irq_requested;             /* true if IRQ requested */
-       
-       unsigned int dma_level;         /* DMA channel */
-       bool dma_requested;             /* true if dma channel requested */
-
-       u16 mbre_bit;
-       u16 loopback_bits;
-       u16 usc_idle_mode;
-
-       MGSL_PARAMS params;             /* communications parameters */
-
-       unsigned char serial_signals;   /* current serial signal states */
-
-       bool irq_occurred;              /* for diagnostics use */
-       unsigned int init_error;        /* Initialization startup error                 (DIAGS) */
-       int     fDiagnosticsmode;       /* Driver in Diagnostic mode?                   (DIAGS) */
-
-       u32 last_mem_alloc;
-       unsigned char* memory_base;     /* shared memory address (PCI only) */
-       u32 phys_memory_base;
-       bool shared_mem_requested;
-
-       unsigned char* lcr_base;        /* local config registers (PCI only) */
-       u32 phys_lcr_base;
-       u32 lcr_offset;
-       bool lcr_mem_requested;
-
-       u32 misc_ctrl_value;
-       char flag_buf[MAX_ASYNC_BUFFER_SIZE];
-       char char_buf[MAX_ASYNC_BUFFER_SIZE];   
-       bool drop_rts_on_tx_done;
-
-       bool loopmode_insert_requested;
-       bool loopmode_send_done_requested;
-       
-       struct  _input_signal_events    input_signal_events;
-
-       /* generic HDLC device parts */
-       int netcount;
-       spinlock_t netlock;
-
-#if SYNCLINK_GENERIC_HDLC
-       struct net_device *netdev;
-#endif
-};
-
-#define MGSL_MAGIC 0x5401
-
-/*
- * The size of the serial xmit buffer is 1 page, or 4096 bytes
- */
-#ifndef SERIAL_XMIT_SIZE
-#define SERIAL_XMIT_SIZE 4096
-#endif
-
-/*
- * These macros define the offsets used in calculating the
- * I/O address of the specified USC registers.
- */
-
-
-#define DCPIN 2                /* Bit 1 of I/O address */
-#define SDPIN 4                /* Bit 2 of I/O address */
-
-#define DCAR 0         /* DMA command/address register */
-#define CCAR SDPIN             /* channel command/address register */
-#define DATAREG DCPIN + SDPIN  /* serial data register */
-#define MSBONLY 0x41
-#define LSBONLY 0x40
-
-/*
- * These macros define the register address (ordinal number)
- * used for writing address/value pairs to the USC.
- */
-
-#define CMR    0x02    /* Channel mode Register */
-#define CCSR   0x04    /* Channel Command/status Register */
-#define CCR    0x06    /* Channel Control Register */
-#define PSR    0x08    /* Port status Register */
-#define PCR    0x0a    /* Port Control Register */
-#define TMDR   0x0c    /* Test mode Data Register */
-#define TMCR   0x0e    /* Test mode Control Register */
-#define CMCR   0x10    /* Clock mode Control Register */
-#define HCR    0x12    /* Hardware Configuration Register */
-#define IVR    0x14    /* Interrupt Vector Register */
-#define IOCR   0x16    /* Input/Output Control Register */
-#define ICR    0x18    /* Interrupt Control Register */
-#define DCCR   0x1a    /* Daisy Chain Control Register */
-#define MISR   0x1c    /* Misc Interrupt status Register */
-#define SICR   0x1e    /* status Interrupt Control Register */
-#define RDR    0x20    /* Receive Data Register */
-#define RMR    0x22    /* Receive mode Register */
-#define RCSR   0x24    /* Receive Command/status Register */
-#define RICR   0x26    /* Receive Interrupt Control Register */
-#define RSR    0x28    /* Receive Sync Register */
-#define RCLR   0x2a    /* Receive count Limit Register */
-#define RCCR   0x2c    /* Receive Character count Register */
-#define TC0R   0x2e    /* Time Constant 0 Register */
-#define TDR    0x30    /* Transmit Data Register */
-#define TMR    0x32    /* Transmit mode Register */
-#define TCSR   0x34    /* Transmit Command/status Register */
-#define TICR   0x36    /* Transmit Interrupt Control Register */
-#define TSR    0x38    /* Transmit Sync Register */
-#define TCLR   0x3a    /* Transmit count Limit Register */
-#define TCCR   0x3c    /* Transmit Character count Register */
-#define TC1R   0x3e    /* Time Constant 1 Register */
-
-
-/*
- * MACRO DEFINITIONS FOR DMA REGISTERS
- */
-
-#define DCR    0x06    /* DMA Control Register (shared) */
-#define DACR   0x08    /* DMA Array count Register (shared) */
-#define BDCR   0x12    /* Burst/Dwell Control Register (shared) */
-#define DIVR   0x14    /* DMA Interrupt Vector Register (shared) */    
-#define DICR   0x18    /* DMA Interrupt Control Register (shared) */
-#define CDIR   0x1a    /* Clear DMA Interrupt Register (shared) */
-#define SDIR   0x1c    /* Set DMA Interrupt Register (shared) */
-
-#define TDMR   0x02    /* Transmit DMA mode Register */
-#define TDIAR  0x1e    /* Transmit DMA Interrupt Arm Register */
-#define TBCR   0x2a    /* Transmit Byte count Register */
-#define TARL   0x2c    /* Transmit Address Register (low) */
-#define TARU   0x2e    /* Transmit Address Register (high) */
-#define NTBCR  0x3a    /* Next Transmit Byte count Register */
-#define NTARL  0x3c    /* Next Transmit Address Register (low) */
-#define NTARU  0x3e    /* Next Transmit Address Register (high) */
-
-#define RDMR   0x82    /* Receive DMA mode Register (non-shared) */
-#define RDIAR  0x9e    /* Receive DMA Interrupt Arm Register */
-#define RBCR   0xaa    /* Receive Byte count Register */
-#define RARL   0xac    /* Receive Address Register (low) */
-#define RARU   0xae    /* Receive Address Register (high) */
-#define NRBCR  0xba    /* Next Receive Byte count Register */
-#define NRARL  0xbc    /* Next Receive Address Register (low) */
-#define NRARU  0xbe    /* Next Receive Address Register (high) */
-
-
-/*
- * MACRO DEFINITIONS FOR MODEM STATUS BITS
- */
-
-#define MODEMSTATUS_DTR 0x80
-#define MODEMSTATUS_DSR 0x40
-#define MODEMSTATUS_RTS 0x20
-#define MODEMSTATUS_CTS 0x10
-#define MODEMSTATUS_RI  0x04
-#define MODEMSTATUS_DCD 0x01
-
-
-/*
- * Channel Command/Address Register (CCAR) Command Codes
- */
-
-#define RTCmd_Null                     0x0000
-#define RTCmd_ResetHighestIus          0x1000
-#define RTCmd_TriggerChannelLoadDma    0x2000
-#define RTCmd_TriggerRxDma             0x2800
-#define RTCmd_TriggerTxDma             0x3000
-#define RTCmd_TriggerRxAndTxDma                0x3800
-#define RTCmd_PurgeRxFifo              0x4800
-#define RTCmd_PurgeTxFifo              0x5000
-#define RTCmd_PurgeRxAndTxFifo         0x5800
-#define RTCmd_LoadRcc                  0x6800
-#define RTCmd_LoadTcc                  0x7000
-#define RTCmd_LoadRccAndTcc            0x7800
-#define RTCmd_LoadTC0                  0x8800
-#define RTCmd_LoadTC1                  0x9000
-#define RTCmd_LoadTC0AndTC1            0x9800
-#define RTCmd_SerialDataLSBFirst       0xa000
-#define RTCmd_SerialDataMSBFirst       0xa800
-#define RTCmd_SelectBigEndian          0xb000
-#define RTCmd_SelectLittleEndian       0xb800
-
-
-/*
- * DMA Command/Address Register (DCAR) Command Codes
- */
-
-#define DmaCmd_Null                    0x0000
-#define DmaCmd_ResetTxChannel          0x1000
-#define DmaCmd_ResetRxChannel          0x1200
-#define DmaCmd_StartTxChannel          0x2000
-#define DmaCmd_StartRxChannel          0x2200
-#define DmaCmd_ContinueTxChannel       0x3000
-#define DmaCmd_ContinueRxChannel       0x3200
-#define DmaCmd_PauseTxChannel          0x4000
-#define DmaCmd_PauseRxChannel          0x4200
-#define DmaCmd_AbortTxChannel          0x5000
-#define DmaCmd_AbortRxChannel          0x5200
-#define DmaCmd_InitTxChannel           0x7000
-#define DmaCmd_InitRxChannel           0x7200
-#define DmaCmd_ResetHighestDmaIus      0x8000
-#define DmaCmd_ResetAllChannels                0x9000
-#define DmaCmd_StartAllChannels                0xa000
-#define DmaCmd_ContinueAllChannels     0xb000
-#define DmaCmd_PauseAllChannels                0xc000
-#define DmaCmd_AbortAllChannels                0xd000
-#define DmaCmd_InitAllChannels         0xf000
-
-#define TCmd_Null                      0x0000
-#define TCmd_ClearTxCRC                        0x2000
-#define TCmd_SelectTicrTtsaData                0x4000
-#define TCmd_SelectTicrTxFifostatus    0x5000
-#define TCmd_SelectTicrIntLevel                0x6000
-#define TCmd_SelectTicrdma_level               0x7000
-#define TCmd_SendFrame                 0x8000
-#define TCmd_SendAbort                 0x9000
-#define TCmd_EnableDleInsertion                0xc000
-#define TCmd_DisableDleInsertion       0xd000
-#define TCmd_ClearEofEom               0xe000
-#define TCmd_SetEofEom                 0xf000
-
-#define RCmd_Null                      0x0000
-#define RCmd_ClearRxCRC                        0x2000
-#define RCmd_EnterHuntmode             0x3000
-#define RCmd_SelectRicrRtsaData                0x4000
-#define RCmd_SelectRicrRxFifostatus    0x5000
-#define RCmd_SelectRicrIntLevel                0x6000
-#define RCmd_SelectRicrdma_level               0x7000
-
-/*
- * Bits for enabling and disabling IRQs in Interrupt Control Register (ICR)
- */
-#define RECEIVE_STATUS         BIT5
-#define RECEIVE_DATA           BIT4
-#define TRANSMIT_STATUS                BIT3
-#define TRANSMIT_DATA          BIT2
-#define IO_PIN                 BIT1
-#define MISC                   BIT0
-
-
-/*
- * Receive status Bits in Receive Command/status Register RCSR
- */
-
-#define RXSTATUS_SHORT_FRAME           BIT8
-#define RXSTATUS_CODE_VIOLATION                BIT8
-#define RXSTATUS_EXITED_HUNT           BIT7
-#define RXSTATUS_IDLE_RECEIVED         BIT6
-#define RXSTATUS_BREAK_RECEIVED                BIT5
-#define RXSTATUS_ABORT_RECEIVED                BIT5
-#define RXSTATUS_RXBOUND               BIT4
-#define RXSTATUS_CRC_ERROR             BIT3
-#define RXSTATUS_FRAMING_ERROR         BIT3
-#define RXSTATUS_ABORT                 BIT2
-#define RXSTATUS_PARITY_ERROR          BIT2
-#define RXSTATUS_OVERRUN               BIT1
-#define RXSTATUS_DATA_AVAILABLE                BIT0
-#define RXSTATUS_ALL                   0x01f6
-#define usc_UnlatchRxstatusBits(a,b) usc_OutReg( (a), RCSR, (u16)((b) & RXSTATUS_ALL) )
-
-/*
- * Values for setting transmit idle mode in 
- * Transmit Control/status Register (TCSR)
- */
-#define IDLEMODE_FLAGS                 0x0000
-#define IDLEMODE_ALT_ONE_ZERO          0x0100
-#define IDLEMODE_ZERO                  0x0200
-#define IDLEMODE_ONE                   0x0300
-#define IDLEMODE_ALT_MARK_SPACE                0x0500
-#define IDLEMODE_SPACE                 0x0600
-#define IDLEMODE_MARK                  0x0700
-#define IDLEMODE_MASK                  0x0700
-
-/*
- * IUSC revision identifiers
- */
-#define        IUSC_SL1660                     0x4d44
-#define IUSC_PRE_SL1660                        0x4553
-
-/*
- * Transmit status Bits in Transmit Command/status Register (TCSR)
- */
-
-#define TCSR_PRESERVE                  0x0F00
-
-#define TCSR_UNDERWAIT                 BIT11
-#define TXSTATUS_PREAMBLE_SENT         BIT7
-#define TXSTATUS_IDLE_SENT             BIT6
-#define TXSTATUS_ABORT_SENT            BIT5
-#define TXSTATUS_EOF_SENT              BIT4
-#define TXSTATUS_EOM_SENT              BIT4
-#define TXSTATUS_CRC_SENT              BIT3
-#define TXSTATUS_ALL_SENT              BIT2
-#define TXSTATUS_UNDERRUN              BIT1
-#define TXSTATUS_FIFO_EMPTY            BIT0
-#define TXSTATUS_ALL                   0x00fa
-#define usc_UnlatchTxstatusBits(a,b) usc_OutReg( (a), TCSR, (u16)((a)->tcsr_value + ((b) & 0x00FF)) )
-                               
-
-#define MISCSTATUS_RXC_LATCHED         BIT15
-#define MISCSTATUS_RXC                 BIT14
-#define MISCSTATUS_TXC_LATCHED         BIT13
-#define MISCSTATUS_TXC                 BIT12
-#define MISCSTATUS_RI_LATCHED          BIT11
-#define MISCSTATUS_RI                  BIT10
-#define MISCSTATUS_DSR_LATCHED         BIT9
-#define MISCSTATUS_DSR                 BIT8
-#define MISCSTATUS_DCD_LATCHED         BIT7
-#define MISCSTATUS_DCD                 BIT6
-#define MISCSTATUS_CTS_LATCHED         BIT5
-#define MISCSTATUS_CTS                 BIT4
-#define MISCSTATUS_RCC_UNDERRUN                BIT3
-#define MISCSTATUS_DPLL_NO_SYNC                BIT2
-#define MISCSTATUS_BRG1_ZERO           BIT1
-#define MISCSTATUS_BRG0_ZERO           BIT0
-
-#define usc_UnlatchIostatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0xaaa0))
-#define usc_UnlatchMiscstatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0x000f))
-
-#define SICR_RXC_ACTIVE                        BIT15
-#define SICR_RXC_INACTIVE              BIT14
-#define SICR_RXC                       (BIT15+BIT14)
-#define SICR_TXC_ACTIVE                        BIT13
-#define SICR_TXC_INACTIVE              BIT12
-#define SICR_TXC                       (BIT13+BIT12)
-#define SICR_RI_ACTIVE                 BIT11
-#define SICR_RI_INACTIVE               BIT10
-#define SICR_RI                                (BIT11+BIT10)
-#define SICR_DSR_ACTIVE                        BIT9
-#define SICR_DSR_INACTIVE              BIT8
-#define SICR_DSR                       (BIT9+BIT8)
-#define SICR_DCD_ACTIVE                        BIT7
-#define SICR_DCD_INACTIVE              BIT6
-#define SICR_DCD                       (BIT7+BIT6)
-#define SICR_CTS_ACTIVE                        BIT5
-#define SICR_CTS_INACTIVE              BIT4
-#define SICR_CTS                       (BIT5+BIT4)
-#define SICR_RCC_UNDERFLOW             BIT3
-#define SICR_DPLL_NO_SYNC              BIT2
-#define SICR_BRG1_ZERO                 BIT1
-#define SICR_BRG0_ZERO                 BIT0
-
-void usc_DisableMasterIrqBit( struct mgsl_struct *info );
-void usc_EnableMasterIrqBit( struct mgsl_struct *info );
-void usc_EnableInterrupts( struct mgsl_struct *info, u16 IrqMask );
-void usc_DisableInterrupts( struct mgsl_struct *info, u16 IrqMask );
-void usc_ClearIrqPendingBits( struct mgsl_struct *info, u16 IrqMask );
-
-#define usc_EnableInterrupts( a, b ) \
-       usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0xc0 + (b)) )
-
-#define usc_DisableInterrupts( a, b ) \
-       usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0x80 + (b)) )
-
-#define usc_EnableMasterIrqBit(a) \
-       usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0x0f00) + 0xb000) )
-
-#define usc_DisableMasterIrqBit(a) \
-       usc_OutReg( (a), ICR, (u16)(usc_InReg((a),ICR) & 0x7f00) )
-
-#define usc_ClearIrqPendingBits( a, b ) usc_OutReg( (a), DCCR, 0x40 + (b) )
-
-/*
- * Transmit status Bits in Transmit Control status Register (TCSR)
- * and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0)
- */
-
-#define TXSTATUS_PREAMBLE_SENT BIT7
-#define TXSTATUS_IDLE_SENT     BIT6
-#define TXSTATUS_ABORT_SENT    BIT5
-#define TXSTATUS_EOF           BIT4
-#define TXSTATUS_CRC_SENT      BIT3
-#define TXSTATUS_ALL_SENT      BIT2
-#define TXSTATUS_UNDERRUN      BIT1
-#define TXSTATUS_FIFO_EMPTY    BIT0
-
-#define DICR_MASTER            BIT15
-#define DICR_TRANSMIT          BIT0
-#define DICR_RECEIVE           BIT1
-
-#define usc_EnableDmaInterrupts(a,b) \
-       usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) | (b)) )
-
-#define usc_DisableDmaInterrupts(a,b) \
-       usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) & ~(b)) )
-
-#define usc_EnableStatusIrqs(a,b) \
-       usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) | (b)) )
-
-#define usc_DisablestatusIrqs(a,b) \
-       usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) & ~(b)) )
-
-/* Transmit status Bits in Transmit Control status Register (TCSR) */
-/* and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0) */
-
-
-#define DISABLE_UNCONDITIONAL    0
-#define DISABLE_END_OF_FRAME     1
-#define ENABLE_UNCONDITIONAL     2
-#define ENABLE_AUTO_CTS          3
-#define ENABLE_AUTO_DCD          3
-#define usc_EnableTransmitter(a,b) \
-       usc_OutReg( (a), TMR, (u16)((usc_InReg((a),TMR) & 0xfffc) | (b)) )
-#define usc_EnableReceiver(a,b) \
-       usc_OutReg( (a), RMR, (u16)((usc_InReg((a),RMR) & 0xfffc) | (b)) )
-
-static u16  usc_InDmaReg( struct mgsl_struct *info, u16 Port );
-static void usc_OutDmaReg( struct mgsl_struct *info, u16 Port, u16 Value );
-static void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd );
-
-static u16  usc_InReg( struct mgsl_struct *info, u16 Port );
-static void usc_OutReg( struct mgsl_struct *info, u16 Port, u16 Value );
-static void usc_RTCmd( struct mgsl_struct *info, u16 Cmd );
-void usc_RCmd( struct mgsl_struct *info, u16 Cmd );
-void usc_TCmd( struct mgsl_struct *info, u16 Cmd );
-
-#define usc_TCmd(a,b) usc_OutReg((a), TCSR, (u16)((a)->tcsr_value + (b)))
-#define usc_RCmd(a,b) usc_OutReg((a), RCSR, (b))
-
-#define usc_SetTransmitSyncChars(a,s0,s1) usc_OutReg((a), TSR, (u16)(((u16)s0<<8)|(u16)s1))
-
-static void usc_process_rxoverrun_sync( struct mgsl_struct *info );
-static void usc_start_receiver( struct mgsl_struct *info );
-static void usc_stop_receiver( struct mgsl_struct *info );
-
-static void usc_start_transmitter( struct mgsl_struct *info );
-static void usc_stop_transmitter( struct mgsl_struct *info );
-static void usc_set_txidle( struct mgsl_struct *info );
-static void usc_load_txfifo( struct mgsl_struct *info );
-
-static void usc_enable_aux_clock( struct mgsl_struct *info, u32 DataRate );
-static void usc_enable_loopback( struct mgsl_struct *info, int enable );
-
-static void usc_get_serial_signals( struct mgsl_struct *info );
-static void usc_set_serial_signals( struct mgsl_struct *info );
-
-static void usc_reset( struct mgsl_struct *info );
-
-static void usc_set_sync_mode( struct mgsl_struct *info );
-static void usc_set_sdlc_mode( struct mgsl_struct *info );
-static void usc_set_async_mode( struct mgsl_struct *info );
-static void usc_enable_async_clock( struct mgsl_struct *info, u32 DataRate );
-
-static void usc_loopback_frame( struct mgsl_struct *info );
-
-static void mgsl_tx_timeout(unsigned long context);
-
-
-static void usc_loopmode_cancel_transmit( struct mgsl_struct * info );
-static void usc_loopmode_insert_request( struct mgsl_struct * info );
-static int usc_loopmode_active( struct mgsl_struct * info);
-static void usc_loopmode_send_done( struct mgsl_struct * info );
-
-static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg);
-
-#if SYNCLINK_GENERIC_HDLC
-#define dev_to_port(D) (dev_to_hdlc(D)->priv)
-static void hdlcdev_tx_done(struct mgsl_struct *info);
-static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size);
-static int  hdlcdev_init(struct mgsl_struct *info);
-static void hdlcdev_exit(struct mgsl_struct *info);
-#endif
-
-/*
- * Defines a BUS descriptor value for the PCI adapter
- * local bus address ranges.
- */
-
-#define BUS_DESCRIPTOR( WrHold, WrDly, RdDly, Nwdd, Nwad, Nxda, Nrdd, Nrad ) \
-(0x00400020 + \
-((WrHold) << 30) + \
-((WrDly)  << 28) + \
-((RdDly)  << 26) + \
-((Nwdd)   << 20) + \
-((Nwad)   << 15) + \
-((Nxda)   << 13) + \
-((Nrdd)   << 11) + \
-((Nrad)   <<  6) )
-
-static void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit);
-
-/*
- * Adapter diagnostic routines
- */
-static bool mgsl_register_test( struct mgsl_struct *info );
-static bool mgsl_irq_test( struct mgsl_struct *info );
-static bool mgsl_dma_test( struct mgsl_struct *info );
-static bool mgsl_memory_test( struct mgsl_struct *info );
-static int mgsl_adapter_test( struct mgsl_struct *info );
-
-/*
- * device and resource management routines
- */
-static int mgsl_claim_resources(struct mgsl_struct *info);
-static void mgsl_release_resources(struct mgsl_struct *info);
-static void mgsl_add_device(struct mgsl_struct *info);
-static struct mgsl_struct* mgsl_allocate_device(void);
-
-/*
- * DMA buffer manupulation functions.
- */
-static void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex );
-static bool mgsl_get_rx_frame( struct mgsl_struct *info );
-static bool mgsl_get_raw_rx_frame( struct mgsl_struct *info );
-static void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info );
-static void mgsl_reset_tx_dma_buffers( struct mgsl_struct *info );
-static int num_free_tx_dma_buffers(struct mgsl_struct *info);
-static void mgsl_load_tx_dma_buffer( struct mgsl_struct *info, const char *Buffer, unsigned int BufferSize);
-static void mgsl_load_pci_memory(char* TargetPtr, const char* SourcePtr, unsigned short count);
-
-/*
- * DMA and Shared Memory buffer allocation and formatting
- */
-static int  mgsl_allocate_dma_buffers(struct mgsl_struct *info);
-static void mgsl_free_dma_buffers(struct mgsl_struct *info);
-static int  mgsl_alloc_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount);
-static void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount);
-static int  mgsl_alloc_buffer_list_memory(struct mgsl_struct *info);
-static void mgsl_free_buffer_list_memory(struct mgsl_struct *info);
-static int mgsl_alloc_intermediate_rxbuffer_memory(struct mgsl_struct *info);
-static void mgsl_free_intermediate_rxbuffer_memory(struct mgsl_struct *info);
-static int mgsl_alloc_intermediate_txbuffer_memory(struct mgsl_struct *info);
-static void mgsl_free_intermediate_txbuffer_memory(struct mgsl_struct *info);
-static bool load_next_tx_holding_buffer(struct mgsl_struct *info);
-static int save_tx_buffer_request(struct mgsl_struct *info,const char *Buffer, unsigned int BufferSize);
-
-/*
- * Bottom half interrupt handlers
- */
-static void mgsl_bh_handler(struct work_struct *work);
-static void mgsl_bh_receive(struct mgsl_struct *info);
-static void mgsl_bh_transmit(struct mgsl_struct *info);
-static void mgsl_bh_status(struct mgsl_struct *info);
-
-/*
- * Interrupt handler routines and dispatch table.
- */
-static void mgsl_isr_null( struct mgsl_struct *info );
-static void mgsl_isr_transmit_data( struct mgsl_struct *info );
-static void mgsl_isr_receive_data( struct mgsl_struct *info );
-static void mgsl_isr_receive_status( struct mgsl_struct *info );
-static void mgsl_isr_transmit_status( struct mgsl_struct *info );
-static void mgsl_isr_io_pin( struct mgsl_struct *info );
-static void mgsl_isr_misc( struct mgsl_struct *info );
-static void mgsl_isr_receive_dma( struct mgsl_struct *info );
-static void mgsl_isr_transmit_dma( struct mgsl_struct *info );
-
-typedef void (*isr_dispatch_func)(struct mgsl_struct *);
-
-static isr_dispatch_func UscIsrTable[7] =
-{
-       mgsl_isr_null,
-       mgsl_isr_misc,
-       mgsl_isr_io_pin,
-       mgsl_isr_transmit_data,
-       mgsl_isr_transmit_status,
-       mgsl_isr_receive_data,
-       mgsl_isr_receive_status
-};
-
-/*
- * ioctl call handlers
- */
-static int tiocmget(struct tty_struct *tty);
-static int tiocmset(struct tty_struct *tty,
-                   unsigned int set, unsigned int clear);
-static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount
-       __user *user_icount);
-static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS  __user *user_params);
-static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS  __user *new_params);
-static int mgsl_get_txidle(struct mgsl_struct * info, int __user *idle_mode);
-static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode);
-static int mgsl_txenable(struct mgsl_struct * info, int enable);
-static int mgsl_txabort(struct mgsl_struct * info);
-static int mgsl_rxenable(struct mgsl_struct * info, int enable);
-static int mgsl_wait_event(struct mgsl_struct * info, int __user *mask);
-static int mgsl_loopmode_send_done( struct mgsl_struct * info );
-
-/* set non-zero on successful registration with PCI subsystem */
-static bool pci_registered;
-
-/*
- * Global linked list of SyncLink devices
- */
-static struct mgsl_struct *mgsl_device_list;
-static int mgsl_device_count;
-
-/*
- * Set this param to non-zero to load eax with the
- * .text section address and breakpoint on module load.
- * This is useful for use with gdb and add-symbol-file command.
- */
-static int break_on_load;
-
-/*
- * Driver major number, defaults to zero to get auto
- * assigned major number. May be forced as module parameter.
- */
-static int ttymajor;
-
-/*
- * Array of user specified options for ISA adapters.
- */
-static int io[MAX_ISA_DEVICES];
-static int irq[MAX_ISA_DEVICES];
-static int dma[MAX_ISA_DEVICES];
-static int debug_level;
-static int maxframe[MAX_TOTAL_DEVICES];
-static int txdmabufs[MAX_TOTAL_DEVICES];
-static int txholdbufs[MAX_TOTAL_DEVICES];
-       
-module_param(break_on_load, bool, 0);
-module_param(ttymajor, int, 0);
-module_param_array(io, int, NULL, 0);
-module_param_array(irq, int, NULL, 0);
-module_param_array(dma, int, NULL, 0);
-module_param(debug_level, int, 0);
-module_param_array(maxframe, int, NULL, 0);
-module_param_array(txdmabufs, int, NULL, 0);
-module_param_array(txholdbufs, int, NULL, 0);
-
-static char *driver_name = "SyncLink serial driver";
-static char *driver_version = "$Revision: 4.38 $";
-
-static int synclink_init_one (struct pci_dev *dev,
-                                    const struct pci_device_id *ent);
-static void synclink_remove_one (struct pci_dev *dev);
-
-static struct pci_device_id synclink_pci_tbl[] = {
-       { PCI_VENDOR_ID_MICROGATE, PCI_DEVICE_ID_MICROGATE_USC, PCI_ANY_ID, PCI_ANY_ID, },
-       { PCI_VENDOR_ID_MICROGATE, 0x0210, PCI_ANY_ID, PCI_ANY_ID, },
-       { 0, }, /* terminate list */
-};
-MODULE_DEVICE_TABLE(pci, synclink_pci_tbl);
-
-MODULE_LICENSE("GPL");
-
-static struct pci_driver synclink_pci_driver = {
-       .name           = "synclink",
-       .id_table       = synclink_pci_tbl,
-       .probe          = synclink_init_one,
-       .remove         = __devexit_p(synclink_remove_one),
-};
-
-static struct tty_driver *serial_driver;
-
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
-
-static void mgsl_change_params(struct mgsl_struct *info);
-static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout);
-
-/*
- * 1st function defined in .text section. Calling this function in
- * init_module() followed by a breakpoint allows a remote debugger
- * (gdb) to get the .text address for the add-symbol-file command.
- * This allows remote debugging of dynamically loadable modules.
- */
-static void* mgsl_get_text_ptr(void)
-{
-       return mgsl_get_text_ptr;
-}
-
-static inline int mgsl_paranoia_check(struct mgsl_struct *info,
-                                       char *name, const char *routine)
-{
-#ifdef MGSL_PARANOIA_CHECK
-       static const char *badmagic =
-               "Warning: bad magic number for mgsl struct (%s) in %s\n";
-       static const char *badinfo =
-               "Warning: null mgsl_struct for (%s) in %s\n";
-
-       if (!info) {
-               printk(badinfo, name, routine);
-               return 1;
-       }
-       if (info->magic != MGSL_MAGIC) {
-               printk(badmagic, name, routine);
-               return 1;
-       }
-#else
-       if (!info)
-               return 1;
-#endif
-       return 0;
-}
-
-/**
- * line discipline callback wrappers
- *
- * The wrappers maintain line discipline references
- * while calling into the line discipline.
- *
- * ldisc_receive_buf  - pass receive data to line discipline
- */
-
-static void ldisc_receive_buf(struct tty_struct *tty,
-                             const __u8 *data, char *flags, int count)
-{
-       struct tty_ldisc *ld;
-       if (!tty)
-               return;
-       ld = tty_ldisc_ref(tty);
-       if (ld) {
-               if (ld->ops->receive_buf)
-                       ld->ops->receive_buf(tty, data, flags, count);
-               tty_ldisc_deref(ld);
-       }
-}
-
-/* mgsl_stop()         throttle (stop) transmitter
- *     
- * Arguments:          tty     pointer to tty info structure
- * Return Value:       None
- */
-static void mgsl_stop(struct tty_struct *tty)
-{
-       struct mgsl_struct *info = tty->driver_data;
-       unsigned long flags;
-       
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_stop"))
-               return;
-       
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk("mgsl_stop(%s)\n",info->device_name);    
-               
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       if (info->tx_enabled)
-               usc_stop_transmitter(info);
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       
-}      /* end of mgsl_stop() */
-
-/* mgsl_start()                release (start) transmitter
- *     
- * Arguments:          tty     pointer to tty info structure
- * Return Value:       None
- */
-static void mgsl_start(struct tty_struct *tty)
-{
-       struct mgsl_struct *info = tty->driver_data;
-       unsigned long flags;
-       
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_start"))
-               return;
-       
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk("mgsl_start(%s)\n",info->device_name);   
-               
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       if (!info->tx_enabled)
-               usc_start_transmitter(info);
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       
-}      /* end of mgsl_start() */
-
-/*
- * Bottom half work queue access functions
- */
-
-/* mgsl_bh_action()    Return next bottom half action to perform.
- * Return Value:       BH action code or 0 if nothing to do.
- */
-static int mgsl_bh_action(struct mgsl_struct *info)
-{
-       unsigned long flags;
-       int rc = 0;
-       
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-
-       if (info->pending_bh & BH_RECEIVE) {
-               info->pending_bh &= ~BH_RECEIVE;
-               rc = BH_RECEIVE;
-       } else if (info->pending_bh & BH_TRANSMIT) {
-               info->pending_bh &= ~BH_TRANSMIT;
-               rc = BH_TRANSMIT;
-       } else if (info->pending_bh & BH_STATUS) {
-               info->pending_bh &= ~BH_STATUS;
-               rc = BH_STATUS;
-       }
-
-       if (!rc) {
-               /* Mark BH routine as complete */
-               info->bh_running = false;
-               info->bh_requested = false;
-       }
-       
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       
-       return rc;
-}
-
-/*
- *     Perform bottom half processing of work items queued by ISR.
- */
-static void mgsl_bh_handler(struct work_struct *work)
-{
-       struct mgsl_struct *info =
-               container_of(work, struct mgsl_struct, task);
-       int action;
-
-       if (!info)
-               return;
-               
-       if ( debug_level >= DEBUG_LEVEL_BH )
-               printk( "%s(%d):mgsl_bh_handler(%s) entry\n",
-                       __FILE__,__LINE__,info->device_name);
-       
-       info->bh_running = true;
-
-       while((action = mgsl_bh_action(info)) != 0) {
-       
-               /* Process work item */
-               if ( debug_level >= DEBUG_LEVEL_BH )
-                       printk( "%s(%d):mgsl_bh_handler() work item action=%d\n",
-                               __FILE__,__LINE__,action);
-
-               switch (action) {
-               
-               case BH_RECEIVE:
-                       mgsl_bh_receive(info);
-                       break;
-               case BH_TRANSMIT:
-                       mgsl_bh_transmit(info);
-                       break;
-               case BH_STATUS:
-                       mgsl_bh_status(info);
-                       break;
-               default:
-                       /* unknown work item ID */
-                       printk("Unknown work item ID=%08X!\n", action);
-                       break;
-               }
-       }
-
-       if ( debug_level >= DEBUG_LEVEL_BH )
-               printk( "%s(%d):mgsl_bh_handler(%s) exit\n",
-                       __FILE__,__LINE__,info->device_name);
-}
-
-static void mgsl_bh_receive(struct mgsl_struct *info)
-{
-       bool (*get_rx_frame)(struct mgsl_struct *info) =
-               (info->params.mode == MGSL_MODE_HDLC ? mgsl_get_rx_frame : mgsl_get_raw_rx_frame);
-
-       if ( debug_level >= DEBUG_LEVEL_BH )
-               printk( "%s(%d):mgsl_bh_receive(%s)\n",
-                       __FILE__,__LINE__,info->device_name);
-       
-       do
-       {
-               if (info->rx_rcc_underrun) {
-                       unsigned long flags;
-                       spin_lock_irqsave(&info->irq_spinlock,flags);
-                       usc_start_receiver(info);
-                       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-                       return;
-               }
-       } while(get_rx_frame(info));
-}
-
-static void mgsl_bh_transmit(struct mgsl_struct *info)
-{
-       struct tty_struct *tty = info->port.tty;
-       unsigned long flags;
-       
-       if ( debug_level >= DEBUG_LEVEL_BH )
-               printk( "%s(%d):mgsl_bh_transmit() entry on %s\n",
-                       __FILE__,__LINE__,info->device_name);
-
-       if (tty)
-               tty_wakeup(tty);
-
-       /* if transmitter idle and loopmode_send_done_requested
-        * then start echoing RxD to TxD
-        */
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       if ( !info->tx_active && info->loopmode_send_done_requested )
-               usc_loopmode_send_done( info );
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-}
-
-static void mgsl_bh_status(struct mgsl_struct *info)
-{
-       if ( debug_level >= DEBUG_LEVEL_BH )
-               printk( "%s(%d):mgsl_bh_status() entry on %s\n",
-                       __FILE__,__LINE__,info->device_name);
-
-       info->ri_chkcount = 0;
-       info->dsr_chkcount = 0;
-       info->dcd_chkcount = 0;
-       info->cts_chkcount = 0;
-}
-
-/* mgsl_isr_receive_status()
- * 
- *     Service a receive status interrupt. The type of status
- *     interrupt is indicated by the state of the RCSR.
- *     This is only used for HDLC mode.
- *
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void mgsl_isr_receive_status( struct mgsl_struct *info )
-{
-       u16 status = usc_InReg( info, RCSR );
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )   
-               printk("%s(%d):mgsl_isr_receive_status status=%04X\n",
-                       __FILE__,__LINE__,status);
-                       
-       if ( (status & RXSTATUS_ABORT_RECEIVED) && 
-               info->loopmode_insert_requested &&
-               usc_loopmode_active(info) )
-       {
-               ++info->icount.rxabort;
-               info->loopmode_insert_requested = false;
-               /* clear CMR:13 to start echoing RxD to TxD */
-               info->cmr_value &= ~BIT13;
-               usc_OutReg(info, CMR, info->cmr_value);
-               /* disable received abort irq (no longer required) */
-               usc_OutReg(info, RICR,
-                       (usc_InReg(info, RICR) & ~RXSTATUS_ABORT_RECEIVED));
-       }
-
-       if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) {
-               if (status & RXSTATUS_EXITED_HUNT)
-                       info->icount.exithunt++;
-               if (status & RXSTATUS_IDLE_RECEIVED)
-                       info->icount.rxidle++;
-               wake_up_interruptible(&info->event_wait_q);
-       }
-
-       if (status & RXSTATUS_OVERRUN){
-               info->icount.rxover++;
-               usc_process_rxoverrun_sync( info );
-       }
-
-       usc_ClearIrqPendingBits( info, RECEIVE_STATUS );
-       usc_UnlatchRxstatusBits( info, status );
-
-}      /* end of mgsl_isr_receive_status() */
-
-/* mgsl_isr_transmit_status()
- * 
- *     Service a transmit status interrupt
- *     HDLC mode :end of transmit frame
- *     Async mode:all data is sent
- *     transmit status is indicated by bits in the TCSR.
- * 
- * Arguments:          info           pointer to device instance data
- * Return Value:       None
- */
-static void mgsl_isr_transmit_status( struct mgsl_struct *info )
-{
-       u16 status = usc_InReg( info, TCSR );
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )   
-               printk("%s(%d):mgsl_isr_transmit_status status=%04X\n",
-                       __FILE__,__LINE__,status);
-       
-       usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
-       usc_UnlatchTxstatusBits( info, status );
-       
-       if ( status & (TXSTATUS_UNDERRUN | TXSTATUS_ABORT_SENT) )
-       {
-               /* finished sending HDLC abort. This may leave  */
-               /* the TxFifo with data from the aborted frame  */
-               /* so purge the TxFifo. Also shutdown the DMA   */
-               /* channel in case there is data remaining in   */
-               /* the DMA buffer                               */
-               usc_DmaCmd( info, DmaCmd_ResetTxChannel );
-               usc_RTCmd( info, RTCmd_PurgeTxFifo );
-       }
-       if ( status & TXSTATUS_EOF_SENT )
-               info->icount.txok++;
-       else if ( status & TXSTATUS_UNDERRUN )
-               info->icount.txunder++;
-       else if ( status & TXSTATUS_ABORT_SENT )
-               info->icount.txabort++;
-       else
-               info->icount.txunder++;
-                       
-       info->tx_active = false;
-       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-       del_timer(&info->tx_timer);     
-       
-       if ( info->drop_rts_on_tx_done ) {
-               usc_get_serial_signals( info );
-               if ( info->serial_signals & SerialSignal_RTS ) {
-                       info->serial_signals &= ~SerialSignal_RTS;
-                       usc_set_serial_signals( info );
-               }
-               info->drop_rts_on_tx_done = false;
-       }
-
-#if SYNCLINK_GENERIC_HDLC
-       if (info->netcount)
-               hdlcdev_tx_done(info);
-       else 
-#endif
-       {
-               if (info->port.tty->stopped || info->port.tty->hw_stopped) {
-                       usc_stop_transmitter(info);
-                       return;
-               }
-               info->pending_bh |= BH_TRANSMIT;
-       }
-
-}      /* end of mgsl_isr_transmit_status() */
-
-/* mgsl_isr_io_pin()
- * 
- *     Service an Input/Output pin interrupt. The type of
- *     interrupt is indicated by bits in the MISR
- *     
- * Arguments:          info           pointer to device instance data
- * Return Value:       None
- */
-static void mgsl_isr_io_pin( struct mgsl_struct *info )
-{
-       struct  mgsl_icount *icount;
-       u16 status = usc_InReg( info, MISR );
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )   
-               printk("%s(%d):mgsl_isr_io_pin status=%04X\n",
-                       __FILE__,__LINE__,status);
-                       
-       usc_ClearIrqPendingBits( info, IO_PIN );
-       usc_UnlatchIostatusBits( info, status );
-
-       if (status & (MISCSTATUS_CTS_LATCHED | MISCSTATUS_DCD_LATCHED |
-                     MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) {
-               icount = &info->icount;
-               /* update input line counters */
-               if (status & MISCSTATUS_RI_LATCHED) {
-                       if ((info->ri_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
-                               usc_DisablestatusIrqs(info,SICR_RI);
-                       icount->rng++;
-                       if ( status & MISCSTATUS_RI )
-                               info->input_signal_events.ri_up++;      
-                       else
-                               info->input_signal_events.ri_down++;    
-               }
-               if (status & MISCSTATUS_DSR_LATCHED) {
-                       if ((info->dsr_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
-                               usc_DisablestatusIrqs(info,SICR_DSR);
-                       icount->dsr++;
-                       if ( status & MISCSTATUS_DSR )
-                               info->input_signal_events.dsr_up++;
-                       else
-                               info->input_signal_events.dsr_down++;
-               }
-               if (status & MISCSTATUS_DCD_LATCHED) {
-                       if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
-                               usc_DisablestatusIrqs(info,SICR_DCD);
-                       icount->dcd++;
-                       if (status & MISCSTATUS_DCD) {
-                               info->input_signal_events.dcd_up++;
-                       } else
-                               info->input_signal_events.dcd_down++;
-#if SYNCLINK_GENERIC_HDLC
-                       if (info->netcount) {
-                               if (status & MISCSTATUS_DCD)
-                                       netif_carrier_on(info->netdev);
-                               else
-                                       netif_carrier_off(info->netdev);
-                       }
-#endif
-               }
-               if (status & MISCSTATUS_CTS_LATCHED)
-               {
-                       if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
-                               usc_DisablestatusIrqs(info,SICR_CTS);
-                       icount->cts++;
-                       if ( status & MISCSTATUS_CTS )
-                               info->input_signal_events.cts_up++;
-                       else
-                               info->input_signal_events.cts_down++;
-               }
-               wake_up_interruptible(&info->status_event_wait_q);
-               wake_up_interruptible(&info->event_wait_q);
-
-               if ( (info->port.flags & ASYNC_CHECK_CD) && 
-                    (status & MISCSTATUS_DCD_LATCHED) ) {
-                       if ( debug_level >= DEBUG_LEVEL_ISR )
-                               printk("%s CD now %s...", info->device_name,
-                                      (status & MISCSTATUS_DCD) ? "on" : "off");
-                       if (status & MISCSTATUS_DCD)
-                               wake_up_interruptible(&info->port.open_wait);
-                       else {
-                               if ( debug_level >= DEBUG_LEVEL_ISR )
-                                       printk("doing serial hangup...");
-                               if (info->port.tty)
-                                       tty_hangup(info->port.tty);
-                       }
-               }
-       
-               if ( (info->port.flags & ASYNC_CTS_FLOW) && 
-                    (status & MISCSTATUS_CTS_LATCHED) ) {
-                       if (info->port.tty->hw_stopped) {
-                               if (status & MISCSTATUS_CTS) {
-                                       if ( debug_level >= DEBUG_LEVEL_ISR )
-                                               printk("CTS tx start...");
-                                       if (info->port.tty)
-                                               info->port.tty->hw_stopped = 0;
-                                       usc_start_transmitter(info);
-                                       info->pending_bh |= BH_TRANSMIT;
-                                       return;
-                               }
-                       } else {
-                               if (!(status & MISCSTATUS_CTS)) {
-                                       if ( debug_level >= DEBUG_LEVEL_ISR )
-                                               printk("CTS tx stop...");
-                                       if (info->port.tty)
-                                               info->port.tty->hw_stopped = 1;
-                                       usc_stop_transmitter(info);
-                               }
-                       }
-               }
-       }
-
-       info->pending_bh |= BH_STATUS;
-       
-       /* for diagnostics set IRQ flag */
-       if ( status & MISCSTATUS_TXC_LATCHED ){
-               usc_OutReg( info, SICR,
-                       (unsigned short)(usc_InReg(info,SICR) & ~(SICR_TXC_ACTIVE+SICR_TXC_INACTIVE)) );
-               usc_UnlatchIostatusBits( info, MISCSTATUS_TXC_LATCHED );
-               info->irq_occurred = true;
-       }
-
-}      /* end of mgsl_isr_io_pin() */
-
-/* mgsl_isr_transmit_data()
- * 
- *     Service a transmit data interrupt (async mode only).
- * 
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void mgsl_isr_transmit_data( struct mgsl_struct *info )
-{
-       if ( debug_level >= DEBUG_LEVEL_ISR )   
-               printk("%s(%d):mgsl_isr_transmit_data xmit_cnt=%d\n",
-                       __FILE__,__LINE__,info->xmit_cnt);
-                       
-       usc_ClearIrqPendingBits( info, TRANSMIT_DATA );
-       
-       if (info->port.tty->stopped || info->port.tty->hw_stopped) {
-               usc_stop_transmitter(info);
-               return;
-       }
-       
-       if ( info->xmit_cnt )
-               usc_load_txfifo( info );
-       else
-               info->tx_active = false;
-               
-       if (info->xmit_cnt < WAKEUP_CHARS)
-               info->pending_bh |= BH_TRANSMIT;
-
-}      /* end of mgsl_isr_transmit_data() */
-
-/* mgsl_isr_receive_data()
- * 
- *     Service a receive data interrupt. This occurs
- *     when operating in asynchronous interrupt transfer mode.
- *     The receive data FIFO is flushed to the receive data buffers. 
- * 
- * Arguments:          info            pointer to device instance data
- * Return Value:       None
- */
-static void mgsl_isr_receive_data( struct mgsl_struct *info )
-{
-       int Fifocount;
-       u16 status;
-       int work = 0;
-       unsigned char DataByte;
-       struct tty_struct *tty = info->port.tty;
-       struct  mgsl_icount *icount = &info->icount;
-       
-       if ( debug_level >= DEBUG_LEVEL_ISR )   
-               printk("%s(%d):mgsl_isr_receive_data\n",
-                       __FILE__,__LINE__);
-
-       usc_ClearIrqPendingBits( info, RECEIVE_DATA );
-       
-       /* select FIFO status for RICR readback */
-       usc_RCmd( info, RCmd_SelectRicrRxFifostatus );
-
-       /* clear the Wordstatus bit so that status readback */
-       /* only reflects the status of this byte */
-       usc_OutReg( info, RICR+LSBONLY, (u16)(usc_InReg(info, RICR+LSBONLY) & ~BIT3 ));
-
-       /* flush the receive FIFO */
-
-       while( (Fifocount = (usc_InReg(info,RICR) >> 8)) ) {
-               int flag;
-
-               /* read one byte from RxFIFO */
-               outw( (inw(info->io_base + CCAR) & 0x0780) | (RDR+LSBONLY),
-                     info->io_base + CCAR );
-               DataByte = inb( info->io_base + CCAR );
-
-               /* get the status of the received byte */
-               status = usc_InReg(info, RCSR);
-               if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR +
-                               RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) )
-                       usc_UnlatchRxstatusBits(info,RXSTATUS_ALL);
-               
-               icount->rx++;
-               
-               flag = 0;
-               if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR +
-                               RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) {
-                       printk("rxerr=%04X\n",status);                                  
-                       /* update error statistics */
-                       if ( status & RXSTATUS_BREAK_RECEIVED ) {
-                               status &= ~(RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR);
-                               icount->brk++;
-                       } else if (status & RXSTATUS_PARITY_ERROR) 
-                               icount->parity++;
-                       else if (status & RXSTATUS_FRAMING_ERROR)
-                               icount->frame++;
-                       else if (status & RXSTATUS_OVERRUN) {
-                               /* must issue purge fifo cmd before */
-                               /* 16C32 accepts more receive chars */
-                               usc_RTCmd(info,RTCmd_PurgeRxFifo);
-                               icount->overrun++;
-                       }
-
-                       /* discard char if tty control flags say so */                                  
-                       if (status & info->ignore_status_mask)
-                               continue;
-                               
-                       status &= info->read_status_mask;
-               
-                       if (status & RXSTATUS_BREAK_RECEIVED) {
-                               flag = TTY_BREAK;
-                               if (info->port.flags & ASYNC_SAK)
-                                       do_SAK(tty);
-                       } else if (status & RXSTATUS_PARITY_ERROR)
-                               flag = TTY_PARITY;
-                       else if (status & RXSTATUS_FRAMING_ERROR)
-                               flag = TTY_FRAME;
-               }       /* end of if (error) */
-               tty_insert_flip_char(tty, DataByte, flag);
-               if (status & RXSTATUS_OVERRUN) {
-                       /* Overrun is special, since it's
-                        * reported immediately, and doesn't
-                        * affect the current character
-                        */
-                       work += tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-               }
-       }
-
-       if ( debug_level >= DEBUG_LEVEL_ISR ) {
-               printk("%s(%d):rx=%d brk=%d parity=%d frame=%d overrun=%d\n",
-                       __FILE__,__LINE__,icount->rx,icount->brk,
-                       icount->parity,icount->frame,icount->overrun);
-       }
-                       
-       if(work)
-               tty_flip_buffer_push(tty);
-}
-
-/* mgsl_isr_misc()
- * 
- *     Service a miscellaneous interrupt source.
- *     
- * Arguments:          info            pointer to device extension (instance data)
- * Return Value:       None
- */
-static void mgsl_isr_misc( struct mgsl_struct *info )
-{
-       u16 status = usc_InReg( info, MISR );
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )   
-               printk("%s(%d):mgsl_isr_misc status=%04X\n",
-                       __FILE__,__LINE__,status);
-                       
-       if ((status & MISCSTATUS_RCC_UNDERRUN) &&
-           (info->params.mode == MGSL_MODE_HDLC)) {
-
-               /* turn off receiver and rx DMA */
-               usc_EnableReceiver(info,DISABLE_UNCONDITIONAL);
-               usc_DmaCmd(info, DmaCmd_ResetRxChannel);
-               usc_UnlatchRxstatusBits(info, RXSTATUS_ALL);
-               usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS);
-               usc_DisableInterrupts(info, RECEIVE_DATA + RECEIVE_STATUS);
-
-               /* schedule BH handler to restart receiver */
-               info->pending_bh |= BH_RECEIVE;
-               info->rx_rcc_underrun = true;
-       }
-
-       usc_ClearIrqPendingBits( info, MISC );
-       usc_UnlatchMiscstatusBits( info, status );
-
-}      /* end of mgsl_isr_misc() */
-
-/* mgsl_isr_null()
- *
- *     Services undefined interrupt vectors from the
- *     USC. (hence this function SHOULD never be called)
- * 
- * Arguments:          info            pointer to device extension (instance data)
- * Return Value:       None
- */
-static void mgsl_isr_null( struct mgsl_struct *info )
-{
-
-}      /* end of mgsl_isr_null() */
-
-/* mgsl_isr_receive_dma()
- * 
- *     Service a receive DMA channel interrupt.
- *     For this driver there are two sources of receive DMA interrupts
- *     as identified in the Receive DMA mode Register (RDMR):
- * 
- *     BIT3    EOA/EOL         End of List, all receive buffers in receive
- *                             buffer list have been filled (no more free buffers
- *                             available). The DMA controller has shut down.
- * 
- *     BIT2    EOB             End of Buffer. This interrupt occurs when a receive
- *                             DMA buffer is terminated in response to completion
- *                             of a good frame or a frame with errors. The status
- *                             of the frame is stored in the buffer entry in the
- *                             list of receive buffer entries.
- * 
- * Arguments:          info            pointer to device instance data
- * Return Value:       None
- */
-static void mgsl_isr_receive_dma( struct mgsl_struct *info )
-{
-       u16 status;
-       
-       /* clear interrupt pending and IUS bit for Rx DMA IRQ */
-       usc_OutDmaReg( info, CDIR, BIT9+BIT1 );
-
-       /* Read the receive DMA status to identify interrupt type. */
-       /* This also clears the status bits. */
-       status = usc_InDmaReg( info, RDMR );
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )   
-               printk("%s(%d):mgsl_isr_receive_dma(%s) status=%04X\n",
-                       __FILE__,__LINE__,info->device_name,status);
-                       
-       info->pending_bh |= BH_RECEIVE;
-       
-       if ( status & BIT3 ) {
-               info->rx_overflow = true;
-               info->icount.buf_overrun++;
-       }
-
-}      /* end of mgsl_isr_receive_dma() */
-
-/* mgsl_isr_transmit_dma()
- *
- *     This function services a transmit DMA channel interrupt.
- *
- *     For this driver there is one source of transmit DMA interrupts
- *     as identified in the Transmit DMA Mode Register (TDMR):
- *
- *             BIT2  EOB       End of Buffer. This interrupt occurs when a
- *                             transmit DMA buffer has been emptied.
- *
- *             The driver maintains enough transmit DMA buffers to hold at least
- *             one max frame size transmit frame. When operating in a buffered
- *             transmit mode, there may be enough transmit DMA buffers to hold at
- *             least two or more max frame size frames. On an EOB condition,
- *             determine if there are any queued transmit buffers and copy into
- *             transmit DMA buffers if we have room.
- *
- * Arguments:          info            pointer to device instance data
- * Return Value:       None
- */
-static void mgsl_isr_transmit_dma( struct mgsl_struct *info )
-{
-       u16 status;
-
-       /* clear interrupt pending and IUS bit for Tx DMA IRQ */
-       usc_OutDmaReg(info, CDIR, BIT8+BIT0 );
-
-       /* Read the transmit DMA status to identify interrupt type. */
-       /* This also clears the status bits. */
-
-       status = usc_InDmaReg( info, TDMR );
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk("%s(%d):mgsl_isr_transmit_dma(%s) status=%04X\n",
-                       __FILE__,__LINE__,info->device_name,status);
-
-       if ( status & BIT2 ) {
-               --info->tx_dma_buffers_used;
-
-               /* if there are transmit frames queued,
-                *  try to load the next one
-                */
-               if ( load_next_tx_holding_buffer(info) ) {
-                       /* if call returns non-zero value, we have
-                        * at least one free tx holding buffer
-                        */
-                       info->pending_bh |= BH_TRANSMIT;
-               }
-       }
-
-}      /* end of mgsl_isr_transmit_dma() */
-
-/* mgsl_interrupt()
- * 
- *     Interrupt service routine entry point.
- *     
- * Arguments:
- * 
- *     irq             interrupt number that caused interrupt
- *     dev_id          device ID supplied during interrupt registration
- *     
- * Return Value: None
- */
-static irqreturn_t mgsl_interrupt(int dummy, void *dev_id)
-{
-       struct mgsl_struct *info = dev_id;
-       u16 UscVector;
-       u16 DmaVector;
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )   
-               printk(KERN_DEBUG "%s(%d):mgsl_interrupt(%d)entry.\n",
-                       __FILE__, __LINE__, info->irq_level);
-
-       spin_lock(&info->irq_spinlock);
-
-       for(;;) {
-               /* Read the interrupt vectors from hardware. */
-               UscVector = usc_InReg(info, IVR) >> 9;
-               DmaVector = usc_InDmaReg(info, DIVR);
-               
-               if ( debug_level >= DEBUG_LEVEL_ISR )   
-                       printk("%s(%d):%s UscVector=%08X DmaVector=%08X\n",
-                               __FILE__,__LINE__,info->device_name,UscVector,DmaVector);
-                       
-               if ( !UscVector && !DmaVector )
-                       break;
-                       
-               /* Dispatch interrupt vector */
-               if ( UscVector )
-                       (*UscIsrTable[UscVector])(info);
-               else if ( (DmaVector&(BIT10|BIT9)) == BIT10)
-                       mgsl_isr_transmit_dma(info);
-               else
-                       mgsl_isr_receive_dma(info);
-
-               if ( info->isr_overflow ) {
-                       printk(KERN_ERR "%s(%d):%s isr overflow irq=%d\n",
-                               __FILE__, __LINE__, info->device_name, info->irq_level);
-                       usc_DisableMasterIrqBit(info);
-                       usc_DisableDmaInterrupts(info,DICR_MASTER);
-                       break;
-               }
-       }
-       
-       /* Request bottom half processing if there's something 
-        * for it to do and the bh is not already running
-        */
-
-       if ( info->pending_bh && !info->bh_running && !info->bh_requested ) {
-               if ( debug_level >= DEBUG_LEVEL_ISR )   
-                       printk("%s(%d):%s queueing bh task.\n",
-                               __FILE__,__LINE__,info->device_name);
-               schedule_work(&info->task);
-               info->bh_requested = true;
-       }
-
-       spin_unlock(&info->irq_spinlock);
-       
-       if ( debug_level >= DEBUG_LEVEL_ISR )   
-               printk(KERN_DEBUG "%s(%d):mgsl_interrupt(%d)exit.\n",
-                       __FILE__, __LINE__, info->irq_level);
-
-       return IRQ_HANDLED;
-}      /* end of mgsl_interrupt() */
-
-/* startup()
- * 
- *     Initialize and start device.
- *     
- * Arguments:          info    pointer to device instance data
- * Return Value:       0 if success, otherwise error code
- */
-static int startup(struct mgsl_struct * info)
-{
-       int retval = 0;
-       
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk("%s(%d):mgsl_startup(%s)\n",__FILE__,__LINE__,info->device_name);
-               
-       if (info->port.flags & ASYNC_INITIALIZED)
-               return 0;
-       
-       if (!info->xmit_buf) {
-               /* allocate a page of memory for a transmit buffer */
-               info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
-               if (!info->xmit_buf) {
-                       printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n",
-                               __FILE__,__LINE__,info->device_name);
-                       return -ENOMEM;
-               }
-       }
-
-       info->pending_bh = 0;
-       
-       memset(&info->icount, 0, sizeof(info->icount));
-
-       setup_timer(&info->tx_timer, mgsl_tx_timeout, (unsigned long)info);
-       
-       /* Allocate and claim adapter resources */
-       retval = mgsl_claim_resources(info);
-       
-       /* perform existence check and diagnostics */
-       if ( !retval )
-               retval = mgsl_adapter_test(info);
-               
-       if ( retval ) {
-               if (capable(CAP_SYS_ADMIN) && info->port.tty)
-                       set_bit(TTY_IO_ERROR, &info->port.tty->flags);
-               mgsl_release_resources(info);
-               return retval;
-       }
-
-       /* program hardware for current parameters */
-       mgsl_change_params(info);
-       
-       if (info->port.tty)
-               clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
-
-       info->port.flags |= ASYNC_INITIALIZED;
-       
-       return 0;
-       
-}      /* end of startup() */
-
-/* shutdown()
- *
- * Called by mgsl_close() and mgsl_hangup() to shutdown hardware
- *
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void shutdown(struct mgsl_struct * info)
-{
-       unsigned long flags;
-       
-       if (!(info->port.flags & ASYNC_INITIALIZED))
-               return;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_shutdown(%s)\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       /* clear status wait queue because status changes */
-       /* can't happen after shutting down the hardware */
-       wake_up_interruptible(&info->status_event_wait_q);
-       wake_up_interruptible(&info->event_wait_q);
-
-       del_timer_sync(&info->tx_timer);
-
-       if (info->xmit_buf) {
-               free_page((unsigned long) info->xmit_buf);
-               info->xmit_buf = NULL;
-       }
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       usc_DisableMasterIrqBit(info);
-       usc_stop_receiver(info);
-       usc_stop_transmitter(info);
-       usc_DisableInterrupts(info,RECEIVE_DATA + RECEIVE_STATUS +
-               TRANSMIT_DATA + TRANSMIT_STATUS + IO_PIN + MISC );
-       usc_DisableDmaInterrupts(info,DICR_MASTER + DICR_TRANSMIT + DICR_RECEIVE);
-       
-       /* Disable DMAEN (Port 7, Bit 14) */
-       /* This disconnects the DMA request signal from the ISA bus */
-       /* on the ISA adapter. This has no effect for the PCI adapter */
-       usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) | BIT14));
-       
-       /* Disable INTEN (Port 6, Bit12) */
-       /* This disconnects the IRQ request signal to the ISA bus */
-       /* on the ISA adapter. This has no effect for the PCI adapter */
-       usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) | BIT12));
-       
-       if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) {
-               info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
-               usc_set_serial_signals(info);
-       }
-       
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-       mgsl_release_resources(info);   
-       
-       if (info->port.tty)
-               set_bit(TTY_IO_ERROR, &info->port.tty->flags);
-
-       info->port.flags &= ~ASYNC_INITIALIZED;
-       
-}      /* end of shutdown() */
-
-static void mgsl_program_hw(struct mgsl_struct *info)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       
-       usc_stop_receiver(info);
-       usc_stop_transmitter(info);
-       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-       
-       if (info->params.mode == MGSL_MODE_HDLC ||
-           info->params.mode == MGSL_MODE_RAW ||
-           info->netcount)
-               usc_set_sync_mode(info);
-       else
-               usc_set_async_mode(info);
-               
-       usc_set_serial_signals(info);
-       
-       info->dcd_chkcount = 0;
-       info->cts_chkcount = 0;
-       info->ri_chkcount = 0;
-       info->dsr_chkcount = 0;
-
-       usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI);          
-       usc_EnableInterrupts(info, IO_PIN);
-       usc_get_serial_signals(info);
-               
-       if (info->netcount || info->port.tty->termios->c_cflag & CREAD)
-               usc_start_receiver(info);
-               
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-}
-
-/* Reconfigure adapter based on new parameters
- */
-static void mgsl_change_params(struct mgsl_struct *info)
-{
-       unsigned cflag;
-       int bits_per_char;
-
-       if (!info->port.tty || !info->port.tty->termios)
-               return;
-               
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_change_params(%s)\n",
-                        __FILE__,__LINE__, info->device_name );
-                        
-       cflag = info->port.tty->termios->c_cflag;
-
-       /* if B0 rate (hangup) specified then negate DTR and RTS */
-       /* otherwise assert DTR and RTS */
-       if (cflag & CBAUD)
-               info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
-       else
-               info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
-       
-       /* byte size and parity */
-       
-       switch (cflag & CSIZE) {
-             case CS5: info->params.data_bits = 5; break;
-             case CS6: info->params.data_bits = 6; break;
-             case CS7: info->params.data_bits = 7; break;
-             case CS8: info->params.data_bits = 8; break;
-             /* Never happens, but GCC is too dumb to figure it out */
-             default:  info->params.data_bits = 7; break;
-             }
-             
-       if (cflag & CSTOPB)
-               info->params.stop_bits = 2;
-       else
-               info->params.stop_bits = 1;
-
-       info->params.parity = ASYNC_PARITY_NONE;
-       if (cflag & PARENB) {
-               if (cflag & PARODD)
-                       info->params.parity = ASYNC_PARITY_ODD;
-               else
-                       info->params.parity = ASYNC_PARITY_EVEN;
-#ifdef CMSPAR
-               if (cflag & CMSPAR)
-                       info->params.parity = ASYNC_PARITY_SPACE;
-#endif
-       }
-
-       /* calculate number of jiffies to transmit a full
-        * FIFO (32 bytes) at specified data rate
-        */
-       bits_per_char = info->params.data_bits + 
-                       info->params.stop_bits + 1;
-
-       /* if port data rate is set to 460800 or less then
-        * allow tty settings to override, otherwise keep the
-        * current data rate.
-        */
-       if (info->params.data_rate <= 460800)
-               info->params.data_rate = tty_get_baud_rate(info->port.tty);
-       
-       if ( info->params.data_rate ) {
-               info->timeout = (32*HZ*bits_per_char) / 
-                               info->params.data_rate;
-       }
-       info->timeout += HZ/50;         /* Add .02 seconds of slop */
-
-       if (cflag & CRTSCTS)
-               info->port.flags |= ASYNC_CTS_FLOW;
-       else
-               info->port.flags &= ~ASYNC_CTS_FLOW;
-               
-       if (cflag & CLOCAL)
-               info->port.flags &= ~ASYNC_CHECK_CD;
-       else
-               info->port.flags |= ASYNC_CHECK_CD;
-
-       /* process tty input control flags */
-       
-       info->read_status_mask = RXSTATUS_OVERRUN;
-       if (I_INPCK(info->port.tty))
-               info->read_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR;
-       if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty))
-               info->read_status_mask |= RXSTATUS_BREAK_RECEIVED;
-       
-       if (I_IGNPAR(info->port.tty))
-               info->ignore_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR;
-       if (I_IGNBRK(info->port.tty)) {
-               info->ignore_status_mask |= RXSTATUS_BREAK_RECEIVED;
-               /* If ignoring parity and break indicators, ignore 
-                * overruns too.  (For real raw support).
-                */
-               if (I_IGNPAR(info->port.tty))
-                       info->ignore_status_mask |= RXSTATUS_OVERRUN;
-       }
-
-       mgsl_program_hw(info);
-
-}      /* end of mgsl_change_params() */
-
-/* mgsl_put_char()
- * 
- *     Add a character to the transmit buffer.
- *     
- * Arguments:          tty     pointer to tty information structure
- *                     ch      character to add to transmit buffer
- *             
- * Return Value:       None
- */
-static int mgsl_put_char(struct tty_struct *tty, unsigned char ch)
-{
-       struct mgsl_struct *info = tty->driver_data;
-       unsigned long flags;
-       int ret = 0;
-
-       if (debug_level >= DEBUG_LEVEL_INFO) {
-               printk(KERN_DEBUG "%s(%d):mgsl_put_char(%d) on %s\n",
-                       __FILE__, __LINE__, ch, info->device_name);
-       }               
-       
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_put_char"))
-               return 0;
-
-       if (!info->xmit_buf)
-               return 0;
-
-       spin_lock_irqsave(&info->irq_spinlock, flags);
-       
-       if ((info->params.mode == MGSL_MODE_ASYNC ) || !info->tx_active) {
-               if (info->xmit_cnt < SERIAL_XMIT_SIZE - 1) {
-                       info->xmit_buf[info->xmit_head++] = ch;
-                       info->xmit_head &= SERIAL_XMIT_SIZE-1;
-                       info->xmit_cnt++;
-                       ret = 1;
-               }
-       }
-       spin_unlock_irqrestore(&info->irq_spinlock, flags);
-       return ret;
-       
-}      /* end of mgsl_put_char() */
-
-/* mgsl_flush_chars()
- * 
- *     Enable transmitter so remaining characters in the
- *     transmit buffer are sent.
- *     
- * Arguments:          tty     pointer to tty information structure
- * Return Value:       None
- */
-static void mgsl_flush_chars(struct tty_struct *tty)
-{
-       struct mgsl_struct *info = tty->driver_data;
-       unsigned long flags;
-                               
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):mgsl_flush_chars() entry on %s xmit_cnt=%d\n",
-                       __FILE__,__LINE__,info->device_name,info->xmit_cnt);
-       
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_flush_chars"))
-               return;
-
-       if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
-           !info->xmit_buf)
-               return;
-
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):mgsl_flush_chars() entry on %s starting transmitter\n",
-                       __FILE__,__LINE__,info->device_name );
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       
-       if (!info->tx_active) {
-               if ( (info->params.mode == MGSL_MODE_HDLC ||
-                       info->params.mode == MGSL_MODE_RAW) && info->xmit_cnt ) {
-                       /* operating in synchronous (frame oriented) mode */
-                       /* copy data from circular xmit_buf to */
-                       /* transmit DMA buffer. */
-                       mgsl_load_tx_dma_buffer(info,
-                                info->xmit_buf,info->xmit_cnt);
-               }
-               usc_start_transmitter(info);
-       }
-       
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       
-}      /* end of mgsl_flush_chars() */
-
-/* mgsl_write()
- * 
- *     Send a block of data
- *     
- * Arguments:
- * 
- *     tty             pointer to tty information structure
- *     buf             pointer to buffer containing send data
- *     count           size of send data in bytes
- *     
- * Return Value:       number of characters written
- */
-static int mgsl_write(struct tty_struct * tty,
-                   const unsigned char *buf, int count)
-{
-       int     c, ret = 0;
-       struct mgsl_struct *info = tty->driver_data;
-       unsigned long flags;
-       
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):mgsl_write(%s) count=%d\n",
-                       __FILE__,__LINE__,info->device_name,count);
-       
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_write"))
-               goto cleanup;
-
-       if (!info->xmit_buf)
-               goto cleanup;
-
-       if ( info->params.mode == MGSL_MODE_HDLC ||
-                       info->params.mode == MGSL_MODE_RAW ) {
-               /* operating in synchronous (frame oriented) mode */
-               /* operating in synchronous (frame oriented) mode */
-               if (info->tx_active) {
-
-                       if ( info->params.mode == MGSL_MODE_HDLC ) {
-                               ret = 0;
-                               goto cleanup;
-                       }
-                       /* transmitter is actively sending data -
-                        * if we have multiple transmit dma and
-                        * holding buffers, attempt to queue this
-                        * frame for transmission at a later time.
-                        */
-                       if (info->tx_holding_count >= info->num_tx_holding_buffers ) {
-                               /* no tx holding buffers available */
-                               ret = 0;
-                               goto cleanup;
-                       }
-
-                       /* queue transmit frame request */
-                       ret = count;
-                       save_tx_buffer_request(info,buf,count);
-
-                       /* if we have sufficient tx dma buffers,
-                        * load the next buffered tx request
-                        */
-                       spin_lock_irqsave(&info->irq_spinlock,flags);
-                       load_next_tx_holding_buffer(info);
-                       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-                       goto cleanup;
-               }
-       
-               /* if operating in HDLC LoopMode and the adapter  */
-               /* has yet to be inserted into the loop, we can't */
-               /* transmit                                       */
-
-               if ( (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) &&
-                       !usc_loopmode_active(info) )
-               {
-                       ret = 0;
-                       goto cleanup;
-               }
-
-               if ( info->xmit_cnt ) {
-                       /* Send accumulated from send_char() calls */
-                       /* as frame and wait before accepting more data. */
-                       ret = 0;
-                       
-                       /* copy data from circular xmit_buf to */
-                       /* transmit DMA buffer. */
-                       mgsl_load_tx_dma_buffer(info,
-                               info->xmit_buf,info->xmit_cnt);
-                       if ( debug_level >= DEBUG_LEVEL_INFO )
-                               printk( "%s(%d):mgsl_write(%s) sync xmit_cnt flushing\n",
-                                       __FILE__,__LINE__,info->device_name);
-               } else {
-                       if ( debug_level >= DEBUG_LEVEL_INFO )
-                               printk( "%s(%d):mgsl_write(%s) sync transmit accepted\n",
-                                       __FILE__,__LINE__,info->device_name);
-                       ret = count;
-                       info->xmit_cnt = count;
-                       mgsl_load_tx_dma_buffer(info,buf,count);
-               }
-       } else {
-               while (1) {
-                       spin_lock_irqsave(&info->irq_spinlock,flags);
-                       c = min_t(int, count,
-                               min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-                                   SERIAL_XMIT_SIZE - info->xmit_head));
-                       if (c <= 0) {
-                               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-                               break;
-                       }
-                       memcpy(info->xmit_buf + info->xmit_head, buf, c);
-                       info->xmit_head = ((info->xmit_head + c) &
-                                          (SERIAL_XMIT_SIZE-1));
-                       info->xmit_cnt += c;
-                       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-                       buf += c;
-                       count -= c;
-                       ret += c;
-               }
-       }       
-       
-       if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
-               spin_lock_irqsave(&info->irq_spinlock,flags);
-               if (!info->tx_active)
-                       usc_start_transmitter(info);
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       }
-cleanup:       
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):mgsl_write(%s) returning=%d\n",
-                       __FILE__,__LINE__,info->device_name,ret);
-                       
-       return ret;
-       
-}      /* end of mgsl_write() */
-
-/* mgsl_write_room()
- *
- *     Return the count of free bytes in transmit buffer
- *     
- * Arguments:          tty     pointer to tty info structure
- * Return Value:       None
- */
-static int mgsl_write_room(struct tty_struct *tty)
-{
-       struct mgsl_struct *info = tty->driver_data;
-       int     ret;
-                               
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_write_room"))
-               return 0;
-       ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
-       if (ret < 0)
-               ret = 0;
-               
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_write_room(%s)=%d\n",
-                        __FILE__,__LINE__, info->device_name,ret );
-                        
-       if ( info->params.mode == MGSL_MODE_HDLC ||
-               info->params.mode == MGSL_MODE_RAW ) {
-               /* operating in synchronous (frame oriented) mode */
-               if ( info->tx_active )
-                       return 0;
-               else
-                       return HDLC_MAX_FRAME_SIZE;
-       }
-       
-       return ret;
-       
-}      /* end of mgsl_write_room() */
-
-/* mgsl_chars_in_buffer()
- *
- *     Return the count of bytes in transmit buffer
- *     
- * Arguments:          tty     pointer to tty info structure
- * Return Value:       None
- */
-static int mgsl_chars_in_buffer(struct tty_struct *tty)
-{
-       struct mgsl_struct *info = tty->driver_data;
-                        
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_chars_in_buffer(%s)\n",
-                        __FILE__,__LINE__, info->device_name );
-                        
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_chars_in_buffer"))
-               return 0;
-               
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_chars_in_buffer(%s)=%d\n",
-                        __FILE__,__LINE__, info->device_name,info->xmit_cnt );
-                        
-       if ( info->params.mode == MGSL_MODE_HDLC ||
-               info->params.mode == MGSL_MODE_RAW ) {
-               /* operating in synchronous (frame oriented) mode */
-               if ( info->tx_active )
-                       return info->max_frame_size;
-               else
-                       return 0;
-       }
-                        
-       return info->xmit_cnt;
-}      /* end of mgsl_chars_in_buffer() */
-
-/* mgsl_flush_buffer()
- *
- *     Discard all data in the send buffer
- *     
- * Arguments:          tty     pointer to tty info structure
- * Return Value:       None
- */
-static void mgsl_flush_buffer(struct tty_struct *tty)
-{
-       struct mgsl_struct *info = tty->driver_data;
-       unsigned long flags;
-       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_flush_buffer(%s) entry\n",
-                        __FILE__,__LINE__, info->device_name );
-       
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_flush_buffer"))
-               return;
-               
-       spin_lock_irqsave(&info->irq_spinlock,flags); 
-       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-       del_timer(&info->tx_timer);     
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       
-       tty_wakeup(tty);
-}
-
-/* mgsl_send_xchar()
- *
- *     Send a high-priority XON/XOFF character
- *     
- * Arguments:          tty     pointer to tty info structure
- *                     ch      character to send
- * Return Value:       None
- */
-static void mgsl_send_xchar(struct tty_struct *tty, char ch)
-{
-       struct mgsl_struct *info = tty->driver_data;
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_send_xchar(%s,%d)\n",
-                        __FILE__,__LINE__, info->device_name, ch );
-                        
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_send_xchar"))
-               return;
-
-       info->x_char = ch;
-       if (ch) {
-               /* Make sure transmit interrupts are on */
-               spin_lock_irqsave(&info->irq_spinlock,flags);
-               if (!info->tx_enabled)
-                       usc_start_transmitter(info);
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       }
-}      /* end of mgsl_send_xchar() */
-
-/* mgsl_throttle()
- * 
- *     Signal remote device to throttle send data (our receive data)
- *     
- * Arguments:          tty     pointer to tty info structure
- * Return Value:       None
- */
-static void mgsl_throttle(struct tty_struct * tty)
-{
-       struct mgsl_struct *info = tty->driver_data;
-       unsigned long flags;
-       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_throttle(%s) entry\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_throttle"))
-               return;
-       
-       if (I_IXOFF(tty))
-               mgsl_send_xchar(tty, STOP_CHAR(tty));
-       if (tty->termios->c_cflag & CRTSCTS) {
-               spin_lock_irqsave(&info->irq_spinlock,flags);
-               info->serial_signals &= ~SerialSignal_RTS;
-               usc_set_serial_signals(info);
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       }
-}      /* end of mgsl_throttle() */
-
-/* mgsl_unthrottle()
- * 
- *     Signal remote device to stop throttling send data (our receive data)
- *     
- * Arguments:          tty     pointer to tty info structure
- * Return Value:       None
- */
-static void mgsl_unthrottle(struct tty_struct * tty)
-{
-       struct mgsl_struct *info = tty->driver_data;
-       unsigned long flags;
-       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_unthrottle(%s) entry\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_unthrottle"))
-               return;
-       
-       if (I_IXOFF(tty)) {
-               if (info->x_char)
-                       info->x_char = 0;
-               else
-                       mgsl_send_xchar(tty, START_CHAR(tty));
-       }
-       
-       if (tty->termios->c_cflag & CRTSCTS) {
-               spin_lock_irqsave(&info->irq_spinlock,flags);
-               info->serial_signals |= SerialSignal_RTS;
-               usc_set_serial_signals(info);
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       }
-       
-}      /* end of mgsl_unthrottle() */
-
-/* mgsl_get_stats()
- * 
- *     get the current serial parameters information
- *
- * Arguments:  info            pointer to device instance data
- *             user_icount     pointer to buffer to hold returned stats
- *     
- * Return Value:       0 if success, otherwise error code
- */
-static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount __user *user_icount)
-{
-       int err;
-       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_get_params(%s)\n",
-                        __FILE__,__LINE__, info->device_name);
-                       
-       if (!user_icount) {
-               memset(&info->icount, 0, sizeof(info->icount));
-       } else {
-               mutex_lock(&info->port.mutex);
-               COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
-               mutex_unlock(&info->port.mutex);
-               if (err)
-                       return -EFAULT;
-       }
-       
-       return 0;
-       
-}      /* end of mgsl_get_stats() */
-
-/* mgsl_get_params()
- * 
- *     get the current serial parameters information
- *
- * Arguments:  info            pointer to device instance data
- *             user_params     pointer to buffer to hold returned params
- *     
- * Return Value:       0 if success, otherwise error code
- */
-static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS __user *user_params)
-{
-       int err;
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_get_params(%s)\n",
-                        __FILE__,__LINE__, info->device_name);
-                       
-       mutex_lock(&info->port.mutex);
-       COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
-       mutex_unlock(&info->port.mutex);
-       if (err) {
-               if ( debug_level >= DEBUG_LEVEL_INFO )
-                       printk( "%s(%d):mgsl_get_params(%s) user buffer copy failed\n",
-                               __FILE__,__LINE__,info->device_name);
-               return -EFAULT;
-       }
-       
-       return 0;
-       
-}      /* end of mgsl_get_params() */
-
-/* mgsl_set_params()
- * 
- *     set the serial parameters
- *     
- * Arguments:
- * 
- *     info            pointer to device instance data
- *     new_params      user buffer containing new serial params
- *
- * Return Value:       0 if success, otherwise error code
- */
-static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS __user *new_params)
-{
-       unsigned long flags;
-       MGSL_PARAMS tmp_params;
-       int err;
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_set_params %s\n", __FILE__,__LINE__,
-                       info->device_name );
-       COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS));
-       if (err) {
-               if ( debug_level >= DEBUG_LEVEL_INFO )
-                       printk( "%s(%d):mgsl_set_params(%s) user buffer copy failed\n",
-                               __FILE__,__LINE__,info->device_name);
-               return -EFAULT;
-       }
-       
-       mutex_lock(&info->port.mutex);
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       
-       mgsl_change_params(info);
-       mutex_unlock(&info->port.mutex);
-       
-       return 0;
-       
-}      /* end of mgsl_set_params() */
-
-/* mgsl_get_txidle()
- * 
- *     get the current transmit idle mode
- *
- * Arguments:  info            pointer to device instance data
- *             idle_mode       pointer to buffer to hold returned idle mode
- *     
- * Return Value:       0 if success, otherwise error code
- */
-static int mgsl_get_txidle(struct mgsl_struct * info, int __user *idle_mode)
-{
-       int err;
-       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_get_txidle(%s)=%d\n",
-                        __FILE__,__LINE__, info->device_name, info->idle_mode);
-                       
-       COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int));
-       if (err) {
-               if ( debug_level >= DEBUG_LEVEL_INFO )
-                       printk( "%s(%d):mgsl_get_txidle(%s) user buffer copy failed\n",
-                               __FILE__,__LINE__,info->device_name);
-               return -EFAULT;
-       }
-       
-       return 0;
-       
-}      /* end of mgsl_get_txidle() */
-
-/* mgsl_set_txidle()   service ioctl to set transmit idle mode
- *     
- * Arguments:          info            pointer to device instance data
- *                     idle_mode       new idle mode
- *
- * Return Value:       0 if success, otherwise error code
- */
-static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode)
-{
-       unsigned long flags;
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_set_txidle(%s,%d)\n", __FILE__,__LINE__,
-                       info->device_name, idle_mode );
-                       
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       info->idle_mode = idle_mode;
-       usc_set_txidle( info );
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       return 0;
-       
-}      /* end of mgsl_set_txidle() */
-
-/* mgsl_txenable()
- * 
- *     enable or disable the transmitter
- *     
- * Arguments:
- * 
- *     info            pointer to device instance data
- *     enable          1 = enable, 0 = disable
- *
- * Return Value:       0 if success, otherwise error code
- */
-static int mgsl_txenable(struct mgsl_struct * info, int enable)
-{
-       unsigned long flags;
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_txenable(%s,%d)\n", __FILE__,__LINE__,
-                       info->device_name, enable);
-                       
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       if ( enable ) {
-               if ( !info->tx_enabled ) {
-
-                       usc_start_transmitter(info);
-                       /*--------------------------------------------------
-                        * if HDLC/SDLC Loop mode, attempt to insert the
-                        * station in the 'loop' by setting CMR:13. Upon
-                        * receipt of the next GoAhead (RxAbort) sequence,
-                        * the OnLoop indicator (CCSR:7) should go active
-                        * to indicate that we are on the loop
-                        *--------------------------------------------------*/
-                       if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
-                               usc_loopmode_insert_request( info );
-               }
-       } else {
-               if ( info->tx_enabled )
-                       usc_stop_transmitter(info);
-       }
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       return 0;
-       
-}      /* end of mgsl_txenable() */
-
-/* mgsl_txabort()      abort send HDLC frame
- *     
- * Arguments:          info            pointer to device instance data
- * Return Value:       0 if success, otherwise error code
- */
-static int mgsl_txabort(struct mgsl_struct * info)
-{
-       unsigned long flags;
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_txabort(%s)\n", __FILE__,__LINE__,
-                       info->device_name);
-                       
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC )
-       {
-               if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
-                       usc_loopmode_cancel_transmit( info );
-               else
-                       usc_TCmd(info,TCmd_SendAbort);
-       }
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       return 0;
-       
-}      /* end of mgsl_txabort() */
-
-/* mgsl_rxenable()     enable or disable the receiver
- *     
- * Arguments:          info            pointer to device instance data
- *                     enable          1 = enable, 0 = disable
- * Return Value:       0 if success, otherwise error code
- */
-static int mgsl_rxenable(struct mgsl_struct * info, int enable)
-{
-       unsigned long flags;
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_rxenable(%s,%d)\n", __FILE__,__LINE__,
-                       info->device_name, enable);
-                       
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       if ( enable ) {
-               if ( !info->rx_enabled )
-                       usc_start_receiver(info);
-       } else {
-               if ( info->rx_enabled )
-                       usc_stop_receiver(info);
-       }
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       return 0;
-       
-}      /* end of mgsl_rxenable() */
-
-/* mgsl_wait_event()   wait for specified event to occur
- *     
- * Arguments:          info    pointer to device instance data
- *                     mask    pointer to bitmask of events to wait for
- * Return Value:       0       if successful and bit mask updated with
- *                             of events triggerred,
- *                     otherwise error code
- */
-static int mgsl_wait_event(struct mgsl_struct * info, int __user * mask_ptr)
-{
-       unsigned long flags;
-       int s;
-       int rc=0;
-       struct mgsl_icount cprev, cnow;
-       int events;
-       int mask;
-       struct  _input_signal_events oldsigs, newsigs;
-       DECLARE_WAITQUEUE(wait, current);
-
-       COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int));
-       if (rc) {
-               return  -EFAULT;
-       }
-                
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_wait_event(%s,%d)\n", __FILE__,__LINE__,
-                       info->device_name, mask);
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-
-       /* return immediately if state matches requested events */
-       usc_get_serial_signals(info);
-       s = info->serial_signals;
-       events = mask &
-               ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
-                 ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
-                 ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) +
-                 ((s & SerialSignal_RI)  ? MgslEvent_RiActive :MgslEvent_RiInactive) );
-       if (events) {
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-               goto exit;
-       }
-
-       /* save current irq counts */
-       cprev = info->icount;
-       oldsigs = info->input_signal_events;
-       
-       /* enable hunt and idle irqs if needed */
-       if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
-               u16 oldreg = usc_InReg(info,RICR);
-               u16 newreg = oldreg +
-                        (mask & MgslEvent_ExitHuntMode ? RXSTATUS_EXITED_HUNT:0) +
-                        (mask & MgslEvent_IdleReceived ? RXSTATUS_IDLE_RECEIVED:0);
-               if (oldreg != newreg)
-                       usc_OutReg(info, RICR, newreg);
-       }
-       
-       set_current_state(TASK_INTERRUPTIBLE);
-       add_wait_queue(&info->event_wait_q, &wait);
-       
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       
-
-       for(;;) {
-               schedule();
-               if (signal_pending(current)) {
-                       rc = -ERESTARTSYS;
-                       break;
-               }
-                       
-               /* get current irq counts */
-               spin_lock_irqsave(&info->irq_spinlock,flags);
-               cnow = info->icount;
-               newsigs = info->input_signal_events;
-               set_current_state(TASK_INTERRUPTIBLE);
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-               /* if no change, wait aborted for some reason */
-               if (newsigs.dsr_up   == oldsigs.dsr_up   &&
-                   newsigs.dsr_down == oldsigs.dsr_down &&
-                   newsigs.dcd_up   == oldsigs.dcd_up   &&
-                   newsigs.dcd_down == oldsigs.dcd_down &&
-                   newsigs.cts_up   == oldsigs.cts_up   &&
-                   newsigs.cts_down == oldsigs.cts_down &&
-                   newsigs.ri_up    == oldsigs.ri_up    &&
-                   newsigs.ri_down  == oldsigs.ri_down  &&
-                   cnow.exithunt    == cprev.exithunt   &&
-                   cnow.rxidle      == cprev.rxidle) {
-                       rc = -EIO;
-                       break;
-               }
-
-               events = mask &
-                       ( (newsigs.dsr_up   != oldsigs.dsr_up   ? MgslEvent_DsrActive:0)   +
-                       (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) +
-                       (newsigs.dcd_up   != oldsigs.dcd_up   ? MgslEvent_DcdActive:0)   +
-                       (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) +
-                       (newsigs.cts_up   != oldsigs.cts_up   ? MgslEvent_CtsActive:0)   +
-                       (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) +
-                       (newsigs.ri_up    != oldsigs.ri_up    ? MgslEvent_RiActive:0)    +
-                       (newsigs.ri_down  != oldsigs.ri_down  ? MgslEvent_RiInactive:0)  +
-                       (cnow.exithunt    != cprev.exithunt   ? MgslEvent_ExitHuntMode:0) +
-                         (cnow.rxidle      != cprev.rxidle     ? MgslEvent_IdleReceived:0) );
-               if (events)
-                       break;
-               
-               cprev = cnow;
-               oldsigs = newsigs;
-       }
-       
-       remove_wait_queue(&info->event_wait_q, &wait);
-       set_current_state(TASK_RUNNING);
-
-       if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
-               spin_lock_irqsave(&info->irq_spinlock,flags);
-               if (!waitqueue_active(&info->event_wait_q)) {
-                       /* disable enable exit hunt mode/idle rcvd IRQs */
-                       usc_OutReg(info, RICR, usc_InReg(info,RICR) &
-                               ~(RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED));
-               }
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       }
-exit:
-       if ( rc == 0 )
-               PUT_USER(rc, events, mask_ptr);
-               
-       return rc;
-       
-}      /* end of mgsl_wait_event() */
-
-static int modem_input_wait(struct mgsl_struct *info,int arg)
-{
-       unsigned long flags;
-       int rc;
-       struct mgsl_icount cprev, cnow;
-       DECLARE_WAITQUEUE(wait, current);
-
-       /* save current irq counts */
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       cprev = info->icount;
-       add_wait_queue(&info->status_event_wait_q, &wait);
-       set_current_state(TASK_INTERRUPTIBLE);
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-       for(;;) {
-               schedule();
-               if (signal_pending(current)) {
-                       rc = -ERESTARTSYS;
-                       break;
-               }
-
-               /* get new irq counts */
-               spin_lock_irqsave(&info->irq_spinlock,flags);
-               cnow = info->icount;
-               set_current_state(TASK_INTERRUPTIBLE);
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-               /* if no change, wait aborted for some reason */
-               if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
-                   cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
-                       rc = -EIO;
-                       break;
-               }
-
-               /* check for change in caller specified modem input */
-               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)) {
-                       rc = 0;
-                       break;
-               }
-
-               cprev = cnow;
-       }
-       remove_wait_queue(&info->status_event_wait_q, &wait);
-       set_current_state(TASK_RUNNING);
-       return rc;
-}
-
-/* return the state of the serial control and status signals
- */
-static int tiocmget(struct tty_struct *tty)
-{
-       struct mgsl_struct *info = tty->driver_data;
-       unsigned int result;
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       usc_get_serial_signals(info);
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-       result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) +
-               ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) +
-               ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) +
-               ((info->serial_signals & SerialSignal_RI)  ? TIOCM_RNG:0) +
-               ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) +
-               ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0);
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s tiocmget() value=%08X\n",
-                        __FILE__,__LINE__, info->device_name, result );
-       return result;
-}
-
-/* set modem control signals (DTR/RTS)
- */
-static int tiocmset(struct tty_struct *tty,
-                                   unsigned int set, unsigned int clear)
-{
-       struct mgsl_struct *info = tty->driver_data;
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s tiocmset(%x,%x)\n",
-                       __FILE__,__LINE__,info->device_name, set, clear);
-
-       if (set & TIOCM_RTS)
-               info->serial_signals |= SerialSignal_RTS;
-       if (set & TIOCM_DTR)
-               info->serial_signals |= SerialSignal_DTR;
-       if (clear & TIOCM_RTS)
-               info->serial_signals &= ~SerialSignal_RTS;
-       if (clear & TIOCM_DTR)
-               info->serial_signals &= ~SerialSignal_DTR;
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       usc_set_serial_signals(info);
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-       return 0;
-}
-
-/* mgsl_break()                Set or clear transmit break condition
- *
- * Arguments:          tty             pointer to tty instance data
- *                     break_state     -1=set break condition, 0=clear
- * Return Value:       error code
- */
-static int mgsl_break(struct tty_struct *tty, int break_state)
-{
-       struct mgsl_struct * info = tty->driver_data;
-       unsigned long flags;
-       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_break(%s,%d)\n",
-                        __FILE__,__LINE__, info->device_name, break_state);
-                        
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_break"))
-               return -EINVAL;
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       if (break_state == -1)
-               usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) | BIT7));
-       else 
-               usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) & ~BIT7));
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       return 0;
-       
-}      /* end of mgsl_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.
- */
-static int msgl_get_icount(struct tty_struct *tty,
-                               struct serial_icounter_struct *icount)
-
-{
-       struct mgsl_struct * info = tty->driver_data;
-       struct mgsl_icount cnow;        /* kernel counter temps */
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       cnow = info->icount;
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-       icount->cts = cnow.cts;
-       icount->dsr = cnow.dsr;
-       icount->rng = cnow.rng;
-       icount->dcd = cnow.dcd;
-       icount->rx = cnow.rx;
-       icount->tx = cnow.tx;
-       icount->frame = cnow.frame;
-       icount->overrun = cnow.overrun;
-       icount->parity = cnow.parity;
-       icount->brk = cnow.brk;
-       icount->buf_overrun = cnow.buf_overrun;
-       return 0;
-}
-
-/* mgsl_ioctl()        Service an IOCTL request
- *     
- * Arguments:
- * 
- *     tty     pointer to tty instance data
- *     cmd     IOCTL command code
- *     arg     command argument/context
- *     
- * Return Value:       0 if success, otherwise error code
- */
-static int mgsl_ioctl(struct tty_struct *tty,
-                   unsigned int cmd, unsigned long arg)
-{
-       struct mgsl_struct * info = tty->driver_data;
-       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__,
-                       info->device_name, cmd );
-       
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_ioctl"))
-               return -ENODEV;
-
-       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-           (cmd != TIOCMIWAIT)) {
-               if (tty->flags & (1 << TTY_IO_ERROR))
-                   return -EIO;
-       }
-
-       return mgsl_ioctl_common(info, cmd, arg);
-}
-
-static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg)
-{
-       void __user *argp = (void __user *)arg;
-       
-       switch (cmd) {
-               case MGSL_IOCGPARAMS:
-                       return mgsl_get_params(info, argp);
-               case MGSL_IOCSPARAMS:
-                       return mgsl_set_params(info, argp);
-               case MGSL_IOCGTXIDLE:
-                       return mgsl_get_txidle(info, argp);
-               case MGSL_IOCSTXIDLE:
-                       return mgsl_set_txidle(info,(int)arg);
-               case MGSL_IOCTXENABLE:
-                       return mgsl_txenable(info,(int)arg);
-               case MGSL_IOCRXENABLE:
-                       return mgsl_rxenable(info,(int)arg);
-               case MGSL_IOCTXABORT:
-                       return mgsl_txabort(info);
-               case MGSL_IOCGSTATS:
-                       return mgsl_get_stats(info, argp);
-               case MGSL_IOCWAITEVENT:
-                       return mgsl_wait_event(info, argp);
-               case MGSL_IOCLOOPTXDONE:
-                       return mgsl_loopmode_send_done(info);
-               /* Wait for modem input (DCD,RI,DSR,CTS) change
-                * as specified by mask in arg (TIOCM_RNG/DSR/CD/CTS)
-                */
-               case TIOCMIWAIT:
-                       return modem_input_wait(info,(int)arg);
-
-               default:
-                       return -ENOIOCTLCMD;
-       }
-       return 0;
-}
-
-/* mgsl_set_termios()
- * 
- *     Set new termios settings
- *     
- * Arguments:
- * 
- *     tty             pointer to tty structure
- *     termios         pointer to buffer to hold returned old termios
- *     
- * Return Value:               None
- */
-static void mgsl_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
-{
-       struct mgsl_struct *info = tty->driver_data;
-       unsigned long flags;
-       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_set_termios %s\n", __FILE__,__LINE__,
-                       tty->driver->name );
-       
-       mgsl_change_params(info);
-
-       /* Handle transition to B0 status */
-       if (old_termios->c_cflag & CBAUD &&
-           !(tty->termios->c_cflag & CBAUD)) {
-               info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
-               spin_lock_irqsave(&info->irq_spinlock,flags);
-               usc_set_serial_signals(info);
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       }
-       
-       /* Handle transition away from B0 status */
-       if (!(old_termios->c_cflag & CBAUD) &&
-           tty->termios->c_cflag & CBAUD) {
-               info->serial_signals |= SerialSignal_DTR;
-               if (!(tty->termios->c_cflag & CRTSCTS) || 
-                   !test_bit(TTY_THROTTLED, &tty->flags)) {
-                       info->serial_signals |= SerialSignal_RTS;
-               }
-               spin_lock_irqsave(&info->irq_spinlock,flags);
-               usc_set_serial_signals(info);
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       }
-       
-       /* Handle turning off CRTSCTS */
-       if (old_termios->c_cflag & CRTSCTS &&
-           !(tty->termios->c_cflag & CRTSCTS)) {
-               tty->hw_stopped = 0;
-               mgsl_start(tty);
-       }
-
-}      /* end of mgsl_set_termios() */
-
-/* mgsl_close()
- * 
- *     Called when port is closed. Wait for remaining data to be
- *     sent. Disable port and free resources.
- *     
- * Arguments:
- * 
- *     tty     pointer to open tty structure
- *     filp    pointer to open file object
- *     
- * Return Value:       None
- */
-static void mgsl_close(struct tty_struct *tty, struct file * filp)
-{
-       struct mgsl_struct * info = tty->driver_data;
-
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_close"))
-               return;
-       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_close(%s) entry, count=%d\n",
-                        __FILE__,__LINE__, info->device_name, info->port.count);
-
-       if (tty_port_close_start(&info->port, tty, filp) == 0)                   
-               goto cleanup;
-
-       mutex_lock(&info->port.mutex);
-       if (info->port.flags & ASYNC_INITIALIZED)
-               mgsl_wait_until_sent(tty, info->timeout);
-       mgsl_flush_buffer(tty);
-       tty_ldisc_flush(tty);
-       shutdown(info);
-       mutex_unlock(&info->port.mutex);
-
-       tty_port_close_end(&info->port, tty);   
-       info->port.tty = NULL;
-cleanup:                       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_close(%s) exit, count=%d\n", __FILE__,__LINE__,
-                       tty->driver->name, info->port.count);
-                       
-}      /* end of mgsl_close() */
-
-/* mgsl_wait_until_sent()
- *
- *     Wait until the transmitter is empty.
- *
- * Arguments:
- *
- *     tty             pointer to tty info structure
- *     timeout         time to wait for send completion
- *
- * Return Value:       None
- */
-static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout)
-{
-       struct mgsl_struct * info = tty->driver_data;
-       unsigned long orig_jiffies, char_time;
-
-       if (!info )
-               return;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_wait_until_sent(%s) entry\n",
-                        __FILE__,__LINE__, info->device_name );
-      
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_wait_until_sent"))
-               return;
-
-       if (!(info->port.flags & ASYNC_INITIALIZED))
-               goto exit;
-        
-       orig_jiffies = jiffies;
-      
-       /* Set check interval to 1/5 of estimated time to
-        * send a character, and make it at least 1. The check
-        * interval should also be less than the timeout.
-        * Note: use tight timings here to satisfy the NIST-PCTS.
-        */ 
-
-       if ( info->params.data_rate ) {
-               char_time = info->timeout/(32 * 5);
-               if (!char_time)
-                       char_time++;
-       } else
-               char_time = 1;
-               
-       if (timeout)
-               char_time = min_t(unsigned long, char_time, timeout);
-               
-       if ( info->params.mode == MGSL_MODE_HDLC ||
-               info->params.mode == MGSL_MODE_RAW ) {
-               while (info->tx_active) {
-                       msleep_interruptible(jiffies_to_msecs(char_time));
-                       if (signal_pending(current))
-                               break;
-                       if (timeout && time_after(jiffies, orig_jiffies + timeout))
-                               break;
-               }
-       } else {
-               while (!(usc_InReg(info,TCSR) & TXSTATUS_ALL_SENT) &&
-                       info->tx_enabled) {
-                       msleep_interruptible(jiffies_to_msecs(char_time));
-                       if (signal_pending(current))
-                               break;
-                       if (timeout && time_after(jiffies, orig_jiffies + timeout))
-                               break;
-               }
-       }
-      
-exit:
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_wait_until_sent(%s) exit\n",
-                        __FILE__,__LINE__, info->device_name );
-                        
-}      /* end of mgsl_wait_until_sent() */
-
-/* mgsl_hangup()
- *
- *     Called by tty_hangup() when a hangup is signaled.
- *     This is the same as to closing all open files for the port.
- *
- * Arguments:          tty     pointer to associated tty object
- * Return Value:       None
- */
-static void mgsl_hangup(struct tty_struct *tty)
-{
-       struct mgsl_struct * info = tty->driver_data;
-       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_hangup(%s)\n",
-                        __FILE__,__LINE__, info->device_name );
-                        
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_hangup"))
-               return;
-
-       mgsl_flush_buffer(tty);
-       shutdown(info);
-       
-       info->port.count = 0;   
-       info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
-       info->port.tty = NULL;
-
-       wake_up_interruptible(&info->port.open_wait);
-       
-}      /* end of mgsl_hangup() */
-
-/*
- * carrier_raised()
- *
- *     Return true if carrier is raised
- */
-
-static int carrier_raised(struct tty_port *port)
-{
-       unsigned long flags;
-       struct mgsl_struct *info = container_of(port, struct mgsl_struct, port);
-       
-       spin_lock_irqsave(&info->irq_spinlock, flags);
-       usc_get_serial_signals(info);
-       spin_unlock_irqrestore(&info->irq_spinlock, flags);
-       return (info->serial_signals & SerialSignal_DCD) ? 1 : 0;
-}
-
-static void dtr_rts(struct tty_port *port, int on)
-{
-       struct mgsl_struct *info = container_of(port, struct mgsl_struct, port);
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       if (on)
-               info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
-       else
-               info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
-       usc_set_serial_signals(info);
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-}
-
-
-/* block_til_ready()
- * 
- *     Block the current process until the specified port
- *     is ready to be opened.
- *     
- * Arguments:
- * 
- *     tty             pointer to tty info structure
- *     filp            pointer to open file object
- *     info            pointer to device instance data
- *     
- * Return Value:       0 if success, otherwise error code
- */
-static int block_til_ready(struct tty_struct *tty, struct file * filp,
-                          struct mgsl_struct *info)
-{
-       DECLARE_WAITQUEUE(wait, current);
-       int             retval;
-       bool            do_clocal = false;
-       bool            extra_count = false;
-       unsigned long   flags;
-       int             dcd;
-       struct tty_port *port = &info->port;
-       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):block_til_ready on %s\n",
-                        __FILE__,__LINE__, tty->driver->name );
-
-       if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){
-               /* nonblock mode is set or port is not enabled */
-               port->flags |= ASYNC_NORMAL_ACTIVE;
-               return 0;
-       }
-
-       if (tty->termios->c_cflag & CLOCAL)
-               do_clocal = true;
-
-       /* Wait for carrier detect and the line to become
-        * free (i.e., not in use by the callout).  While we are in
-        * this loop, port->count is dropped by one, so that
-        * mgsl_close() knows when to free things.  We restore it upon
-        * exit, either normal or abnormal.
-        */
-        
-       retval = 0;
-       add_wait_queue(&port->open_wait, &wait);
-       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):block_til_ready before block on %s count=%d\n",
-                        __FILE__,__LINE__, tty->driver->name, port->count );
-
-       spin_lock_irqsave(&info->irq_spinlock, flags);
-       if (!tty_hung_up_p(filp)) {
-               extra_count = true;
-               port->count--;
-       }
-       spin_unlock_irqrestore(&info->irq_spinlock, flags);
-       port->blocked_open++;
-       
-       while (1) {
-               if (tty->termios->c_cflag & CBAUD)
-                       tty_port_raise_dtr_rts(port);
-               
-               set_current_state(TASK_INTERRUPTIBLE);
-               
-               if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){
-                       retval = (port->flags & ASYNC_HUP_NOTIFY) ?
-                                       -EAGAIN : -ERESTARTSYS;
-                       break;
-               }
-               
-               dcd = tty_port_carrier_raised(&info->port);
-               
-               if (!(port->flags & ASYNC_CLOSING) && (do_clocal || dcd))
-                       break;
-                       
-               if (signal_pending(current)) {
-                       retval = -ERESTARTSYS;
-                       break;
-               }
-               
-               if (debug_level >= DEBUG_LEVEL_INFO)
-                       printk("%s(%d):block_til_ready blocking on %s count=%d\n",
-                                __FILE__,__LINE__, tty->driver->name, port->count );
-                                
-               tty_unlock();
-               schedule();
-               tty_lock();
-       }
-       
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&port->open_wait, &wait);
-       
-       /* FIXME: Racy on hangup during close wait */
-       if (extra_count)
-               port->count++;
-       port->blocked_open--;
-       
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):block_til_ready after blocking on %s count=%d\n",
-                        __FILE__,__LINE__, tty->driver->name, port->count );
-                        
-       if (!retval)
-               port->flags |= ASYNC_NORMAL_ACTIVE;
-               
-       return retval;
-       
-}      /* end of block_til_ready() */
-
-/* mgsl_open()
- *
- *     Called when a port is opened.  Init and enable port.
- *     Perform serial-specific initialization for the tty structure.
- *
- * Arguments:          tty     pointer to tty info structure
- *                     filp    associated file pointer
- *
- * Return Value:       0 if success, otherwise error code
- */
-static int mgsl_open(struct tty_struct *tty, struct file * filp)
-{
-       struct mgsl_struct      *info;
-       int                     retval, line;
-       unsigned long flags;
-
-       /* verify range of specified line number */     
-       line = tty->index;
-       if ((line < 0) || (line >= mgsl_device_count)) {
-               printk("%s(%d):mgsl_open with invalid line #%d.\n",
-                       __FILE__,__LINE__,line);
-               return -ENODEV;
-       }
-
-       /* find the info structure for the specified line */
-       info = mgsl_device_list;
-       while(info && info->line != line)
-               info = info->next_device;
-       if (mgsl_paranoia_check(info, tty->name, "mgsl_open"))
-               return -ENODEV;
-       
-       tty->driver_data = info;
-       info->port.tty = tty;
-               
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_open(%s), old ref count = %d\n",
-                        __FILE__,__LINE__,tty->driver->name, info->port.count);
-
-       /* If port is closing, signal caller to try again */
-       if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){
-               if (info->port.flags & ASYNC_CLOSING)
-                       interruptible_sleep_on(&info->port.close_wait);
-               retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ?
-                       -EAGAIN : -ERESTARTSYS);
-               goto cleanup;
-       }
-       
-       info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-
-       spin_lock_irqsave(&info->netlock, flags);
-       if (info->netcount) {
-               retval = -EBUSY;
-               spin_unlock_irqrestore(&info->netlock, flags);
-               goto cleanup;
-       }
-       info->port.count++;
-       spin_unlock_irqrestore(&info->netlock, flags);
-
-       if (info->port.count == 1) {
-               /* 1st open on this device, init hardware */
-               retval = startup(info);
-               if (retval < 0)
-                       goto cleanup;
-       }
-
-       retval = block_til_ready(tty, filp, info);
-       if (retval) {
-               if (debug_level >= DEBUG_LEVEL_INFO)
-                       printk("%s(%d):block_til_ready(%s) returned %d\n",
-                                __FILE__,__LINE__, info->device_name, retval);
-               goto cleanup;
-       }
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):mgsl_open(%s) success\n",
-                        __FILE__,__LINE__, info->device_name);
-       retval = 0;
-       
-cleanup:                       
-       if (retval) {
-               if (tty->count == 1)
-                       info->port.tty = NULL; /* tty layer will release tty struct */
-               if(info->port.count)
-                       info->port.count--;
-       }
-       
-       return retval;
-       
-}      /* end of mgsl_open() */
-
-/*
- * /proc fs routines....
- */
-
-static inline void line_info(struct seq_file *m, struct mgsl_struct *info)
-{
-       char    stat_buf[30];
-       unsigned long flags;
-
-       if (info->bus_type == MGSL_BUS_TYPE_PCI) {
-               seq_printf(m, "%s:PCI io:%04X irq:%d mem:%08X lcr:%08X",
-                       info->device_name, info->io_base, info->irq_level,
-                       info->phys_memory_base, info->phys_lcr_base);
-       } else {
-               seq_printf(m, "%s:(E)ISA io:%04X irq:%d dma:%d",
-                       info->device_name, info->io_base, 
-                       info->irq_level, info->dma_level);
-       }
-
-       /* output current serial signal states */
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       usc_get_serial_signals(info);
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       
-       stat_buf[0] = 0;
-       stat_buf[1] = 0;
-       if (info->serial_signals & SerialSignal_RTS)
-               strcat(stat_buf, "|RTS");
-       if (info->serial_signals & SerialSignal_CTS)
-               strcat(stat_buf, "|CTS");
-       if (info->serial_signals & SerialSignal_DTR)
-               strcat(stat_buf, "|DTR");
-       if (info->serial_signals & SerialSignal_DSR)
-               strcat(stat_buf, "|DSR");
-       if (info->serial_signals & SerialSignal_DCD)
-               strcat(stat_buf, "|CD");
-       if (info->serial_signals & SerialSignal_RI)
-               strcat(stat_buf, "|RI");
-
-       if (info->params.mode == MGSL_MODE_HDLC ||
-           info->params.mode == MGSL_MODE_RAW ) {
-               seq_printf(m, " HDLC txok:%d rxok:%d",
-                             info->icount.txok, info->icount.rxok);
-               if (info->icount.txunder)
-                       seq_printf(m, " txunder:%d", info->icount.txunder);
-               if (info->icount.txabort)
-                       seq_printf(m, " txabort:%d", info->icount.txabort);
-               if (info->icount.rxshort)
-                       seq_printf(m, " rxshort:%d", info->icount.rxshort);
-               if (info->icount.rxlong)
-                       seq_printf(m, " rxlong:%d", info->icount.rxlong);
-               if (info->icount.rxover)
-                       seq_printf(m, " rxover:%d", info->icount.rxover);
-               if (info->icount.rxcrc)
-                       seq_printf(m, " rxcrc:%d", info->icount.rxcrc);
-       } else {
-               seq_printf(m, " ASYNC tx:%d rx:%d",
-                             info->icount.tx, info->icount.rx);
-               if (info->icount.frame)
-                       seq_printf(m, " fe:%d", info->icount.frame);
-               if (info->icount.parity)
-                       seq_printf(m, " pe:%d", info->icount.parity);
-               if (info->icount.brk)
-                       seq_printf(m, " brk:%d", info->icount.brk);
-               if (info->icount.overrun)
-                       seq_printf(m, " oe:%d", info->icount.overrun);
-       }
-       
-       /* Append serial signal status to end */
-       seq_printf(m, " %s\n", stat_buf+1);
-       
-       seq_printf(m, "txactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
-        info->tx_active,info->bh_requested,info->bh_running,
-        info->pending_bh);
-        
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       {       
-       u16 Tcsr = usc_InReg( info, TCSR );
-       u16 Tdmr = usc_InDmaReg( info, TDMR );
-       u16 Ticr = usc_InReg( info, TICR );
-       u16 Rscr = usc_InReg( info, RCSR );
-       u16 Rdmr = usc_InDmaReg( info, RDMR );
-       u16 Ricr = usc_InReg( info, RICR );
-       u16 Icr = usc_InReg( info, ICR );
-       u16 Dccr = usc_InReg( info, DCCR );
-       u16 Tmr = usc_InReg( info, TMR );
-       u16 Tccr = usc_InReg( info, TCCR );
-       u16 Ccar = inw( info->io_base + CCAR );
-       seq_printf(m, "tcsr=%04X tdmr=%04X ticr=%04X rcsr=%04X rdmr=%04X\n"
-                        "ricr=%04X icr =%04X dccr=%04X tmr=%04X tccr=%04X ccar=%04X\n",
-                       Tcsr,Tdmr,Ticr,Rscr,Rdmr,Ricr,Icr,Dccr,Tmr,Tccr,Ccar );
-       }
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-}
-
-/* Called to print information about devices */
-static int mgsl_proc_show(struct seq_file *m, void *v)
-{
-       struct mgsl_struct *info;
-       
-       seq_printf(m, "synclink driver:%s\n", driver_version);
-       
-       info = mgsl_device_list;
-       while( info ) {
-               line_info(m, info);
-               info = info->next_device;
-       }
-       return 0;
-}
-
-static int mgsl_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, mgsl_proc_show, NULL);
-}
-
-static const struct file_operations mgsl_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = mgsl_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-/* mgsl_allocate_dma_buffers()
- * 
- *     Allocate and format DMA buffers (ISA adapter)
- *     or format shared memory buffers (PCI adapter).
- * 
- * Arguments:          info    pointer to device instance data
- * Return Value:       0 if success, otherwise error
- */
-static int mgsl_allocate_dma_buffers(struct mgsl_struct *info)
-{
-       unsigned short BuffersPerFrame;
-
-       info->last_mem_alloc = 0;
-
-       /* Calculate the number of DMA buffers necessary to hold the */
-       /* largest allowable frame size. Note: If the max frame size is */
-       /* not an even multiple of the DMA buffer size then we need to */
-       /* round the buffer count per frame up one. */
-
-       BuffersPerFrame = (unsigned short)(info->max_frame_size/DMABUFFERSIZE);
-       if ( info->max_frame_size % DMABUFFERSIZE )
-               BuffersPerFrame++;
-
-       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
-               /*
-                * The PCI adapter has 256KBytes of shared memory to use.
-                * This is 64 PAGE_SIZE buffers.
-                *
-                * The first page is used for padding at this time so the
-                * buffer list does not begin at offset 0 of the PCI
-                * adapter's shared memory.
-                *
-                * The 2nd page is used for the buffer list. A 4K buffer
-                * list can hold 128 DMA_BUFFER structures at 32 bytes
-                * each.
-                *
-                * This leaves 62 4K pages.
-                *
-                * The next N pages are used for transmit frame(s). We
-                * reserve enough 4K page blocks to hold the required
-                * number of transmit dma buffers (num_tx_dma_buffers),
-                * each of MaxFrameSize size.
-                *
-                * Of the remaining pages (62-N), determine how many can
-                * be used to receive full MaxFrameSize inbound frames
-                */
-               info->tx_buffer_count = info->num_tx_dma_buffers * BuffersPerFrame;
-               info->rx_buffer_count = 62 - info->tx_buffer_count;
-       } else {
-               /* Calculate the number of PAGE_SIZE buffers needed for */
-               /* receive and transmit DMA buffers. */
-
-
-               /* Calculate the number of DMA buffers necessary to */
-               /* hold 7 max size receive frames and one max size transmit frame. */
-               /* The receive buffer count is bumped by one so we avoid an */
-               /* End of List condition if all receive buffers are used when */
-               /* using linked list DMA buffers. */
-
-               info->tx_buffer_count = info->num_tx_dma_buffers * BuffersPerFrame;
-               info->rx_buffer_count = (BuffersPerFrame * MAXRXFRAMES) + 6;
-               
-               /* 
-                * limit total TxBuffers & RxBuffers to 62 4K total 
-                * (ala PCI Allocation) 
-                */
-               
-               if ( (info->tx_buffer_count + info->rx_buffer_count) > 62 )
-                       info->rx_buffer_count = 62 - info->tx_buffer_count;
-
-       }
-
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk("%s(%d):Allocating %d TX and %d RX DMA buffers.\n",
-                       __FILE__,__LINE__, info->tx_buffer_count,info->rx_buffer_count);
-       
-       if ( mgsl_alloc_buffer_list_memory( info ) < 0 ||
-                 mgsl_alloc_frame_memory(info, info->rx_buffer_list, info->rx_buffer_count) < 0 || 
-                 mgsl_alloc_frame_memory(info, info->tx_buffer_list, info->tx_buffer_count) < 0 || 
-                 mgsl_alloc_intermediate_rxbuffer_memory(info) < 0  ||
-                 mgsl_alloc_intermediate_txbuffer_memory(info) < 0 ) {
-               printk("%s(%d):Can't allocate DMA buffer memory\n",__FILE__,__LINE__);
-               return -ENOMEM;
-       }
-       
-       mgsl_reset_rx_dma_buffers( info );
-       mgsl_reset_tx_dma_buffers( info );
-
-       return 0;
-
-}      /* end of mgsl_allocate_dma_buffers() */
-
-/*
- * mgsl_alloc_buffer_list_memory()
- * 
- * Allocate a common DMA buffer for use as the
- * receive and transmit buffer lists.
- * 
- * A buffer list is a set of buffer entries where each entry contains
- * a pointer to an actual buffer and a pointer to the next buffer entry
- * (plus some other info about the buffer).
- * 
- * The buffer entries for a list are built to form a circular list so
- * that when the entire list has been traversed you start back at the
- * beginning.
- * 
- * This function allocates memory for just the buffer entries.
- * The links (pointer to next entry) are filled in with the physical
- * address of the next entry so the adapter can navigate the list
- * using bus master DMA. The pointers to the actual buffers are filled
- * out later when the actual buffers are allocated.
- * 
- * Arguments:          info    pointer to device instance data
- * Return Value:       0 if success, otherwise error
- */
-static int mgsl_alloc_buffer_list_memory( struct mgsl_struct *info )
-{
-       unsigned int i;
-
-       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
-               /* PCI adapter uses shared memory. */
-               info->buffer_list = info->memory_base + info->last_mem_alloc;
-               info->buffer_list_phys = info->last_mem_alloc;
-               info->last_mem_alloc += BUFFERLISTSIZE;
-       } else {
-               /* ISA adapter uses system memory. */
-               /* The buffer lists are allocated as a common buffer that both */
-               /* the processor and adapter can access. This allows the driver to */
-               /* inspect portions of the buffer while other portions are being */
-               /* updated by the adapter using Bus Master DMA. */
-
-               info->buffer_list = dma_alloc_coherent(NULL, BUFFERLISTSIZE, &info->buffer_list_dma_addr, GFP_KERNEL);
-               if (info->buffer_list == NULL)
-                       return -ENOMEM;
-               info->buffer_list_phys = (u32)(info->buffer_list_dma_addr);
-       }
-
-       /* We got the memory for the buffer entry lists. */
-       /* Initialize the memory block to all zeros. */
-       memset( info->buffer_list, 0, BUFFERLISTSIZE );
-
-       /* Save virtual address pointers to the receive and */
-       /* transmit buffer lists. (Receive 1st). These pointers will */
-       /* be used by the processor to access the lists. */
-       info->rx_buffer_list = (DMABUFFERENTRY *)info->buffer_list;
-       info->tx_buffer_list = (DMABUFFERENTRY *)info->buffer_list;
-       info->tx_buffer_list += info->rx_buffer_count;
-
-       /*
-        * Build the links for the buffer entry lists such that
-        * two circular lists are built. (Transmit and Receive).
-        *
-        * Note: the links are physical addresses
-        * which are read by the adapter to determine the next
-        * buffer entry to use.
-        */
-
-       for ( i = 0; i < info->rx_buffer_count; i++ ) {
-               /* calculate and store physical address of this buffer entry */
-               info->rx_buffer_list[i].phys_entry =
-                       info->buffer_list_phys + (i * sizeof(DMABUFFERENTRY));
-
-               /* calculate and store physical address of */
-               /* next entry in cirular list of entries */
-
-               info->rx_buffer_list[i].link = info->buffer_list_phys;
-
-               if ( i < info->rx_buffer_count - 1 )
-                       info->rx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY);
-       }
-
-       for ( i = 0; i < info->tx_buffer_count; i++ ) {
-               /* calculate and store physical address of this buffer entry */
-               info->tx_buffer_list[i].phys_entry = info->buffer_list_phys +
-                       ((info->rx_buffer_count + i) * sizeof(DMABUFFERENTRY));
-
-               /* calculate and store physical address of */
-               /* next entry in cirular list of entries */
-
-               info->tx_buffer_list[i].link = info->buffer_list_phys +
-                       info->rx_buffer_count * sizeof(DMABUFFERENTRY);
-
-               if ( i < info->tx_buffer_count - 1 )
-                       info->tx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY);
-       }
-
-       return 0;
-
-}      /* end of mgsl_alloc_buffer_list_memory() */
-
-/* Free DMA buffers allocated for use as the
- * receive and transmit buffer lists.
- * Warning:
- * 
- *     The data transfer buffers associated with the buffer list
- *     MUST be freed before freeing the buffer list itself because
- *     the buffer list contains the information necessary to free
- *     the individual buffers!
- */
-static void mgsl_free_buffer_list_memory( struct mgsl_struct *info )
-{
-       if (info->buffer_list && info->bus_type != MGSL_BUS_TYPE_PCI)
-               dma_free_coherent(NULL, BUFFERLISTSIZE, info->buffer_list, info->buffer_list_dma_addr);
-               
-       info->buffer_list = NULL;
-       info->rx_buffer_list = NULL;
-       info->tx_buffer_list = NULL;
-
-}      /* end of mgsl_free_buffer_list_memory() */
-
-/*
- * mgsl_alloc_frame_memory()
- * 
- *     Allocate the frame DMA buffers used by the specified buffer list.
- *     Each DMA buffer will be one memory page in size. This is necessary
- *     because memory can fragment enough that it may be impossible
- *     contiguous pages.
- * 
- * Arguments:
- * 
- *     info            pointer to device instance data
- *     BufferList      pointer to list of buffer entries
- *     Buffercount     count of buffer entries in buffer list
- * 
- * Return Value:       0 if success, otherwise -ENOMEM
- */
-static int mgsl_alloc_frame_memory(struct mgsl_struct *info,DMABUFFERENTRY *BufferList,int Buffercount)
-{
-       int i;
-       u32 phys_addr;
-
-       /* Allocate page sized buffers for the receive buffer list */
-
-       for ( i = 0; i < Buffercount; i++ ) {
-               if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
-                       /* PCI adapter uses shared memory buffers. */
-                       BufferList[i].virt_addr = info->memory_base + info->last_mem_alloc;
-                       phys_addr = info->last_mem_alloc;
-                       info->last_mem_alloc += DMABUFFERSIZE;
-               } else {
-                       /* ISA adapter uses system memory. */
-                       BufferList[i].virt_addr = dma_alloc_coherent(NULL, DMABUFFERSIZE, &BufferList[i].dma_addr, GFP_KERNEL);
-                       if (BufferList[i].virt_addr == NULL)
-                               return -ENOMEM;
-                       phys_addr = (u32)(BufferList[i].dma_addr);
-               }
-               BufferList[i].phys_addr = phys_addr;
-       }
-
-       return 0;
-
-}      /* end of mgsl_alloc_frame_memory() */
-
-/*
- * mgsl_free_frame_memory()
- * 
- *     Free the buffers associated with
- *     each buffer entry of a buffer list.
- * 
- * Arguments:
- * 
- *     info            pointer to device instance data
- *     BufferList      pointer to list of buffer entries
- *     Buffercount     count of buffer entries in buffer list
- * 
- * Return Value:       None
- */
-static void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList, int Buffercount)
-{
-       int i;
-
-       if ( BufferList ) {
-               for ( i = 0 ; i < Buffercount ; i++ ) {
-                       if ( BufferList[i].virt_addr ) {
-                               if ( info->bus_type != MGSL_BUS_TYPE_PCI )
-                                       dma_free_coherent(NULL, DMABUFFERSIZE, BufferList[i].virt_addr, BufferList[i].dma_addr);
-                               BufferList[i].virt_addr = NULL;
-                       }
-               }
-       }
-
-}      /* end of mgsl_free_frame_memory() */
-
-/* mgsl_free_dma_buffers()
- * 
- *     Free DMA buffers
- *     
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void mgsl_free_dma_buffers( struct mgsl_struct *info )
-{
-       mgsl_free_frame_memory( info, info->rx_buffer_list, info->rx_buffer_count );
-       mgsl_free_frame_memory( info, info->tx_buffer_list, info->tx_buffer_count );
-       mgsl_free_buffer_list_memory( info );
-
-}      /* end of mgsl_free_dma_buffers() */
-
-
-/*
- * mgsl_alloc_intermediate_rxbuffer_memory()
- * 
- *     Allocate a buffer large enough to hold max_frame_size. This buffer
- *     is used to pass an assembled frame to the line discipline.
- * 
- * Arguments:
- * 
- *     info            pointer to device instance data
- * 
- * Return Value:       0 if success, otherwise -ENOMEM
- */
-static int mgsl_alloc_intermediate_rxbuffer_memory(struct mgsl_struct *info)
-{
-       info->intermediate_rxbuffer = kmalloc(info->max_frame_size, GFP_KERNEL | GFP_DMA);
-       if ( info->intermediate_rxbuffer == NULL )
-               return -ENOMEM;
-
-       return 0;
-
-}      /* end of mgsl_alloc_intermediate_rxbuffer_memory() */
-
-/*
- * mgsl_free_intermediate_rxbuffer_memory()
- * 
- * 
- * Arguments:
- * 
- *     info            pointer to device instance data
- * 
- * Return Value:       None
- */
-static void mgsl_free_intermediate_rxbuffer_memory(struct mgsl_struct *info)
-{
-       kfree(info->intermediate_rxbuffer);
-       info->intermediate_rxbuffer = NULL;
-
-}      /* end of mgsl_free_intermediate_rxbuffer_memory() */
-
-/*
- * mgsl_alloc_intermediate_txbuffer_memory()
- *
- *     Allocate intermdiate transmit buffer(s) large enough to hold max_frame_size.
- *     This buffer is used to load transmit frames into the adapter's dma transfer
- *     buffers when there is sufficient space.
- *
- * Arguments:
- *
- *     info            pointer to device instance data
- *
- * Return Value:       0 if success, otherwise -ENOMEM
- */
-static int mgsl_alloc_intermediate_txbuffer_memory(struct mgsl_struct *info)
-{
-       int i;
-
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk("%s %s(%d)  allocating %d tx holding buffers\n",
-                               info->device_name, __FILE__,__LINE__,info->num_tx_holding_buffers);
-
-       memset(info->tx_holding_buffers,0,sizeof(info->tx_holding_buffers));
-
-       for ( i=0; i<info->num_tx_holding_buffers; ++i) {
-               info->tx_holding_buffers[i].buffer =
-                       kmalloc(info->max_frame_size, GFP_KERNEL);
-               if (info->tx_holding_buffers[i].buffer == NULL) {
-                       for (--i; i >= 0; i--) {
-                               kfree(info->tx_holding_buffers[i].buffer);
-                               info->tx_holding_buffers[i].buffer = NULL;
-                       }
-                       return -ENOMEM;
-               }
-       }
-
-       return 0;
-
-}      /* end of mgsl_alloc_intermediate_txbuffer_memory() */
-
-/*
- * mgsl_free_intermediate_txbuffer_memory()
- *
- *
- * Arguments:
- *
- *     info            pointer to device instance data
- *
- * Return Value:       None
- */
-static void mgsl_free_intermediate_txbuffer_memory(struct mgsl_struct *info)
-{
-       int i;
-
-       for ( i=0; i<info->num_tx_holding_buffers; ++i ) {
-               kfree(info->tx_holding_buffers[i].buffer);
-               info->tx_holding_buffers[i].buffer = NULL;
-       }
-
-       info->get_tx_holding_index = 0;
-       info->put_tx_holding_index = 0;
-       info->tx_holding_count = 0;
-
-}      /* end of mgsl_free_intermediate_txbuffer_memory() */
-
-
-/*
- * load_next_tx_holding_buffer()
- *
- * attempts to load the next buffered tx request into the
- * tx dma buffers
- *
- * Arguments:
- *
- *     info            pointer to device instance data
- *
- * Return Value:       true if next buffered tx request loaded
- *                     into adapter's tx dma buffer,
- *                     false otherwise
- */
-static bool load_next_tx_holding_buffer(struct mgsl_struct *info)
-{
-       bool ret = false;
-
-       if ( info->tx_holding_count ) {
-               /* determine if we have enough tx dma buffers
-                * to accommodate the next tx frame
-                */
-               struct tx_holding_buffer *ptx =
-                       &info->tx_holding_buffers[info->get_tx_holding_index];
-               int num_free = num_free_tx_dma_buffers(info);
-               int num_needed = ptx->buffer_size / DMABUFFERSIZE;
-               if ( ptx->buffer_size % DMABUFFERSIZE )
-                       ++num_needed;
-
-               if (num_needed <= num_free) {
-                       info->xmit_cnt = ptx->buffer_size;
-                       mgsl_load_tx_dma_buffer(info,ptx->buffer,ptx->buffer_size);
-
-                       --info->tx_holding_count;
-                       if ( ++info->get_tx_holding_index >= info->num_tx_holding_buffers)
-                               info->get_tx_holding_index=0;
-
-                       /* restart transmit timer */
-                       mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(5000));
-
-                       ret = true;
-               }
-       }
-
-       return ret;
-}
-
-/*
- * save_tx_buffer_request()
- *
- * attempt to store transmit frame request for later transmission
- *
- * Arguments:
- *
- *     info            pointer to device instance data
- *     Buffer          pointer to buffer containing frame to load
- *     BufferSize      size in bytes of frame in Buffer
- *
- * Return Value:       1 if able to store, 0 otherwise
- */
-static int save_tx_buffer_request(struct mgsl_struct *info,const char *Buffer, unsigned int BufferSize)
-{
-       struct tx_holding_buffer *ptx;
-
-       if ( info->tx_holding_count >= info->num_tx_holding_buffers ) {
-               return 0;               /* all buffers in use */
-       }
-
-       ptx = &info->tx_holding_buffers[info->put_tx_holding_index];
-       ptx->buffer_size = BufferSize;
-       memcpy( ptx->buffer, Buffer, BufferSize);
-
-       ++info->tx_holding_count;
-       if ( ++info->put_tx_holding_index >= info->num_tx_holding_buffers)
-               info->put_tx_holding_index=0;
-
-       return 1;
-}
-
-static int mgsl_claim_resources(struct mgsl_struct *info)
-{
-       if (request_region(info->io_base,info->io_addr_size,"synclink") == NULL) {
-               printk( "%s(%d):I/O address conflict on device %s Addr=%08X\n",
-                       __FILE__,__LINE__,info->device_name, info->io_base);
-               return -ENODEV;
-       }
-       info->io_addr_requested = true;
-       
-       if ( request_irq(info->irq_level,mgsl_interrupt,info->irq_flags,
-               info->device_name, info ) < 0 ) {
-               printk( "%s(%d):Cant request interrupt on device %s IRQ=%d\n",
-                       __FILE__,__LINE__,info->device_name, info->irq_level );
-               goto errout;
-       }
-       info->irq_requested = true;
-       
-       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
-               if (request_mem_region(info->phys_memory_base,0x40000,"synclink") == NULL) {
-                       printk( "%s(%d):mem addr conflict device %s Addr=%08X\n",
-                               __FILE__,__LINE__,info->device_name, info->phys_memory_base);
-                       goto errout;
-               }
-               info->shared_mem_requested = true;
-               if (request_mem_region(info->phys_lcr_base + info->lcr_offset,128,"synclink") == NULL) {
-                       printk( "%s(%d):lcr mem addr conflict device %s Addr=%08X\n",
-                               __FILE__,__LINE__,info->device_name, info->phys_lcr_base + info->lcr_offset);
-                       goto errout;
-               }
-               info->lcr_mem_requested = true;
-
-               info->memory_base = ioremap_nocache(info->phys_memory_base,
-                                                               0x40000);
-               if (!info->memory_base) {
-                       printk( "%s(%d):Cant map shared memory on device %s MemAddr=%08X\n",
-                               __FILE__,__LINE__,info->device_name, info->phys_memory_base );
-                       goto errout;
-               }
-               
-               if ( !mgsl_memory_test(info) ) {
-                       printk( "%s(%d):Failed shared memory test %s MemAddr=%08X\n",
-                               __FILE__,__LINE__,info->device_name, info->phys_memory_base );
-                       goto errout;
-               }
-               
-               info->lcr_base = ioremap_nocache(info->phys_lcr_base,
-                                                               PAGE_SIZE);
-               if (!info->lcr_base) {
-                       printk( "%s(%d):Cant map LCR memory on device %s MemAddr=%08X\n",
-                               __FILE__,__LINE__,info->device_name, info->phys_lcr_base );
-                       goto errout;
-               }
-               info->lcr_base += info->lcr_offset;
-               
-       } else {
-               /* claim DMA channel */
-               
-               if (request_dma(info->dma_level,info->device_name) < 0){
-                       printk( "%s(%d):Cant request DMA channel on device %s DMA=%d\n",
-                               __FILE__,__LINE__,info->device_name, info->dma_level );
-                       mgsl_release_resources( info );
-                       return -ENODEV;
-               }
-               info->dma_requested = true;
-
-               /* ISA adapter uses bus master DMA */           
-               set_dma_mode(info->dma_level,DMA_MODE_CASCADE);
-               enable_dma(info->dma_level);
-       }
-       
-       if ( mgsl_allocate_dma_buffers(info) < 0 ) {
-               printk( "%s(%d):Cant allocate DMA buffers on device %s DMA=%d\n",
-                       __FILE__,__LINE__,info->device_name, info->dma_level );
-               goto errout;
-       }       
-       
-       return 0;
-errout:
-       mgsl_release_resources(info);
-       return -ENODEV;
-
-}      /* end of mgsl_claim_resources() */
-
-static void mgsl_release_resources(struct mgsl_struct *info)
-{
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):mgsl_release_resources(%s) entry\n",
-                       __FILE__,__LINE__,info->device_name );
-                       
-       if ( info->irq_requested ) {
-               free_irq(info->irq_level, info);
-               info->irq_requested = false;
-       }
-       if ( info->dma_requested ) {
-               disable_dma(info->dma_level);
-               free_dma(info->dma_level);
-               info->dma_requested = false;
-       }
-       mgsl_free_dma_buffers(info);
-       mgsl_free_intermediate_rxbuffer_memory(info);
-       mgsl_free_intermediate_txbuffer_memory(info);
-       
-       if ( info->io_addr_requested ) {
-               release_region(info->io_base,info->io_addr_size);
-               info->io_addr_requested = false;
-       }
-       if ( info->shared_mem_requested ) {
-               release_mem_region(info->phys_memory_base,0x40000);
-               info->shared_mem_requested = false;
-       }
-       if ( info->lcr_mem_requested ) {
-               release_mem_region(info->phys_lcr_base + info->lcr_offset,128);
-               info->lcr_mem_requested = false;
-       }
-       if (info->memory_base){
-               iounmap(info->memory_base);
-               info->memory_base = NULL;
-       }
-       if (info->lcr_base){
-               iounmap(info->lcr_base - info->lcr_offset);
-               info->lcr_base = NULL;
-       }
-       
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):mgsl_release_resources(%s) exit\n",
-                       __FILE__,__LINE__,info->device_name );
-                       
-}      /* end of mgsl_release_resources() */
-
-/* mgsl_add_device()
- * 
- *     Add the specified device instance data structure to the
- *     global linked list of devices and increment the device count.
- *     
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void mgsl_add_device( struct mgsl_struct *info )
-{
-       info->next_device = NULL;
-       info->line = mgsl_device_count;
-       sprintf(info->device_name,"ttySL%d",info->line);
-       
-       if (info->line < MAX_TOTAL_DEVICES) {
-               if (maxframe[info->line])
-                       info->max_frame_size = maxframe[info->line];
-
-               if (txdmabufs[info->line]) {
-                       info->num_tx_dma_buffers = txdmabufs[info->line];
-                       if (info->num_tx_dma_buffers < 1)
-                               info->num_tx_dma_buffers = 1;
-               }
-
-               if (txholdbufs[info->line]) {
-                       info->num_tx_holding_buffers = txholdbufs[info->line];
-                       if (info->num_tx_holding_buffers < 1)
-                               info->num_tx_holding_buffers = 1;
-                       else if (info->num_tx_holding_buffers > MAX_TX_HOLDING_BUFFERS)
-                               info->num_tx_holding_buffers = MAX_TX_HOLDING_BUFFERS;
-               }
-       }
-
-       mgsl_device_count++;
-       
-       if ( !mgsl_device_list )
-               mgsl_device_list = info;
-       else {  
-               struct mgsl_struct *current_dev = mgsl_device_list;
-               while( current_dev->next_device )
-                       current_dev = current_dev->next_device;
-               current_dev->next_device = info;
-       }
-       
-       if ( info->max_frame_size < 4096 )
-               info->max_frame_size = 4096;
-       else if ( info->max_frame_size > 65535 )
-               info->max_frame_size = 65535;
-       
-       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
-               printk( "SyncLink PCI v%d %s: IO=%04X IRQ=%d Mem=%08X,%08X MaxFrameSize=%u\n",
-                       info->hw_version + 1, info->device_name, info->io_base, info->irq_level,
-                       info->phys_memory_base, info->phys_lcr_base,
-                       info->max_frame_size );
-       } else {
-               printk( "SyncLink ISA %s: IO=%04X IRQ=%d DMA=%d MaxFrameSize=%u\n",
-                       info->device_name, info->io_base, info->irq_level, info->dma_level,
-                       info->max_frame_size );
-       }
-
-#if SYNCLINK_GENERIC_HDLC
-       hdlcdev_init(info);
-#endif
-
-}      /* end of mgsl_add_device() */
-
-static const struct tty_port_operations mgsl_port_ops = {
-       .carrier_raised = carrier_raised,
-       .dtr_rts = dtr_rts,
-};
-
-
-/* mgsl_allocate_device()
- * 
- *     Allocate and initialize a device instance structure
- *     
- * Arguments:          none
- * Return Value:       pointer to mgsl_struct if success, otherwise NULL
- */
-static struct mgsl_struct* mgsl_allocate_device(void)
-{
-       struct mgsl_struct *info;
-       
-       info = kzalloc(sizeof(struct mgsl_struct),
-                GFP_KERNEL);
-                
-       if (!info) {
-               printk("Error can't allocate device instance data\n");
-       } else {
-               tty_port_init(&info->port);
-               info->port.ops = &mgsl_port_ops;
-               info->magic = MGSL_MAGIC;
-               INIT_WORK(&info->task, mgsl_bh_handler);
-               info->max_frame_size = 4096;
-               info->port.close_delay = 5*HZ/10;
-               info->port.closing_wait = 30*HZ;
-               init_waitqueue_head(&info->status_event_wait_q);
-               init_waitqueue_head(&info->event_wait_q);
-               spin_lock_init(&info->irq_spinlock);
-               spin_lock_init(&info->netlock);
-               memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
-               info->idle_mode = HDLC_TXIDLE_FLAGS;            
-               info->num_tx_dma_buffers = 1;
-               info->num_tx_holding_buffers = 0;
-       }
-       
-       return info;
-
-}      /* end of mgsl_allocate_device()*/
-
-static const struct tty_operations mgsl_ops = {
-       .open = mgsl_open,
-       .close = mgsl_close,
-       .write = mgsl_write,
-       .put_char = mgsl_put_char,
-       .flush_chars = mgsl_flush_chars,
-       .write_room = mgsl_write_room,
-       .chars_in_buffer = mgsl_chars_in_buffer,
-       .flush_buffer = mgsl_flush_buffer,
-       .ioctl = mgsl_ioctl,
-       .throttle = mgsl_throttle,
-       .unthrottle = mgsl_unthrottle,
-       .send_xchar = mgsl_send_xchar,
-       .break_ctl = mgsl_break,
-       .wait_until_sent = mgsl_wait_until_sent,
-       .set_termios = mgsl_set_termios,
-       .stop = mgsl_stop,
-       .start = mgsl_start,
-       .hangup = mgsl_hangup,
-       .tiocmget = tiocmget,
-       .tiocmset = tiocmset,
-       .get_icount = msgl_get_icount,
-       .proc_fops = &mgsl_proc_fops,
-};
-
-/*
- * perform tty device initialization
- */
-static int mgsl_init_tty(void)
-{
-       int rc;
-
-       serial_driver = alloc_tty_driver(128);
-       if (!serial_driver)
-               return -ENOMEM;
-       
-       serial_driver->owner = THIS_MODULE;
-       serial_driver->driver_name = "synclink";
-       serial_driver->name = "ttySL";
-       serial_driver->major = ttymajor;
-       serial_driver->minor_start = 64;
-       serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
-       serial_driver->subtype = SERIAL_TYPE_NORMAL;
-       serial_driver->init_termios = tty_std_termios;
-       serial_driver->init_termios.c_cflag =
-               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-       serial_driver->init_termios.c_ispeed = 9600;
-       serial_driver->init_termios.c_ospeed = 9600;
-       serial_driver->flags = TTY_DRIVER_REAL_RAW;
-       tty_set_operations(serial_driver, &mgsl_ops);
-       if ((rc = tty_register_driver(serial_driver)) < 0) {
-               printk("%s(%d):Couldn't register serial driver\n",
-                       __FILE__,__LINE__);
-               put_tty_driver(serial_driver);
-               serial_driver = NULL;
-               return rc;
-       }
-                       
-       printk("%s %s, tty major#%d\n",
-               driver_name, driver_version,
-               serial_driver->major);
-       return 0;
-}
-
-/* enumerate user specified ISA adapters
- */
-static void mgsl_enum_isa_devices(void)
-{
-       struct mgsl_struct *info;
-       int i;
-               
-       /* Check for user specified ISA devices */
-       
-       for (i=0 ;(i < MAX_ISA_DEVICES) && io[i] && irq[i]; i++){
-               if ( debug_level >= DEBUG_LEVEL_INFO )
-                       printk("ISA device specified io=%04X,irq=%d,dma=%d\n",
-                               io[i], irq[i], dma[i] );
-               
-               info = mgsl_allocate_device();
-               if ( !info ) {
-                       /* error allocating device instance data */
-                       if ( debug_level >= DEBUG_LEVEL_ERROR )
-                               printk( "can't allocate device instance data.\n");
-                       continue;
-               }
-               
-               /* Copy user configuration info to device instance data */
-               info->io_base = (unsigned int)io[i];
-               info->irq_level = (unsigned int)irq[i];
-               info->irq_level = irq_canonicalize(info->irq_level);
-               info->dma_level = (unsigned int)dma[i];
-               info->bus_type = MGSL_BUS_TYPE_ISA;
-               info->io_addr_size = 16;
-               info->irq_flags = 0;
-               
-               mgsl_add_device( info );
-       }
-}
-
-static void synclink_cleanup(void)
-{
-       int rc;
-       struct mgsl_struct *info;
-       struct mgsl_struct *tmp;
-
-       printk("Unloading %s: %s\n", driver_name, driver_version);
-
-       if (serial_driver) {
-               if ((rc = tty_unregister_driver(serial_driver)))
-                       printk("%s(%d) failed to unregister tty driver err=%d\n",
-                              __FILE__,__LINE__,rc);
-               put_tty_driver(serial_driver);
-       }
-
-       info = mgsl_device_list;
-       while(info) {
-#if SYNCLINK_GENERIC_HDLC
-               hdlcdev_exit(info);
-#endif
-               mgsl_release_resources(info);
-               tmp = info;
-               info = info->next_device;
-               kfree(tmp);
-       }
-       
-       if (pci_registered)
-               pci_unregister_driver(&synclink_pci_driver);
-}
-
-static int __init synclink_init(void)
-{
-       int rc;
-
-       if (break_on_load) {
-               mgsl_get_text_ptr();
-               BREAKPOINT();
-       }
-
-       printk("%s %s\n", driver_name, driver_version);
-
-       mgsl_enum_isa_devices();
-       if ((rc = pci_register_driver(&synclink_pci_driver)) < 0)
-               printk("%s:failed to register PCI driver, error=%d\n",__FILE__,rc);
-       else
-               pci_registered = true;
-
-       if ((rc = mgsl_init_tty()) < 0)
-               goto error;
-
-       return 0;
-
-error:
-       synclink_cleanup();
-       return rc;
-}
-
-static void __exit synclink_exit(void)
-{
-       synclink_cleanup();
-}
-
-module_init(synclink_init);
-module_exit(synclink_exit);
-
-/*
- * usc_RTCmd()
- *
- * Issue a USC Receive/Transmit command to the
- * Channel Command/Address Register (CCAR).
- *
- * Notes:
- *
- *    The command is encoded in the most significant 5 bits <15..11>
- *    of the CCAR value. Bits <10..7> of the CCAR must be preserved
- *    and Bits <6..0> must be written as zeros.
- *
- * Arguments:
- *
- *    info   pointer to device information structure
- *    Cmd    command mask (use symbolic macros)
- *
- * Return Value:
- *
- *    None
- */
-static void usc_RTCmd( struct mgsl_struct *info, u16 Cmd )
-{
-       /* output command to CCAR in bits <15..11> */
-       /* preserve bits <10..7>, bits <6..0> must be zero */
-
-       outw( Cmd + info->loopback_bits, info->io_base + CCAR );
-
-       /* Read to flush write to CCAR */
-       if ( info->bus_type == MGSL_BUS_TYPE_PCI )
-               inw( info->io_base + CCAR );
-
-}      /* end of usc_RTCmd() */
-
-/*
- * usc_DmaCmd()
- *
- *    Issue a DMA command to the DMA Command/Address Register (DCAR).
- *
- * Arguments:
- *
- *    info   pointer to device information structure
- *    Cmd    DMA command mask (usc_DmaCmd_XX Macros)
- *
- * Return Value:
- *
- *       None
- */
-static void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd )
-{
-       /* write command mask to DCAR */
-       outw( Cmd + info->mbre_bit, info->io_base );
-
-       /* Read to flush write to DCAR */
-       if ( info->bus_type == MGSL_BUS_TYPE_PCI )
-               inw( info->io_base );
-
-}      /* end of usc_DmaCmd() */
-
-/*
- * usc_OutDmaReg()
- *
- *    Write a 16-bit value to a USC DMA register
- *
- * Arguments:
- *
- *    info      pointer to device info structure
- *    RegAddr   register address (number) for write
- *    RegValue  16-bit value to write to register
- *
- * Return Value:
- *
- *    None
- *
- */
-static void usc_OutDmaReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue )
-{
-       /* Note: The DCAR is located at the adapter base address */
-       /* Note: must preserve state of BIT8 in DCAR */
-
-       outw( RegAddr + info->mbre_bit, info->io_base );
-       outw( RegValue, info->io_base );
-
-       /* Read to flush write to DCAR */
-       if ( info->bus_type == MGSL_BUS_TYPE_PCI )
-               inw( info->io_base );
-
-}      /* end of usc_OutDmaReg() */
-/*
- * usc_InDmaReg()
- *
- *    Read a 16-bit value from a DMA register
- *
- * Arguments:
- *
- *    info     pointer to device info structure
- *    RegAddr  register address (number) to read from
- *
- * Return Value:
- *
- *    The 16-bit value read from register
- *
- */
-static u16 usc_InDmaReg( struct mgsl_struct *info, u16 RegAddr )
-{
-       /* Note: The DCAR is located at the adapter base address */
-       /* Note: must preserve state of BIT8 in DCAR */
-
-       outw( RegAddr + info->mbre_bit, info->io_base );
-       return inw( info->io_base );
-
-}      /* end of usc_InDmaReg() */
-
-/*
- *
- * usc_OutReg()
- *
- *    Write a 16-bit value to a USC serial channel register 
- *
- * Arguments:
- *
- *    info      pointer to device info structure
- *    RegAddr   register address (number) to write to
- *    RegValue  16-bit value to write to register
- *
- * Return Value:
- *
- *    None
- *
- */
-static void usc_OutReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue )
-{
-       outw( RegAddr + info->loopback_bits, info->io_base + CCAR );
-       outw( RegValue, info->io_base + CCAR );
-
-       /* Read to flush write to CCAR */
-       if ( info->bus_type == MGSL_BUS_TYPE_PCI )
-               inw( info->io_base + CCAR );
-
-}      /* end of usc_OutReg() */
-
-/*
- * usc_InReg()
- *
- *    Reads a 16-bit value from a USC serial channel register
- *
- * Arguments:
- *
- *    info       pointer to device extension
- *    RegAddr    register address (number) to read from
- *
- * Return Value:
- *
- *    16-bit value read from register
- */
-static u16 usc_InReg( struct mgsl_struct *info, u16 RegAddr )
-{
-       outw( RegAddr + info->loopback_bits, info->io_base + CCAR );
-       return inw( info->io_base + CCAR );
-
-}      /* end of usc_InReg() */
-
-/* usc_set_sdlc_mode()
- *
- *    Set up the adapter for SDLC DMA communications.
- *
- * Arguments:          info    pointer to device instance data
- * Return Value:       NONE
- */
-static void usc_set_sdlc_mode( struct mgsl_struct *info )
-{
-       u16 RegValue;
-       bool PreSL1660;
-       
-       /*
-        * determine if the IUSC on the adapter is pre-SL1660. If
-        * not, take advantage of the UnderWait feature of more
-        * modern chips. If an underrun occurs and this bit is set,
-        * the transmitter will idle the programmed idle pattern
-        * until the driver has time to service the underrun. Otherwise,
-        * the dma controller may get the cycles previously requested
-        * and begin transmitting queued tx data.
-        */
-       usc_OutReg(info,TMCR,0x1f);
-       RegValue=usc_InReg(info,TMDR);
-       PreSL1660 = (RegValue == IUSC_PRE_SL1660);
-
-       if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
-       {
-          /*
-          ** Channel Mode Register (CMR)
-          **
-          ** <15..14>    10    Tx Sub Modes, Send Flag on Underrun
-          ** <13>        0     0 = Transmit Disabled (initially)
-          ** <12>        0     1 = Consecutive Idles share common 0
-          ** <11..8>     1110  Transmitter Mode = HDLC/SDLC Loop
-          ** <7..4>      0000  Rx Sub Modes, addr/ctrl field handling
-          ** <3..0>      0110  Receiver Mode = HDLC/SDLC
-          **
-          ** 1000 1110 0000 0110 = 0x8e06
-          */
-          RegValue = 0x8e06;
-          /*--------------------------------------------------
-           * ignore user options for UnderRun Actions and
-           * preambles
-           *--------------------------------------------------*/
-       }
-       else
-       {       
-               /* Channel mode Register (CMR)
-                *
-                * <15..14>  00    Tx Sub modes, Underrun Action
-                * <13>      0     1 = Send Preamble before opening flag
-                * <12>      0     1 = Consecutive Idles share common 0
-                * <11..8>   0110  Transmitter mode = HDLC/SDLC
-                * <7..4>    0000  Rx Sub modes, addr/ctrl field handling
-                * <3..0>    0110  Receiver mode = HDLC/SDLC
-                *
-                * 0000 0110 0000 0110 = 0x0606
-                */
-               if (info->params.mode == MGSL_MODE_RAW) {
-                       RegValue = 0x0001;              /* Set Receive mode = external sync */
-
-                       usc_OutReg( info, IOCR,         /* Set IOCR DCD is RxSync Detect Input */
-                               (unsigned short)((usc_InReg(info, IOCR) & ~(BIT13|BIT12)) | BIT12));
-
-                       /*
-                        * TxSubMode:
-                        *      CMR <15>                0       Don't send CRC on Tx Underrun
-                        *      CMR <14>                x       undefined
-                        *      CMR <13>                0       Send preamble before openning sync
-                        *      CMR <12>                0       Send 8-bit syncs, 1=send Syncs per TxLength
-                        *
-                        * TxMode:
-                        *      CMR <11-8)      0100    MonoSync
-                        *
-                        *      0x00 0100 xxxx xxxx  04xx
-                        */
-                       RegValue |= 0x0400;
-               }
-               else {
-
-               RegValue = 0x0606;
-
-               if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 )
-                       RegValue |= BIT14;
-               else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG )
-                       RegValue |= BIT15;
-               else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC )
-                       RegValue |= BIT15 + BIT14;
-               }
-
-               if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE )
-                       RegValue |= BIT13;
-       }
-
-       if ( info->params.mode == MGSL_MODE_HDLC &&
-               (info->params.flags & HDLC_FLAG_SHARE_ZERO) )
-               RegValue |= BIT12;
-
-       if ( info->params.addr_filter != 0xff )
-       {
-               /* set up receive address filtering */
-               usc_OutReg( info, RSR, info->params.addr_filter );
-               RegValue |= BIT4;
-       }
-
-       usc_OutReg( info, CMR, RegValue );
-       info->cmr_value = RegValue;
-
-       /* Receiver mode Register (RMR)
-        *
-        * <15..13>  000    encoding
-        * <12..11>  00     FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1)
-        * <10>      1      1 = Set CRC to all 1s (use for SDLC/HDLC)
-        * <9>       0      1 = Include Receive chars in CRC
-        * <8>       1      1 = Use Abort/PE bit as abort indicator
-        * <7..6>    00     Even parity
-        * <5>       0      parity disabled
-        * <4..2>    000    Receive Char Length = 8 bits
-        * <1..0>    00     Disable Receiver
-        *
-        * 0000 0101 0000 0000 = 0x0500
-        */
-
-       RegValue = 0x0500;
-
-       switch ( info->params.encoding ) {
-       case HDLC_ENCODING_NRZB:               RegValue |= BIT13; break;
-       case HDLC_ENCODING_NRZI_MARK:          RegValue |= BIT14; break;
-       case HDLC_ENCODING_NRZI_SPACE:         RegValue |= BIT14 + BIT13; break;
-       case HDLC_ENCODING_BIPHASE_MARK:       RegValue |= BIT15; break;
-       case HDLC_ENCODING_BIPHASE_SPACE:      RegValue |= BIT15 + BIT13; break;
-       case HDLC_ENCODING_BIPHASE_LEVEL:      RegValue |= BIT15 + BIT14; break;
-       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break;
-       }
-
-       if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT )
-               RegValue |= BIT9;
-       else if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_32_CCITT )
-               RegValue |= ( BIT12 | BIT10 | BIT9 );
-
-       usc_OutReg( info, RMR, RegValue );
-
-       /* Set the Receive count Limit Register (RCLR) to 0xffff. */
-       /* When an opening flag of an SDLC frame is recognized the */
-       /* Receive Character count (RCC) is loaded with the value in */
-       /* RCLR. The RCC is decremented for each received byte.  The */
-       /* value of RCC is stored after the closing flag of the frame */
-       /* allowing the frame size to be computed. */
-
-       usc_OutReg( info, RCLR, RCLRVALUE );
-
-       usc_RCmd( info, RCmd_SelectRicrdma_level );
-
-       /* Receive Interrupt Control Register (RICR)
-        *
-        * <15..8>      ?       RxFIFO DMA Request Level
-        * <7>          0       Exited Hunt IA (Interrupt Arm)
-        * <6>          0       Idle Received IA
-        * <5>          0       Break/Abort IA
-        * <4>          0       Rx Bound IA
-        * <3>          1       Queued status reflects oldest 2 bytes in FIFO
-        * <2>          0       Abort/PE IA
-        * <1>          1       Rx Overrun IA
-        * <0>          0       Select TC0 value for readback
-        *
-        *      0000 0000 0000 1000 = 0x000a
-        */
-
-       /* Carry over the Exit Hunt and Idle Received bits */
-       /* in case they have been armed by usc_ArmEvents.   */
-
-       RegValue = usc_InReg( info, RICR ) & 0xc0;
-
-       if ( info->bus_type == MGSL_BUS_TYPE_PCI )
-               usc_OutReg( info, RICR, (u16)(0x030a | RegValue) );
-       else
-               usc_OutReg( info, RICR, (u16)(0x140a | RegValue) );
-
-       /* Unlatch all Rx status bits and clear Rx status IRQ Pending */
-
-       usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
-       usc_ClearIrqPendingBits( info, RECEIVE_STATUS );
-
-       /* Transmit mode Register (TMR)
-        *      
-        * <15..13>     000     encoding
-        * <12..11>     00      FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1)
-        * <10>         1       1 = Start CRC as all 1s (use for SDLC/HDLC)
-        * <9>          0       1 = Tx CRC Enabled
-        * <8>          0       1 = Append CRC to end of transmit frame
-        * <7..6>       00      Transmit parity Even
-        * <5>          0       Transmit parity Disabled
-        * <4..2>       000     Tx Char Length = 8 bits
-        * <1..0>       00      Disable Transmitter
-        *
-        *      0000 0100 0000 0000 = 0x0400
-        */
-
-       RegValue = 0x0400;
-
-       switch ( info->params.encoding ) {
-       case HDLC_ENCODING_NRZB:               RegValue |= BIT13; break;
-       case HDLC_ENCODING_NRZI_MARK:          RegValue |= BIT14; break;
-       case HDLC_ENCODING_NRZI_SPACE:         RegValue |= BIT14 + BIT13; break;
-       case HDLC_ENCODING_BIPHASE_MARK:       RegValue |= BIT15; break;
-       case HDLC_ENCODING_BIPHASE_SPACE:      RegValue |= BIT15 + BIT13; break;
-       case HDLC_ENCODING_BIPHASE_LEVEL:      RegValue |= BIT15 + BIT14; break;
-       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break;
-       }
-
-       if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT )
-               RegValue |= BIT9 + BIT8;
-       else if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_32_CCITT )
-               RegValue |= ( BIT12 | BIT10 | BIT9 | BIT8);
-
-       usc_OutReg( info, TMR, RegValue );
-
-       usc_set_txidle( info );
-
-
-       usc_TCmd( info, TCmd_SelectTicrdma_level );
-
-       /* Transmit Interrupt Control Register (TICR)
-        *
-        * <15..8>      ?       Transmit FIFO DMA Level
-        * <7>          0       Present IA (Interrupt Arm)
-        * <6>          0       Idle Sent IA
-        * <5>          1       Abort Sent IA
-        * <4>          1       EOF/EOM Sent IA
-        * <3>          0       CRC Sent IA
-        * <2>          1       1 = Wait for SW Trigger to Start Frame
-        * <1>          1       Tx Underrun IA
-        * <0>          0       TC0 constant on read back
-        *
-        *      0000 0000 0011 0110 = 0x0036
-        */
-
-       if ( info->bus_type == MGSL_BUS_TYPE_PCI )
-               usc_OutReg( info, TICR, 0x0736 );
-       else                                                            
-               usc_OutReg( info, TICR, 0x1436 );
-
-       usc_UnlatchTxstatusBits( info, TXSTATUS_ALL );
-       usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
-
-       /*
-       ** Transmit Command/Status Register (TCSR)
-       **
-       ** <15..12>     0000    TCmd
-       ** <11>         0/1     UnderWait
-       ** <10..08>     000     TxIdle
-       ** <7>          x       PreSent
-       ** <6>          x       IdleSent
-       ** <5>          x       AbortSent
-       ** <4>          x       EOF/EOM Sent
-       ** <3>          x       CRC Sent
-       ** <2>          x       All Sent
-       ** <1>          x       TxUnder
-       ** <0>          x       TxEmpty
-       ** 
-       ** 0000 0000 0000 0000 = 0x0000
-       */
-       info->tcsr_value = 0;
-
-       if ( !PreSL1660 )
-               info->tcsr_value |= TCSR_UNDERWAIT;
-               
-       usc_OutReg( info, TCSR, info->tcsr_value );
-
-       /* Clock mode Control Register (CMCR)
-        *
-        * <15..14>     00      counter 1 Source = Disabled
-        * <13..12>     00      counter 0 Source = Disabled
-        * <11..10>     11      BRG1 Input is TxC Pin
-        * <9..8>       11      BRG0 Input is TxC Pin
-        * <7..6>       01      DPLL Input is BRG1 Output
-        * <5..3>       XXX     TxCLK comes from Port 0
-        * <2..0>       XXX     RxCLK comes from Port 1
-        *
-        *      0000 1111 0111 0111 = 0x0f77
-        */
-
-       RegValue = 0x0f40;
-
-       if ( info->params.flags & HDLC_FLAG_RXC_DPLL )
-               RegValue |= 0x0003;     /* RxCLK from DPLL */
-       else if ( info->params.flags & HDLC_FLAG_RXC_BRG )
-               RegValue |= 0x0004;     /* RxCLK from BRG0 */
-       else if ( info->params.flags & HDLC_FLAG_RXC_TXCPIN)
-               RegValue |= 0x0006;     /* RxCLK from TXC Input */
-       else
-               RegValue |= 0x0007;     /* RxCLK from Port1 */
-
-       if ( info->params.flags & HDLC_FLAG_TXC_DPLL )
-               RegValue |= 0x0018;     /* TxCLK from DPLL */
-       else if ( info->params.flags & HDLC_FLAG_TXC_BRG )
-               RegValue |= 0x0020;     /* TxCLK from BRG0 */
-       else if ( info->params.flags & HDLC_FLAG_TXC_RXCPIN)
-               RegValue |= 0x0038;     /* RxCLK from TXC Input */
-       else
-               RegValue |= 0x0030;     /* TxCLK from Port0 */
-
-       usc_OutReg( info, CMCR, RegValue );
-
-
-       /* Hardware Configuration Register (HCR)
-        *
-        * <15..14>     00      CTR0 Divisor:00=32,01=16,10=8,11=4
-        * <13>         0       CTR1DSel:0=CTR0Div determines CTR0Div
-        * <12>         0       CVOK:0=report code violation in biphase
-        * <11..10>     00      DPLL Divisor:00=32,01=16,10=8,11=4
-        * <9..8>       XX      DPLL mode:00=disable,01=NRZ,10=Biphase,11=Biphase Level
-        * <7..6>       00      reserved
-        * <5>          0       BRG1 mode:0=continuous,1=single cycle
-        * <4>          X       BRG1 Enable
-        * <3..2>       00      reserved
-        * <1>          0       BRG0 mode:0=continuous,1=single cycle
-        * <0>          0       BRG0 Enable
-        */
-
-       RegValue = 0x0000;
-
-       if ( info->params.flags & (HDLC_FLAG_RXC_DPLL + HDLC_FLAG_TXC_DPLL) ) {
-               u32 XtalSpeed;
-               u32 DpllDivisor;
-               u16 Tc;
-
-               /*  DPLL is enabled. Use BRG1 to provide continuous reference clock  */
-               /*  for DPLL. DPLL mode in HCR is dependent on the encoding used. */
-
-               if ( info->bus_type == MGSL_BUS_TYPE_PCI )
-                       XtalSpeed = 11059200;
-               else
-                       XtalSpeed = 14745600;
-
-               if ( info->params.flags & HDLC_FLAG_DPLL_DIV16 ) {
-                       DpllDivisor = 16;
-                       RegValue |= BIT10;
-               }
-               else if ( info->params.flags & HDLC_FLAG_DPLL_DIV8 ) {
-                       DpllDivisor = 8;
-                       RegValue |= BIT11;
-               }
-               else
-                       DpllDivisor = 32;
-
-               /*  Tc = (Xtal/Speed) - 1 */
-               /*  If twice the remainder of (Xtal/Speed) is greater than Speed */
-               /*  then rounding up gives a more precise time constant. Instead */
-               /*  of rounding up and then subtracting 1 we just don't subtract */
-               /*  the one in this case. */
-
-               /*--------------------------------------------------
-                * ejz: for DPLL mode, application should use the
-                * same clock speed as the partner system, even 
-                * though clocking is derived from the input RxData.
-                * In case the user uses a 0 for the clock speed,
-                * default to 0xffffffff and don't try to divide by
-                * zero
-                *--------------------------------------------------*/
-               if ( info->params.clock_speed )
-               {
-                       Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed);
-                       if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2)
-                              / info->params.clock_speed) )
-                               Tc--;
-               }
-               else
-                       Tc = -1;
-                                 
-
-               /* Write 16-bit Time Constant for BRG1 */
-               usc_OutReg( info, TC1R, Tc );
-
-               RegValue |= BIT4;               /* enable BRG1 */
-
-               switch ( info->params.encoding ) {
-               case HDLC_ENCODING_NRZ:
-               case HDLC_ENCODING_NRZB:
-               case HDLC_ENCODING_NRZI_MARK:
-               case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT8; break;
-               case HDLC_ENCODING_BIPHASE_MARK:
-               case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT9; break;
-               case HDLC_ENCODING_BIPHASE_LEVEL:
-               case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT9 + BIT8; break;
-               }
-       }
-
-       usc_OutReg( info, HCR, RegValue );
-
-
-       /* Channel Control/status Register (CCSR)
-        *
-        * <15>         X       RCC FIFO Overflow status (RO)
-        * <14>         X       RCC FIFO Not Empty status (RO)
-        * <13>         0       1 = Clear RCC FIFO (WO)
-        * <12>         X       DPLL Sync (RW)
-        * <11>         X       DPLL 2 Missed Clocks status (RO)
-        * <10>         X       DPLL 1 Missed Clock status (RO)
-        * <9..8>       00      DPLL Resync on rising and falling edges (RW)
-        * <7>          X       SDLC Loop On status (RO)
-        * <6>          X       SDLC Loop Send status (RO)
-        * <5>          1       Bypass counters for TxClk and RxClk (RW)
-        * <4..2>       000     Last Char of SDLC frame has 8 bits (RW)
-        * <1..0>       00      reserved
-        *
-        *      0000 0000 0010 0000 = 0x0020
-        */
-
-       usc_OutReg( info, CCSR, 0x1020 );
-
-
-       if ( info->params.flags & HDLC_FLAG_AUTO_CTS ) {
-               usc_OutReg( info, SICR,
-                           (u16)(usc_InReg(info,SICR) | SICR_CTS_INACTIVE) );
-       }
-       
-
-       /* enable Master Interrupt Enable bit (MIE) */
-       usc_EnableMasterIrqBit( info );
-
-       usc_ClearIrqPendingBits( info, RECEIVE_STATUS + RECEIVE_DATA +
-                               TRANSMIT_STATUS + TRANSMIT_DATA + MISC);
-
-       /* arm RCC underflow interrupt */
-       usc_OutReg(info, SICR, (u16)(usc_InReg(info,SICR) | BIT3));
-       usc_EnableInterrupts(info, MISC);
-
-       info->mbre_bit = 0;
-       outw( 0, info->io_base );                       /* clear Master Bus Enable (DCAR) */
-       usc_DmaCmd( info, DmaCmd_ResetAllChannels );    /* disable both DMA channels */
-       info->mbre_bit = BIT8;
-       outw( BIT8, info->io_base );                    /* set Master Bus Enable (DCAR) */
-
-       if (info->bus_type == MGSL_BUS_TYPE_ISA) {
-               /* Enable DMAEN (Port 7, Bit 14) */
-               /* This connects the DMA request signal to the ISA bus */
-               usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) & ~BIT14));
-       }
-
-       /* DMA Control Register (DCR)
-        *
-        * <15..14>     10      Priority mode = Alternating Tx/Rx
-        *              01      Rx has priority
-        *              00      Tx has priority
-        *
-        * <13>         1       Enable Priority Preempt per DCR<15..14>
-        *                      (WARNING DCR<11..10> must be 00 when this is 1)
-        *              0       Choose activate channel per DCR<11..10>
-        *
-        * <12>         0       Little Endian for Array/List
-        * <11..10>     00      Both Channels can use each bus grant
-        * <9..6>       0000    reserved
-        * <5>          0       7 CLK - Minimum Bus Re-request Interval
-        * <4>          0       1 = drive D/C and S/D pins
-        * <3>          1       1 = Add one wait state to all DMA cycles.
-        * <2>          0       1 = Strobe /UAS on every transfer.
-        * <1..0>       11      Addr incrementing only affects LS24 bits
-        *
-        *      0110 0000 0000 1011 = 0x600b
-        */
-
-       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
-               /* PCI adapter does not need DMA wait state */
-               usc_OutDmaReg( info, DCR, 0xa00b );
-       }
-       else
-               usc_OutDmaReg( info, DCR, 0x800b );
-
-
-       /* Receive DMA mode Register (RDMR)
-        *
-        * <15..14>     11      DMA mode = Linked List Buffer mode
-        * <13>         1       RSBinA/L = store Rx status Block in Arrary/List entry
-        * <12>         1       Clear count of List Entry after fetching
-        * <11..10>     00      Address mode = Increment
-        * <9>          1       Terminate Buffer on RxBound
-        * <8>          0       Bus Width = 16bits
-        * <7..0>       ?       status Bits (write as 0s)
-        *
-        * 1111 0010 0000 0000 = 0xf200
-        */
-
-       usc_OutDmaReg( info, RDMR, 0xf200 );
-
-
-       /* Transmit DMA mode Register (TDMR)
-        *
-        * <15..14>     11      DMA mode = Linked List Buffer mode
-        * <13>         1       TCBinA/L = fetch Tx Control Block from List entry
-        * <12>         1       Clear count of List Entry after fetching
-        * <11..10>     00      Address mode = Increment
-        * <9>          1       Terminate Buffer on end of frame
-        * <8>          0       Bus Width = 16bits
-        * <7..0>       ?       status Bits (Read Only so write as 0)
-        *
-        *      1111 0010 0000 0000 = 0xf200
-        */
-
-       usc_OutDmaReg( info, TDMR, 0xf200 );
-
-
-       /* DMA Interrupt Control Register (DICR)
-        *
-        * <15>         1       DMA Interrupt Enable
-        * <14>         0       1 = Disable IEO from USC
-        * <13>         0       1 = Don't provide vector during IntAck
-        * <12>         1       1 = Include status in Vector
-        * <10..2>      0       reserved, Must be 0s
-        * <1>          0       1 = Rx DMA Interrupt Enabled
-        * <0>          0       1 = Tx DMA Interrupt Enabled
-        *
-        *      1001 0000 0000 0000 = 0x9000
-        */
-
-       usc_OutDmaReg( info, DICR, 0x9000 );
-
-       usc_InDmaReg( info, RDMR );             /* clear pending receive DMA IRQ bits */
-       usc_InDmaReg( info, TDMR );             /* clear pending transmit DMA IRQ bits */
-       usc_OutDmaReg( info, CDIR, 0x0303 );    /* clear IUS and Pending for Tx and Rx */
-
-       /* Channel Control Register (CCR)
-        *
-        * <15..14>     10      Use 32-bit Tx Control Blocks (TCBs)
-        * <13>         0       Trigger Tx on SW Command Disabled
-        * <12>         0       Flag Preamble Disabled
-        * <11..10>     00      Preamble Length
-        * <9..8>       00      Preamble Pattern
-        * <7..6>       10      Use 32-bit Rx status Blocks (RSBs)
-        * <5>          0       Trigger Rx on SW Command Disabled
-        * <4..0>       0       reserved
-        *
-        *      1000 0000 1000 0000 = 0x8080
-        */
-
-       RegValue = 0x8080;
-
-       switch ( info->params.preamble_length ) {
-       case HDLC_PREAMBLE_LENGTH_16BITS: RegValue |= BIT10; break;
-       case HDLC_PREAMBLE_LENGTH_32BITS: RegValue |= BIT11; break;
-       case HDLC_PREAMBLE_LENGTH_64BITS: RegValue |= BIT11 + BIT10; break;
-       }
-
-       switch ( info->params.preamble ) {
-       case HDLC_PREAMBLE_PATTERN_FLAGS: RegValue |= BIT8 + BIT12; break;
-       case HDLC_PREAMBLE_PATTERN_ONES:  RegValue |= BIT8; break;
-       case HDLC_PREAMBLE_PATTERN_10:    RegValue |= BIT9; break;
-       case HDLC_PREAMBLE_PATTERN_01:    RegValue |= BIT9 + BIT8; break;
-       }
-
-       usc_OutReg( info, CCR, RegValue );
-
-
-       /*
-        * Burst/Dwell Control Register
-        *
-        * <15..8>      0x20    Maximum number of transfers per bus grant
-        * <7..0>       0x00    Maximum number of clock cycles per bus grant
-        */
-
-       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
-               /* don't limit bus occupancy on PCI adapter */
-               usc_OutDmaReg( info, BDCR, 0x0000 );
-       }
-       else
-               usc_OutDmaReg( info, BDCR, 0x2000 );
-
-       usc_stop_transmitter(info);
-       usc_stop_receiver(info);
-       
-}      /* end of usc_set_sdlc_mode() */
-
-/* usc_enable_loopback()
- *
- * Set the 16C32 for internal loopback mode.
- * The TxCLK and RxCLK signals are generated from the BRG0 and
- * the TxD is looped back to the RxD internally.
- *
- * Arguments:          info    pointer to device instance data
- *                     enable  1 = enable loopback, 0 = disable
- * Return Value:       None
- */
-static void usc_enable_loopback(struct mgsl_struct *info, int enable)
-{
-       if (enable) {
-               /* blank external TXD output */
-               usc_OutReg(info,IOCR,usc_InReg(info,IOCR) | (BIT7+BIT6));
-       
-               /* Clock mode Control Register (CMCR)
-                *
-                * <15..14>     00      counter 1 Disabled
-                * <13..12>     00      counter 0 Disabled
-                * <11..10>     11      BRG1 Input is TxC Pin
-                * <9..8>       11      BRG0 Input is TxC Pin
-                * <7..6>       01      DPLL Input is BRG1 Output
-                * <5..3>       100     TxCLK comes from BRG0
-                * <2..0>       100     RxCLK comes from BRG0
-                *
-                * 0000 1111 0110 0100 = 0x0f64
-                */
-
-               usc_OutReg( info, CMCR, 0x0f64 );
-
-               /* Write 16-bit Time Constant for BRG0 */
-               /* use clock speed if available, otherwise use 8 for diagnostics */
-               if (info->params.clock_speed) {
-                       if (info->bus_type == MGSL_BUS_TYPE_PCI)
-                               usc_OutReg(info, TC0R, (u16)((11059200/info->params.clock_speed)-1));
-                       else
-                               usc_OutReg(info, TC0R, (u16)((14745600/info->params.clock_speed)-1));
-               } else
-                       usc_OutReg(info, TC0R, (u16)8);
-
-               /* Hardware Configuration Register (HCR) Clear Bit 1, BRG0
-                  mode = Continuous Set Bit 0 to enable BRG0.  */
-               usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) );
-
-               /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */
-               usc_OutReg(info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004));
-
-               /* set Internal Data loopback mode */
-               info->loopback_bits = 0x300;
-               outw( 0x0300, info->io_base + CCAR );
-       } else {
-               /* enable external TXD output */
-               usc_OutReg(info,IOCR,usc_InReg(info,IOCR) & ~(BIT7+BIT6));
-       
-               /* clear Internal Data loopback mode */
-               info->loopback_bits = 0;
-               outw( 0,info->io_base + CCAR );
-       }
-       
-}      /* end of usc_enable_loopback() */
-
-/* usc_enable_aux_clock()
- *
- * Enabled the AUX clock output at the specified frequency.
- *
- * Arguments:
- *
- *     info            pointer to device extension
- *     data_rate       data rate of clock in bits per second
- *                     A data rate of 0 disables the AUX clock.
- *
- * Return Value:       None
- */
-static void usc_enable_aux_clock( struct mgsl_struct *info, u32 data_rate )
-{
-       u32 XtalSpeed;
-       u16 Tc;
-
-       if ( data_rate ) {
-               if ( info->bus_type == MGSL_BUS_TYPE_PCI )
-                       XtalSpeed = 11059200;
-               else
-                       XtalSpeed = 14745600;
-
-
-               /* Tc = (Xtal/Speed) - 1 */
-               /* If twice the remainder of (Xtal/Speed) is greater than Speed */
-               /* then rounding up gives a more precise time constant. Instead */
-               /* of rounding up and then subtracting 1 we just don't subtract */
-               /* the one in this case. */
-
-
-               Tc = (u16)(XtalSpeed/data_rate);
-               if ( !(((XtalSpeed % data_rate) * 2) / data_rate) )
-                       Tc--;
-
-               /* Write 16-bit Time Constant for BRG0 */
-               usc_OutReg( info, TC0R, Tc );
-
-               /*
-                * Hardware Configuration Register (HCR)
-                * Clear Bit 1, BRG0 mode = Continuous
-                * Set Bit 0 to enable BRG0.
-                */
-
-               usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) );
-
-               /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */
-               usc_OutReg( info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) );
-       } else {
-               /* data rate == 0 so turn off BRG0 */
-               usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) );
-       }
-
-}      /* end of usc_enable_aux_clock() */
-
-/*
- *
- * usc_process_rxoverrun_sync()
- *
- *             This function processes a receive overrun by resetting the
- *             receive DMA buffers and issuing a Purge Rx FIFO command
- *             to allow the receiver to continue receiving.
- *
- * Arguments:
- *
- *     info            pointer to device extension
- *
- * Return Value: None
- */
-static void usc_process_rxoverrun_sync( struct mgsl_struct *info )
-{
-       int start_index;
-       int end_index;
-       int frame_start_index;
-       bool start_of_frame_found = false;
-       bool end_of_frame_found = false;
-       bool reprogram_dma = false;
-
-       DMABUFFERENTRY *buffer_list = info->rx_buffer_list;
-       u32 phys_addr;
-
-       usc_DmaCmd( info, DmaCmd_PauseRxChannel );
-       usc_RCmd( info, RCmd_EnterHuntmode );
-       usc_RTCmd( info, RTCmd_PurgeRxFifo );
-
-       /* CurrentRxBuffer points to the 1st buffer of the next */
-       /* possibly available receive frame. */
-       
-       frame_start_index = start_index = end_index = info->current_rx_buffer;
-
-       /* Search for an unfinished string of buffers. This means */
-       /* that a receive frame started (at least one buffer with */
-       /* count set to zero) but there is no terminiting buffer */
-       /* (status set to non-zero). */
-
-       while( !buffer_list[end_index].count )
-       {
-               /* Count field has been reset to zero by 16C32. */
-               /* This buffer is currently in use. */
-
-               if ( !start_of_frame_found )
-               {
-                       start_of_frame_found = true;
-                       frame_start_index = end_index;
-                       end_of_frame_found = false;
-               }
-
-               if ( buffer_list[end_index].status )
-               {
-                       /* Status field has been set by 16C32. */
-                       /* This is the last buffer of a received frame. */
-
-                       /* We want to leave the buffers for this frame intact. */
-                       /* Move on to next possible frame. */
-
-                       start_of_frame_found = false;
-                       end_of_frame_found = true;
-               }
-
-               /* advance to next buffer entry in linked list */
-               end_index++;
-               if ( end_index == info->rx_buffer_count )
-                       end_index = 0;
-
-               if ( start_index == end_index )
-               {
-                       /* The entire list has been searched with all Counts == 0 and */
-                       /* all Status == 0. The receive buffers are */
-                       /* completely screwed, reset all receive buffers! */
-                       mgsl_reset_rx_dma_buffers( info );
-                       frame_start_index = 0;
-                       start_of_frame_found = false;
-                       reprogram_dma = true;
-                       break;
-               }
-       }
-
-       if ( start_of_frame_found && !end_of_frame_found )
-       {
-               /* There is an unfinished string of receive DMA buffers */
-               /* as a result of the receiver overrun. */
-
-               /* Reset the buffers for the unfinished frame */
-               /* and reprogram the receive DMA controller to start */
-               /* at the 1st buffer of unfinished frame. */
-
-               start_index = frame_start_index;
-
-               do
-               {
-                       *((unsigned long *)&(info->rx_buffer_list[start_index++].count)) = DMABUFFERSIZE;
-
-                       /* Adjust index for wrap around. */
-                       if ( start_index == info->rx_buffer_count )
-                               start_index = 0;
-
-               } while( start_index != end_index );
-
-               reprogram_dma = true;
-       }
-
-       if ( reprogram_dma )
-       {
-               usc_UnlatchRxstatusBits(info,RXSTATUS_ALL);
-               usc_ClearIrqPendingBits(info, RECEIVE_DATA|RECEIVE_STATUS);
-               usc_UnlatchRxstatusBits(info, RECEIVE_DATA|RECEIVE_STATUS);
-               
-               usc_EnableReceiver(info,DISABLE_UNCONDITIONAL);
-               
-               /* This empties the receive FIFO and loads the RCC with RCLR */
-               usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) );
-
-               /* program 16C32 with physical address of 1st DMA buffer entry */
-               phys_addr = info->rx_buffer_list[frame_start_index].phys_entry;
-               usc_OutDmaReg( info, NRARL, (u16)phys_addr );
-               usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) );
-
-               usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
-               usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS );
-               usc_EnableInterrupts( info, RECEIVE_STATUS );
-
-               /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */
-               /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */
-
-               usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 );
-               usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) );
-               usc_DmaCmd( info, DmaCmd_InitRxChannel );
-               if ( info->params.flags & HDLC_FLAG_AUTO_DCD )
-                       usc_EnableReceiver(info,ENABLE_AUTO_DCD);
-               else
-                       usc_EnableReceiver(info,ENABLE_UNCONDITIONAL);
-       }
-       else
-       {
-               /* This empties the receive FIFO and loads the RCC with RCLR */
-               usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) );
-               usc_RTCmd( info, RTCmd_PurgeRxFifo );
-       }
-
-}      /* end of usc_process_rxoverrun_sync() */
-
-/* usc_stop_receiver()
- *
- *     Disable USC receiver
- *
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void usc_stop_receiver( struct mgsl_struct *info )
-{
-       if (debug_level >= DEBUG_LEVEL_ISR)
-               printk("%s(%d):usc_stop_receiver(%s)\n",
-                        __FILE__,__LINE__, info->device_name );
-                        
-       /* Disable receive DMA channel. */
-       /* This also disables receive DMA channel interrupts */
-       usc_DmaCmd( info, DmaCmd_ResetRxChannel );
-
-       usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
-       usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS );
-       usc_DisableInterrupts( info, RECEIVE_DATA + RECEIVE_STATUS );
-
-       usc_EnableReceiver(info,DISABLE_UNCONDITIONAL);
-
-       /* This empties the receive FIFO and loads the RCC with RCLR */
-       usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) );
-       usc_RTCmd( info, RTCmd_PurgeRxFifo );
-
-       info->rx_enabled = false;
-       info->rx_overflow = false;
-       info->rx_rcc_underrun = false;
-       
-}      /* end of stop_receiver() */
-
-/* usc_start_receiver()
- *
- *     Enable the USC receiver 
- *
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void usc_start_receiver( struct mgsl_struct *info )
-{
-       u32 phys_addr;
-       
-       if (debug_level >= DEBUG_LEVEL_ISR)
-               printk("%s(%d):usc_start_receiver(%s)\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       mgsl_reset_rx_dma_buffers( info );
-       usc_stop_receiver( info );
-
-       usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) );
-       usc_RTCmd( info, RTCmd_PurgeRxFifo );
-
-       if ( info->params.mode == MGSL_MODE_HDLC ||
-               info->params.mode == MGSL_MODE_RAW ) {
-               /* DMA mode Transfers */
-               /* Program the DMA controller. */
-               /* Enable the DMA controller end of buffer interrupt. */
-
-               /* program 16C32 with physical address of 1st DMA buffer entry */
-               phys_addr = info->rx_buffer_list[0].phys_entry;
-               usc_OutDmaReg( info, NRARL, (u16)phys_addr );
-               usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) );
-
-               usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
-               usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS );
-               usc_EnableInterrupts( info, RECEIVE_STATUS );
-
-               /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */
-               /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */
-
-               usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 );
-               usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) );
-               usc_DmaCmd( info, DmaCmd_InitRxChannel );
-               if ( info->params.flags & HDLC_FLAG_AUTO_DCD )
-                       usc_EnableReceiver(info,ENABLE_AUTO_DCD);
-               else
-                       usc_EnableReceiver(info,ENABLE_UNCONDITIONAL);
-       } else {
-               usc_UnlatchRxstatusBits(info, RXSTATUS_ALL);
-               usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS);
-               usc_EnableInterrupts(info, RECEIVE_DATA);
-
-               usc_RTCmd( info, RTCmd_PurgeRxFifo );
-               usc_RCmd( info, RCmd_EnterHuntmode );
-
-               usc_EnableReceiver(info,ENABLE_UNCONDITIONAL);
-       }
-
-       usc_OutReg( info, CCSR, 0x1020 );
-
-       info->rx_enabled = true;
-
-}      /* end of usc_start_receiver() */
-
-/* usc_start_transmitter()
- *
- *     Enable the USC transmitter and send a transmit frame if
- *     one is loaded in the DMA buffers.
- *
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void usc_start_transmitter( struct mgsl_struct *info )
-{
-       u32 phys_addr;
-       unsigned int FrameSize;
-
-       if (debug_level >= DEBUG_LEVEL_ISR)
-               printk("%s(%d):usc_start_transmitter(%s)\n",
-                        __FILE__,__LINE__, info->device_name );
-                        
-       if ( info->xmit_cnt ) {
-
-               /* If auto RTS enabled and RTS is inactive, then assert */
-               /* RTS and set a flag indicating that the driver should */
-               /* negate RTS when the transmission completes. */
-
-               info->drop_rts_on_tx_done = false;
-
-               if ( info->params.flags & HDLC_FLAG_AUTO_RTS ) {
-                       usc_get_serial_signals( info );
-                       if ( !(info->serial_signals & SerialSignal_RTS) ) {
-                               info->serial_signals |= SerialSignal_RTS;
-                               usc_set_serial_signals( info );
-                               info->drop_rts_on_tx_done = true;
-                       }
-               }
-
-
-               if ( info->params.mode == MGSL_MODE_ASYNC ) {
-                       if ( !info->tx_active ) {
-                               usc_UnlatchTxstatusBits(info, TXSTATUS_ALL);
-                               usc_ClearIrqPendingBits(info, TRANSMIT_STATUS + TRANSMIT_DATA);
-                               usc_EnableInterrupts(info, TRANSMIT_DATA);
-                               usc_load_txfifo(info);
-                       }
-               } else {
-                       /* Disable transmit DMA controller while programming. */
-                       usc_DmaCmd( info, DmaCmd_ResetTxChannel );
-                       
-                       /* Transmit DMA buffer is loaded, so program USC */
-                       /* to send the frame contained in the buffers.   */
-
-                       FrameSize = info->tx_buffer_list[info->start_tx_dma_buffer].rcc;
-
-                       /* if operating in Raw sync mode, reset the rcc component
-                        * of the tx dma buffer entry, otherwise, the serial controller
-                        * will send a closing sync char after this count.
-                        */
-                       if ( info->params.mode == MGSL_MODE_RAW )
-                               info->tx_buffer_list[info->start_tx_dma_buffer].rcc = 0;
-
-                       /* Program the Transmit Character Length Register (TCLR) */
-                       /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */
-                       usc_OutReg( info, TCLR, (u16)FrameSize );
-
-                       usc_RTCmd( info, RTCmd_PurgeTxFifo );
-
-                       /* Program the address of the 1st DMA Buffer Entry in linked list */
-                       phys_addr = info->tx_buffer_list[info->start_tx_dma_buffer].phys_entry;
-                       usc_OutDmaReg( info, NTARL, (u16)phys_addr );
-                       usc_OutDmaReg( info, NTARU, (u16)(phys_addr >> 16) );
-
-                       usc_UnlatchTxstatusBits( info, TXSTATUS_ALL );
-                       usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
-                       usc_EnableInterrupts( info, TRANSMIT_STATUS );
-
-                       if ( info->params.mode == MGSL_MODE_RAW &&
-                                       info->num_tx_dma_buffers > 1 ) {
-                          /* When running external sync mode, attempt to 'stream' transmit  */
-                          /* by filling tx dma buffers as they become available. To do this */
-                          /* we need to enable Tx DMA EOB Status interrupts :               */
-                          /*                                                                */
-                          /* 1. Arm End of Buffer (EOB) Transmit DMA Interrupt (BIT2 of TDIAR) */
-                          /* 2. Enable Transmit DMA Interrupts (BIT0 of DICR) */
-
-                          usc_OutDmaReg( info, TDIAR, BIT2|BIT3 );
-                          usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT0) );
-                       }
-
-                       /* Initialize Transmit DMA Channel */
-                       usc_DmaCmd( info, DmaCmd_InitTxChannel );
-                       
-                       usc_TCmd( info, TCmd_SendFrame );
-                       
-                       mod_timer(&info->tx_timer, jiffies +
-                                       msecs_to_jiffies(5000));
-               }
-               info->tx_active = true;
-       }
-
-       if ( !info->tx_enabled ) {
-               info->tx_enabled = true;
-               if ( info->params.flags & HDLC_FLAG_AUTO_CTS )
-                       usc_EnableTransmitter(info,ENABLE_AUTO_CTS);
-               else
-                       usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL);
-       }
-
-}      /* end of usc_start_transmitter() */
-
-/* usc_stop_transmitter()
- *
- *     Stops the transmitter and DMA
- *
- * Arguments:          info    pointer to device isntance data
- * Return Value:       None
- */
-static void usc_stop_transmitter( struct mgsl_struct *info )
-{
-       if (debug_level >= DEBUG_LEVEL_ISR)
-               printk("%s(%d):usc_stop_transmitter(%s)\n",
-                        __FILE__,__LINE__, info->device_name );
-                        
-       del_timer(&info->tx_timer);     
-                        
-       usc_UnlatchTxstatusBits( info, TXSTATUS_ALL );
-       usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA );
-       usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA );
-
-       usc_EnableTransmitter(info,DISABLE_UNCONDITIONAL);
-       usc_DmaCmd( info, DmaCmd_ResetTxChannel );
-       usc_RTCmd( info, RTCmd_PurgeTxFifo );
-
-       info->tx_enabled = false;
-       info->tx_active = false;
-
-}      /* end of usc_stop_transmitter() */
-
-/* usc_load_txfifo()
- *
- *     Fill the transmit FIFO until the FIFO is full or
- *     there is no more data to load.
- *
- * Arguments:          info    pointer to device extension (instance data)
- * Return Value:       None
- */
-static void usc_load_txfifo( struct mgsl_struct *info )
-{
-       int Fifocount;
-       u8 TwoBytes[2];
-       
-       if ( !info->xmit_cnt && !info->x_char )
-               return; 
-               
-       /* Select transmit FIFO status readback in TICR */
-       usc_TCmd( info, TCmd_SelectTicrTxFifostatus );
-
-       /* load the Transmit FIFO until FIFOs full or all data sent */
-
-       while( (Fifocount = usc_InReg(info, TICR) >> 8) && info->xmit_cnt ) {
-               /* there is more space in the transmit FIFO and */
-               /* there is more data in transmit buffer */
-
-               if ( (info->xmit_cnt > 1) && (Fifocount > 1) && !info->x_char ) {
-                       /* write a 16-bit word from transmit buffer to 16C32 */
-                               
-                       TwoBytes[0] = info->xmit_buf[info->xmit_tail++];
-                       info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
-                       TwoBytes[1] = info->xmit_buf[info->xmit_tail++];
-                       info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
-                       
-                       outw( *((u16 *)TwoBytes), info->io_base + DATAREG);
-                               
-                       info->xmit_cnt -= 2;
-                       info->icount.tx += 2;
-               } else {
-                       /* only 1 byte left to transmit or 1 FIFO slot left */
-                       
-                       outw( (inw( info->io_base + CCAR) & 0x0780) | (TDR+LSBONLY),
-                               info->io_base + CCAR );
-                       
-                       if (info->x_char) {
-                               /* transmit pending high priority char */
-                               outw( info->x_char,info->io_base + CCAR );
-                               info->x_char = 0;
-                       } else {
-                               outw( info->xmit_buf[info->xmit_tail++],info->io_base + CCAR );
-                               info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
-                               info->xmit_cnt--;
-                       }
-                       info->icount.tx++;
-               }
-       }
-
-}      /* end of usc_load_txfifo() */
-
-/* usc_reset()
- *
- *     Reset the adapter to a known state and prepare it for further use.
- *
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void usc_reset( struct mgsl_struct *info )
-{
-       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
-               int i;
-               u32 readval;
-
-               /* Set BIT30 of Misc Control Register */
-               /* (Local Control Register 0x50) to force reset of USC. */
-
-               volatile u32 *MiscCtrl = (u32 *)(info->lcr_base + 0x50);
-               u32 *LCR0BRDR = (u32 *)(info->lcr_base + 0x28);
-
-               info->misc_ctrl_value |= BIT30;
-               *MiscCtrl = info->misc_ctrl_value;
-
-               /*
-                * Force at least 170ns delay before clearing 
-                * reset bit. Each read from LCR takes at least 
-                * 30ns so 10 times for 300ns to be safe.
-                */
-               for(i=0;i<10;i++)
-                       readval = *MiscCtrl;
-
-               info->misc_ctrl_value &= ~BIT30;
-               *MiscCtrl = info->misc_ctrl_value;
-
-               *LCR0BRDR = BUS_DESCRIPTOR(
-                       1,              // Write Strobe Hold (0-3)
-                       2,              // Write Strobe Delay (0-3)
-                       2,              // Read Strobe Delay  (0-3)
-                       0,              // NWDD (Write data-data) (0-3)
-                       4,              // NWAD (Write Addr-data) (0-31)
-                       0,              // NXDA (Read/Write Data-Addr) (0-3)
-                       0,              // NRDD (Read Data-Data) (0-3)
-                       5               // NRAD (Read Addr-Data) (0-31)
-                       );
-       } else {
-               /* do HW reset */
-               outb( 0,info->io_base + 8 );
-       }
-
-       info->mbre_bit = 0;
-       info->loopback_bits = 0;
-       info->usc_idle_mode = 0;
-
-       /*
-        * Program the Bus Configuration Register (BCR)
-        *
-        * <15>         0       Don't use separate address
-        * <14..6>      0       reserved
-        * <5..4>       00      IAckmode = Default, don't care
-        * <3>          1       Bus Request Totem Pole output
-        * <2>          1       Use 16 Bit data bus
-        * <1>          0       IRQ Totem Pole output
-        * <0>          0       Don't Shift Right Addr
-        *
-        * 0000 0000 0000 1100 = 0x000c
-        *
-        * By writing to io_base + SDPIN the Wait/Ack pin is
-        * programmed to work as a Wait pin.
-        */
-       
-       outw( 0x000c,info->io_base + SDPIN );
-
-
-       outw( 0,info->io_base );
-       outw( 0,info->io_base + CCAR );
-
-       /* select little endian byte ordering */
-       usc_RTCmd( info, RTCmd_SelectLittleEndian );
-
-
-       /* Port Control Register (PCR)
-        *
-        * <15..14>     11      Port 7 is Output (~DMAEN, Bit 14 : 0 = Enabled)
-        * <13..12>     11      Port 6 is Output (~INTEN, Bit 12 : 0 = Enabled)
-        * <11..10>     00      Port 5 is Input (No Connect, Don't Care)
-        * <9..8>       00      Port 4 is Input (No Connect, Don't Care)
-        * <7..6>       11      Port 3 is Output (~RTS, Bit 6 : 0 = Enabled )
-        * <5..4>       11      Port 2 is Output (~DTR, Bit 4 : 0 = Enabled )
-        * <3..2>       01      Port 1 is Input (Dedicated RxC)
-        * <1..0>       01      Port 0 is Input (Dedicated TxC)
-        *
-        *      1111 0000 1111 0101 = 0xf0f5
-        */
-
-       usc_OutReg( info, PCR, 0xf0f5 );
-
-
-       /*
-        * Input/Output Control Register
-        *
-        * <15..14>     00      CTS is active low input
-        * <13..12>     00      DCD is active low input
-        * <11..10>     00      TxREQ pin is input (DSR)
-        * <9..8>       00      RxREQ pin is input (RI)
-        * <7..6>       00      TxD is output (Transmit Data)
-        * <5..3>       000     TxC Pin in Input (14.7456MHz Clock)
-        * <2..0>       100     RxC is Output (drive with BRG0)
-        *
-        *      0000 0000 0000 0100 = 0x0004
-        */
-
-       usc_OutReg( info, IOCR, 0x0004 );
-
-}      /* end of usc_reset() */
-
-/* usc_set_async_mode()
- *
- *     Program adapter for asynchronous communications.
- *
- * Arguments:          info            pointer to device instance data
- * Return Value:       None
- */
-static void usc_set_async_mode( struct mgsl_struct *info )
-{
-       u16 RegValue;
-
-       /* disable interrupts while programming USC */
-       usc_DisableMasterIrqBit( info );
-
-       outw( 0, info->io_base );                       /* clear Master Bus Enable (DCAR) */
-       usc_DmaCmd( info, DmaCmd_ResetAllChannels );    /* disable both DMA channels */
-
-       usc_loopback_frame( info );
-
-       /* Channel mode Register (CMR)
-        *
-        * <15..14>     00      Tx Sub modes, 00 = 1 Stop Bit
-        * <13..12>     00                    00 = 16X Clock
-        * <11..8>      0000    Transmitter mode = Asynchronous
-        * <7..6>       00      reserved?
-        * <5..4>       00      Rx Sub modes, 00 = 16X Clock
-        * <3..0>       0000    Receiver mode = Asynchronous
-        *
-        * 0000 0000 0000 0000 = 0x0
-        */
-
-       RegValue = 0;
-       if ( info->params.stop_bits != 1 )
-               RegValue |= BIT14;
-       usc_OutReg( info, CMR, RegValue );
-
-       
-       /* Receiver mode Register (RMR)
-        *
-        * <15..13>     000     encoding = None
-        * <12..08>     00000   reserved (Sync Only)
-        * <7..6>       00      Even parity
-        * <5>          0       parity disabled
-        * <4..2>       000     Receive Char Length = 8 bits
-        * <1..0>       00      Disable Receiver
-        *
-        * 0000 0000 0000 0000 = 0x0
-        */
-
-       RegValue = 0;
-
-       if ( info->params.data_bits != 8 )
-               RegValue |= BIT4+BIT3+BIT2;
-
-       if ( info->params.parity != ASYNC_PARITY_NONE ) {
-               RegValue |= BIT5;
-               if ( info->params.parity != ASYNC_PARITY_ODD )
-                       RegValue |= BIT6;
-       }
-
-       usc_OutReg( info, RMR, RegValue );
-
-
-       /* Set IRQ trigger level */
-
-       usc_RCmd( info, RCmd_SelectRicrIntLevel );
-
-       
-       /* Receive Interrupt Control Register (RICR)
-        *
-        * <15..8>      ?               RxFIFO IRQ Request Level
-        *
-        * Note: For async mode the receive FIFO level must be set
-        * to 0 to avoid the situation where the FIFO contains fewer bytes
-        * than the trigger level and no more data is expected.
-        *
-        * <7>          0               Exited Hunt IA (Interrupt Arm)
-        * <6>          0               Idle Received IA
-        * <5>          0               Break/Abort IA
-        * <4>          0               Rx Bound IA
-        * <3>          0               Queued status reflects oldest byte in FIFO
-        * <2>          0               Abort/PE IA
-        * <1>          0               Rx Overrun IA
-        * <0>          0               Select TC0 value for readback
-        *
-        * 0000 0000 0100 0000 = 0x0000 + (FIFOLEVEL in MSB)
-        */
-       
-       usc_OutReg( info, RICR, 0x0000 );
-
-       usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
-       usc_ClearIrqPendingBits( info, RECEIVE_STATUS );
-
-       
-       /* Transmit mode Register (TMR)
-        *
-        * <15..13>     000     encoding = None
-        * <12..08>     00000   reserved (Sync Only)
-        * <7..6>       00      Transmit parity Even
-        * <5>          0       Transmit parity Disabled
-        * <4..2>       000     Tx Char Length = 8 bits
-        * <1..0>       00      Disable Transmitter
-        *
-        * 0000 0000 0000 0000 = 0x0
-        */
-
-       RegValue = 0;
-
-       if ( info->params.data_bits != 8 )
-               RegValue |= BIT4+BIT3+BIT2;
-
-       if ( info->params.parity != ASYNC_PARITY_NONE ) {
-               RegValue |= BIT5;
-               if ( info->params.parity != ASYNC_PARITY_ODD )
-                       RegValue |= BIT6;
-       }
-
-       usc_OutReg( info, TMR, RegValue );
-
-       usc_set_txidle( info );
-
-
-       /* Set IRQ trigger level */
-
-       usc_TCmd( info, TCmd_SelectTicrIntLevel );
-
-       
-       /* Transmit Interrupt Control Register (TICR)
-        *
-        * <15..8>      ?       Transmit FIFO IRQ Level
-        * <7>          0       Present IA (Interrupt Arm)
-        * <6>          1       Idle Sent IA
-        * <5>          0       Abort Sent IA
-        * <4>          0       EOF/EOM Sent IA
-        * <3>          0       CRC Sent IA
-        * <2>          0       1 = Wait for SW Trigger to Start Frame
-        * <1>          0       Tx Underrun IA
-        * <0>          0       TC0 constant on read back
-        *
-        *      0000 0000 0100 0000 = 0x0040
-        */
-
-       usc_OutReg( info, TICR, 0x1f40 );
-
-       usc_UnlatchTxstatusBits( info, TXSTATUS_ALL );
-       usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
-
-       usc_enable_async_clock( info, info->params.data_rate );
-
-       
-       /* Channel Control/status Register (CCSR)
-        *
-        * <15>         X       RCC FIFO Overflow status (RO)
-        * <14>         X       RCC FIFO Not Empty status (RO)
-        * <13>         0       1 = Clear RCC FIFO (WO)
-        * <12>         X       DPLL in Sync status (RO)
-        * <11>         X       DPLL 2 Missed Clocks status (RO)
-        * <10>         X       DPLL 1 Missed Clock status (RO)
-        * <9..8>       00      DPLL Resync on rising and falling edges (RW)
-        * <7>          X       SDLC Loop On status (RO)
-        * <6>          X       SDLC Loop Send status (RO)
-        * <5>          1       Bypass counters for TxClk and RxClk (RW)
-        * <4..2>       000     Last Char of SDLC frame has 8 bits (RW)
-        * <1..0>       00      reserved
-        *
-        *      0000 0000 0010 0000 = 0x0020
-        */
-       
-       usc_OutReg( info, CCSR, 0x0020 );
-
-       usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA +
-                             RECEIVE_DATA + RECEIVE_STATUS );
-
-       usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA +
-                               RECEIVE_DATA + RECEIVE_STATUS );
-
-       usc_EnableMasterIrqBit( info );
-
-       if (info->bus_type == MGSL_BUS_TYPE_ISA) {
-               /* Enable INTEN (Port 6, Bit12) */
-               /* This connects the IRQ request signal to the ISA bus */
-               usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12));
-       }
-
-       if (info->params.loopback) {
-               info->loopback_bits = 0x300;
-               outw(0x0300, info->io_base + CCAR);
-       }
-
-}      /* end of usc_set_async_mode() */
-
-/* usc_loopback_frame()
- *
- *     Loop back a small (2 byte) dummy SDLC frame.
- *     Interrupts and DMA are NOT used. The purpose of this is to
- *     clear any 'stale' status info left over from running in async mode.
- *
- *     The 16C32 shows the strange behaviour of marking the 1st
- *     received SDLC frame with a CRC error even when there is no
- *     CRC error. To get around this a small dummy from of 2 bytes
- *     is looped back when switching from async to sync mode.
- *
- * Arguments:          info            pointer to device instance data
- * Return Value:       None
- */
-static void usc_loopback_frame( struct mgsl_struct *info )
-{
-       int i;
-       unsigned long oldmode = info->params.mode;
-
-       info->params.mode = MGSL_MODE_HDLC;
-       
-       usc_DisableMasterIrqBit( info );
-
-       usc_set_sdlc_mode( info );
-       usc_enable_loopback( info, 1 );
-
-       /* Write 16-bit Time Constant for BRG0 */
-       usc_OutReg( info, TC0R, 0 );
-       
-       /* Channel Control Register (CCR)
-        *
-        * <15..14>     00      Don't use 32-bit Tx Control Blocks (TCBs)
-        * <13>         0       Trigger Tx on SW Command Disabled
-        * <12>         0       Flag Preamble Disabled
-        * <11..10>     00      Preamble Length = 8-Bits
-        * <9..8>       01      Preamble Pattern = flags
-        * <7..6>       10      Don't use 32-bit Rx status Blocks (RSBs)
-        * <5>          0       Trigger Rx on SW Command Disabled
-        * <4..0>       0       reserved
-        *
-        *      0000 0001 0000 0000 = 0x0100
-        */
-
-       usc_OutReg( info, CCR, 0x0100 );
-
-       /* SETUP RECEIVER */
-       usc_RTCmd( info, RTCmd_PurgeRxFifo );
-       usc_EnableReceiver(info,ENABLE_UNCONDITIONAL);
-
-       /* SETUP TRANSMITTER */
-       /* Program the Transmit Character Length Register (TCLR) */
-       /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */
-       usc_OutReg( info, TCLR, 2 );
-       usc_RTCmd( info, RTCmd_PurgeTxFifo );
-
-       /* unlatch Tx status bits, and start transmit channel. */
-       usc_UnlatchTxstatusBits(info,TXSTATUS_ALL);
-       outw(0,info->io_base + DATAREG);
-
-       /* ENABLE TRANSMITTER */
-       usc_TCmd( info, TCmd_SendFrame );
-       usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL);
-                                                       
-       /* WAIT FOR RECEIVE COMPLETE */
-       for (i=0 ; i<1000 ; i++)
-               if (usc_InReg( info, RCSR ) & (BIT8 + BIT4 + BIT3 + BIT1))
-                       break;
-
-       /* clear Internal Data loopback mode */
-       usc_enable_loopback(info, 0);
-
-       usc_EnableMasterIrqBit(info);
-
-       info->params.mode = oldmode;
-
-}      /* end of usc_loopback_frame() */
-
-/* usc_set_sync_mode() Programs the USC for SDLC communications.
- *
- * Arguments:          info    pointer to adapter info structure
- * Return Value:       None
- */
-static void usc_set_sync_mode( struct mgsl_struct *info )
-{
-       usc_loopback_frame( info );
-       usc_set_sdlc_mode( info );
-
-       if (info->bus_type == MGSL_BUS_TYPE_ISA) {
-               /* Enable INTEN (Port 6, Bit12) */
-               /* This connects the IRQ request signal to the ISA bus */
-               usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12));
-       }
-
-       usc_enable_aux_clock(info, info->params.clock_speed);
-
-       if (info->params.loopback)
-               usc_enable_loopback(info,1);
-
-}      /* end of mgsl_set_sync_mode() */
-
-/* usc_set_txidle()    Set the HDLC idle mode for the transmitter.
- *
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void usc_set_txidle( struct mgsl_struct *info )
-{
-       u16 usc_idle_mode = IDLEMODE_FLAGS;
-
-       /* Map API idle mode to USC register bits */
-
-       switch( info->idle_mode ){
-       case HDLC_TXIDLE_FLAGS:                 usc_idle_mode = IDLEMODE_FLAGS; break;
-       case HDLC_TXIDLE_ALT_ZEROS_ONES:        usc_idle_mode = IDLEMODE_ALT_ONE_ZERO; break;
-       case HDLC_TXIDLE_ZEROS:                 usc_idle_mode = IDLEMODE_ZERO; break;
-       case HDLC_TXIDLE_ONES:                  usc_idle_mode = IDLEMODE_ONE; break;
-       case HDLC_TXIDLE_ALT_MARK_SPACE:        usc_idle_mode = IDLEMODE_ALT_MARK_SPACE; break;
-       case HDLC_TXIDLE_SPACE:                 usc_idle_mode = IDLEMODE_SPACE; break;
-       case HDLC_TXIDLE_MARK:                  usc_idle_mode = IDLEMODE_MARK; break;
-       }
-
-       info->usc_idle_mode = usc_idle_mode;
-       //usc_OutReg(info, TCSR, usc_idle_mode);
-       info->tcsr_value &= ~IDLEMODE_MASK;     /* clear idle mode bits */
-       info->tcsr_value += usc_idle_mode;
-       usc_OutReg(info, TCSR, info->tcsr_value);
-
-       /*
-        * if SyncLink WAN adapter is running in external sync mode, the
-        * transmitter has been set to Monosync in order to try to mimic
-        * a true raw outbound bit stream. Monosync still sends an open/close
-        * sync char at the start/end of a frame. Try to match those sync
-        * patterns to the idle mode set here
-        */
-       if ( info->params.mode == MGSL_MODE_RAW ) {
-               unsigned char syncpat = 0;
-               switch( info->idle_mode ) {
-               case HDLC_TXIDLE_FLAGS:
-                       syncpat = 0x7e;
-                       break;
-               case HDLC_TXIDLE_ALT_ZEROS_ONES:
-                       syncpat = 0x55;
-                       break;
-               case HDLC_TXIDLE_ZEROS:
-               case HDLC_TXIDLE_SPACE:
-                       syncpat = 0x00;
-                       break;
-               case HDLC_TXIDLE_ONES:
-               case HDLC_TXIDLE_MARK:
-                       syncpat = 0xff;
-                       break;
-               case HDLC_TXIDLE_ALT_MARK_SPACE:
-                       syncpat = 0xaa;
-                       break;
-               }
-
-               usc_SetTransmitSyncChars(info,syncpat,syncpat);
-       }
-
-}      /* end of usc_set_txidle() */
-
-/* usc_get_serial_signals()
- *
- *     Query the adapter for the state of the V24 status (input) signals.
- *
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void usc_get_serial_signals( struct mgsl_struct *info )
-{
-       u16 status;
-
-       /* clear all serial signals except DTR and RTS */
-       info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS;
-
-       /* Read the Misc Interrupt status Register (MISR) to get */
-       /* the V24 status signals. */
-
-       status = usc_InReg( info, MISR );
-
-       /* set serial signal bits to reflect MISR */
-
-       if ( status & MISCSTATUS_CTS )
-               info->serial_signals |= SerialSignal_CTS;
-
-       if ( status & MISCSTATUS_DCD )
-               info->serial_signals |= SerialSignal_DCD;
-
-       if ( status & MISCSTATUS_RI )
-               info->serial_signals |= SerialSignal_RI;
-
-       if ( status & MISCSTATUS_DSR )
-               info->serial_signals |= SerialSignal_DSR;
-
-}      /* end of usc_get_serial_signals() */
-
-/* usc_set_serial_signals()
- *
- *     Set the state of DTR and RTS based on contents of
- *     serial_signals member of device extension.
- *     
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void usc_set_serial_signals( struct mgsl_struct *info )
-{
-       u16 Control;
-       unsigned char V24Out = info->serial_signals;
-
-       /* get the current value of the Port Control Register (PCR) */
-
-       Control = usc_InReg( info, PCR );
-
-       if ( V24Out & SerialSignal_RTS )
-               Control &= ~(BIT6);
-       else
-               Control |= BIT6;
-
-       if ( V24Out & SerialSignal_DTR )
-               Control &= ~(BIT4);
-       else
-               Control |= BIT4;
-
-       usc_OutReg( info, PCR, Control );
-
-}      /* end of usc_set_serial_signals() */
-
-/* usc_enable_async_clock()
- *
- *     Enable the async clock at the specified frequency.
- *
- * Arguments:          info            pointer to device instance data
- *                     data_rate       data rate of clock in bps
- *                                     0 disables the AUX clock.
- * Return Value:       None
- */
-static void usc_enable_async_clock( struct mgsl_struct *info, u32 data_rate )
-{
-       if ( data_rate )        {
-               /*
-                * Clock mode Control Register (CMCR)
-                * 
-                * <15..14>     00      counter 1 Disabled
-                * <13..12>     00      counter 0 Disabled
-                * <11..10>     11      BRG1 Input is TxC Pin
-                * <9..8>       11      BRG0 Input is TxC Pin
-                * <7..6>       01      DPLL Input is BRG1 Output
-                * <5..3>       100     TxCLK comes from BRG0
-                * <2..0>       100     RxCLK comes from BRG0
-                *
-                * 0000 1111 0110 0100 = 0x0f64
-                */
-               
-               usc_OutReg( info, CMCR, 0x0f64 );
-
-
-               /*
-                * Write 16-bit Time Constant for BRG0
-                * Time Constant = (ClkSpeed / data_rate) - 1
-                * ClkSpeed = 921600 (ISA), 691200 (PCI)
-                */
-
-               if ( info->bus_type == MGSL_BUS_TYPE_PCI )
-                       usc_OutReg( info, TC0R, (u16)((691200/data_rate) - 1) );
-               else
-                       usc_OutReg( info, TC0R, (u16)((921600/data_rate) - 1) );
-
-               
-               /*
-                * Hardware Configuration Register (HCR)
-                * Clear Bit 1, BRG0 mode = Continuous
-                * Set Bit 0 to enable BRG0.
-                */
-
-               usc_OutReg( info, HCR,
-                           (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) );
-
-
-               /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */
-
-               usc_OutReg( info, IOCR,
-                           (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) );
-       } else {
-               /* data rate == 0 so turn off BRG0 */
-               usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) );
-       }
-
-}      /* end of usc_enable_async_clock() */
-
-/*
- * Buffer Structures:
- *
- * Normal memory access uses virtual addresses that can make discontiguous
- * physical memory pages appear to be contiguous in the virtual address
- * space (the processors memory mapping handles the conversions).
- *
- * DMA transfers require physically contiguous memory. This is because
- * the DMA system controller and DMA bus masters deal with memory using
- * only physical addresses.
- *
- * This causes a problem under Windows NT when large DMA buffers are
- * needed. Fragmentation of the nonpaged pool prevents allocations of
- * physically contiguous buffers larger than the PAGE_SIZE.
- *
- * However the 16C32 supports Bus Master Scatter/Gather DMA which
- * allows DMA transfers to physically discontiguous buffers. Information
- * about each data transfer buffer is contained in a memory structure
- * called a 'buffer entry'. A list of buffer entries is maintained
- * to track and control the use of the data transfer buffers.
- *
- * To support this strategy we will allocate sufficient PAGE_SIZE
- * contiguous memory buffers to allow for the total required buffer
- * space.
- *
- * The 16C32 accesses the list of buffer entries using Bus Master
- * DMA. Control information is read from the buffer entries by the
- * 16C32 to control data transfers. status information is written to
- * the buffer entries by the 16C32 to indicate the status of completed
- * transfers.
- *
- * The CPU writes control information to the buffer entries to control
- * the 16C32 and reads status information from the buffer entries to
- * determine information about received and transmitted frames.
- *
- * Because the CPU and 16C32 (adapter) both need simultaneous access
- * to the buffer entries, the buffer entry memory is allocated with
- * HalAllocateCommonBuffer(). This restricts the size of the buffer
- * entry list to PAGE_SIZE.
- *
- * The actual data buffers on the other hand will only be accessed
- * by the CPU or the adapter but not by both simultaneously. This allows
- * Scatter/Gather packet based DMA procedures for using physically
- * discontiguous pages.
- */
-
-/*
- * mgsl_reset_tx_dma_buffers()
- *
- *     Set the count for all transmit buffers to 0 to indicate the
- *     buffer is available for use and set the current buffer to the
- *     first buffer. This effectively makes all buffers free and
- *     discards any data in buffers.
- *
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void mgsl_reset_tx_dma_buffers( struct mgsl_struct *info )
-{
-       unsigned int i;
-
-       for ( i = 0; i < info->tx_buffer_count; i++ ) {
-               *((unsigned long *)&(info->tx_buffer_list[i].count)) = 0;
-       }
-
-       info->current_tx_buffer = 0;
-       info->start_tx_dma_buffer = 0;
-       info->tx_dma_buffers_used = 0;
-
-       info->get_tx_holding_index = 0;
-       info->put_tx_holding_index = 0;
-       info->tx_holding_count = 0;
-
-}      /* end of mgsl_reset_tx_dma_buffers() */
-
-/*
- * num_free_tx_dma_buffers()
- *
- *     returns the number of free tx dma buffers available
- *
- * Arguments:          info    pointer to device instance data
- * Return Value:       number of free tx dma buffers
- */
-static int num_free_tx_dma_buffers(struct mgsl_struct *info)
-{
-       return info->tx_buffer_count - info->tx_dma_buffers_used;
-}
-
-/*
- * mgsl_reset_rx_dma_buffers()
- * 
- *     Set the count for all receive buffers to DMABUFFERSIZE
- *     and set the current buffer to the first buffer. This effectively
- *     makes all buffers free and discards any data in buffers.
- * 
- * Arguments:          info    pointer to device instance data
- * Return Value:       None
- */
-static void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info )
-{
-       unsigned int i;
-
-       for ( i = 0; i < info->rx_buffer_count; i++ ) {
-               *((unsigned long *)&(info->rx_buffer_list[i].count)) = DMABUFFERSIZE;
-//             info->rx_buffer_list[i].count = DMABUFFERSIZE;
-//             info->rx_buffer_list[i].status = 0;
-       }
-
-       info->current_rx_buffer = 0;
-
-}      /* end of mgsl_reset_rx_dma_buffers() */
-
-/*
- * mgsl_free_rx_frame_buffers()
- * 
- *     Free the receive buffers used by a received SDLC
- *     frame such that the buffers can be reused.
- * 
- * Arguments:
- * 
- *     info                    pointer to device instance data
- *     StartIndex              index of 1st receive buffer of frame
- *     EndIndex                index of last receive buffer of frame
- * 
- * Return Value:       None
- */
-static void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex )
-{
-       bool Done = false;
-       DMABUFFERENTRY *pBufEntry;
-       unsigned int Index;
-
-       /* Starting with 1st buffer entry of the frame clear the status */
-       /* field and set the count field to DMA Buffer Size. */
-
-       Index = StartIndex;
-
-       while( !Done ) {
-               pBufEntry = &(info->rx_buffer_list[Index]);
-
-               if ( Index == EndIndex ) {
-                       /* This is the last buffer of the frame! */
-                       Done = true;
-               }
-
-               /* reset current buffer for reuse */
-//             pBufEntry->status = 0;
-//             pBufEntry->count = DMABUFFERSIZE;
-               *((unsigned long *)&(pBufEntry->count)) = DMABUFFERSIZE;
-
-               /* advance to next buffer entry in linked list */
-               Index++;
-               if ( Index == info->rx_buffer_count )
-                       Index = 0;
-       }
-
-       /* set current buffer to next buffer after last buffer of frame */
-       info->current_rx_buffer = Index;
-
-}      /* end of free_rx_frame_buffers() */
-
-/* mgsl_get_rx_frame()
- * 
- *     This function attempts to return a received SDLC frame from the
- *     receive DMA buffers. Only frames received without errors are returned.
- *
- * Arguments:          info    pointer to device extension
- * Return Value:       true if frame returned, otherwise false
- */
-static bool mgsl_get_rx_frame(struct mgsl_struct *info)
-{
-       unsigned int StartIndex, EndIndex;      /* index of 1st and last buffers of Rx frame */
-       unsigned short status;
-       DMABUFFERENTRY *pBufEntry;
-       unsigned int framesize = 0;
-       bool ReturnCode = false;
-       unsigned long flags;
-       struct tty_struct *tty = info->port.tty;
-       bool return_frame = false;
-       
-       /*
-        * current_rx_buffer points to the 1st buffer of the next available
-        * receive frame. To find the last buffer of the frame look for
-        * a non-zero status field in the buffer entries. (The status
-        * field is set by the 16C32 after completing a receive frame.
-        */
-
-       StartIndex = EndIndex = info->current_rx_buffer;
-
-       while( !info->rx_buffer_list[EndIndex].status ) {
-               /*
-                * If the count field of the buffer entry is non-zero then
-                * this buffer has not been used. (The 16C32 clears the count
-                * field when it starts using the buffer.) If an unused buffer
-                * is encountered then there are no frames available.
-                */
-
-               if ( info->rx_buffer_list[EndIndex].count )
-                       goto Cleanup;
-
-               /* advance to next buffer entry in linked list */
-               EndIndex++;
-               if ( EndIndex == info->rx_buffer_count )
-                       EndIndex = 0;
-
-               /* if entire list searched then no frame available */
-               if ( EndIndex == StartIndex ) {
-                       /* If this occurs then something bad happened,
-                        * all buffers have been 'used' but none mark
-                        * the end of a frame. Reset buffers and receiver.
-                        */
-
-                       if ( info->rx_enabled ){
-                               spin_lock_irqsave(&info->irq_spinlock,flags);
-                               usc_start_receiver(info);
-                               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-                       }
-                       goto Cleanup;
-               }
-       }
-
-
-       /* check status of receive frame */
-       
-       status = info->rx_buffer_list[EndIndex].status;
-
-       if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN +
-                       RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) {
-               if ( status & RXSTATUS_SHORT_FRAME )
-                       info->icount.rxshort++;
-               else if ( status & RXSTATUS_ABORT )
-                       info->icount.rxabort++;
-               else if ( status & RXSTATUS_OVERRUN )
-                       info->icount.rxover++;
-               else {
-                       info->icount.rxcrc++;
-                       if ( info->params.crc_type & HDLC_CRC_RETURN_EX )
-                               return_frame = true;
-               }
-               framesize = 0;
-#if SYNCLINK_GENERIC_HDLC
-               {
-                       info->netdev->stats.rx_errors++;
-                       info->netdev->stats.rx_frame_errors++;
-               }
-#endif
-       } else
-               return_frame = true;
-
-       if ( return_frame ) {
-               /* receive frame has no errors, get frame size.
-                * The frame size is the starting value of the RCC (which was
-                * set to 0xffff) minus the ending value of the RCC (decremented
-                * once for each receive character) minus 2 for the 16-bit CRC.
-                */
-
-               framesize = RCLRVALUE - info->rx_buffer_list[EndIndex].rcc;
-
-               /* adjust frame size for CRC if any */
-               if ( info->params.crc_type == HDLC_CRC_16_CCITT )
-                       framesize -= 2;
-               else if ( info->params.crc_type == HDLC_CRC_32_CCITT )
-                       framesize -= 4;         
-       }
-
-       if ( debug_level >= DEBUG_LEVEL_BH )
-               printk("%s(%d):mgsl_get_rx_frame(%s) status=%04X size=%d\n",
-                       __FILE__,__LINE__,info->device_name,status,framesize);
-                       
-       if ( debug_level >= DEBUG_LEVEL_DATA )
-               mgsl_trace_block(info,info->rx_buffer_list[StartIndex].virt_addr,
-                       min_t(int, framesize, DMABUFFERSIZE),0);
-               
-       if (framesize) {
-               if ( ( (info->params.crc_type & HDLC_CRC_RETURN_EX) &&
-                               ((framesize+1) > info->max_frame_size) ) ||
-                       (framesize > info->max_frame_size) )
-                       info->icount.rxlong++;
-               else {
-                       /* copy dma buffer(s) to contiguous intermediate buffer */
-                       int copy_count = framesize;
-                       int index = StartIndex;
-                       unsigned char *ptmp = info->intermediate_rxbuffer;
-
-                       if ( !(status & RXSTATUS_CRC_ERROR))
-                       info->icount.rxok++;
-                       
-                       while(copy_count) {
-                               int partial_count;
-                               if ( copy_count > DMABUFFERSIZE )
-                                       partial_count = DMABUFFERSIZE;
-                               else
-                                       partial_count = copy_count;
-                       
-                               pBufEntry = &(info->rx_buffer_list[index]);
-                               memcpy( ptmp, pBufEntry->virt_addr, partial_count );
-                               ptmp += partial_count;
-                               copy_count -= partial_count;
-                               
-                               if ( ++index == info->rx_buffer_count )
-                                       index = 0;
-                       }
-
-                       if ( info->params.crc_type & HDLC_CRC_RETURN_EX ) {
-                               ++framesize;
-                               *ptmp = (status & RXSTATUS_CRC_ERROR ?
-                                               RX_CRC_ERROR :
-                                               RX_OK);
-
-                               if ( debug_level >= DEBUG_LEVEL_DATA )
-                                       printk("%s(%d):mgsl_get_rx_frame(%s) rx frame status=%d\n",
-                                               __FILE__,__LINE__,info->device_name,
-                                               *ptmp);
-                       }
-
-#if SYNCLINK_GENERIC_HDLC
-                       if (info->netcount)
-                               hdlcdev_rx(info,info->intermediate_rxbuffer,framesize);
-                       else
-#endif
-                               ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
-               }
-       }
-       /* Free the buffers used by this frame. */
-       mgsl_free_rx_frame_buffers( info, StartIndex, EndIndex );
-
-       ReturnCode = true;
-
-Cleanup:
-
-       if ( info->rx_enabled && info->rx_overflow ) {
-               /* The receiver needs to restarted because of 
-                * a receive overflow (buffer or FIFO). If the 
-                * receive buffers are now empty, then restart receiver.
-                */
-
-               if ( !info->rx_buffer_list[EndIndex].status &&
-                       info->rx_buffer_list[EndIndex].count ) {
-                       spin_lock_irqsave(&info->irq_spinlock,flags);
-                       usc_start_receiver(info);
-                       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-               }
-       }
-
-       return ReturnCode;
-
-}      /* end of mgsl_get_rx_frame() */
-
-/* mgsl_get_raw_rx_frame()
- *
- *             This function attempts to return a received frame from the
- *     receive DMA buffers when running in external loop mode. In this mode,
- *     we will return at most one DMABUFFERSIZE frame to the application.
- *     The USC receiver is triggering off of DCD going active to start a new
- *     frame, and DCD going inactive to terminate the frame (similar to
- *     processing a closing flag character).
- *
- *     In this routine, we will return DMABUFFERSIZE "chunks" at a time.
- *     If DCD goes inactive, the last Rx DMA Buffer will have a non-zero
- *     status field and the RCC field will indicate the length of the
- *     entire received frame. We take this RCC field and get the modulus
- *     of RCC and DMABUFFERSIZE to determine if number of bytes in the
- *     last Rx DMA buffer and return that last portion of the frame.
- *
- * Arguments:          info    pointer to device extension
- * Return Value:       true if frame returned, otherwise false
- */
-static bool mgsl_get_raw_rx_frame(struct mgsl_struct *info)
-{
-       unsigned int CurrentIndex, NextIndex;
-       unsigned short status;
-       DMABUFFERENTRY *pBufEntry;
-       unsigned int framesize = 0;
-       bool ReturnCode = false;
-       unsigned long flags;
-       struct tty_struct *tty = info->port.tty;
-
-       /*
-        * current_rx_buffer points to the 1st buffer of the next available
-        * receive frame. The status field is set by the 16C32 after
-        * completing a receive frame. If the status field of this buffer
-        * is zero, either the USC is still filling this buffer or this
-        * is one of a series of buffers making up a received frame.
-        *
-        * If the count field of this buffer is zero, the USC is either
-        * using this buffer or has used this buffer. Look at the count
-        * field of the next buffer. If that next buffer's count is
-        * non-zero, the USC is still actively using the current buffer.
-        * Otherwise, if the next buffer's count field is zero, the
-        * current buffer is complete and the USC is using the next
-        * buffer.
-        */
-       CurrentIndex = NextIndex = info->current_rx_buffer;
-       ++NextIndex;
-       if ( NextIndex == info->rx_buffer_count )
-               NextIndex = 0;
-
-       if ( info->rx_buffer_list[CurrentIndex].status != 0 ||
-               (info->rx_buffer_list[CurrentIndex].count == 0 &&
-                       info->rx_buffer_list[NextIndex].count == 0)) {
-               /*
-                * Either the status field of this dma buffer is non-zero
-                * (indicating the last buffer of a receive frame) or the next
-                * buffer is marked as in use -- implying this buffer is complete
-                * and an intermediate buffer for this received frame.
-                */
-
-               status = info->rx_buffer_list[CurrentIndex].status;
-
-               if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN +
-                               RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) {
-                       if ( status & RXSTATUS_SHORT_FRAME )
-                               info->icount.rxshort++;
-                       else if ( status & RXSTATUS_ABORT )
-                               info->icount.rxabort++;
-                       else if ( status & RXSTATUS_OVERRUN )
-                               info->icount.rxover++;
-                       else
-                               info->icount.rxcrc++;
-                       framesize = 0;
-               } else {
-                       /*
-                        * A receive frame is available, get frame size and status.
-                        *
-                        * The frame size is the starting value of the RCC (which was
-                        * set to 0xffff) minus the ending value of the RCC (decremented
-                        * once for each receive character) minus 2 or 4 for the 16-bit
-                        * or 32-bit CRC.
-                        *
-                        * If the status field is zero, this is an intermediate buffer.
-                        * It's size is 4K.
-                        *
-                        * If the DMA Buffer Entry's Status field is non-zero, the
-                        * receive operation completed normally (ie: DCD dropped). The
-                        * RCC field is valid and holds the received frame size.
-                        * It is possible that the RCC field will be zero on a DMA buffer
-                        * entry with a non-zero status. This can occur if the total
-                        * frame size (number of bytes between the time DCD goes active
-                        * to the time DCD goes inactive) exceeds 65535 bytes. In this
-                        * case the 16C32 has underrun on the RCC count and appears to
-                        * stop updating this counter to let us know the actual received
-                        * frame size. If this happens (non-zero status and zero RCC),
-                        * simply return the entire RxDMA Buffer
-                        */
-                       if ( status ) {
-                               /*
-                                * In the event that the final RxDMA Buffer is
-                                * terminated with a non-zero status and the RCC
-                                * field is zero, we interpret this as the RCC
-                                * having underflowed (received frame > 65535 bytes).
-                                *
-                                * Signal the event to the user by passing back
-                                * a status of RxStatus_CrcError returning the full
-                                * buffer and let the app figure out what data is
-                                * actually valid
-                                */
-                               if ( info->rx_buffer_list[CurrentIndex].rcc )
-                                       framesize = RCLRVALUE - info->rx_buffer_list[CurrentIndex].rcc;
-                               else
-                                       framesize = DMABUFFERSIZE;
-                       }
-                       else
-                               framesize = DMABUFFERSIZE;
-               }
-
-               if ( framesize > DMABUFFERSIZE ) {
-                       /*
-                        * if running in raw sync mode, ISR handler for
-                        * End Of Buffer events terminates all buffers at 4K.
-                        * If this frame size is said to be >4K, get the
-                        * actual number of bytes of the frame in this buffer.
-                        */
-                       framesize = framesize % DMABUFFERSIZE;
-               }
-
-
-               if ( debug_level >= DEBUG_LEVEL_BH )
-                       printk("%s(%d):mgsl_get_raw_rx_frame(%s) status=%04X size=%d\n",
-                               __FILE__,__LINE__,info->device_name,status,framesize);
-
-               if ( debug_level >= DEBUG_LEVEL_DATA )
-                       mgsl_trace_block(info,info->rx_buffer_list[CurrentIndex].virt_addr,
-                               min_t(int, framesize, DMABUFFERSIZE),0);
-
-               if (framesize) {
-                       /* copy dma buffer(s) to contiguous intermediate buffer */
-                       /* NOTE: we never copy more than DMABUFFERSIZE bytes    */
-
-                       pBufEntry = &(info->rx_buffer_list[CurrentIndex]);
-                       memcpy( info->intermediate_rxbuffer, pBufEntry->virt_addr, framesize);
-                       info->icount.rxok++;
-
-                       ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
-               }
-
-               /* Free the buffers used by this frame. */
-               mgsl_free_rx_frame_buffers( info, CurrentIndex, CurrentIndex );
-
-               ReturnCode = true;
-       }
-
-
-       if ( info->rx_enabled && info->rx_overflow ) {
-               /* The receiver needs to restarted because of
-                * a receive overflow (buffer or FIFO). If the
-                * receive buffers are now empty, then restart receiver.
-                */
-
-               if ( !info->rx_buffer_list[CurrentIndex].status &&
-                       info->rx_buffer_list[CurrentIndex].count ) {
-                       spin_lock_irqsave(&info->irq_spinlock,flags);
-                       usc_start_receiver(info);
-                       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-               }
-       }
-
-       return ReturnCode;
-
-}      /* end of mgsl_get_raw_rx_frame() */
-
-/* mgsl_load_tx_dma_buffer()
- * 
- *     Load the transmit DMA buffer with the specified data.
- * 
- * Arguments:
- * 
- *     info            pointer to device extension
- *     Buffer          pointer to buffer containing frame to load
- *     BufferSize      size in bytes of frame in Buffer
- * 
- * Return Value:       None
- */
-static void mgsl_load_tx_dma_buffer(struct mgsl_struct *info,
-               const char *Buffer, unsigned int BufferSize)
-{
-       unsigned short Copycount;
-       unsigned int i = 0;
-       DMABUFFERENTRY *pBufEntry;
-       
-       if ( debug_level >= DEBUG_LEVEL_DATA )
-               mgsl_trace_block(info,Buffer, min_t(int, BufferSize, DMABUFFERSIZE), 1);
-
-       if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) {
-               /* set CMR:13 to start transmit when
-                * next GoAhead (abort) is received
-                */
-               info->cmr_value |= BIT13;                         
-       }
-               
-       /* begin loading the frame in the next available tx dma
-        * buffer, remember it's starting location for setting
-        * up tx dma operation
-        */
-       i = info->current_tx_buffer;
-       info->start_tx_dma_buffer = i;
-
-       /* Setup the status and RCC (Frame Size) fields of the 1st */
-       /* buffer entry in the transmit DMA buffer list. */
-
-       info->tx_buffer_list[i].status = info->cmr_value & 0xf000;
-       info->tx_buffer_list[i].rcc    = BufferSize;
-       info->tx_buffer_list[i].count  = BufferSize;
-
-       /* Copy frame data from 1st source buffer to the DMA buffers. */
-       /* The frame data may span multiple DMA buffers. */
-
-       while( BufferSize ){
-               /* Get a pointer to next DMA buffer entry. */
-               pBufEntry = &info->tx_buffer_list[i++];
-                       
-               if ( i == info->tx_buffer_count )
-                       i=0;
-
-               /* Calculate the number of bytes that can be copied from */
-               /* the source buffer to this DMA buffer. */
-               if ( BufferSize > DMABUFFERSIZE )
-                       Copycount = DMABUFFERSIZE;
-               else
-                       Copycount = BufferSize;
-
-               /* Actually copy data from source buffer to DMA buffer. */
-               /* Also set the data count for this individual DMA buffer. */
-               if ( info->bus_type == MGSL_BUS_TYPE_PCI )
-                       mgsl_load_pci_memory(pBufEntry->virt_addr, Buffer,Copycount);
-               else
-                       memcpy(pBufEntry->virt_addr, Buffer, Copycount);
-
-               pBufEntry->count = Copycount;
-
-               /* Advance source pointer and reduce remaining data count. */
-               Buffer += Copycount;
-               BufferSize -= Copycount;
-
-               ++info->tx_dma_buffers_used;
-       }
-
-       /* remember next available tx dma buffer */
-       info->current_tx_buffer = i;
-
-}      /* end of mgsl_load_tx_dma_buffer() */
-
-/*
- * mgsl_register_test()
- * 
- *     Performs a register test of the 16C32.
- *     
- * Arguments:          info    pointer to device instance data
- * Return Value:               true if test passed, otherwise false
- */
-static bool mgsl_register_test( struct mgsl_struct *info )
-{
-       static unsigned short BitPatterns[] =
-               { 0x0000, 0xffff, 0xaaaa, 0x5555, 0x1234, 0x6969, 0x9696, 0x0f0f };
-       static unsigned int Patterncount = ARRAY_SIZE(BitPatterns);
-       unsigned int i;
-       bool rc = true;
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       usc_reset(info);
-
-       /* Verify the reset state of some registers. */
-
-       if ( (usc_InReg( info, SICR ) != 0) ||
-                 (usc_InReg( info, IVR  ) != 0) ||
-                 (usc_InDmaReg( info, DIVR ) != 0) ){
-               rc = false;
-       }
-
-       if ( rc ){
-               /* Write bit patterns to various registers but do it out of */
-               /* sync, then read back and verify values. */
-
-               for ( i = 0 ; i < Patterncount ; i++ ) {
-                       usc_OutReg( info, TC0R, BitPatterns[i] );
-                       usc_OutReg( info, TC1R, BitPatterns[(i+1)%Patterncount] );
-                       usc_OutReg( info, TCLR, BitPatterns[(i+2)%Patterncount] );
-                       usc_OutReg( info, RCLR, BitPatterns[(i+3)%Patterncount] );
-                       usc_OutReg( info, RSR,  BitPatterns[(i+4)%Patterncount] );
-                       usc_OutDmaReg( info, TBCR, BitPatterns[(i+5)%Patterncount] );
-
-                       if ( (usc_InReg( info, TC0R ) != BitPatterns[i]) ||
-                                 (usc_InReg( info, TC1R ) != BitPatterns[(i+1)%Patterncount]) ||
-                                 (usc_InReg( info, TCLR ) != BitPatterns[(i+2)%Patterncount]) ||
-                                 (usc_InReg( info, RCLR ) != BitPatterns[(i+3)%Patterncount]) ||
-                                 (usc_InReg( info, RSR )  != BitPatterns[(i+4)%Patterncount]) ||
-                                 (usc_InDmaReg( info, TBCR ) != BitPatterns[(i+5)%Patterncount]) ){
-                               rc = false;
-                               break;
-                       }
-               }
-       }
-
-       usc_reset(info);
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-       return rc;
-
-}      /* end of mgsl_register_test() */
-
-/* mgsl_irq_test()     Perform interrupt test of the 16C32.
- * 
- * Arguments:          info    pointer to device instance data
- * Return Value:       true if test passed, otherwise false
- */
-static bool mgsl_irq_test( struct mgsl_struct *info )
-{
-       unsigned long EndTime;
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       usc_reset(info);
-
-       /*
-        * Setup 16C32 to interrupt on TxC pin (14MHz clock) transition. 
-        * The ISR sets irq_occurred to true.
-        */
-
-       info->irq_occurred = false;
-
-       /* Enable INTEN gate for ISA adapter (Port 6, Bit12) */
-       /* Enable INTEN (Port 6, Bit12) */
-       /* This connects the IRQ request signal to the ISA bus */
-       /* on the ISA adapter. This has no effect for the PCI adapter */
-       usc_OutReg( info, PCR, (unsigned short)((usc_InReg(info, PCR) | BIT13) & ~BIT12) );
-
-       usc_EnableMasterIrqBit(info);
-       usc_EnableInterrupts(info, IO_PIN);
-       usc_ClearIrqPendingBits(info, IO_PIN);
-       
-       usc_UnlatchIostatusBits(info, MISCSTATUS_TXC_LATCHED);
-       usc_EnableStatusIrqs(info, SICR_TXC_ACTIVE + SICR_TXC_INACTIVE);
-
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-       EndTime=100;
-       while( EndTime-- && !info->irq_occurred ) {
-               msleep_interruptible(10);
-       }
-       
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       usc_reset(info);
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       
-       return info->irq_occurred;
-
-}      /* end of mgsl_irq_test() */
-
-/* mgsl_dma_test()
- * 
- *     Perform a DMA test of the 16C32. A small frame is
- *     transmitted via DMA from a transmit buffer to a receive buffer
- *     using single buffer DMA mode.
- *     
- * Arguments:          info    pointer to device instance data
- * Return Value:       true if test passed, otherwise false
- */
-static bool mgsl_dma_test( struct mgsl_struct *info )
-{
-       unsigned short FifoLevel;
-       unsigned long phys_addr;
-       unsigned int FrameSize;
-       unsigned int i;
-       char *TmpPtr;
-       bool rc = true;
-       unsigned short status=0;
-       unsigned long EndTime;
-       unsigned long flags;
-       MGSL_PARAMS tmp_params;
-
-       /* save current port options */
-       memcpy(&tmp_params,&info->params,sizeof(MGSL_PARAMS));
-       /* load default port options */
-       memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
-       
-#define TESTFRAMESIZE 40
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       
-       /* setup 16C32 for SDLC DMA transfer mode */
-
-       usc_reset(info);
-       usc_set_sdlc_mode(info);
-       usc_enable_loopback(info,1);
-       
-       /* Reprogram the RDMR so that the 16C32 does NOT clear the count
-        * field of the buffer entry after fetching buffer address. This
-        * way we can detect a DMA failure for a DMA read (which should be
-        * non-destructive to system memory) before we try and write to
-        * memory (where a failure could corrupt system memory).
-        */
-
-       /* Receive DMA mode Register (RDMR)
-        * 
-        * <15..14>     11      DMA mode = Linked List Buffer mode
-        * <13>         1       RSBinA/L = store Rx status Block in List entry
-        * <12>         0       1 = Clear count of List Entry after fetching
-        * <11..10>     00      Address mode = Increment
-        * <9>          1       Terminate Buffer on RxBound
-        * <8>          0       Bus Width = 16bits
-        * <7..0>               ?       status Bits (write as 0s)
-        * 
-        * 1110 0010 0000 0000 = 0xe200
-        */
-
-       usc_OutDmaReg( info, RDMR, 0xe200 );
-       
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-
-       /* SETUP TRANSMIT AND RECEIVE DMA BUFFERS */
-
-       FrameSize = TESTFRAMESIZE;
-
-       /* setup 1st transmit buffer entry: */
-       /* with frame size and transmit control word */
-
-       info->tx_buffer_list[0].count  = FrameSize;
-       info->tx_buffer_list[0].rcc    = FrameSize;
-       info->tx_buffer_list[0].status = 0x4000;
-
-       /* build a transmit frame in 1st transmit DMA buffer */
-
-       TmpPtr = info->tx_buffer_list[0].virt_addr;
-       for (i = 0; i < FrameSize; i++ )
-               *TmpPtr++ = i;
-
-       /* setup 1st receive buffer entry: */
-       /* clear status, set max receive buffer size */
-
-       info->rx_buffer_list[0].status = 0;
-       info->rx_buffer_list[0].count = FrameSize + 4;
-
-       /* zero out the 1st receive buffer */
-
-       memset( info->rx_buffer_list[0].virt_addr, 0, FrameSize + 4 );
-
-       /* Set count field of next buffer entries to prevent */
-       /* 16C32 from using buffers after the 1st one. */
-
-       info->tx_buffer_list[1].count = 0;
-       info->rx_buffer_list[1].count = 0;
-       
-
-       /***************************/
-       /* Program 16C32 receiver. */
-       /***************************/
-       
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-
-       /* setup DMA transfers */
-       usc_RTCmd( info, RTCmd_PurgeRxFifo );
-
-       /* program 16C32 receiver with physical address of 1st DMA buffer entry */
-       phys_addr = info->rx_buffer_list[0].phys_entry;
-       usc_OutDmaReg( info, NRARL, (unsigned short)phys_addr );
-       usc_OutDmaReg( info, NRARU, (unsigned short)(phys_addr >> 16) );
-
-       /* Clear the Rx DMA status bits (read RDMR) and start channel */
-       usc_InDmaReg( info, RDMR );
-       usc_DmaCmd( info, DmaCmd_InitRxChannel );
-
-       /* Enable Receiver (RMR <1..0> = 10) */
-       usc_OutReg( info, RMR, (unsigned short)((usc_InReg(info, RMR) & 0xfffc) | 0x0002) );
-       
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-
-       /*************************************************************/
-       /* WAIT FOR RECEIVER TO DMA ALL PARAMETERS FROM BUFFER ENTRY */
-       /*************************************************************/
-
-       /* Wait 100ms for interrupt. */
-       EndTime = jiffies + msecs_to_jiffies(100);
-
-       for(;;) {
-               if (time_after(jiffies, EndTime)) {
-                       rc = false;
-                       break;
-               }
-
-               spin_lock_irqsave(&info->irq_spinlock,flags);
-               status = usc_InDmaReg( info, RDMR );
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-               if ( !(status & BIT4) && (status & BIT5) ) {
-                       /* INITG (BIT 4) is inactive (no entry read in progress) AND */
-                       /* BUSY  (BIT 5) is active (channel still active). */
-                       /* This means the buffer entry read has completed. */
-                       break;
-               }
-       }
-
-
-       /******************************/
-       /* Program 16C32 transmitter. */
-       /******************************/
-       
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-
-       /* Program the Transmit Character Length Register (TCLR) */
-       /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */
-
-       usc_OutReg( info, TCLR, (unsigned short)info->tx_buffer_list[0].count );
-       usc_RTCmd( info, RTCmd_PurgeTxFifo );
-
-       /* Program the address of the 1st DMA Buffer Entry in linked list */
-
-       phys_addr = info->tx_buffer_list[0].phys_entry;
-       usc_OutDmaReg( info, NTARL, (unsigned short)phys_addr );
-       usc_OutDmaReg( info, NTARU, (unsigned short)(phys_addr >> 16) );
-
-       /* unlatch Tx status bits, and start transmit channel. */
-
-       usc_OutReg( info, TCSR, (unsigned short)(( usc_InReg(info, TCSR) & 0x0f00) | 0xfa) );
-       usc_DmaCmd( info, DmaCmd_InitTxChannel );
-
-       /* wait for DMA controller to fill transmit FIFO */
-
-       usc_TCmd( info, TCmd_SelectTicrTxFifostatus );
-       
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-
-       /**********************************/
-       /* WAIT FOR TRANSMIT FIFO TO FILL */
-       /**********************************/
-       
-       /* Wait 100ms */
-       EndTime = jiffies + msecs_to_jiffies(100);
-
-       for(;;) {
-               if (time_after(jiffies, EndTime)) {
-                       rc = false;
-                       break;
-               }
-
-               spin_lock_irqsave(&info->irq_spinlock,flags);
-               FifoLevel = usc_InReg(info, TICR) >> 8;
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-                       
-               if ( FifoLevel < 16 )
-                       break;
-               else
-                       if ( FrameSize < 32 ) {
-                               /* This frame is smaller than the entire transmit FIFO */
-                               /* so wait for the entire frame to be loaded. */
-                               if ( FifoLevel <= (32 - FrameSize) )
-                                       break;
-                       }
-       }
-
-
-       if ( rc )
-       {
-               /* Enable 16C32 transmitter. */
-
-               spin_lock_irqsave(&info->irq_spinlock,flags);
-               
-               /* Transmit mode Register (TMR), <1..0> = 10, Enable Transmitter */
-               usc_TCmd( info, TCmd_SendFrame );
-               usc_OutReg( info, TMR, (unsigned short)((usc_InReg(info, TMR) & 0xfffc) | 0x0002) );
-               
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-                                               
-               /******************************/
-               /* WAIT FOR TRANSMIT COMPLETE */
-               /******************************/
-
-               /* Wait 100ms */
-               EndTime = jiffies + msecs_to_jiffies(100);
-
-               /* While timer not expired wait for transmit complete */
-
-               spin_lock_irqsave(&info->irq_spinlock,flags);
-               status = usc_InReg( info, TCSR );
-               spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-               while ( !(status & (BIT6+BIT5+BIT4+BIT2+BIT1)) ) {
-                       if (time_after(jiffies, EndTime)) {
-                               rc = false;
-                               break;
-                       }
-
-                       spin_lock_irqsave(&info->irq_spinlock,flags);
-                       status = usc_InReg( info, TCSR );
-                       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-               }
-       }
-
-
-       if ( rc ){
-               /* CHECK FOR TRANSMIT ERRORS */
-               if ( status & (BIT5 + BIT1) ) 
-                       rc = false;
-       }
-
-       if ( rc ) {
-               /* WAIT FOR RECEIVE COMPLETE */
-
-               /* Wait 100ms */
-               EndTime = jiffies + msecs_to_jiffies(100);
-
-               /* Wait for 16C32 to write receive status to buffer entry. */
-               status=info->rx_buffer_list[0].status;
-               while ( status == 0 ) {
-                       if (time_after(jiffies, EndTime)) {
-                               rc = false;
-                               break;
-                       }
-                       status=info->rx_buffer_list[0].status;
-               }
-       }
-
-
-       if ( rc ) {
-               /* CHECK FOR RECEIVE ERRORS */
-               status = info->rx_buffer_list[0].status;
-
-               if ( status & (BIT8 + BIT3 + BIT1) ) {
-                       /* receive error has occurred */
-                       rc = false;
-               } else {
-                       if ( memcmp( info->tx_buffer_list[0].virt_addr ,
-                               info->rx_buffer_list[0].virt_addr, FrameSize ) ){
-                               rc = false;
-                       }
-               }
-       }
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       usc_reset( info );
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-       /* restore current port options */
-       memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
-       
-       return rc;
-
-}      /* end of mgsl_dma_test() */
-
-/* mgsl_adapter_test()
- * 
- *     Perform the register, IRQ, and DMA tests for the 16C32.
- *     
- * Arguments:          info    pointer to device instance data
- * Return Value:       0 if success, otherwise -ENODEV
- */
-static int mgsl_adapter_test( struct mgsl_struct *info )
-{
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):Testing device %s\n",
-                       __FILE__,__LINE__,info->device_name );
-                       
-       if ( !mgsl_register_test( info ) ) {
-               info->init_error = DiagStatus_AddressFailure;
-               printk( "%s(%d):Register test failure for device %s Addr=%04X\n",
-                       __FILE__,__LINE__,info->device_name, (unsigned short)(info->io_base) );
-               return -ENODEV;
-       }
-
-       if ( !mgsl_irq_test( info ) ) {
-               info->init_error = DiagStatus_IrqFailure;
-               printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n",
-                       __FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) );
-               return -ENODEV;
-       }
-
-       if ( !mgsl_dma_test( info ) ) {
-               info->init_error = DiagStatus_DmaFailure;
-               printk( "%s(%d):DMA test failure for device %s DMA=%d\n",
-                       __FILE__,__LINE__,info->device_name, (unsigned short)(info->dma_level) );
-               return -ENODEV;
-       }
-
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):device %s passed diagnostics\n",
-                       __FILE__,__LINE__,info->device_name );
-                       
-       return 0;
-
-}      /* end of mgsl_adapter_test() */
-
-/* mgsl_memory_test()
- * 
- *     Test the shared memory on a PCI adapter.
- * 
- * Arguments:          info    pointer to device instance data
- * Return Value:       true if test passed, otherwise false
- */
-static bool mgsl_memory_test( struct mgsl_struct *info )
-{
-       static unsigned long BitPatterns[] =
-               { 0x0, 0x55555555, 0xaaaaaaaa, 0x66666666, 0x99999999, 0xffffffff, 0x12345678 };
-       unsigned long Patterncount = ARRAY_SIZE(BitPatterns);
-       unsigned long i;
-       unsigned long TestLimit = SHARED_MEM_ADDRESS_SIZE/sizeof(unsigned long);
-       unsigned long * TestAddr;
-
-       if ( info->bus_type != MGSL_BUS_TYPE_PCI )
-               return true;
-
-       TestAddr = (unsigned long *)info->memory_base;
-
-       /* Test data lines with test pattern at one location. */
-
-       for ( i = 0 ; i < Patterncount ; i++ ) {
-               *TestAddr = BitPatterns[i];
-               if ( *TestAddr != BitPatterns[i] )
-                       return false;
-       }
-
-       /* Test address lines with incrementing pattern over */
-       /* entire address range. */
-
-       for ( i = 0 ; i < TestLimit ; i++ ) {
-               *TestAddr = i * 4;
-               TestAddr++;
-       }
-
-       TestAddr = (unsigned long *)info->memory_base;
-
-       for ( i = 0 ; i < TestLimit ; i++ ) {
-               if ( *TestAddr != i * 4 )
-                       return false;
-               TestAddr++;
-       }
-
-       memset( info->memory_base, 0, SHARED_MEM_ADDRESS_SIZE );
-
-       return true;
-
-}      /* End Of mgsl_memory_test() */
-
-
-/* mgsl_load_pci_memory()
- * 
- *     Load a large block of data into the PCI shared memory.
- *     Use this instead of memcpy() or memmove() to move data
- *     into the PCI shared memory.
- * 
- * Notes:
- * 
- *     This function prevents the PCI9050 interface chip from hogging
- *     the adapter local bus, which can starve the 16C32 by preventing
- *     16C32 bus master cycles.
- * 
- *     The PCI9050 documentation says that the 9050 will always release
- *     control of the local bus after completing the current read
- *     or write operation.
- * 
- *     It appears that as long as the PCI9050 write FIFO is full, the
- *     PCI9050 treats all of the writes as a single burst transaction
- *     and will not release the bus. This causes DMA latency problems
- *     at high speeds when copying large data blocks to the shared
- *     memory.
- * 
- *     This function in effect, breaks the a large shared memory write
- *     into multiple transations by interleaving a shared memory read
- *     which will flush the write FIFO and 'complete' the write
- *     transation. This allows any pending DMA request to gain control
- *     of the local bus in a timely fasion.
- * 
- * Arguments:
- * 
- *     TargetPtr       pointer to target address in PCI shared memory
- *     SourcePtr       pointer to source buffer for data
- *     count           count in bytes of data to copy
- *
- * Return Value:       None
- */
-static void mgsl_load_pci_memory( char* TargetPtr, const char* SourcePtr,
-       unsigned short count )
-{
-       /* 16 32-bit writes @ 60ns each = 960ns max latency on local bus */
-#define PCI_LOAD_INTERVAL 64
-
-       unsigned short Intervalcount = count / PCI_LOAD_INTERVAL;
-       unsigned short Index;
-       unsigned long Dummy;
-
-       for ( Index = 0 ; Index < Intervalcount ; Index++ )
-       {
-               memcpy(TargetPtr, SourcePtr, PCI_LOAD_INTERVAL);
-               Dummy = *((volatile unsigned long *)TargetPtr);
-               TargetPtr += PCI_LOAD_INTERVAL;
-               SourcePtr += PCI_LOAD_INTERVAL;
-       }
-
-       memcpy( TargetPtr, SourcePtr, count % PCI_LOAD_INTERVAL );
-
-}      /* End Of mgsl_load_pci_memory() */
-
-static void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit)
-{
-       int i;
-       int linecount;
-       if (xmit)
-               printk("%s tx data:\n",info->device_name);
-       else
-               printk("%s rx data:\n",info->device_name);
-               
-       while(count) {
-               if (count > 16)
-                       linecount = 16;
-               else
-                       linecount = count;
-                       
-               for(i=0;i<linecount;i++)
-                       printk("%02X ",(unsigned char)data[i]);
-               for(;i<17;i++)
-                       printk("   ");
-               for(i=0;i<linecount;i++) {
-                       if (data[i]>=040 && data[i]<=0176)
-                               printk("%c",data[i]);
-                       else
-                               printk(".");
-               }
-               printk("\n");
-               
-               data  += linecount;
-               count -= linecount;
-       }
-}      /* end of mgsl_trace_block() */
-
-/* mgsl_tx_timeout()
- * 
- *     called when HDLC frame times out
- *     update stats and do tx completion processing
- *     
- * Arguments:  context         pointer to device instance data
- * Return Value:       None
- */
-static void mgsl_tx_timeout(unsigned long context)
-{
-       struct mgsl_struct *info = (struct mgsl_struct*)context;
-       unsigned long flags;
-       
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):mgsl_tx_timeout(%s)\n",
-                       __FILE__,__LINE__,info->device_name);
-       if(info->tx_active &&
-          (info->params.mode == MGSL_MODE_HDLC ||
-           info->params.mode == MGSL_MODE_RAW) ) {
-               info->icount.txtimeout++;
-       }
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       info->tx_active = false;
-       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-
-       if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
-               usc_loopmode_cancel_transmit( info );
-
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-       
-#if SYNCLINK_GENERIC_HDLC
-       if (info->netcount)
-               hdlcdev_tx_done(info);
-       else
-#endif
-               mgsl_bh_transmit(info);
-       
-}      /* end of mgsl_tx_timeout() */
-
-/* signal that there are no more frames to send, so that
- * line is 'released' by echoing RxD to TxD when current
- * transmission is complete (or immediately if no tx in progress).
- */
-static int mgsl_loopmode_send_done( struct mgsl_struct * info )
-{
-       unsigned long flags;
-       
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) {
-               if (info->tx_active)
-                       info->loopmode_send_done_requested = true;
-               else
-                       usc_loopmode_send_done(info);
-       }
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-       return 0;
-}
-
-/* release the line by echoing RxD to TxD
- * upon completion of a transmit frame
- */
-static void usc_loopmode_send_done( struct mgsl_struct * info )
-{
-       info->loopmode_send_done_requested = false;
-       /* clear CMR:13 to 0 to start echoing RxData to TxData */
-       info->cmr_value &= ~BIT13;                        
-       usc_OutReg(info, CMR, info->cmr_value);
-}
-
-/* abort a transmit in progress while in HDLC LoopMode
- */
-static void usc_loopmode_cancel_transmit( struct mgsl_struct * info )
-{
-       /* reset tx dma channel and purge TxFifo */
-       usc_RTCmd( info, RTCmd_PurgeTxFifo );
-       usc_DmaCmd( info, DmaCmd_ResetTxChannel );
-       usc_loopmode_send_done( info );
-}
-
-/* for HDLC/SDLC LoopMode, setting CMR:13 after the transmitter is enabled
- * is an Insert Into Loop action. Upon receipt of a GoAhead sequence (RxAbort)
- * we must clear CMR:13 to begin repeating TxData to RxData
- */
-static void usc_loopmode_insert_request( struct mgsl_struct * info )
-{
-       info->loopmode_insert_requested = true;
-       /* enable RxAbort irq. On next RxAbort, clear CMR:13 to
-        * begin repeating TxData on RxData (complete insertion)
-        */
-       usc_OutReg( info, RICR, 
-               (usc_InReg( info, RICR ) | RXSTATUS_ABORT_RECEIVED ) );
-               
-       /* set CMR:13 to insert into loop on next GoAhead (RxAbort) */
-       info->cmr_value |= BIT13;
-       usc_OutReg(info, CMR, info->cmr_value);
-}
-
-/* return 1 if station is inserted into the loop, otherwise 0
- */
-static int usc_loopmode_active( struct mgsl_struct * info)
-{
-       return usc_InReg( info, CCSR ) & BIT7 ? 1 : 0 ;
-}
-
-#if SYNCLINK_GENERIC_HDLC
-
-/**
- * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.)
- * set encoding and frame check sequence (FCS) options
- *
- * dev       pointer to network device structure
- * encoding  serial encoding setting
- * parity    FCS setting
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
-                         unsigned short parity)
-{
-       struct mgsl_struct *info = dev_to_port(dev);
-       unsigned char  new_encoding;
-       unsigned short new_crctype;
-
-       /* return error if TTY interface open */
-       if (info->port.count)
-               return -EBUSY;
-
-       switch (encoding)
-       {
-       case ENCODING_NRZ:        new_encoding = HDLC_ENCODING_NRZ; break;
-       case ENCODING_NRZI:       new_encoding = HDLC_ENCODING_NRZI_SPACE; break;
-       case ENCODING_FM_MARK:    new_encoding = HDLC_ENCODING_BIPHASE_MARK; break;
-       case ENCODING_FM_SPACE:   new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break;
-       case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break;
-       default: return -EINVAL;
-       }
-
-       switch (parity)
-       {
-       case PARITY_NONE:            new_crctype = HDLC_CRC_NONE; break;
-       case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break;
-       case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break;
-       default: return -EINVAL;
-       }
-
-       info->params.encoding = new_encoding;
-       info->params.crc_type = new_crctype;
-
-       /* if network interface up, reprogram hardware */
-       if (info->netcount)
-               mgsl_program_hw(info);
-
-       return 0;
-}
-
-/**
- * called by generic HDLC layer to send frame
- *
- * skb  socket buffer containing HDLC frame
- * dev  pointer to network device structure
- */
-static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
-                                     struct net_device *dev)
-{
-       struct mgsl_struct *info = dev_to_port(dev);
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk(KERN_INFO "%s:hdlc_xmit(%s)\n",__FILE__,dev->name);
-
-       /* stop sending until this frame completes */
-       netif_stop_queue(dev);
-
-       /* copy data to device buffers */
-       info->xmit_cnt = skb->len;
-       mgsl_load_tx_dma_buffer(info, skb->data, skb->len);
-
-       /* update network statistics */
-       dev->stats.tx_packets++;
-       dev->stats.tx_bytes += skb->len;
-
-       /* done with socket buffer, so free it */
-       dev_kfree_skb(skb);
-
-       /* save start time for transmit timeout detection */
-       dev->trans_start = jiffies;
-
-       /* start hardware transmitter if necessary */
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       if (!info->tx_active)
-               usc_start_transmitter(info);
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-       return NETDEV_TX_OK;
-}
-
-/**
- * called by network layer when interface enabled
- * claim resources and initialize hardware
- *
- * dev  pointer to network device structure
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_open(struct net_device *dev)
-{
-       struct mgsl_struct *info = dev_to_port(dev);
-       int rc;
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s:hdlcdev_open(%s)\n",__FILE__,dev->name);
-
-       /* generic HDLC layer open processing */
-       if ((rc = hdlc_open(dev)))
-               return rc;
-
-       /* arbitrate between network and tty opens */
-       spin_lock_irqsave(&info->netlock, flags);
-       if (info->port.count != 0 || info->netcount != 0) {
-               printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name);
-               spin_unlock_irqrestore(&info->netlock, flags);
-               return -EBUSY;
-       }
-       info->netcount=1;
-       spin_unlock_irqrestore(&info->netlock, flags);
-
-       /* claim resources and init adapter */
-       if ((rc = startup(info)) != 0) {
-               spin_lock_irqsave(&info->netlock, flags);
-               info->netcount=0;
-               spin_unlock_irqrestore(&info->netlock, flags);
-               return rc;
-       }
-
-       /* assert DTR and RTS, apply hardware settings */
-       info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
-       mgsl_program_hw(info);
-
-       /* enable network layer transmit */
-       dev->trans_start = jiffies;
-       netif_start_queue(dev);
-
-       /* inform generic HDLC layer of current DCD status */
-       spin_lock_irqsave(&info->irq_spinlock, flags);
-       usc_get_serial_signals(info);
-       spin_unlock_irqrestore(&info->irq_spinlock, flags);
-       if (info->serial_signals & SerialSignal_DCD)
-               netif_carrier_on(dev);
-       else
-               netif_carrier_off(dev);
-       return 0;
-}
-
-/**
- * called by network layer when interface is disabled
- * shutdown hardware and release resources
- *
- * dev  pointer to network device structure
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_close(struct net_device *dev)
-{
-       struct mgsl_struct *info = dev_to_port(dev);
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s:hdlcdev_close(%s)\n",__FILE__,dev->name);
-
-       netif_stop_queue(dev);
-
-       /* shutdown adapter and release resources */
-       shutdown(info);
-
-       hdlc_close(dev);
-
-       spin_lock_irqsave(&info->netlock, flags);
-       info->netcount=0;
-       spin_unlock_irqrestore(&info->netlock, flags);
-
-       return 0;
-}
-
-/**
- * called by network layer to process IOCTL call to network device
- *
- * dev  pointer to network device structure
- * ifr  pointer to network interface request structure
- * cmd  IOCTL command code
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
-       const size_t size = sizeof(sync_serial_settings);
-       sync_serial_settings new_line;
-       sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
-       struct mgsl_struct *info = dev_to_port(dev);
-       unsigned int flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s:hdlcdev_ioctl(%s)\n",__FILE__,dev->name);
-
-       /* return error if TTY interface open */
-       if (info->port.count)
-               return -EBUSY;
-
-       if (cmd != SIOCWANDEV)
-               return hdlc_ioctl(dev, ifr, cmd);
-
-       switch(ifr->ifr_settings.type) {
-       case IF_GET_IFACE: /* return current sync_serial_settings */
-
-               ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
-               if (ifr->ifr_settings.size < size) {
-                       ifr->ifr_settings.size = size; /* data size wanted */
-                       return -ENOBUFS;
-               }
-
-               flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
-                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
-                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
-                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
-
-               switch (flags){
-               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break;
-               case (HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_INT; break;
-               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_TXINT; break;
-               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break;
-               default: new_line.clock_type = CLOCK_DEFAULT;
-               }
-
-               new_line.clock_rate = info->params.clock_speed;
-               new_line.loopback   = info->params.loopback ? 1:0;
-
-               if (copy_to_user(line, &new_line, size))
-                       return -EFAULT;
-               return 0;
-
-       case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */
-
-               if(!capable(CAP_NET_ADMIN))
-                       return -EPERM;
-               if (copy_from_user(&new_line, line, size))
-                       return -EFAULT;
-
-               switch (new_line.clock_type)
-               {
-               case CLOCK_EXT:      flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break;
-               case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break;
-               case CLOCK_INT:      flags = HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG;    break;
-               case CLOCK_TXINT:    flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG;    break;
-               case CLOCK_DEFAULT:  flags = info->params.flags &
-                                            (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
-                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
-                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
-                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN); break;
-               default: return -EINVAL;
-               }
-
-               if (new_line.loopback != 0 && new_line.loopback != 1)
-                       return -EINVAL;
-
-               info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
-                                       HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
-                                       HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
-                                       HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
-               info->params.flags |= flags;
-
-               info->params.loopback = new_line.loopback;
-
-               if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG))
-                       info->params.clock_speed = new_line.clock_rate;
-               else
-                       info->params.clock_speed = 0;
-
-               /* if network interface up, reprogram hardware */
-               if (info->netcount)
-                       mgsl_program_hw(info);
-               return 0;
-
-       default:
-               return hdlc_ioctl(dev, ifr, cmd);
-       }
-}
-
-/**
- * called by network layer when transmit timeout is detected
- *
- * dev  pointer to network device structure
- */
-static void hdlcdev_tx_timeout(struct net_device *dev)
-{
-       struct mgsl_struct *info = dev_to_port(dev);
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("hdlcdev_tx_timeout(%s)\n",dev->name);
-
-       dev->stats.tx_errors++;
-       dev->stats.tx_aborted_errors++;
-
-       spin_lock_irqsave(&info->irq_spinlock,flags);
-       usc_stop_transmitter(info);
-       spin_unlock_irqrestore(&info->irq_spinlock,flags);
-
-       netif_wake_queue(dev);
-}
-
-/**
- * called by device driver when transmit completes
- * reenable network layer transmit if stopped
- *
- * info  pointer to device instance information
- */
-static void hdlcdev_tx_done(struct mgsl_struct *info)
-{
-       if (netif_queue_stopped(info->netdev))
-               netif_wake_queue(info->netdev);
-}
-
-/**
- * called by device driver when frame received
- * pass frame to network layer
- *
- * info  pointer to device instance information
- * buf   pointer to buffer contianing frame data
- * size  count of data bytes in buf
- */
-static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size)
-{
-       struct sk_buff *skb = dev_alloc_skb(size);
-       struct net_device *dev = info->netdev;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("hdlcdev_rx(%s)\n", dev->name);
-
-       if (skb == NULL) {
-               printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n",
-                      dev->name);
-               dev->stats.rx_dropped++;
-               return;
-       }
-
-       memcpy(skb_put(skb, size), buf, size);
-
-       skb->protocol = hdlc_type_trans(skb, dev);
-
-       dev->stats.rx_packets++;
-       dev->stats.rx_bytes += size;
-
-       netif_rx(skb);
-}
-
-static const struct net_device_ops hdlcdev_ops = {
-       .ndo_open       = hdlcdev_open,
-       .ndo_stop       = hdlcdev_close,
-       .ndo_change_mtu = hdlc_change_mtu,
-       .ndo_start_xmit = hdlc_start_xmit,
-       .ndo_do_ioctl   = hdlcdev_ioctl,
-       .ndo_tx_timeout = hdlcdev_tx_timeout,
-};
-
-/**
- * called by device driver when adding device instance
- * do generic HDLC initialization
- *
- * info  pointer to device instance information
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_init(struct mgsl_struct *info)
-{
-       int rc;
-       struct net_device *dev;
-       hdlc_device *hdlc;
-
-       /* allocate and initialize network and HDLC layer objects */
-
-       if (!(dev = alloc_hdlcdev(info))) {
-               printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__);
-               return -ENOMEM;
-       }
-
-       /* for network layer reporting purposes only */
-       dev->base_addr = info->io_base;
-       dev->irq       = info->irq_level;
-       dev->dma       = info->dma_level;
-
-       /* network layer callbacks and settings */
-       dev->netdev_ops     = &hdlcdev_ops;
-       dev->watchdog_timeo = 10 * HZ;
-       dev->tx_queue_len   = 50;
-
-       /* generic HDLC layer callbacks and settings */
-       hdlc         = dev_to_hdlc(dev);
-       hdlc->attach = hdlcdev_attach;
-       hdlc->xmit   = hdlcdev_xmit;
-
-       /* register objects with HDLC layer */
-       if ((rc = register_hdlc_device(dev))) {
-               printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__);
-               free_netdev(dev);
-               return rc;
-       }
-
-       info->netdev = dev;
-       return 0;
-}
-
-/**
- * called by device driver when removing device instance
- * do generic HDLC cleanup
- *
- * info  pointer to device instance information
- */
-static void hdlcdev_exit(struct mgsl_struct *info)
-{
-       unregister_hdlc_device(info->netdev);
-       free_netdev(info->netdev);
-       info->netdev = NULL;
-}
-
-#endif /* CONFIG_HDLC */
-
-
-static int __devinit synclink_init_one (struct pci_dev *dev,
-                                       const struct pci_device_id *ent)
-{
-       struct mgsl_struct *info;
-
-       if (pci_enable_device(dev)) {
-               printk("error enabling pci device %p\n", dev);
-               return -EIO;
-       }
-
-       if (!(info = mgsl_allocate_device())) {
-               printk("can't allocate device instance data.\n");
-               return -EIO;
-       }
-
-        /* Copy user configuration info to device instance data */
-               
-       info->io_base = pci_resource_start(dev, 2);
-       info->irq_level = dev->irq;
-       info->phys_memory_base = pci_resource_start(dev, 3);
-                               
-        /* Because veremap only works on page boundaries we must map
-        * a larger area than is actually implemented for the LCR
-        * memory range. We map a full page starting at the page boundary.
-        */
-       info->phys_lcr_base = pci_resource_start(dev, 0);
-       info->lcr_offset    = info->phys_lcr_base & (PAGE_SIZE-1);
-       info->phys_lcr_base &= ~(PAGE_SIZE-1);
-                               
-       info->bus_type = MGSL_BUS_TYPE_PCI;
-       info->io_addr_size = 8;
-       info->irq_flags = IRQF_SHARED;
-
-       if (dev->device == 0x0210) {
-               /* Version 1 PCI9030 based universal PCI adapter */
-               info->misc_ctrl_value = 0x007c4080;
-               info->hw_version = 1;
-       } else {
-               /* Version 0 PCI9050 based 5V PCI adapter
-                * A PCI9050 bug prevents reading LCR registers if 
-                * LCR base address bit 7 is set. Maintain shadow
-                * value so we can write to LCR misc control reg.
-                */
-               info->misc_ctrl_value = 0x087e4546;
-               info->hw_version = 0;
-       }
-                               
-       mgsl_add_device(info);
-
-       return 0;
-}
-
-static void __devexit synclink_remove_one (struct pci_dev *dev)
-{
-}
-
diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c
deleted file mode 100644 (file)
index a35dd54..0000000
+++ /dev/null
@@ -1,5161 +0,0 @@
-/*
- * Device driver for Microgate SyncLink GT serial adapters.
- *
- * written by Paul Fulghum for Microgate Corporation
- * paulkf@microgate.com
- *
- * Microgate and SyncLink are trademarks of Microgate Corporation
- *
- * This code is released under the GNU General Public License (GPL)
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * DEBUG OUTPUT DEFINITIONS
- *
- * uncomment lines below to enable specific types of debug output
- *
- * DBGINFO   information - most verbose output
- * DBGERR    serious errors
- * DBGBH     bottom half service routine debugging
- * DBGISR    interrupt service routine debugging
- * DBGDATA   output receive and transmit data
- * DBGTBUF   output transmit DMA buffers and registers
- * DBGRBUF   output receive DMA buffers and registers
- */
-
-#define DBGINFO(fmt) if (debug_level >= DEBUG_LEVEL_INFO) printk fmt
-#define DBGERR(fmt) if (debug_level >= DEBUG_LEVEL_ERROR) printk fmt
-#define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt
-#define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt
-#define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label))
-/*#define DBGTBUF(info) dump_tbufs(info)*/
-/*#define DBGRBUF(info) dump_rbufs(info)*/
-
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/netdevice.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/ioctl.h>
-#include <linux/termios.h>
-#include <linux/bitops.h>
-#include <linux/workqueue.h>
-#include <linux/hdlc.h>
-#include <linux/synclink.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <asm/types.h>
-#include <asm/uaccess.h>
-
-#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE))
-#define SYNCLINK_GENERIC_HDLC 1
-#else
-#define SYNCLINK_GENERIC_HDLC 0
-#endif
-
-/*
- * module identification
- */
-static char *driver_name     = "SyncLink GT";
-static char *tty_driver_name = "synclink_gt";
-static char *tty_dev_prefix  = "ttySLG";
-MODULE_LICENSE("GPL");
-#define MGSL_MAGIC 0x5401
-#define MAX_DEVICES 32
-
-static struct pci_device_id pci_table[] = {
-       {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
-       {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT2_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
-       {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT4_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
-       {PCI_VENDOR_ID_MICROGATE, SYNCLINK_AC_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
-       {0,}, /* terminate list */
-};
-MODULE_DEVICE_TABLE(pci, pci_table);
-
-static int  init_one(struct pci_dev *dev,const struct pci_device_id *ent);
-static void remove_one(struct pci_dev *dev);
-static struct pci_driver pci_driver = {
-       .name           = "synclink_gt",
-       .id_table       = pci_table,
-       .probe          = init_one,
-       .remove         = __devexit_p(remove_one),
-};
-
-static bool pci_registered;
-
-/*
- * module configuration and status
- */
-static struct slgt_info *slgt_device_list;
-static int slgt_device_count;
-
-static int ttymajor;
-static int debug_level;
-static int maxframe[MAX_DEVICES];
-
-module_param(ttymajor, int, 0);
-module_param(debug_level, int, 0);
-module_param_array(maxframe, int, NULL, 0);
-
-MODULE_PARM_DESC(ttymajor, "TTY major device number override: 0=auto assigned");
-MODULE_PARM_DESC(debug_level, "Debug syslog output: 0=disabled, 1 to 5=increasing detail");
-MODULE_PARM_DESC(maxframe, "Maximum frame size used by device (4096 to 65535)");
-
-/*
- * tty support and callbacks
- */
-static struct tty_driver *serial_driver;
-
-static int  open(struct tty_struct *tty, struct file * filp);
-static void close(struct tty_struct *tty, struct file * filp);
-static void hangup(struct tty_struct *tty);
-static void set_termios(struct tty_struct *tty, struct ktermios *old_termios);
-
-static int  write(struct tty_struct *tty, const unsigned char *buf, int count);
-static int put_char(struct tty_struct *tty, unsigned char ch);
-static void send_xchar(struct tty_struct *tty, char ch);
-static void wait_until_sent(struct tty_struct *tty, int timeout);
-static int  write_room(struct tty_struct *tty);
-static void flush_chars(struct tty_struct *tty);
-static void flush_buffer(struct tty_struct *tty);
-static void tx_hold(struct tty_struct *tty);
-static void tx_release(struct tty_struct *tty);
-
-static int  ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
-static int  chars_in_buffer(struct tty_struct *tty);
-static void throttle(struct tty_struct * tty);
-static void unthrottle(struct tty_struct * tty);
-static int set_break(struct tty_struct *tty, int break_state);
-
-/*
- * generic HDLC support and callbacks
- */
-#if SYNCLINK_GENERIC_HDLC
-#define dev_to_port(D) (dev_to_hdlc(D)->priv)
-static void hdlcdev_tx_done(struct slgt_info *info);
-static void hdlcdev_rx(struct slgt_info *info, char *buf, int size);
-static int  hdlcdev_init(struct slgt_info *info);
-static void hdlcdev_exit(struct slgt_info *info);
-#endif
-
-
-/*
- * device specific structures, macros and functions
- */
-
-#define SLGT_MAX_PORTS 4
-#define SLGT_REG_SIZE  256
-
-/*
- * conditional wait facility
- */
-struct cond_wait {
-       struct cond_wait *next;
-       wait_queue_head_t q;
-       wait_queue_t wait;
-       unsigned int data;
-};
-static void init_cond_wait(struct cond_wait *w, unsigned int data);
-static void add_cond_wait(struct cond_wait **head, struct cond_wait *w);
-static void remove_cond_wait(struct cond_wait **head, struct cond_wait *w);
-static void flush_cond_wait(struct cond_wait **head);
-
-/*
- * DMA buffer descriptor and access macros
- */
-struct slgt_desc
-{
-       __le16 count;
-       __le16 status;
-       __le32 pbuf;  /* physical address of data buffer */
-       __le32 next;  /* physical address of next descriptor */
-
-       /* driver book keeping */
-       char *buf;          /* virtual  address of data buffer */
-       unsigned int pdesc; /* physical address of this descriptor */
-       dma_addr_t buf_dma_addr;
-       unsigned short buf_count;
-};
-
-#define set_desc_buffer(a,b) (a).pbuf = cpu_to_le32((unsigned int)(b))
-#define set_desc_next(a,b) (a).next   = cpu_to_le32((unsigned int)(b))
-#define set_desc_count(a,b)(a).count  = cpu_to_le16((unsigned short)(b))
-#define set_desc_eof(a,b)  (a).status = cpu_to_le16((b) ? (le16_to_cpu((a).status) | BIT0) : (le16_to_cpu((a).status) & ~BIT0))
-#define set_desc_status(a, b) (a).status = cpu_to_le16((unsigned short)(b))
-#define desc_count(a)      (le16_to_cpu((a).count))
-#define desc_status(a)     (le16_to_cpu((a).status))
-#define desc_complete(a)   (le16_to_cpu((a).status) & BIT15)
-#define desc_eof(a)        (le16_to_cpu((a).status) & BIT2)
-#define desc_crc_error(a)  (le16_to_cpu((a).status) & BIT1)
-#define desc_abort(a)      (le16_to_cpu((a).status) & BIT0)
-#define desc_residue(a)    ((le16_to_cpu((a).status) & 0x38) >> 3)
-
-struct _input_signal_events {
-       int ri_up;
-       int ri_down;
-       int dsr_up;
-       int dsr_down;
-       int dcd_up;
-       int dcd_down;
-       int cts_up;
-       int cts_down;
-};
-
-/*
- * device instance data structure
- */
-struct slgt_info {
-       void *if_ptr;           /* General purpose pointer (used by SPPP) */
-       struct tty_port port;
-
-       struct slgt_info *next_device;  /* device list link */
-
-       int magic;
-
-       char device_name[25];
-       struct pci_dev *pdev;
-
-       int port_count;  /* count of ports on adapter */
-       int adapter_num; /* adapter instance number */
-       int port_num;    /* port instance number */
-
-       /* array of pointers to port contexts on this adapter */
-       struct slgt_info *port_array[SLGT_MAX_PORTS];
-
-       int                     line;           /* tty line instance number */
-
-       struct mgsl_icount      icount;
-
-       int                     timeout;
-       int                     x_char;         /* xon/xoff character */
-       unsigned int            read_status_mask;
-       unsigned int            ignore_status_mask;
-
-       wait_queue_head_t       status_event_wait_q;
-       wait_queue_head_t       event_wait_q;
-       struct timer_list       tx_timer;
-       struct timer_list       rx_timer;
-
-       unsigned int            gpio_present;
-       struct cond_wait        *gpio_wait_q;
-
-       spinlock_t lock;        /* spinlock for synchronizing with ISR */
-
-       struct work_struct task;
-       u32 pending_bh;
-       bool bh_requested;
-       bool bh_running;
-
-       int isr_overflow;
-       bool irq_requested;     /* true if IRQ requested */
-       bool irq_occurred;      /* for diagnostics use */
-
-       /* device configuration */
-
-       unsigned int bus_type;
-       unsigned int irq_level;
-       unsigned long irq_flags;
-
-       unsigned char __iomem * reg_addr;  /* memory mapped registers address */
-       u32 phys_reg_addr;
-       bool reg_addr_requested;
-
-       MGSL_PARAMS params;       /* communications parameters */
-       u32 idle_mode;
-       u32 max_frame_size;       /* as set by device config */
-
-       unsigned int rbuf_fill_level;
-       unsigned int rx_pio;
-       unsigned int if_mode;
-       unsigned int base_clock;
-       unsigned int xsync;
-       unsigned int xctrl;
-
-       /* device status */
-
-       bool rx_enabled;
-       bool rx_restart;
-
-       bool tx_enabled;
-       bool tx_active;
-
-       unsigned char signals;    /* serial signal states */
-       int init_error;  /* initialization error */
-
-       unsigned char *tx_buf;
-       int tx_count;
-
-       char flag_buf[MAX_ASYNC_BUFFER_SIZE];
-       char char_buf[MAX_ASYNC_BUFFER_SIZE];
-       bool drop_rts_on_tx_done;
-       struct  _input_signal_events    input_signal_events;
-
-       int dcd_chkcount;       /* check counts to prevent */
-       int cts_chkcount;       /* too many IRQs if a signal */
-       int dsr_chkcount;       /* is floating */
-       int ri_chkcount;
-
-       char *bufs;             /* virtual address of DMA buffer lists */
-       dma_addr_t bufs_dma_addr; /* physical address of buffer descriptors */
-
-       unsigned int rbuf_count;
-       struct slgt_desc *rbufs;
-       unsigned int rbuf_current;
-       unsigned int rbuf_index;
-       unsigned int rbuf_fill_index;
-       unsigned short rbuf_fill_count;
-
-       unsigned int tbuf_count;
-       struct slgt_desc *tbufs;
-       unsigned int tbuf_current;
-       unsigned int tbuf_start;
-
-       unsigned char *tmp_rbuf;
-       unsigned int tmp_rbuf_count;
-
-       /* SPPP/Cisco HDLC device parts */
-
-       int netcount;
-       spinlock_t netlock;
-#if SYNCLINK_GENERIC_HDLC
-       struct net_device *netdev;
-#endif
-
-};
-
-static MGSL_PARAMS default_params = {
-       .mode            = MGSL_MODE_HDLC,
-       .loopback        = 0,
-       .flags           = HDLC_FLAG_UNDERRUN_ABORT15,
-       .encoding        = HDLC_ENCODING_NRZI_SPACE,
-       .clock_speed     = 0,
-       .addr_filter     = 0xff,
-       .crc_type        = HDLC_CRC_16_CCITT,
-       .preamble_length = HDLC_PREAMBLE_LENGTH_8BITS,
-       .preamble        = HDLC_PREAMBLE_PATTERN_NONE,
-       .data_rate       = 9600,
-       .data_bits       = 8,
-       .stop_bits       = 1,
-       .parity          = ASYNC_PARITY_NONE
-};
-
-
-#define BH_RECEIVE  1
-#define BH_TRANSMIT 2
-#define BH_STATUS   4
-#define IO_PIN_SHUTDOWN_LIMIT 100
-
-#define DMABUFSIZE 256
-#define DESC_LIST_SIZE 4096
-
-#define MASK_PARITY  BIT1
-#define MASK_FRAMING BIT0
-#define MASK_BREAK   BIT14
-#define MASK_OVERRUN BIT4
-
-#define GSR   0x00 /* global status */
-#define JCR   0x04 /* JTAG control */
-#define IODR  0x08 /* GPIO direction */
-#define IOER  0x0c /* GPIO interrupt enable */
-#define IOVR  0x10 /* GPIO value */
-#define IOSR  0x14 /* GPIO interrupt status */
-#define TDR   0x80 /* tx data */
-#define RDR   0x80 /* rx data */
-#define TCR   0x82 /* tx control */
-#define TIR   0x84 /* tx idle */
-#define TPR   0x85 /* tx preamble */
-#define RCR   0x86 /* rx control */
-#define VCR   0x88 /* V.24 control */
-#define CCR   0x89 /* clock control */
-#define BDR   0x8a /* baud divisor */
-#define SCR   0x8c /* serial control */
-#define SSR   0x8e /* serial status */
-#define RDCSR 0x90 /* rx DMA control/status */
-#define TDCSR 0x94 /* tx DMA control/status */
-#define RDDAR 0x98 /* rx DMA descriptor address */
-#define TDDAR 0x9c /* tx DMA descriptor address */
-#define XSR   0x40 /* extended sync pattern */
-#define XCR   0x44 /* extended control */
-
-#define RXIDLE      BIT14
-#define RXBREAK     BIT14
-#define IRQ_TXDATA  BIT13
-#define IRQ_TXIDLE  BIT12
-#define IRQ_TXUNDER BIT11 /* HDLC */
-#define IRQ_RXDATA  BIT10
-#define IRQ_RXIDLE  BIT9  /* HDLC */
-#define IRQ_RXBREAK BIT9  /* async */
-#define IRQ_RXOVER  BIT8
-#define IRQ_DSR     BIT7
-#define IRQ_CTS     BIT6
-#define IRQ_DCD     BIT5
-#define IRQ_RI      BIT4
-#define IRQ_ALL     0x3ff0
-#define IRQ_MASTER  BIT0
-
-#define slgt_irq_on(info, mask) \
-       wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) | (mask)))
-#define slgt_irq_off(info, mask) \
-       wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) & ~(mask)))
-
-static __u8  rd_reg8(struct slgt_info *info, unsigned int addr);
-static void  wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value);
-static __u16 rd_reg16(struct slgt_info *info, unsigned int addr);
-static void  wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value);
-static __u32 rd_reg32(struct slgt_info *info, unsigned int addr);
-static void  wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value);
-
-static void  msc_set_vcr(struct slgt_info *info);
-
-static int  startup(struct slgt_info *info);
-static int  block_til_ready(struct tty_struct *tty, struct file * filp,struct slgt_info *info);
-static void shutdown(struct slgt_info *info);
-static void program_hw(struct slgt_info *info);
-static void change_params(struct slgt_info *info);
-
-static int  register_test(struct slgt_info *info);
-static int  irq_test(struct slgt_info *info);
-static int  loopback_test(struct slgt_info *info);
-static int  adapter_test(struct slgt_info *info);
-
-static void reset_adapter(struct slgt_info *info);
-static void reset_port(struct slgt_info *info);
-static void async_mode(struct slgt_info *info);
-static void sync_mode(struct slgt_info *info);
-
-static void rx_stop(struct slgt_info *info);
-static void rx_start(struct slgt_info *info);
-static void reset_rbufs(struct slgt_info *info);
-static void free_rbufs(struct slgt_info *info, unsigned int first, unsigned int last);
-static void rdma_reset(struct slgt_info *info);
-static bool rx_get_frame(struct slgt_info *info);
-static bool rx_get_buf(struct slgt_info *info);
-
-static void tx_start(struct slgt_info *info);
-static void tx_stop(struct slgt_info *info);
-static void tx_set_idle(struct slgt_info *info);
-static unsigned int free_tbuf_count(struct slgt_info *info);
-static unsigned int tbuf_bytes(struct slgt_info *info);
-static void reset_tbufs(struct slgt_info *info);
-static void tdma_reset(struct slgt_info *info);
-static bool tx_load(struct slgt_info *info, const char *buf, unsigned int count);
-
-static void get_signals(struct slgt_info *info);
-static void set_signals(struct slgt_info *info);
-static void enable_loopback(struct slgt_info *info);
-static void set_rate(struct slgt_info *info, u32 data_rate);
-
-static int  bh_action(struct slgt_info *info);
-static void bh_handler(struct work_struct *work);
-static void bh_transmit(struct slgt_info *info);
-static void isr_serial(struct slgt_info *info);
-static void isr_rdma(struct slgt_info *info);
-static void isr_txeom(struct slgt_info *info, unsigned short status);
-static void isr_tdma(struct slgt_info *info);
-
-static int  alloc_dma_bufs(struct slgt_info *info);
-static void free_dma_bufs(struct slgt_info *info);
-static int  alloc_desc(struct slgt_info *info);
-static void free_desc(struct slgt_info *info);
-static int  alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count);
-static void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count);
-
-static int  alloc_tmp_rbuf(struct slgt_info *info);
-static void free_tmp_rbuf(struct slgt_info *info);
-
-static void tx_timeout(unsigned long context);
-static void rx_timeout(unsigned long context);
-
-/*
- * ioctl handlers
- */
-static int  get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount);
-static int  get_params(struct slgt_info *info, MGSL_PARAMS __user *params);
-static int  set_params(struct slgt_info *info, MGSL_PARAMS __user *params);
-static int  get_txidle(struct slgt_info *info, int __user *idle_mode);
-static int  set_txidle(struct slgt_info *info, int idle_mode);
-static int  tx_enable(struct slgt_info *info, int enable);
-static int  tx_abort(struct slgt_info *info);
-static int  rx_enable(struct slgt_info *info, int enable);
-static int  modem_input_wait(struct slgt_info *info,int arg);
-static int  wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr);
-static int  tiocmget(struct tty_struct *tty);
-static int  tiocmset(struct tty_struct *tty,
-                               unsigned int set, unsigned int clear);
-static int set_break(struct tty_struct *tty, int break_state);
-static int  get_interface(struct slgt_info *info, int __user *if_mode);
-static int  set_interface(struct slgt_info *info, int if_mode);
-static int  set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
-static int  get_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
-static int  wait_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
-static int  get_xsync(struct slgt_info *info, int __user *if_mode);
-static int  set_xsync(struct slgt_info *info, int if_mode);
-static int  get_xctrl(struct slgt_info *info, int __user *if_mode);
-static int  set_xctrl(struct slgt_info *info, int if_mode);
-
-/*
- * driver functions
- */
-static void add_device(struct slgt_info *info);
-static void device_init(int adapter_num, struct pci_dev *pdev);
-static int  claim_resources(struct slgt_info *info);
-static void release_resources(struct slgt_info *info);
-
-/*
- * DEBUG OUTPUT CODE
- */
-#ifndef DBGINFO
-#define DBGINFO(fmt)
-#endif
-#ifndef DBGERR
-#define DBGERR(fmt)
-#endif
-#ifndef DBGBH
-#define DBGBH(fmt)
-#endif
-#ifndef DBGISR
-#define DBGISR(fmt)
-#endif
-
-#ifdef DBGDATA
-static void trace_block(struct slgt_info *info, const char *data, int count, const char *label)
-{
-       int i;
-       int linecount;
-       printk("%s %s data:\n",info->device_name, label);
-       while(count) {
-               linecount = (count > 16) ? 16 : count;
-               for(i=0; i < linecount; i++)
-                       printk("%02X ",(unsigned char)data[i]);
-               for(;i<17;i++)
-                       printk("   ");
-               for(i=0;i<linecount;i++) {
-                       if (data[i]>=040 && data[i]<=0176)
-                               printk("%c",data[i]);
-                       else
-                               printk(".");
-               }
-               printk("\n");
-               data  += linecount;
-               count -= linecount;
-       }
-}
-#else
-#define DBGDATA(info, buf, size, label)
-#endif
-
-#ifdef DBGTBUF
-static void dump_tbufs(struct slgt_info *info)
-{
-       int i;
-       printk("tbuf_current=%d\n", info->tbuf_current);
-       for (i=0 ; i < info->tbuf_count ; i++) {
-               printk("%d: count=%04X status=%04X\n",
-                       i, le16_to_cpu(info->tbufs[i].count), le16_to_cpu(info->tbufs[i].status));
-       }
-}
-#else
-#define DBGTBUF(info)
-#endif
-
-#ifdef DBGRBUF
-static void dump_rbufs(struct slgt_info *info)
-{
-       int i;
-       printk("rbuf_current=%d\n", info->rbuf_current);
-       for (i=0 ; i < info->rbuf_count ; i++) {
-               printk("%d: count=%04X status=%04X\n",
-                       i, le16_to_cpu(info->rbufs[i].count), le16_to_cpu(info->rbufs[i].status));
-       }
-}
-#else
-#define DBGRBUF(info)
-#endif
-
-static inline int sanity_check(struct slgt_info *info, char *devname, const char *name)
-{
-#ifdef SANITY_CHECK
-       if (!info) {
-               printk("null struct slgt_info for (%s) in %s\n", devname, name);
-               return 1;
-       }
-       if (info->magic != MGSL_MAGIC) {
-               printk("bad magic number struct slgt_info (%s) in %s\n", devname, name);
-               return 1;
-       }
-#else
-       if (!info)
-               return 1;
-#endif
-       return 0;
-}
-
-/**
- * line discipline callback wrappers
- *
- * The wrappers maintain line discipline references
- * while calling into the line discipline.
- *
- * ldisc_receive_buf  - pass receive data to line discipline
- */
-static void ldisc_receive_buf(struct tty_struct *tty,
-                             const __u8 *data, char *flags, int count)
-{
-       struct tty_ldisc *ld;
-       if (!tty)
-               return;
-       ld = tty_ldisc_ref(tty);
-       if (ld) {
-               if (ld->ops->receive_buf)
-                       ld->ops->receive_buf(tty, data, flags, count);
-               tty_ldisc_deref(ld);
-       }
-}
-
-/* tty callbacks */
-
-static int open(struct tty_struct *tty, struct file *filp)
-{
-       struct slgt_info *info;
-       int retval, line;
-       unsigned long flags;
-
-       line = tty->index;
-       if ((line < 0) || (line >= slgt_device_count)) {
-               DBGERR(("%s: open with invalid line #%d.\n", driver_name, line));
-               return -ENODEV;
-       }
-
-       info = slgt_device_list;
-       while(info && info->line != line)
-               info = info->next_device;
-       if (sanity_check(info, tty->name, "open"))
-               return -ENODEV;
-       if (info->init_error) {
-               DBGERR(("%s init error=%d\n", info->device_name, info->init_error));
-               return -ENODEV;
-       }
-
-       tty->driver_data = info;
-       info->port.tty = tty;
-
-       DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count));
-
-       /* If port is closing, signal caller to try again */
-       if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){
-               if (info->port.flags & ASYNC_CLOSING)
-                       interruptible_sleep_on(&info->port.close_wait);
-               retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ?
-                       -EAGAIN : -ERESTARTSYS);
-               goto cleanup;
-       }
-
-       mutex_lock(&info->port.mutex);
-       info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-
-       spin_lock_irqsave(&info->netlock, flags);
-       if (info->netcount) {
-               retval = -EBUSY;
-               spin_unlock_irqrestore(&info->netlock, flags);
-               mutex_unlock(&info->port.mutex);
-               goto cleanup;
-       }
-       info->port.count++;
-       spin_unlock_irqrestore(&info->netlock, flags);
-
-       if (info->port.count == 1) {
-               /* 1st open on this device, init hardware */
-               retval = startup(info);
-               if (retval < 0) {
-                       mutex_unlock(&info->port.mutex);
-                       goto cleanup;
-               }
-       }
-       mutex_unlock(&info->port.mutex);
-       retval = block_til_ready(tty, filp, info);
-       if (retval) {
-               DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval));
-               goto cleanup;
-       }
-
-       retval = 0;
-
-cleanup:
-       if (retval) {
-               if (tty->count == 1)
-                       info->port.tty = NULL; /* tty layer will release tty struct */
-               if(info->port.count)
-                       info->port.count--;
-       }
-
-       DBGINFO(("%s open rc=%d\n", info->device_name, retval));
-       return retval;
-}
-
-static void close(struct tty_struct *tty, struct file *filp)
-{
-       struct slgt_info *info = tty->driver_data;
-
-       if (sanity_check(info, tty->name, "close"))
-               return;
-       DBGINFO(("%s close entry, count=%d\n", info->device_name, info->port.count));
-
-       if (tty_port_close_start(&info->port, tty, filp) == 0)
-               goto cleanup;
-
-       mutex_lock(&info->port.mutex);
-       if (info->port.flags & ASYNC_INITIALIZED)
-               wait_until_sent(tty, info->timeout);
-       flush_buffer(tty);
-       tty_ldisc_flush(tty);
-
-       shutdown(info);
-       mutex_unlock(&info->port.mutex);
-
-       tty_port_close_end(&info->port, tty);
-       info->port.tty = NULL;
-cleanup:
-       DBGINFO(("%s close exit, count=%d\n", tty->driver->name, info->port.count));
-}
-
-static void hangup(struct tty_struct *tty)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned long flags;
-
-       if (sanity_check(info, tty->name, "hangup"))
-               return;
-       DBGINFO(("%s hangup\n", info->device_name));
-
-       flush_buffer(tty);
-
-       mutex_lock(&info->port.mutex);
-       shutdown(info);
-
-       spin_lock_irqsave(&info->port.lock, flags);
-       info->port.count = 0;
-       info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
-       info->port.tty = NULL;
-       spin_unlock_irqrestore(&info->port.lock, flags);
-       mutex_unlock(&info->port.mutex);
-
-       wake_up_interruptible(&info->port.open_wait);
-}
-
-static void set_termios(struct tty_struct *tty, struct ktermios *old_termios)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned long flags;
-
-       DBGINFO(("%s set_termios\n", tty->driver->name));
-
-       change_params(info);
-
-       /* Handle transition to B0 status */
-       if (old_termios->c_cflag & CBAUD &&
-           !(tty->termios->c_cflag & CBAUD)) {
-               info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
-               spin_lock_irqsave(&info->lock,flags);
-               set_signals(info);
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
-
-       /* Handle transition away from B0 status */
-       if (!(old_termios->c_cflag & CBAUD) &&
-           tty->termios->c_cflag & CBAUD) {
-               info->signals |= SerialSignal_DTR;
-               if (!(tty->termios->c_cflag & CRTSCTS) ||
-                   !test_bit(TTY_THROTTLED, &tty->flags)) {
-                       info->signals |= SerialSignal_RTS;
-               }
-               spin_lock_irqsave(&info->lock,flags);
-               set_signals(info);
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
-
-       /* Handle turning off CRTSCTS */
-       if (old_termios->c_cflag & CRTSCTS &&
-           !(tty->termios->c_cflag & CRTSCTS)) {
-               tty->hw_stopped = 0;
-               tx_release(tty);
-       }
-}
-
-static void update_tx_timer(struct slgt_info *info)
-{
-       /*
-        * use worst case speed of 1200bps to calculate transmit timeout
-        * based on data in buffers (tbuf_bytes) and FIFO (128 bytes)
-        */
-       if (info->params.mode == MGSL_MODE_HDLC) {
-               int timeout  = (tbuf_bytes(info) * 7) + 1000;
-               mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout));
-       }
-}
-
-static int write(struct tty_struct *tty,
-                const unsigned char *buf, int count)
-{
-       int ret = 0;
-       struct slgt_info *info = tty->driver_data;
-       unsigned long flags;
-
-       if (sanity_check(info, tty->name, "write"))
-               return -EIO;
-
-       DBGINFO(("%s write count=%d\n", info->device_name, count));
-
-       if (!info->tx_buf || (count > info->max_frame_size))
-               return -EIO;
-
-       if (!count || tty->stopped || tty->hw_stopped)
-               return 0;
-
-       spin_lock_irqsave(&info->lock, flags);
-
-       if (info->tx_count) {
-               /* send accumulated data from send_char() */
-               if (!tx_load(info, info->tx_buf, info->tx_count))
-                       goto cleanup;
-               info->tx_count = 0;
-       }
-
-       if (tx_load(info, buf, count))
-               ret = count;
-
-cleanup:
-       spin_unlock_irqrestore(&info->lock, flags);
-       DBGINFO(("%s write rc=%d\n", info->device_name, ret));
-       return ret;
-}
-
-static int put_char(struct tty_struct *tty, unsigned char ch)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned long flags;
-       int ret = 0;
-
-       if (sanity_check(info, tty->name, "put_char"))
-               return 0;
-       DBGINFO(("%s put_char(%d)\n", info->device_name, ch));
-       if (!info->tx_buf)
-               return 0;
-       spin_lock_irqsave(&info->lock,flags);
-       if (info->tx_count < info->max_frame_size) {
-               info->tx_buf[info->tx_count++] = ch;
-               ret = 1;
-       }
-       spin_unlock_irqrestore(&info->lock,flags);
-       return ret;
-}
-
-static void send_xchar(struct tty_struct *tty, char ch)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned long flags;
-
-       if (sanity_check(info, tty->name, "send_xchar"))
-               return;
-       DBGINFO(("%s send_xchar(%d)\n", info->device_name, ch));
-       info->x_char = ch;
-       if (ch) {
-               spin_lock_irqsave(&info->lock,flags);
-               if (!info->tx_enabled)
-                       tx_start(info);
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
-}
-
-static void wait_until_sent(struct tty_struct *tty, int timeout)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned long orig_jiffies, char_time;
-
-       if (!info )
-               return;
-       if (sanity_check(info, tty->name, "wait_until_sent"))
-               return;
-       DBGINFO(("%s wait_until_sent entry\n", info->device_name));
-       if (!(info->port.flags & ASYNC_INITIALIZED))
-               goto exit;
-
-       orig_jiffies = jiffies;
-
-       /* Set check interval to 1/5 of estimated time to
-        * send a character, and make it at least 1. The check
-        * interval should also be less than the timeout.
-        * Note: use tight timings here to satisfy the NIST-PCTS.
-        */
-
-       if (info->params.data_rate) {
-               char_time = info->timeout/(32 * 5);
-               if (!char_time)
-                       char_time++;
-       } else
-               char_time = 1;
-
-       if (timeout)
-               char_time = min_t(unsigned long, char_time, timeout);
-
-       while (info->tx_active) {
-               msleep_interruptible(jiffies_to_msecs(char_time));
-               if (signal_pending(current))
-                       break;
-               if (timeout && time_after(jiffies, orig_jiffies + timeout))
-                       break;
-       }
-exit:
-       DBGINFO(("%s wait_until_sent exit\n", info->device_name));
-}
-
-static int write_room(struct tty_struct *tty)
-{
-       struct slgt_info *info = tty->driver_data;
-       int ret;
-
-       if (sanity_check(info, tty->name, "write_room"))
-               return 0;
-       ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE;
-       DBGINFO(("%s write_room=%d\n", info->device_name, ret));
-       return ret;
-}
-
-static void flush_chars(struct tty_struct *tty)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned long flags;
-
-       if (sanity_check(info, tty->name, "flush_chars"))
-               return;
-       DBGINFO(("%s flush_chars entry tx_count=%d\n", info->device_name, info->tx_count));
-
-       if (info->tx_count <= 0 || tty->stopped ||
-           tty->hw_stopped || !info->tx_buf)
-               return;
-
-       DBGINFO(("%s flush_chars start transmit\n", info->device_name));
-
-       spin_lock_irqsave(&info->lock,flags);
-       if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count))
-               info->tx_count = 0;
-       spin_unlock_irqrestore(&info->lock,flags);
-}
-
-static void flush_buffer(struct tty_struct *tty)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned long flags;
-
-       if (sanity_check(info, tty->name, "flush_buffer"))
-               return;
-       DBGINFO(("%s flush_buffer\n", info->device_name));
-
-       spin_lock_irqsave(&info->lock, flags);
-       info->tx_count = 0;
-       spin_unlock_irqrestore(&info->lock, flags);
-
-       tty_wakeup(tty);
-}
-
-/*
- * throttle (stop) transmitter
- */
-static void tx_hold(struct tty_struct *tty)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned long flags;
-
-       if (sanity_check(info, tty->name, "tx_hold"))
-               return;
-       DBGINFO(("%s tx_hold\n", info->device_name));
-       spin_lock_irqsave(&info->lock,flags);
-       if (info->tx_enabled && info->params.mode == MGSL_MODE_ASYNC)
-               tx_stop(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-}
-
-/*
- * release (start) transmitter
- */
-static void tx_release(struct tty_struct *tty)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned long flags;
-
-       if (sanity_check(info, tty->name, "tx_release"))
-               return;
-       DBGINFO(("%s tx_release\n", info->device_name));
-       spin_lock_irqsave(&info->lock, flags);
-       if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count))
-               info->tx_count = 0;
-       spin_unlock_irqrestore(&info->lock, flags);
-}
-
-/*
- * Service an IOCTL request
- *
- * Arguments
- *
- *     tty     pointer to tty instance data
- *     cmd     IOCTL command code
- *     arg     command argument/context
- *
- * Return 0 if success, otherwise error code
- */
-static int ioctl(struct tty_struct *tty,
-                unsigned int cmd, unsigned long arg)
-{
-       struct slgt_info *info = tty->driver_data;
-       void __user *argp = (void __user *)arg;
-       int ret;
-
-       if (sanity_check(info, tty->name, "ioctl"))
-               return -ENODEV;
-       DBGINFO(("%s ioctl() cmd=%08X\n", info->device_name, cmd));
-
-       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-           (cmd != TIOCMIWAIT)) {
-               if (tty->flags & (1 << TTY_IO_ERROR))
-                   return -EIO;
-       }
-
-       switch (cmd) {
-       case MGSL_IOCWAITEVENT:
-               return wait_mgsl_event(info, argp);
-       case TIOCMIWAIT:
-               return modem_input_wait(info,(int)arg);
-       case MGSL_IOCSGPIO:
-               return set_gpio(info, argp);
-       case MGSL_IOCGGPIO:
-               return get_gpio(info, argp);
-       case MGSL_IOCWAITGPIO:
-               return wait_gpio(info, argp);
-       case MGSL_IOCGXSYNC:
-               return get_xsync(info, argp);
-       case MGSL_IOCSXSYNC:
-               return set_xsync(info, (int)arg);
-       case MGSL_IOCGXCTRL:
-               return get_xctrl(info, argp);
-       case MGSL_IOCSXCTRL:
-               return set_xctrl(info, (int)arg);
-       }
-       mutex_lock(&info->port.mutex);
-       switch (cmd) {
-       case MGSL_IOCGPARAMS:
-               ret = get_params(info, argp);
-               break;
-       case MGSL_IOCSPARAMS:
-               ret = set_params(info, argp);
-               break;
-       case MGSL_IOCGTXIDLE:
-               ret = get_txidle(info, argp);
-               break;
-       case MGSL_IOCSTXIDLE:
-               ret = set_txidle(info, (int)arg);
-               break;
-       case MGSL_IOCTXENABLE:
-               ret = tx_enable(info, (int)arg);
-               break;
-       case MGSL_IOCRXENABLE:
-               ret = rx_enable(info, (int)arg);
-               break;
-       case MGSL_IOCTXABORT:
-               ret = tx_abort(info);
-               break;
-       case MGSL_IOCGSTATS:
-               ret = get_stats(info, argp);
-               break;
-       case MGSL_IOCGIF:
-               ret = get_interface(info, argp);
-               break;
-       case MGSL_IOCSIF:
-               ret = set_interface(info,(int)arg);
-               break;
-       default:
-               ret = -ENOIOCTLCMD;
-       }
-       mutex_unlock(&info->port.mutex);
-       return ret;
-}
-
-static int get_icount(struct tty_struct *tty,
-                               struct serial_icounter_struct *icount)
-
-{
-       struct slgt_info *info = tty->driver_data;
-       struct mgsl_icount cnow;        /* kernel counter temps */
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->lock,flags);
-       cnow = info->icount;
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       icount->cts = cnow.cts;
-       icount->dsr = cnow.dsr;
-       icount->rng = cnow.rng;
-       icount->dcd = cnow.dcd;
-       icount->rx = cnow.rx;
-       icount->tx = cnow.tx;
-       icount->frame = cnow.frame;
-       icount->overrun = cnow.overrun;
-       icount->parity = cnow.parity;
-       icount->brk = cnow.brk;
-       icount->buf_overrun = cnow.buf_overrun;
-
-       return 0;
-}
-
-/*
- * support for 32 bit ioctl calls on 64 bit systems
- */
-#ifdef CONFIG_COMPAT
-static long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params)
-{
-       struct MGSL_PARAMS32 tmp_params;
-
-       DBGINFO(("%s get_params32\n", info->device_name));
-       memset(&tmp_params, 0, sizeof(tmp_params));
-       tmp_params.mode            = (compat_ulong_t)info->params.mode;
-       tmp_params.loopback        = info->params.loopback;
-       tmp_params.flags           = info->params.flags;
-       tmp_params.encoding        = info->params.encoding;
-       tmp_params.clock_speed     = (compat_ulong_t)info->params.clock_speed;
-       tmp_params.addr_filter     = info->params.addr_filter;
-       tmp_params.crc_type        = info->params.crc_type;
-       tmp_params.preamble_length = info->params.preamble_length;
-       tmp_params.preamble        = info->params.preamble;
-       tmp_params.data_rate       = (compat_ulong_t)info->params.data_rate;
-       tmp_params.data_bits       = info->params.data_bits;
-       tmp_params.stop_bits       = info->params.stop_bits;
-       tmp_params.parity          = info->params.parity;
-       if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32)))
-               return -EFAULT;
-       return 0;
-}
-
-static long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params)
-{
-       struct MGSL_PARAMS32 tmp_params;
-
-       DBGINFO(("%s set_params32\n", info->device_name));
-       if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32)))
-               return -EFAULT;
-
-       spin_lock(&info->lock);
-       if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) {
-               info->base_clock = tmp_params.clock_speed;
-       } else {
-               info->params.mode            = tmp_params.mode;
-               info->params.loopback        = tmp_params.loopback;
-               info->params.flags           = tmp_params.flags;
-               info->params.encoding        = tmp_params.encoding;
-               info->params.clock_speed     = tmp_params.clock_speed;
-               info->params.addr_filter     = tmp_params.addr_filter;
-               info->params.crc_type        = tmp_params.crc_type;
-               info->params.preamble_length = tmp_params.preamble_length;
-               info->params.preamble        = tmp_params.preamble;
-               info->params.data_rate       = tmp_params.data_rate;
-               info->params.data_bits       = tmp_params.data_bits;
-               info->params.stop_bits       = tmp_params.stop_bits;
-               info->params.parity          = tmp_params.parity;
-       }
-       spin_unlock(&info->lock);
-
-       program_hw(info);
-
-       return 0;
-}
-
-static long slgt_compat_ioctl(struct tty_struct *tty,
-                        unsigned int cmd, unsigned long arg)
-{
-       struct slgt_info *info = tty->driver_data;
-       int rc = -ENOIOCTLCMD;
-
-       if (sanity_check(info, tty->name, "compat_ioctl"))
-               return -ENODEV;
-       DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd));
-
-       switch (cmd) {
-
-       case MGSL_IOCSPARAMS32:
-               rc = set_params32(info, compat_ptr(arg));
-               break;
-
-       case MGSL_IOCGPARAMS32:
-               rc = get_params32(info, compat_ptr(arg));
-               break;
-
-       case MGSL_IOCGPARAMS:
-       case MGSL_IOCSPARAMS:
-       case MGSL_IOCGTXIDLE:
-       case MGSL_IOCGSTATS:
-       case MGSL_IOCWAITEVENT:
-       case MGSL_IOCGIF:
-       case MGSL_IOCSGPIO:
-       case MGSL_IOCGGPIO:
-       case MGSL_IOCWAITGPIO:
-       case MGSL_IOCGXSYNC:
-       case MGSL_IOCGXCTRL:
-       case MGSL_IOCSTXIDLE:
-       case MGSL_IOCTXENABLE:
-       case MGSL_IOCRXENABLE:
-       case MGSL_IOCTXABORT:
-       case TIOCMIWAIT:
-       case MGSL_IOCSIF:
-       case MGSL_IOCSXSYNC:
-       case MGSL_IOCSXCTRL:
-               rc = ioctl(tty, cmd, arg);
-               break;
-       }
-
-       DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc));
-       return rc;
-}
-#else
-#define slgt_compat_ioctl NULL
-#endif /* ifdef CONFIG_COMPAT */
-
-/*
- * proc fs support
- */
-static inline void line_info(struct seq_file *m, struct slgt_info *info)
-{
-       char stat_buf[30];
-       unsigned long flags;
-
-       seq_printf(m, "%s: IO=%08X IRQ=%d MaxFrameSize=%u\n",
-                     info->device_name, info->phys_reg_addr,
-                     info->irq_level, info->max_frame_size);
-
-       /* output current serial signal states */
-       spin_lock_irqsave(&info->lock,flags);
-       get_signals(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       stat_buf[0] = 0;
-       stat_buf[1] = 0;
-       if (info->signals & SerialSignal_RTS)
-               strcat(stat_buf, "|RTS");
-       if (info->signals & SerialSignal_CTS)
-               strcat(stat_buf, "|CTS");
-       if (info->signals & SerialSignal_DTR)
-               strcat(stat_buf, "|DTR");
-       if (info->signals & SerialSignal_DSR)
-               strcat(stat_buf, "|DSR");
-       if (info->signals & SerialSignal_DCD)
-               strcat(stat_buf, "|CD");
-       if (info->signals & SerialSignal_RI)
-               strcat(stat_buf, "|RI");
-
-       if (info->params.mode != MGSL_MODE_ASYNC) {
-               seq_printf(m, "\tHDLC txok:%d rxok:%d",
-                              info->icount.txok, info->icount.rxok);
-               if (info->icount.txunder)
-                       seq_printf(m, " txunder:%d", info->icount.txunder);
-               if (info->icount.txabort)
-                       seq_printf(m, " txabort:%d", info->icount.txabort);
-               if (info->icount.rxshort)
-                       seq_printf(m, " rxshort:%d", info->icount.rxshort);
-               if (info->icount.rxlong)
-                       seq_printf(m, " rxlong:%d", info->icount.rxlong);
-               if (info->icount.rxover)
-                       seq_printf(m, " rxover:%d", info->icount.rxover);
-               if (info->icount.rxcrc)
-                       seq_printf(m, " rxcrc:%d", info->icount.rxcrc);
-       } else {
-               seq_printf(m, "\tASYNC tx:%d rx:%d",
-                              info->icount.tx, info->icount.rx);
-               if (info->icount.frame)
-                       seq_printf(m, " fe:%d", info->icount.frame);
-               if (info->icount.parity)
-                       seq_printf(m, " pe:%d", info->icount.parity);
-               if (info->icount.brk)
-                       seq_printf(m, " brk:%d", info->icount.brk);
-               if (info->icount.overrun)
-                       seq_printf(m, " oe:%d", info->icount.overrun);
-       }
-
-       /* Append serial signal status to end */
-       seq_printf(m, " %s\n", stat_buf+1);
-
-       seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
-                      info->tx_active,info->bh_requested,info->bh_running,
-                      info->pending_bh);
-}
-
-/* Called to print information about devices
- */
-static int synclink_gt_proc_show(struct seq_file *m, void *v)
-{
-       struct slgt_info *info;
-
-       seq_puts(m, "synclink_gt driver\n");
-
-       info = slgt_device_list;
-       while( info ) {
-               line_info(m, info);
-               info = info->next_device;
-       }
-       return 0;
-}
-
-static int synclink_gt_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, synclink_gt_proc_show, NULL);
-}
-
-static const struct file_operations synclink_gt_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = synclink_gt_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-/*
- * return count of bytes in transmit buffer
- */
-static int chars_in_buffer(struct tty_struct *tty)
-{
-       struct slgt_info *info = tty->driver_data;
-       int count;
-       if (sanity_check(info, tty->name, "chars_in_buffer"))
-               return 0;
-       count = tbuf_bytes(info);
-       DBGINFO(("%s chars_in_buffer()=%d\n", info->device_name, count));
-       return count;
-}
-
-/*
- * signal remote device to throttle send data (our receive data)
- */
-static void throttle(struct tty_struct * tty)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned long flags;
-
-       if (sanity_check(info, tty->name, "throttle"))
-               return;
-       DBGINFO(("%s throttle\n", info->device_name));
-       if (I_IXOFF(tty))
-               send_xchar(tty, STOP_CHAR(tty));
-       if (tty->termios->c_cflag & CRTSCTS) {
-               spin_lock_irqsave(&info->lock,flags);
-               info->signals &= ~SerialSignal_RTS;
-               set_signals(info);
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
-}
-
-/*
- * signal remote device to stop throttling send data (our receive data)
- */
-static void unthrottle(struct tty_struct * tty)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned long flags;
-
-       if (sanity_check(info, tty->name, "unthrottle"))
-               return;
-       DBGINFO(("%s unthrottle\n", info->device_name));
-       if (I_IXOFF(tty)) {
-               if (info->x_char)
-                       info->x_char = 0;
-               else
-                       send_xchar(tty, START_CHAR(tty));
-       }
-       if (tty->termios->c_cflag & CRTSCTS) {
-               spin_lock_irqsave(&info->lock,flags);
-               info->signals |= SerialSignal_RTS;
-               set_signals(info);
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
-}
-
-/*
- * set or clear transmit break condition
- * break_state -1=set break condition, 0=clear
- */
-static int set_break(struct tty_struct *tty, int break_state)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned short value;
-       unsigned long flags;
-
-       if (sanity_check(info, tty->name, "set_break"))
-               return -EINVAL;
-       DBGINFO(("%s set_break(%d)\n", info->device_name, break_state));
-
-       spin_lock_irqsave(&info->lock,flags);
-       value = rd_reg16(info, TCR);
-       if (break_state == -1)
-               value |= BIT6;
-       else
-               value &= ~BIT6;
-       wr_reg16(info, TCR, value);
-       spin_unlock_irqrestore(&info->lock,flags);
-       return 0;
-}
-
-#if SYNCLINK_GENERIC_HDLC
-
-/**
- * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.)
- * set encoding and frame check sequence (FCS) options
- *
- * dev       pointer to network device structure
- * encoding  serial encoding setting
- * parity    FCS setting
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
-                         unsigned short parity)
-{
-       struct slgt_info *info = dev_to_port(dev);
-       unsigned char  new_encoding;
-       unsigned short new_crctype;
-
-       /* return error if TTY interface open */
-       if (info->port.count)
-               return -EBUSY;
-
-       DBGINFO(("%s hdlcdev_attach\n", info->device_name));
-
-       switch (encoding)
-       {
-       case ENCODING_NRZ:        new_encoding = HDLC_ENCODING_NRZ; break;
-       case ENCODING_NRZI:       new_encoding = HDLC_ENCODING_NRZI_SPACE; break;
-       case ENCODING_FM_MARK:    new_encoding = HDLC_ENCODING_BIPHASE_MARK; break;
-       case ENCODING_FM_SPACE:   new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break;
-       case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break;
-       default: return -EINVAL;
-       }
-
-       switch (parity)
-       {
-       case PARITY_NONE:            new_crctype = HDLC_CRC_NONE; break;
-       case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break;
-       case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break;
-       default: return -EINVAL;
-       }
-
-       info->params.encoding = new_encoding;
-       info->params.crc_type = new_crctype;
-
-       /* if network interface up, reprogram hardware */
-       if (info->netcount)
-               program_hw(info);
-
-       return 0;
-}
-
-/**
- * called by generic HDLC layer to send frame
- *
- * skb  socket buffer containing HDLC frame
- * dev  pointer to network device structure
- */
-static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
-                                     struct net_device *dev)
-{
-       struct slgt_info *info = dev_to_port(dev);
-       unsigned long flags;
-
-       DBGINFO(("%s hdlc_xmit\n", dev->name));
-
-       if (!skb->len)
-               return NETDEV_TX_OK;
-
-       /* stop sending until this frame completes */
-       netif_stop_queue(dev);
-
-       /* update network statistics */
-       dev->stats.tx_packets++;
-       dev->stats.tx_bytes += skb->len;
-
-       /* save start time for transmit timeout detection */
-       dev->trans_start = jiffies;
-
-       spin_lock_irqsave(&info->lock, flags);
-       tx_load(info, skb->data, skb->len);
-       spin_unlock_irqrestore(&info->lock, flags);
-
-       /* done with socket buffer, so free it */
-       dev_kfree_skb(skb);
-
-       return NETDEV_TX_OK;
-}
-
-/**
- * called by network layer when interface enabled
- * claim resources and initialize hardware
- *
- * dev  pointer to network device structure
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_open(struct net_device *dev)
-{
-       struct slgt_info *info = dev_to_port(dev);
-       int rc;
-       unsigned long flags;
-
-       if (!try_module_get(THIS_MODULE))
-               return -EBUSY;
-
-       DBGINFO(("%s hdlcdev_open\n", dev->name));
-
-       /* generic HDLC layer open processing */
-       if ((rc = hdlc_open(dev)))
-               return rc;
-
-       /* arbitrate between network and tty opens */
-       spin_lock_irqsave(&info->netlock, flags);
-       if (info->port.count != 0 || info->netcount != 0) {
-               DBGINFO(("%s hdlc_open busy\n", dev->name));
-               spin_unlock_irqrestore(&info->netlock, flags);
-               return -EBUSY;
-       }
-       info->netcount=1;
-       spin_unlock_irqrestore(&info->netlock, flags);
-
-       /* claim resources and init adapter */
-       if ((rc = startup(info)) != 0) {
-               spin_lock_irqsave(&info->netlock, flags);
-               info->netcount=0;
-               spin_unlock_irqrestore(&info->netlock, flags);
-               return rc;
-       }
-
-       /* assert DTR and RTS, apply hardware settings */
-       info->signals |= SerialSignal_RTS + SerialSignal_DTR;
-       program_hw(info);
-
-       /* enable network layer transmit */
-       dev->trans_start = jiffies;
-       netif_start_queue(dev);
-
-       /* inform generic HDLC layer of current DCD status */
-       spin_lock_irqsave(&info->lock, flags);
-       get_signals(info);
-       spin_unlock_irqrestore(&info->lock, flags);
-       if (info->signals & SerialSignal_DCD)
-               netif_carrier_on(dev);
-       else
-               netif_carrier_off(dev);
-       return 0;
-}
-
-/**
- * called by network layer when interface is disabled
- * shutdown hardware and release resources
- *
- * dev  pointer to network device structure
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_close(struct net_device *dev)
-{
-       struct slgt_info *info = dev_to_port(dev);
-       unsigned long flags;
-
-       DBGINFO(("%s hdlcdev_close\n", dev->name));
-
-       netif_stop_queue(dev);
-
-       /* shutdown adapter and release resources */
-       shutdown(info);
-
-       hdlc_close(dev);
-
-       spin_lock_irqsave(&info->netlock, flags);
-       info->netcount=0;
-       spin_unlock_irqrestore(&info->netlock, flags);
-
-       module_put(THIS_MODULE);
-       return 0;
-}
-
-/**
- * called by network layer to process IOCTL call to network device
- *
- * dev  pointer to network device structure
- * ifr  pointer to network interface request structure
- * cmd  IOCTL command code
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
-       const size_t size = sizeof(sync_serial_settings);
-       sync_serial_settings new_line;
-       sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
-       struct slgt_info *info = dev_to_port(dev);
-       unsigned int flags;
-
-       DBGINFO(("%s hdlcdev_ioctl\n", dev->name));
-
-       /* return error if TTY interface open */
-       if (info->port.count)
-               return -EBUSY;
-
-       if (cmd != SIOCWANDEV)
-               return hdlc_ioctl(dev, ifr, cmd);
-
-       memset(&new_line, 0, sizeof(new_line));
-
-       switch(ifr->ifr_settings.type) {
-       case IF_GET_IFACE: /* return current sync_serial_settings */
-
-               ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
-               if (ifr->ifr_settings.size < size) {
-                       ifr->ifr_settings.size = size; /* data size wanted */
-                       return -ENOBUFS;
-               }
-
-               flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
-                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
-                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
-                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
-
-               switch (flags){
-               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break;
-               case (HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_INT; break;
-               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_TXINT; break;
-               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break;
-               default: new_line.clock_type = CLOCK_DEFAULT;
-               }
-
-               new_line.clock_rate = info->params.clock_speed;
-               new_line.loopback   = info->params.loopback ? 1:0;
-
-               if (copy_to_user(line, &new_line, size))
-                       return -EFAULT;
-               return 0;
-
-       case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */
-
-               if(!capable(CAP_NET_ADMIN))
-                       return -EPERM;
-               if (copy_from_user(&new_line, line, size))
-                       return -EFAULT;
-
-               switch (new_line.clock_type)
-               {
-               case CLOCK_EXT:      flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break;
-               case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break;
-               case CLOCK_INT:      flags = HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG;    break;
-               case CLOCK_TXINT:    flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG;    break;
-               case CLOCK_DEFAULT:  flags = info->params.flags &
-                                            (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
-                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
-                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
-                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN); break;
-               default: return -EINVAL;
-               }
-
-               if (new_line.loopback != 0 && new_line.loopback != 1)
-                       return -EINVAL;
-
-               info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
-                                       HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
-                                       HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
-                                       HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
-               info->params.flags |= flags;
-
-               info->params.loopback = new_line.loopback;
-
-               if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG))
-                       info->params.clock_speed = new_line.clock_rate;
-               else
-                       info->params.clock_speed = 0;
-
-               /* if network interface up, reprogram hardware */
-               if (info->netcount)
-                       program_hw(info);
-               return 0;
-
-       default:
-               return hdlc_ioctl(dev, ifr, cmd);
-       }
-}
-
-/**
- * called by network layer when transmit timeout is detected
- *
- * dev  pointer to network device structure
- */
-static void hdlcdev_tx_timeout(struct net_device *dev)
-{
-       struct slgt_info *info = dev_to_port(dev);
-       unsigned long flags;
-
-       DBGINFO(("%s hdlcdev_tx_timeout\n", dev->name));
-
-       dev->stats.tx_errors++;
-       dev->stats.tx_aborted_errors++;
-
-       spin_lock_irqsave(&info->lock,flags);
-       tx_stop(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       netif_wake_queue(dev);
-}
-
-/**
- * called by device driver when transmit completes
- * reenable network layer transmit if stopped
- *
- * info  pointer to device instance information
- */
-static void hdlcdev_tx_done(struct slgt_info *info)
-{
-       if (netif_queue_stopped(info->netdev))
-               netif_wake_queue(info->netdev);
-}
-
-/**
- * called by device driver when frame received
- * pass frame to network layer
- *
- * info  pointer to device instance information
- * buf   pointer to buffer contianing frame data
- * size  count of data bytes in buf
- */
-static void hdlcdev_rx(struct slgt_info *info, char *buf, int size)
-{
-       struct sk_buff *skb = dev_alloc_skb(size);
-       struct net_device *dev = info->netdev;
-
-       DBGINFO(("%s hdlcdev_rx\n", dev->name));
-
-       if (skb == NULL) {
-               DBGERR(("%s: can't alloc skb, drop packet\n", dev->name));
-               dev->stats.rx_dropped++;
-               return;
-       }
-
-       memcpy(skb_put(skb, size), buf, size);
-
-       skb->protocol = hdlc_type_trans(skb, dev);
-
-       dev->stats.rx_packets++;
-       dev->stats.rx_bytes += size;
-
-       netif_rx(skb);
-}
-
-static const struct net_device_ops hdlcdev_ops = {
-       .ndo_open       = hdlcdev_open,
-       .ndo_stop       = hdlcdev_close,
-       .ndo_change_mtu = hdlc_change_mtu,
-       .ndo_start_xmit = hdlc_start_xmit,
-       .ndo_do_ioctl   = hdlcdev_ioctl,
-       .ndo_tx_timeout = hdlcdev_tx_timeout,
-};
-
-/**
- * called by device driver when adding device instance
- * do generic HDLC initialization
- *
- * info  pointer to device instance information
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_init(struct slgt_info *info)
-{
-       int rc;
-       struct net_device *dev;
-       hdlc_device *hdlc;
-
-       /* allocate and initialize network and HDLC layer objects */
-
-       if (!(dev = alloc_hdlcdev(info))) {
-               printk(KERN_ERR "%s hdlc device alloc failure\n", info->device_name);
-               return -ENOMEM;
-       }
-
-       /* for network layer reporting purposes only */
-       dev->mem_start = info->phys_reg_addr;
-       dev->mem_end   = info->phys_reg_addr + SLGT_REG_SIZE - 1;
-       dev->irq       = info->irq_level;
-
-       /* network layer callbacks and settings */
-       dev->netdev_ops     = &hdlcdev_ops;
-       dev->watchdog_timeo = 10 * HZ;
-       dev->tx_queue_len   = 50;
-
-       /* generic HDLC layer callbacks and settings */
-       hdlc         = dev_to_hdlc(dev);
-       hdlc->attach = hdlcdev_attach;
-       hdlc->xmit   = hdlcdev_xmit;
-
-       /* register objects with HDLC layer */
-       if ((rc = register_hdlc_device(dev))) {
-               printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__);
-               free_netdev(dev);
-               return rc;
-       }
-
-       info->netdev = dev;
-       return 0;
-}
-
-/**
- * called by device driver when removing device instance
- * do generic HDLC cleanup
- *
- * info  pointer to device instance information
- */
-static void hdlcdev_exit(struct slgt_info *info)
-{
-       unregister_hdlc_device(info->netdev);
-       free_netdev(info->netdev);
-       info->netdev = NULL;
-}
-
-#endif /* ifdef CONFIG_HDLC */
-
-/*
- * get async data from rx DMA buffers
- */
-static void rx_async(struct slgt_info *info)
-{
-       struct tty_struct *tty = info->port.tty;
-       struct mgsl_icount *icount = &info->icount;
-       unsigned int start, end;
-       unsigned char *p;
-       unsigned char status;
-       struct slgt_desc *bufs = info->rbufs;
-       int i, count;
-       int chars = 0;
-       int stat;
-       unsigned char ch;
-
-       start = end = info->rbuf_current;
-
-       while(desc_complete(bufs[end])) {
-               count = desc_count(bufs[end]) - info->rbuf_index;
-               p     = bufs[end].buf + info->rbuf_index;
-
-               DBGISR(("%s rx_async count=%d\n", info->device_name, count));
-               DBGDATA(info, p, count, "rx");
-
-               for(i=0 ; i < count; i+=2, p+=2) {
-                       ch = *p;
-                       icount->rx++;
-
-                       stat = 0;
-
-                       if ((status = *(p+1) & (BIT1 + BIT0))) {
-                               if (status & BIT1)
-                                       icount->parity++;
-                               else if (status & BIT0)
-                                       icount->frame++;
-                               /* discard char if tty control flags say so */
-                               if (status & info->ignore_status_mask)
-                                       continue;
-                               if (status & BIT1)
-                                       stat = TTY_PARITY;
-                               else if (status & BIT0)
-                                       stat = TTY_FRAME;
-                       }
-                       if (tty) {
-                               tty_insert_flip_char(tty, ch, stat);
-                               chars++;
-                       }
-               }
-
-               if (i < count) {
-                       /* receive buffer not completed */
-                       info->rbuf_index += i;
-                       mod_timer(&info->rx_timer, jiffies + 1);
-                       break;
-               }
-
-               info->rbuf_index = 0;
-               free_rbufs(info, end, end);
-
-               if (++end == info->rbuf_count)
-                       end = 0;
-
-               /* if entire list searched then no frame available */
-               if (end == start)
-                       break;
-       }
-
-       if (tty && chars)
-               tty_flip_buffer_push(tty);
-}
-
-/*
- * return next bottom half action to perform
- */
-static int bh_action(struct slgt_info *info)
-{
-       unsigned long flags;
-       int rc;
-
-       spin_lock_irqsave(&info->lock,flags);
-
-       if (info->pending_bh & BH_RECEIVE) {
-               info->pending_bh &= ~BH_RECEIVE;
-               rc = BH_RECEIVE;
-       } else if (info->pending_bh & BH_TRANSMIT) {
-               info->pending_bh &= ~BH_TRANSMIT;
-               rc = BH_TRANSMIT;
-       } else if (info->pending_bh & BH_STATUS) {
-               info->pending_bh &= ~BH_STATUS;
-               rc = BH_STATUS;
-       } else {
-               /* Mark BH routine as complete */
-               info->bh_running = false;
-               info->bh_requested = false;
-               rc = 0;
-       }
-
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       return rc;
-}
-
-/*
- * perform bottom half processing
- */
-static void bh_handler(struct work_struct *work)
-{
-       struct slgt_info *info = container_of(work, struct slgt_info, task);
-       int action;
-
-       if (!info)
-               return;
-       info->bh_running = true;
-
-       while((action = bh_action(info))) {
-               switch (action) {
-               case BH_RECEIVE:
-                       DBGBH(("%s bh receive\n", info->device_name));
-                       switch(info->params.mode) {
-                       case MGSL_MODE_ASYNC:
-                               rx_async(info);
-                               break;
-                       case MGSL_MODE_HDLC:
-                               while(rx_get_frame(info));
-                               break;
-                       case MGSL_MODE_RAW:
-                       case MGSL_MODE_MONOSYNC:
-                       case MGSL_MODE_BISYNC:
-                       case MGSL_MODE_XSYNC:
-                               while(rx_get_buf(info));
-                               break;
-                       }
-                       /* restart receiver if rx DMA buffers exhausted */
-                       if (info->rx_restart)
-                               rx_start(info);
-                       break;
-               case BH_TRANSMIT:
-                       bh_transmit(info);
-                       break;
-               case BH_STATUS:
-                       DBGBH(("%s bh status\n", info->device_name));
-                       info->ri_chkcount = 0;
-                       info->dsr_chkcount = 0;
-                       info->dcd_chkcount = 0;
-                       info->cts_chkcount = 0;
-                       break;
-               default:
-                       DBGBH(("%s unknown action\n", info->device_name));
-                       break;
-               }
-       }
-       DBGBH(("%s bh_handler exit\n", info->device_name));
-}
-
-static void bh_transmit(struct slgt_info *info)
-{
-       struct tty_struct *tty = info->port.tty;
-
-       DBGBH(("%s bh_transmit\n", info->device_name));
-       if (tty)
-               tty_wakeup(tty);
-}
-
-static void dsr_change(struct slgt_info *info, unsigned short status)
-{
-       if (status & BIT3) {
-               info->signals |= SerialSignal_DSR;
-               info->input_signal_events.dsr_up++;
-       } else {
-               info->signals &= ~SerialSignal_DSR;
-               info->input_signal_events.dsr_down++;
-       }
-       DBGISR(("dsr_change %s signals=%04X\n", info->device_name, info->signals));
-       if ((info->dsr_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
-               slgt_irq_off(info, IRQ_DSR);
-               return;
-       }
-       info->icount.dsr++;
-       wake_up_interruptible(&info->status_event_wait_q);
-       wake_up_interruptible(&info->event_wait_q);
-       info->pending_bh |= BH_STATUS;
-}
-
-static void cts_change(struct slgt_info *info, unsigned short status)
-{
-       if (status & BIT2) {
-               info->signals |= SerialSignal_CTS;
-               info->input_signal_events.cts_up++;
-       } else {
-               info->signals &= ~SerialSignal_CTS;
-               info->input_signal_events.cts_down++;
-       }
-       DBGISR(("cts_change %s signals=%04X\n", info->device_name, info->signals));
-       if ((info->cts_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
-               slgt_irq_off(info, IRQ_CTS);
-               return;
-       }
-       info->icount.cts++;
-       wake_up_interruptible(&info->status_event_wait_q);
-       wake_up_interruptible(&info->event_wait_q);
-       info->pending_bh |= BH_STATUS;
-
-       if (info->port.flags & ASYNC_CTS_FLOW) {
-               if (info->port.tty) {
-                       if (info->port.tty->hw_stopped) {
-                               if (info->signals & SerialSignal_CTS) {
-                                       info->port.tty->hw_stopped = 0;
-                                       info->pending_bh |= BH_TRANSMIT;
-                                       return;
-                               }
-                       } else {
-                               if (!(info->signals & SerialSignal_CTS))
-                                       info->port.tty->hw_stopped = 1;
-                       }
-               }
-       }
-}
-
-static void dcd_change(struct slgt_info *info, unsigned short status)
-{
-       if (status & BIT1) {
-               info->signals |= SerialSignal_DCD;
-               info->input_signal_events.dcd_up++;
-       } else {
-               info->signals &= ~SerialSignal_DCD;
-               info->input_signal_events.dcd_down++;
-       }
-       DBGISR(("dcd_change %s signals=%04X\n", info->device_name, info->signals));
-       if ((info->dcd_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
-               slgt_irq_off(info, IRQ_DCD);
-               return;
-       }
-       info->icount.dcd++;
-#if SYNCLINK_GENERIC_HDLC
-       if (info->netcount) {
-               if (info->signals & SerialSignal_DCD)
-                       netif_carrier_on(info->netdev);
-               else
-                       netif_carrier_off(info->netdev);
-       }
-#endif
-       wake_up_interruptible(&info->status_event_wait_q);
-       wake_up_interruptible(&info->event_wait_q);
-       info->pending_bh |= BH_STATUS;
-
-       if (info->port.flags & ASYNC_CHECK_CD) {
-               if (info->signals & SerialSignal_DCD)
-                       wake_up_interruptible(&info->port.open_wait);
-               else {
-                       if (info->port.tty)
-                               tty_hangup(info->port.tty);
-               }
-       }
-}
-
-static void ri_change(struct slgt_info *info, unsigned short status)
-{
-       if (status & BIT0) {
-               info->signals |= SerialSignal_RI;
-               info->input_signal_events.ri_up++;
-       } else {
-               info->signals &= ~SerialSignal_RI;
-               info->input_signal_events.ri_down++;
-       }
-       DBGISR(("ri_change %s signals=%04X\n", info->device_name, info->signals));
-       if ((info->ri_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
-               slgt_irq_off(info, IRQ_RI);
-               return;
-       }
-       info->icount.rng++;
-       wake_up_interruptible(&info->status_event_wait_q);
-       wake_up_interruptible(&info->event_wait_q);
-       info->pending_bh |= BH_STATUS;
-}
-
-static void isr_rxdata(struct slgt_info *info)
-{
-       unsigned int count = info->rbuf_fill_count;
-       unsigned int i = info->rbuf_fill_index;
-       unsigned short reg;
-
-       while (rd_reg16(info, SSR) & IRQ_RXDATA) {
-               reg = rd_reg16(info, RDR);
-               DBGISR(("isr_rxdata %s RDR=%04X\n", info->device_name, reg));
-               if (desc_complete(info->rbufs[i])) {
-                       /* all buffers full */
-                       rx_stop(info);
-                       info->rx_restart = 1;
-                       continue;
-               }
-               info->rbufs[i].buf[count++] = (unsigned char)reg;
-               /* async mode saves status byte to buffer for each data byte */
-               if (info->params.mode == MGSL_MODE_ASYNC)
-                       info->rbufs[i].buf[count++] = (unsigned char)(reg >> 8);
-               if (count == info->rbuf_fill_level || (reg & BIT10)) {
-                       /* buffer full or end of frame */
-                       set_desc_count(info->rbufs[i], count);
-                       set_desc_status(info->rbufs[i], BIT15 | (reg >> 8));
-                       info->rbuf_fill_count = count = 0;
-                       if (++i == info->rbuf_count)
-                               i = 0;
-                       info->pending_bh |= BH_RECEIVE;
-               }
-       }
-
-       info->rbuf_fill_index = i;
-       info->rbuf_fill_count = count;
-}
-
-static void isr_serial(struct slgt_info *info)
-{
-       unsigned short status = rd_reg16(info, SSR);
-
-       DBGISR(("%s isr_serial status=%04X\n", info->device_name, status));
-
-       wr_reg16(info, SSR, status); /* clear pending */
-
-       info->irq_occurred = true;
-
-       if (info->params.mode == MGSL_MODE_ASYNC) {
-               if (status & IRQ_TXIDLE) {
-                       if (info->tx_active)
-                               isr_txeom(info, status);
-               }
-               if (info->rx_pio && (status & IRQ_RXDATA))
-                       isr_rxdata(info);
-               if ((status & IRQ_RXBREAK) && (status & RXBREAK)) {
-                       info->icount.brk++;
-                       /* process break detection if tty control allows */
-                       if (info->port.tty) {
-                               if (!(status & info->ignore_status_mask)) {
-                                       if (info->read_status_mask & MASK_BREAK) {
-                                               tty_insert_flip_char(info->port.tty, 0, TTY_BREAK);
-                                               if (info->port.flags & ASYNC_SAK)
-                                                       do_SAK(info->port.tty);
-                                       }
-                               }
-                       }
-               }
-       } else {
-               if (status & (IRQ_TXIDLE + IRQ_TXUNDER))
-                       isr_txeom(info, status);
-               if (info->rx_pio && (status & IRQ_RXDATA))
-                       isr_rxdata(info);
-               if (status & IRQ_RXIDLE) {
-                       if (status & RXIDLE)
-                               info->icount.rxidle++;
-                       else
-                               info->icount.exithunt++;
-                       wake_up_interruptible(&info->event_wait_q);
-               }
-
-               if (status & IRQ_RXOVER)
-                       rx_start(info);
-       }
-
-       if (status & IRQ_DSR)
-               dsr_change(info, status);
-       if (status & IRQ_CTS)
-               cts_change(info, status);
-       if (status & IRQ_DCD)
-               dcd_change(info, status);
-       if (status & IRQ_RI)
-               ri_change(info, status);
-}
-
-static void isr_rdma(struct slgt_info *info)
-{
-       unsigned int status = rd_reg32(info, RDCSR);
-
-       DBGISR(("%s isr_rdma status=%08x\n", info->device_name, status));
-
-       /* RDCSR (rx DMA control/status)
-        *
-        * 31..07  reserved
-        * 06      save status byte to DMA buffer
-        * 05      error
-        * 04      eol (end of list)
-        * 03      eob (end of buffer)
-        * 02      IRQ enable
-        * 01      reset
-        * 00      enable
-        */
-       wr_reg32(info, RDCSR, status);  /* clear pending */
-
-       if (status & (BIT5 + BIT4)) {
-               DBGISR(("%s isr_rdma rx_restart=1\n", info->device_name));
-               info->rx_restart = true;
-       }
-       info->pending_bh |= BH_RECEIVE;
-}
-
-static void isr_tdma(struct slgt_info *info)
-{
-       unsigned int status = rd_reg32(info, TDCSR);
-
-       DBGISR(("%s isr_tdma status=%08x\n", info->device_name, status));
-
-       /* TDCSR (tx DMA control/status)
-        *
-        * 31..06  reserved
-        * 05      error
-        * 04      eol (end of list)
-        * 03      eob (end of buffer)
-        * 02      IRQ enable
-        * 01      reset
-        * 00      enable
-        */
-       wr_reg32(info, TDCSR, status);  /* clear pending */
-
-       if (status & (BIT5 + BIT4 + BIT3)) {
-               // another transmit buffer has completed
-               // run bottom half to get more send data from user
-               info->pending_bh |= BH_TRANSMIT;
-       }
-}
-
-/*
- * return true if there are unsent tx DMA buffers, otherwise false
- *
- * if there are unsent buffers then info->tbuf_start
- * is set to index of first unsent buffer
- */
-static bool unsent_tbufs(struct slgt_info *info)
-{
-       unsigned int i = info->tbuf_current;
-       bool rc = false;
-
-       /*
-        * search backwards from last loaded buffer (precedes tbuf_current)
-        * for first unsent buffer (desc_count > 0)
-        */
-
-       do {
-               if (i)
-                       i--;
-               else
-                       i = info->tbuf_count - 1;
-               if (!desc_count(info->tbufs[i]))
-                       break;
-               info->tbuf_start = i;
-               rc = true;
-       } while (i != info->tbuf_current);
-
-       return rc;
-}
-
-static void isr_txeom(struct slgt_info *info, unsigned short status)
-{
-       DBGISR(("%s txeom status=%04x\n", info->device_name, status));
-
-       slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER);
-       tdma_reset(info);
-       if (status & IRQ_TXUNDER) {
-               unsigned short val = rd_reg16(info, TCR);
-               wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */
-               wr_reg16(info, TCR, val); /* clear reset bit */
-       }
-
-       if (info->tx_active) {
-               if (info->params.mode != MGSL_MODE_ASYNC) {
-                       if (status & IRQ_TXUNDER)
-                               info->icount.txunder++;
-                       else if (status & IRQ_TXIDLE)
-                               info->icount.txok++;
-               }
-
-               if (unsent_tbufs(info)) {
-                       tx_start(info);
-                       update_tx_timer(info);
-                       return;
-               }
-               info->tx_active = false;
-
-               del_timer(&info->tx_timer);
-
-               if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done) {
-                       info->signals &= ~SerialSignal_RTS;
-                       info->drop_rts_on_tx_done = false;
-                       set_signals(info);
-               }
-
-#if SYNCLINK_GENERIC_HDLC
-               if (info->netcount)
-                       hdlcdev_tx_done(info);
-               else
-#endif
-               {
-                       if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) {
-                               tx_stop(info);
-                               return;
-                       }
-                       info->pending_bh |= BH_TRANSMIT;
-               }
-       }
-}
-
-static void isr_gpio(struct slgt_info *info, unsigned int changed, unsigned int state)
-{
-       struct cond_wait *w, *prev;
-
-       /* wake processes waiting for specific transitions */
-       for (w = info->gpio_wait_q, prev = NULL ; w != NULL ; w = w->next) {
-               if (w->data & changed) {
-                       w->data = state;
-                       wake_up_interruptible(&w->q);
-                       if (prev != NULL)
-                               prev->next = w->next;
-                       else
-                               info->gpio_wait_q = w->next;
-               } else
-                       prev = w;
-       }
-}
-
-/* interrupt service routine
- *
- *     irq     interrupt number
- *     dev_id  device ID supplied during interrupt registration
- */
-static irqreturn_t slgt_interrupt(int dummy, void *dev_id)
-{
-       struct slgt_info *info = dev_id;
-       unsigned int gsr;
-       unsigned int i;
-
-       DBGISR(("slgt_interrupt irq=%d entry\n", info->irq_level));
-
-       while((gsr = rd_reg32(info, GSR) & 0xffffff00)) {
-               DBGISR(("%s gsr=%08x\n", info->device_name, gsr));
-               info->irq_occurred = true;
-               for(i=0; i < info->port_count ; i++) {
-                       if (info->port_array[i] == NULL)
-                               continue;
-                       spin_lock(&info->port_array[i]->lock);
-                       if (gsr & (BIT8 << i))
-                               isr_serial(info->port_array[i]);
-                       if (gsr & (BIT16 << (i*2)))
-                               isr_rdma(info->port_array[i]);
-                       if (gsr & (BIT17 << (i*2)))
-                               isr_tdma(info->port_array[i]);
-                       spin_unlock(&info->port_array[i]->lock);
-               }
-       }
-
-       if (info->gpio_present) {
-               unsigned int state;
-               unsigned int changed;
-               spin_lock(&info->lock);
-               while ((changed = rd_reg32(info, IOSR)) != 0) {
-                       DBGISR(("%s iosr=%08x\n", info->device_name, changed));
-                       /* read latched state of GPIO signals */
-                       state = rd_reg32(info, IOVR);
-                       /* clear pending GPIO interrupt bits */
-                       wr_reg32(info, IOSR, changed);
-                       for (i=0 ; i < info->port_count ; i++) {
-                               if (info->port_array[i] != NULL)
-                                       isr_gpio(info->port_array[i], changed, state);
-                       }
-               }
-               spin_unlock(&info->lock);
-       }
-
-       for(i=0; i < info->port_count ; i++) {
-               struct slgt_info *port = info->port_array[i];
-               if (port == NULL)
-                       continue;
-               spin_lock(&port->lock);
-               if ((port->port.count || port->netcount) &&
-                   port->pending_bh && !port->bh_running &&
-                   !port->bh_requested) {
-                       DBGISR(("%s bh queued\n", port->device_name));
-                       schedule_work(&port->task);
-                       port->bh_requested = true;
-               }
-               spin_unlock(&port->lock);
-       }
-
-       DBGISR(("slgt_interrupt irq=%d exit\n", info->irq_level));
-       return IRQ_HANDLED;
-}
-
-static int startup(struct slgt_info *info)
-{
-       DBGINFO(("%s startup\n", info->device_name));
-
-       if (info->port.flags & ASYNC_INITIALIZED)
-               return 0;
-
-       if (!info->tx_buf) {
-               info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL);
-               if (!info->tx_buf) {
-                       DBGERR(("%s can't allocate tx buffer\n", info->device_name));
-                       return -ENOMEM;
-               }
-       }
-
-       info->pending_bh = 0;
-
-       memset(&info->icount, 0, sizeof(info->icount));
-
-       /* program hardware for current parameters */
-       change_params(info);
-
-       if (info->port.tty)
-               clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
-
-       info->port.flags |= ASYNC_INITIALIZED;
-
-       return 0;
-}
-
-/*
- *  called by close() and hangup() to shutdown hardware
- */
-static void shutdown(struct slgt_info *info)
-{
-       unsigned long flags;
-
-       if (!(info->port.flags & ASYNC_INITIALIZED))
-               return;
-
-       DBGINFO(("%s shutdown\n", info->device_name));
-
-       /* clear status wait queue because status changes */
-       /* can't happen after shutting down the hardware */
-       wake_up_interruptible(&info->status_event_wait_q);
-       wake_up_interruptible(&info->event_wait_q);
-
-       del_timer_sync(&info->tx_timer);
-       del_timer_sync(&info->rx_timer);
-
-       kfree(info->tx_buf);
-       info->tx_buf = NULL;
-
-       spin_lock_irqsave(&info->lock,flags);
-
-       tx_stop(info);
-       rx_stop(info);
-
-       slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
-
-       if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) {
-               info->signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
-               set_signals(info);
-       }
-
-       flush_cond_wait(&info->gpio_wait_q);
-
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       if (info->port.tty)
-               set_bit(TTY_IO_ERROR, &info->port.tty->flags);
-
-       info->port.flags &= ~ASYNC_INITIALIZED;
-}
-
-static void program_hw(struct slgt_info *info)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->lock,flags);
-
-       rx_stop(info);
-       tx_stop(info);
-
-       if (info->params.mode != MGSL_MODE_ASYNC ||
-           info->netcount)
-               sync_mode(info);
-       else
-               async_mode(info);
-
-       set_signals(info);
-
-       info->dcd_chkcount = 0;
-       info->cts_chkcount = 0;
-       info->ri_chkcount = 0;
-       info->dsr_chkcount = 0;
-
-       slgt_irq_on(info, IRQ_DCD | IRQ_CTS | IRQ_DSR | IRQ_RI);
-       get_signals(info);
-
-       if (info->netcount ||
-           (info->port.tty && info->port.tty->termios->c_cflag & CREAD))
-               rx_start(info);
-
-       spin_unlock_irqrestore(&info->lock,flags);
-}
-
-/*
- * reconfigure adapter based on new parameters
- */
-static void change_params(struct slgt_info *info)
-{
-       unsigned cflag;
-       int bits_per_char;
-
-       if (!info->port.tty || !info->port.tty->termios)
-               return;
-       DBGINFO(("%s change_params\n", info->device_name));
-
-       cflag = info->port.tty->termios->c_cflag;
-
-       /* if B0 rate (hangup) specified then negate DTR and RTS */
-       /* otherwise assert DTR and RTS */
-       if (cflag & CBAUD)
-               info->signals |= SerialSignal_RTS + SerialSignal_DTR;
-       else
-               info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
-
-       /* byte size and parity */
-
-       switch (cflag & CSIZE) {
-       case CS5: info->params.data_bits = 5; break;
-       case CS6: info->params.data_bits = 6; break;
-       case CS7: info->params.data_bits = 7; break;
-       case CS8: info->params.data_bits = 8; break;
-       default:  info->params.data_bits = 7; break;
-       }
-
-       info->params.stop_bits = (cflag & CSTOPB) ? 2 : 1;
-
-       if (cflag & PARENB)
-               info->params.parity = (cflag & PARODD) ? ASYNC_PARITY_ODD : ASYNC_PARITY_EVEN;
-       else
-               info->params.parity = ASYNC_PARITY_NONE;
-
-       /* calculate number of jiffies to transmit a full
-        * FIFO (32 bytes) at specified data rate
-        */
-       bits_per_char = info->params.data_bits +
-                       info->params.stop_bits + 1;
-
-       info->params.data_rate = tty_get_baud_rate(info->port.tty);
-
-       if (info->params.data_rate) {
-               info->timeout = (32*HZ*bits_per_char) /
-                               info->params.data_rate;
-       }
-       info->timeout += HZ/50;         /* Add .02 seconds of slop */
-
-       if (cflag & CRTSCTS)
-               info->port.flags |= ASYNC_CTS_FLOW;
-       else
-               info->port.flags &= ~ASYNC_CTS_FLOW;
-
-       if (cflag & CLOCAL)
-               info->port.flags &= ~ASYNC_CHECK_CD;
-       else
-               info->port.flags |= ASYNC_CHECK_CD;
-
-       /* process tty input control flags */
-
-       info->read_status_mask = IRQ_RXOVER;
-       if (I_INPCK(info->port.tty))
-               info->read_status_mask |= MASK_PARITY | MASK_FRAMING;
-       if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty))
-               info->read_status_mask |= MASK_BREAK;
-       if (I_IGNPAR(info->port.tty))
-               info->ignore_status_mask |= MASK_PARITY | MASK_FRAMING;
-       if (I_IGNBRK(info->port.tty)) {
-               info->ignore_status_mask |= MASK_BREAK;
-               /* If ignoring parity and break indicators, ignore
-                * overruns too.  (For real raw support).
-                */
-               if (I_IGNPAR(info->port.tty))
-                       info->ignore_status_mask |= MASK_OVERRUN;
-       }
-
-       program_hw(info);
-}
-
-static int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount)
-{
-       DBGINFO(("%s get_stats\n",  info->device_name));
-       if (!user_icount) {
-               memset(&info->icount, 0, sizeof(info->icount));
-       } else {
-               if (copy_to_user(user_icount, &info->icount, sizeof(struct mgsl_icount)))
-                       return -EFAULT;
-       }
-       return 0;
-}
-
-static int get_params(struct slgt_info *info, MGSL_PARAMS __user *user_params)
-{
-       DBGINFO(("%s get_params\n", info->device_name));
-       if (copy_to_user(user_params, &info->params, sizeof(MGSL_PARAMS)))
-               return -EFAULT;
-       return 0;
-}
-
-static int set_params(struct slgt_info *info, MGSL_PARAMS __user *new_params)
-{
-       unsigned long flags;
-       MGSL_PARAMS tmp_params;
-
-       DBGINFO(("%s set_params\n", info->device_name));
-       if (copy_from_user(&tmp_params, new_params, sizeof(MGSL_PARAMS)))
-               return -EFAULT;
-
-       spin_lock_irqsave(&info->lock, flags);
-       if (tmp_params.mode == MGSL_MODE_BASE_CLOCK)
-               info->base_clock = tmp_params.clock_speed;
-       else
-               memcpy(&info->params, &tmp_params, sizeof(MGSL_PARAMS));
-       spin_unlock_irqrestore(&info->lock, flags);
-
-       program_hw(info);
-
-       return 0;
-}
-
-static int get_txidle(struct slgt_info *info, int __user *idle_mode)
-{
-       DBGINFO(("%s get_txidle=%d\n", info->device_name, info->idle_mode));
-       if (put_user(info->idle_mode, idle_mode))
-               return -EFAULT;
-       return 0;
-}
-
-static int set_txidle(struct slgt_info *info, int idle_mode)
-{
-       unsigned long flags;
-       DBGINFO(("%s set_txidle(%d)\n", info->device_name, idle_mode));
-       spin_lock_irqsave(&info->lock,flags);
-       info->idle_mode = idle_mode;
-       if (info->params.mode != MGSL_MODE_ASYNC)
-               tx_set_idle(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-       return 0;
-}
-
-static int tx_enable(struct slgt_info *info, int enable)
-{
-       unsigned long flags;
-       DBGINFO(("%s tx_enable(%d)\n", info->device_name, enable));
-       spin_lock_irqsave(&info->lock,flags);
-       if (enable) {
-               if (!info->tx_enabled)
-                       tx_start(info);
-       } else {
-               if (info->tx_enabled)
-                       tx_stop(info);
-       }
-       spin_unlock_irqrestore(&info->lock,flags);
-       return 0;
-}
-
-/*
- * abort transmit HDLC frame
- */
-static int tx_abort(struct slgt_info *info)
-{
-       unsigned long flags;
-       DBGINFO(("%s tx_abort\n", info->device_name));
-       spin_lock_irqsave(&info->lock,flags);
-       tdma_reset(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-       return 0;
-}
-
-static int rx_enable(struct slgt_info *info, int enable)
-{
-       unsigned long flags;
-       unsigned int rbuf_fill_level;
-       DBGINFO(("%s rx_enable(%08x)\n", info->device_name, enable));
-       spin_lock_irqsave(&info->lock,flags);
-       /*
-        * enable[31..16] = receive DMA buffer fill level
-        * 0 = noop (leave fill level unchanged)
-        * fill level must be multiple of 4 and <= buffer size
-        */
-       rbuf_fill_level = ((unsigned int)enable) >> 16;
-       if (rbuf_fill_level) {
-               if ((rbuf_fill_level > DMABUFSIZE) || (rbuf_fill_level % 4)) {
-                       spin_unlock_irqrestore(&info->lock, flags);
-                       return -EINVAL;
-               }
-               info->rbuf_fill_level = rbuf_fill_level;
-               if (rbuf_fill_level < 128)
-                       info->rx_pio = 1; /* PIO mode */
-               else
-                       info->rx_pio = 0; /* DMA mode */
-               rx_stop(info); /* restart receiver to use new fill level */
-       }
-
-       /*
-        * enable[1..0] = receiver enable command
-        * 0 = disable
-        * 1 = enable
-        * 2 = enable or force hunt mode if already enabled
-        */
-       enable &= 3;
-       if (enable) {
-               if (!info->rx_enabled)
-                       rx_start(info);
-               else if (enable == 2) {
-                       /* force hunt mode (write 1 to RCR[3]) */
-                       wr_reg16(info, RCR, rd_reg16(info, RCR) | BIT3);
-               }
-       } else {
-               if (info->rx_enabled)
-                       rx_stop(info);
-       }
-       spin_unlock_irqrestore(&info->lock,flags);
-       return 0;
-}
-
-/*
- *  wait for specified event to occur
- */
-static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr)
-{
-       unsigned long flags;
-       int s;
-       int rc=0;
-       struct mgsl_icount cprev, cnow;
-       int events;
-       int mask;
-       struct  _input_signal_events oldsigs, newsigs;
-       DECLARE_WAITQUEUE(wait, current);
-
-       if (get_user(mask, mask_ptr))
-               return -EFAULT;
-
-       DBGINFO(("%s wait_mgsl_event(%d)\n", info->device_name, mask));
-
-       spin_lock_irqsave(&info->lock,flags);
-
-       /* return immediately if state matches requested events */
-       get_signals(info);
-       s = info->signals;
-
-       events = mask &
-               ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
-                 ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
-                 ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) +
-                 ((s & SerialSignal_RI)  ? MgslEvent_RiActive :MgslEvent_RiInactive) );
-       if (events) {
-               spin_unlock_irqrestore(&info->lock,flags);
-               goto exit;
-       }
-
-       /* save current irq counts */
-       cprev = info->icount;
-       oldsigs = info->input_signal_events;
-
-       /* enable hunt and idle irqs if needed */
-       if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) {
-               unsigned short val = rd_reg16(info, SCR);
-               if (!(val & IRQ_RXIDLE))
-                       wr_reg16(info, SCR, (unsigned short)(val | IRQ_RXIDLE));
-       }
-
-       set_current_state(TASK_INTERRUPTIBLE);
-       add_wait_queue(&info->event_wait_q, &wait);
-
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       for(;;) {
-               schedule();
-               if (signal_pending(current)) {
-                       rc = -ERESTARTSYS;
-                       break;
-               }
-
-               /* get current irq counts */
-               spin_lock_irqsave(&info->lock,flags);
-               cnow = info->icount;
-               newsigs = info->input_signal_events;
-               set_current_state(TASK_INTERRUPTIBLE);
-               spin_unlock_irqrestore(&info->lock,flags);
-
-               /* if no change, wait aborted for some reason */
-               if (newsigs.dsr_up   == oldsigs.dsr_up   &&
-                   newsigs.dsr_down == oldsigs.dsr_down &&
-                   newsigs.dcd_up   == oldsigs.dcd_up   &&
-                   newsigs.dcd_down == oldsigs.dcd_down &&
-                   newsigs.cts_up   == oldsigs.cts_up   &&
-                   newsigs.cts_down == oldsigs.cts_down &&
-                   newsigs.ri_up    == oldsigs.ri_up    &&
-                   newsigs.ri_down  == oldsigs.ri_down  &&
-                   cnow.exithunt    == cprev.exithunt   &&
-                   cnow.rxidle      == cprev.rxidle) {
-                       rc = -EIO;
-                       break;
-               }
-
-               events = mask &
-                       ( (newsigs.dsr_up   != oldsigs.dsr_up   ? MgslEvent_DsrActive:0)   +
-                         (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) +
-                         (newsigs.dcd_up   != oldsigs.dcd_up   ? MgslEvent_DcdActive:0)   +
-                         (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) +
-                         (newsigs.cts_up   != oldsigs.cts_up   ? MgslEvent_CtsActive:0)   +
-                         (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) +
-                         (newsigs.ri_up    != oldsigs.ri_up    ? MgslEvent_RiActive:0)    +
-                         (newsigs.ri_down  != oldsigs.ri_down  ? MgslEvent_RiInactive:0)  +
-                         (cnow.exithunt    != cprev.exithunt   ? MgslEvent_ExitHuntMode:0) +
-                         (cnow.rxidle      != cprev.rxidle     ? MgslEvent_IdleReceived:0) );
-               if (events)
-                       break;
-
-               cprev = cnow;
-               oldsigs = newsigs;
-       }
-
-       remove_wait_queue(&info->event_wait_q, &wait);
-       set_current_state(TASK_RUNNING);
-
-
-       if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
-               spin_lock_irqsave(&info->lock,flags);
-               if (!waitqueue_active(&info->event_wait_q)) {
-                       /* disable enable exit hunt mode/idle rcvd IRQs */
-                       wr_reg16(info, SCR,
-                               (unsigned short)(rd_reg16(info, SCR) & ~IRQ_RXIDLE));
-               }
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
-exit:
-       if (rc == 0)
-               rc = put_user(events, mask_ptr);
-       return rc;
-}
-
-static int get_interface(struct slgt_info *info, int __user *if_mode)
-{
-       DBGINFO(("%s get_interface=%x\n", info->device_name, info->if_mode));
-       if (put_user(info->if_mode, if_mode))
-               return -EFAULT;
-       return 0;
-}
-
-static int set_interface(struct slgt_info *info, int if_mode)
-{
-       unsigned long flags;
-       unsigned short val;
-
-       DBGINFO(("%s set_interface=%x)\n", info->device_name, if_mode));
-       spin_lock_irqsave(&info->lock,flags);
-       info->if_mode = if_mode;
-
-       msc_set_vcr(info);
-
-       /* TCR (tx control) 07  1=RTS driver control */
-       val = rd_reg16(info, TCR);
-       if (info->if_mode & MGSL_INTERFACE_RTS_EN)
-               val |= BIT7;
-       else
-               val &= ~BIT7;
-       wr_reg16(info, TCR, val);
-
-       spin_unlock_irqrestore(&info->lock,flags);
-       return 0;
-}
-
-static int get_xsync(struct slgt_info *info, int __user *xsync)
-{
-       DBGINFO(("%s get_xsync=%x\n", info->device_name, info->xsync));
-       if (put_user(info->xsync, xsync))
-               return -EFAULT;
-       return 0;
-}
-
-/*
- * set extended sync pattern (1 to 4 bytes) for extended sync mode
- *
- * sync pattern is contained in least significant bytes of value
- * most significant byte of sync pattern is oldest (1st sent/detected)
- */
-static int set_xsync(struct slgt_info *info, int xsync)
-{
-       unsigned long flags;
-
-       DBGINFO(("%s set_xsync=%x)\n", info->device_name, xsync));
-       spin_lock_irqsave(&info->lock, flags);
-       info->xsync = xsync;
-       wr_reg32(info, XSR, xsync);
-       spin_unlock_irqrestore(&info->lock, flags);
-       return 0;
-}
-
-static int get_xctrl(struct slgt_info *info, int __user *xctrl)
-{
-       DBGINFO(("%s get_xctrl=%x\n", info->device_name, info->xctrl));
-       if (put_user(info->xctrl, xctrl))
-               return -EFAULT;
-       return 0;
-}
-
-/*
- * set extended control options
- *
- * xctrl[31:19] reserved, must be zero
- * xctrl[18:17] extended sync pattern length in bytes
- *              00 = 1 byte  in xsr[7:0]
- *              01 = 2 bytes in xsr[15:0]
- *              10 = 3 bytes in xsr[23:0]
- *              11 = 4 bytes in xsr[31:0]
- * xctrl[16]    1 = enable terminal count, 0=disabled
- * xctrl[15:0]  receive terminal count for fixed length packets
- *              value is count minus one (0 = 1 byte packet)
- *              when terminal count is reached, receiver
- *              automatically returns to hunt mode and receive
- *              FIFO contents are flushed to DMA buffers with
- *              end of frame (EOF) status
- */
-static int set_xctrl(struct slgt_info *info, int xctrl)
-{
-       unsigned long flags;
-
-       DBGINFO(("%s set_xctrl=%x)\n", info->device_name, xctrl));
-       spin_lock_irqsave(&info->lock, flags);
-       info->xctrl = xctrl;
-       wr_reg32(info, XCR, xctrl);
-       spin_unlock_irqrestore(&info->lock, flags);
-       return 0;
-}
-
-/*
- * set general purpose IO pin state and direction
- *
- * user_gpio fields:
- * state   each bit indicates a pin state
- * smask   set bit indicates pin state to set
- * dir     each bit indicates a pin direction (0=input, 1=output)
- * dmask   set bit indicates pin direction to set
- */
-static int set_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio)
-{
-       unsigned long flags;
-       struct gpio_desc gpio;
-       __u32 data;
-
-       if (!info->gpio_present)
-               return -EINVAL;
-       if (copy_from_user(&gpio, user_gpio, sizeof(gpio)))
-               return -EFAULT;
-       DBGINFO(("%s set_gpio state=%08x smask=%08x dir=%08x dmask=%08x\n",
-                info->device_name, gpio.state, gpio.smask,
-                gpio.dir, gpio.dmask));
-
-       spin_lock_irqsave(&info->port_array[0]->lock, flags);
-       if (gpio.dmask) {
-               data = rd_reg32(info, IODR);
-               data |= gpio.dmask & gpio.dir;
-               data &= ~(gpio.dmask & ~gpio.dir);
-               wr_reg32(info, IODR, data);
-       }
-       if (gpio.smask) {
-               data = rd_reg32(info, IOVR);
-               data |= gpio.smask & gpio.state;
-               data &= ~(gpio.smask & ~gpio.state);
-               wr_reg32(info, IOVR, data);
-       }
-       spin_unlock_irqrestore(&info->port_array[0]->lock, flags);
-
-       return 0;
-}
-
-/*
- * get general purpose IO pin state and direction
- */
-static int get_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio)
-{
-       struct gpio_desc gpio;
-       if (!info->gpio_present)
-               return -EINVAL;
-       gpio.state = rd_reg32(info, IOVR);
-       gpio.smask = 0xffffffff;
-       gpio.dir   = rd_reg32(info, IODR);
-       gpio.dmask = 0xffffffff;
-       if (copy_to_user(user_gpio, &gpio, sizeof(gpio)))
-               return -EFAULT;
-       DBGINFO(("%s get_gpio state=%08x dir=%08x\n",
-                info->device_name, gpio.state, gpio.dir));
-       return 0;
-}
-
-/*
- * conditional wait facility
- */
-static void init_cond_wait(struct cond_wait *w, unsigned int data)
-{
-       init_waitqueue_head(&w->q);
-       init_waitqueue_entry(&w->wait, current);
-       w->data = data;
-}
-
-static void add_cond_wait(struct cond_wait **head, struct cond_wait *w)
-{
-       set_current_state(TASK_INTERRUPTIBLE);
-       add_wait_queue(&w->q, &w->wait);
-       w->next = *head;
-       *head = w;
-}
-
-static void remove_cond_wait(struct cond_wait **head, struct cond_wait *cw)
-{
-       struct cond_wait *w, *prev;
-       remove_wait_queue(&cw->q, &cw->wait);
-       set_current_state(TASK_RUNNING);
-       for (w = *head, prev = NULL ; w != NULL ; prev = w, w = w->next) {
-               if (w == cw) {
-                       if (prev != NULL)
-                               prev->next = w->next;
-                       else
-                               *head = w->next;
-                       break;
-               }
-       }
-}
-
-static void flush_cond_wait(struct cond_wait **head)
-{
-       while (*head != NULL) {
-               wake_up_interruptible(&(*head)->q);
-               *head = (*head)->next;
-       }
-}
-
-/*
- * wait for general purpose I/O pin(s) to enter specified state
- *
- * user_gpio fields:
- * state - bit indicates target pin state
- * smask - set bit indicates watched pin
- *
- * The wait ends when at least one watched pin enters the specified
- * state. When 0 (no error) is returned, user_gpio->state is set to the
- * state of all GPIO pins when the wait ends.
- *
- * Note: Each pin may be a dedicated input, dedicated output, or
- * configurable input/output. The number and configuration of pins
- * varies with the specific adapter model. Only input pins (dedicated
- * or configured) can be monitored with this function.
- */
-static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio)
-{
-       unsigned long flags;
-       int rc = 0;
-       struct gpio_desc gpio;
-       struct cond_wait wait;
-       u32 state;
-
-       if (!info->gpio_present)
-               return -EINVAL;
-       if (copy_from_user(&gpio, user_gpio, sizeof(gpio)))
-               return -EFAULT;
-       DBGINFO(("%s wait_gpio() state=%08x smask=%08x\n",
-                info->device_name, gpio.state, gpio.smask));
-       /* ignore output pins identified by set IODR bit */
-       if ((gpio.smask &= ~rd_reg32(info, IODR)) == 0)
-               return -EINVAL;
-       init_cond_wait(&wait, gpio.smask);
-
-       spin_lock_irqsave(&info->port_array[0]->lock, flags);
-       /* enable interrupts for watched pins */
-       wr_reg32(info, IOER, rd_reg32(info, IOER) | gpio.smask);
-       /* get current pin states */
-       state = rd_reg32(info, IOVR);
-
-       if (gpio.smask & ~(state ^ gpio.state)) {
-               /* already in target state */
-               gpio.state = state;
-       } else {
-               /* wait for target state */
-               add_cond_wait(&info->gpio_wait_q, &wait);
-               spin_unlock_irqrestore(&info->port_array[0]->lock, flags);
-               schedule();
-               if (signal_pending(current))
-                       rc = -ERESTARTSYS;
-               else
-                       gpio.state = wait.data;
-               spin_lock_irqsave(&info->port_array[0]->lock, flags);
-               remove_cond_wait(&info->gpio_wait_q, &wait);
-       }
-
-       /* disable all GPIO interrupts if no waiting processes */
-       if (info->gpio_wait_q == NULL)
-               wr_reg32(info, IOER, 0);
-       spin_unlock_irqrestore(&info->port_array[0]->lock, flags);
-
-       if ((rc == 0) && copy_to_user(user_gpio, &gpio, sizeof(gpio)))
-               rc = -EFAULT;
-       return rc;
-}
-
-static int modem_input_wait(struct slgt_info *info,int arg)
-{
-       unsigned long flags;
-       int rc;
-       struct mgsl_icount cprev, cnow;
-       DECLARE_WAITQUEUE(wait, current);
-
-       /* save current irq counts */
-       spin_lock_irqsave(&info->lock,flags);
-       cprev = info->icount;
-       add_wait_queue(&info->status_event_wait_q, &wait);
-       set_current_state(TASK_INTERRUPTIBLE);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       for(;;) {
-               schedule();
-               if (signal_pending(current)) {
-                       rc = -ERESTARTSYS;
-                       break;
-               }
-
-               /* get new irq counts */
-               spin_lock_irqsave(&info->lock,flags);
-               cnow = info->icount;
-               set_current_state(TASK_INTERRUPTIBLE);
-               spin_unlock_irqrestore(&info->lock,flags);
-
-               /* if no change, wait aborted for some reason */
-               if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
-                   cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
-                       rc = -EIO;
-                       break;
-               }
-
-               /* check for change in caller specified modem input */
-               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)) {
-                       rc = 0;
-                       break;
-               }
-
-               cprev = cnow;
-       }
-       remove_wait_queue(&info->status_event_wait_q, &wait);
-       set_current_state(TASK_RUNNING);
-       return rc;
-}
-
-/*
- *  return state of serial control and status signals
- */
-static int tiocmget(struct tty_struct *tty)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned int result;
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->lock,flags);
-       get_signals(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       result = ((info->signals & SerialSignal_RTS) ? TIOCM_RTS:0) +
-               ((info->signals & SerialSignal_DTR) ? TIOCM_DTR:0) +
-               ((info->signals & SerialSignal_DCD) ? TIOCM_CAR:0) +
-               ((info->signals & SerialSignal_RI)  ? TIOCM_RNG:0) +
-               ((info->signals & SerialSignal_DSR) ? TIOCM_DSR:0) +
-               ((info->signals & SerialSignal_CTS) ? TIOCM_CTS:0);
-
-       DBGINFO(("%s tiocmget value=%08X\n", info->device_name, result));
-       return result;
-}
-
-/*
- * set modem control signals (DTR/RTS)
- *
- *     cmd     signal command: TIOCMBIS = set bit TIOCMBIC = clear bit
- *             TIOCMSET = set/clear signal values
- *     value   bit mask for command
- */
-static int tiocmset(struct tty_struct *tty,
-                   unsigned int set, unsigned int clear)
-{
-       struct slgt_info *info = tty->driver_data;
-       unsigned long flags;
-
-       DBGINFO(("%s tiocmset(%x,%x)\n", info->device_name, set, clear));
-
-       if (set & TIOCM_RTS)
-               info->signals |= SerialSignal_RTS;
-       if (set & TIOCM_DTR)
-               info->signals |= SerialSignal_DTR;
-       if (clear & TIOCM_RTS)
-               info->signals &= ~SerialSignal_RTS;
-       if (clear & TIOCM_DTR)
-               info->signals &= ~SerialSignal_DTR;
-
-       spin_lock_irqsave(&info->lock,flags);
-       set_signals(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-       return 0;
-}
-
-static int carrier_raised(struct tty_port *port)
-{
-       unsigned long flags;
-       struct slgt_info *info = container_of(port, struct slgt_info, port);
-
-       spin_lock_irqsave(&info->lock,flags);
-       get_signals(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-       return (info->signals & SerialSignal_DCD) ? 1 : 0;
-}
-
-static void dtr_rts(struct tty_port *port, int on)
-{
-       unsigned long flags;
-       struct slgt_info *info = container_of(port, struct slgt_info, port);
-
-       spin_lock_irqsave(&info->lock,flags);
-       if (on)
-               info->signals |= SerialSignal_RTS + SerialSignal_DTR;
-       else
-               info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
-       set_signals(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-}
-
-
-/*
- *  block current process until the device is ready to open
- */
-static int block_til_ready(struct tty_struct *tty, struct file *filp,
-                          struct slgt_info *info)
-{
-       DECLARE_WAITQUEUE(wait, current);
-       int             retval;
-       bool            do_clocal = false;
-       bool            extra_count = false;
-       unsigned long   flags;
-       int             cd;
-       struct tty_port *port = &info->port;
-
-       DBGINFO(("%s block_til_ready\n", tty->driver->name));
-
-       if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){
-               /* nonblock mode is set or port is not enabled */
-               port->flags |= ASYNC_NORMAL_ACTIVE;
-               return 0;
-       }
-
-       if (tty->termios->c_cflag & CLOCAL)
-               do_clocal = true;
-
-       /* Wait for carrier detect and the line to become
-        * free (i.e., not in use by the callout).  While we are in
-        * this loop, port->count is dropped by one, so that
-        * close() knows when to free things.  We restore it upon
-        * exit, either normal or abnormal.
-        */
-
-       retval = 0;
-       add_wait_queue(&port->open_wait, &wait);
-
-       spin_lock_irqsave(&info->lock, flags);
-       if (!tty_hung_up_p(filp)) {
-               extra_count = true;
-               port->count--;
-       }
-       spin_unlock_irqrestore(&info->lock, flags);
-       port->blocked_open++;
-
-       while (1) {
-               if ((tty->termios->c_cflag & CBAUD))
-                       tty_port_raise_dtr_rts(port);
-
-               set_current_state(TASK_INTERRUPTIBLE);
-
-               if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){
-                       retval = (port->flags & ASYNC_HUP_NOTIFY) ?
-                                       -EAGAIN : -ERESTARTSYS;
-                       break;
-               }
-
-               cd = tty_port_carrier_raised(port);
-
-               if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd ))
-                       break;
-
-               if (signal_pending(current)) {
-                       retval = -ERESTARTSYS;
-                       break;
-               }
-
-               DBGINFO(("%s block_til_ready wait\n", tty->driver->name));
-               tty_unlock();
-               schedule();
-               tty_lock();
-       }
-
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&port->open_wait, &wait);
-
-       if (extra_count)
-               port->count++;
-       port->blocked_open--;
-
-       if (!retval)
-               port->flags |= ASYNC_NORMAL_ACTIVE;
-
-       DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval));
-       return retval;
-}
-
-static int alloc_tmp_rbuf(struct slgt_info *info)
-{
-       info->tmp_rbuf = kmalloc(info->max_frame_size + 5, GFP_KERNEL);
-       if (info->tmp_rbuf == NULL)
-               return -ENOMEM;
-       return 0;
-}
-
-static void free_tmp_rbuf(struct slgt_info *info)
-{
-       kfree(info->tmp_rbuf);
-       info->tmp_rbuf = NULL;
-}
-
-/*
- * allocate DMA descriptor lists.
- */
-static int alloc_desc(struct slgt_info *info)
-{
-       unsigned int i;
-       unsigned int pbufs;
-
-       /* allocate memory to hold descriptor lists */
-       info->bufs = pci_alloc_consistent(info->pdev, DESC_LIST_SIZE, &info->bufs_dma_addr);
-       if (info->bufs == NULL)
-               return -ENOMEM;
-
-       memset(info->bufs, 0, DESC_LIST_SIZE);
-
-       info->rbufs = (struct slgt_desc*)info->bufs;
-       info->tbufs = ((struct slgt_desc*)info->bufs) + info->rbuf_count;
-
-       pbufs = (unsigned int)info->bufs_dma_addr;
-
-       /*
-        * Build circular lists of descriptors
-        */
-
-       for (i=0; i < info->rbuf_count; i++) {
-               /* physical address of this descriptor */
-               info->rbufs[i].pdesc = pbufs + (i * sizeof(struct slgt_desc));
-
-               /* physical address of next descriptor */
-               if (i == info->rbuf_count - 1)
-                       info->rbufs[i].next = cpu_to_le32(pbufs);
-               else
-                       info->rbufs[i].next = cpu_to_le32(pbufs + ((i+1) * sizeof(struct slgt_desc)));
-               set_desc_count(info->rbufs[i], DMABUFSIZE);
-       }
-
-       for (i=0; i < info->tbuf_count; i++) {
-               /* physical address of this descriptor */
-               info->tbufs[i].pdesc = pbufs + ((info->rbuf_count + i) * sizeof(struct slgt_desc));
-
-               /* physical address of next descriptor */
-               if (i == info->tbuf_count - 1)
-                       info->tbufs[i].next = cpu_to_le32(pbufs + info->rbuf_count * sizeof(struct slgt_desc));
-               else
-                       info->tbufs[i].next = cpu_to_le32(pbufs + ((info->rbuf_count + i + 1) * sizeof(struct slgt_desc)));
-       }
-
-       return 0;
-}
-
-static void free_desc(struct slgt_info *info)
-{
-       if (info->bufs != NULL) {
-               pci_free_consistent(info->pdev, DESC_LIST_SIZE, info->bufs, info->bufs_dma_addr);
-               info->bufs  = NULL;
-               info->rbufs = NULL;
-               info->tbufs = NULL;
-       }
-}
-
-static int alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count)
-{
-       int i;
-       for (i=0; i < count; i++) {
-               if ((bufs[i].buf = pci_alloc_consistent(info->pdev, DMABUFSIZE, &bufs[i].buf_dma_addr)) == NULL)
-                       return -ENOMEM;
-               bufs[i].pbuf  = cpu_to_le32((unsigned int)bufs[i].buf_dma_addr);
-       }
-       return 0;
-}
-
-static void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count)
-{
-       int i;
-       for (i=0; i < count; i++) {
-               if (bufs[i].buf == NULL)
-                       continue;
-               pci_free_consistent(info->pdev, DMABUFSIZE, bufs[i].buf, bufs[i].buf_dma_addr);
-               bufs[i].buf = NULL;
-       }
-}
-
-static int alloc_dma_bufs(struct slgt_info *info)
-{
-       info->rbuf_count = 32;
-       info->tbuf_count = 32;
-
-       if (alloc_desc(info) < 0 ||
-           alloc_bufs(info, info->rbufs, info->rbuf_count) < 0 ||
-           alloc_bufs(info, info->tbufs, info->tbuf_count) < 0 ||
-           alloc_tmp_rbuf(info) < 0) {
-               DBGERR(("%s DMA buffer alloc fail\n", info->device_name));
-               return -ENOMEM;
-       }
-       reset_rbufs(info);
-       return 0;
-}
-
-static void free_dma_bufs(struct slgt_info *info)
-{
-       if (info->bufs) {
-               free_bufs(info, info->rbufs, info->rbuf_count);
-               free_bufs(info, info->tbufs, info->tbuf_count);
-               free_desc(info);
-       }
-       free_tmp_rbuf(info);
-}
-
-static int claim_resources(struct slgt_info *info)
-{
-       if (request_mem_region(info->phys_reg_addr, SLGT_REG_SIZE, "synclink_gt") == NULL) {
-               DBGERR(("%s reg addr conflict, addr=%08X\n",
-                       info->device_name, info->phys_reg_addr));
-               info->init_error = DiagStatus_AddressConflict;
-               goto errout;
-       }
-       else
-               info->reg_addr_requested = true;
-
-       info->reg_addr = ioremap_nocache(info->phys_reg_addr, SLGT_REG_SIZE);
-       if (!info->reg_addr) {
-               DBGERR(("%s cant map device registers, addr=%08X\n",
-                       info->device_name, info->phys_reg_addr));
-               info->init_error = DiagStatus_CantAssignPciResources;
-               goto errout;
-       }
-       return 0;
-
-errout:
-       release_resources(info);
-       return -ENODEV;
-}
-
-static void release_resources(struct slgt_info *info)
-{
-       if (info->irq_requested) {
-               free_irq(info->irq_level, info);
-               info->irq_requested = false;
-       }
-
-       if (info->reg_addr_requested) {
-               release_mem_region(info->phys_reg_addr, SLGT_REG_SIZE);
-               info->reg_addr_requested = false;
-       }
-
-       if (info->reg_addr) {
-               iounmap(info->reg_addr);
-               info->reg_addr = NULL;
-       }
-}
-
-/* Add the specified device instance data structure to the
- * global linked list of devices and increment the device count.
- */
-static void add_device(struct slgt_info *info)
-{
-       char *devstr;
-
-       info->next_device = NULL;
-       info->line = slgt_device_count;
-       sprintf(info->device_name, "%s%d", tty_dev_prefix, info->line);
-
-       if (info->line < MAX_DEVICES) {
-               if (maxframe[info->line])
-                       info->max_frame_size = maxframe[info->line];
-       }
-
-       slgt_device_count++;
-
-       if (!slgt_device_list)
-               slgt_device_list = info;
-       else {
-               struct slgt_info *current_dev = slgt_device_list;
-               while(current_dev->next_device)
-                       current_dev = current_dev->next_device;
-               current_dev->next_device = info;
-       }
-
-       if (info->max_frame_size < 4096)
-               info->max_frame_size = 4096;
-       else if (info->max_frame_size > 65535)
-               info->max_frame_size = 65535;
-
-       switch(info->pdev->device) {
-       case SYNCLINK_GT_DEVICE_ID:
-               devstr = "GT";
-               break;
-       case SYNCLINK_GT2_DEVICE_ID:
-               devstr = "GT2";
-               break;
-       case SYNCLINK_GT4_DEVICE_ID:
-               devstr = "GT4";
-               break;
-       case SYNCLINK_AC_DEVICE_ID:
-               devstr = "AC";
-               info->params.mode = MGSL_MODE_ASYNC;
-               break;
-       default:
-               devstr = "(unknown model)";
-       }
-       printk("SyncLink %s %s IO=%08x IRQ=%d MaxFrameSize=%u\n",
-               devstr, info->device_name, info->phys_reg_addr,
-               info->irq_level, info->max_frame_size);
-
-#if SYNCLINK_GENERIC_HDLC
-       hdlcdev_init(info);
-#endif
-}
-
-static const struct tty_port_operations slgt_port_ops = {
-       .carrier_raised = carrier_raised,
-       .dtr_rts = dtr_rts,
-};
-
-/*
- *  allocate device instance structure, return NULL on failure
- */
-static struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev)
-{
-       struct slgt_info *info;
-
-       info = kzalloc(sizeof(struct slgt_info), GFP_KERNEL);
-
-       if (!info) {
-               DBGERR(("%s device alloc failed adapter=%d port=%d\n",
-                       driver_name, adapter_num, port_num));
-       } else {
-               tty_port_init(&info->port);
-               info->port.ops = &slgt_port_ops;
-               info->magic = MGSL_MAGIC;
-               INIT_WORK(&info->task, bh_handler);
-               info->max_frame_size = 4096;
-               info->base_clock = 14745600;
-               info->rbuf_fill_level = DMABUFSIZE;
-               info->port.close_delay = 5*HZ/10;
-               info->port.closing_wait = 30*HZ;
-               init_waitqueue_head(&info->status_event_wait_q);
-               init_waitqueue_head(&info->event_wait_q);
-               spin_lock_init(&info->netlock);
-               memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
-               info->idle_mode = HDLC_TXIDLE_FLAGS;
-               info->adapter_num = adapter_num;
-               info->port_num = port_num;
-
-               setup_timer(&info->tx_timer, tx_timeout, (unsigned long)info);
-               setup_timer(&info->rx_timer, rx_timeout, (unsigned long)info);
-
-               /* Copy configuration info to device instance data */
-               info->pdev = pdev;
-               info->irq_level = pdev->irq;
-               info->phys_reg_addr = pci_resource_start(pdev,0);
-
-               info->bus_type = MGSL_BUS_TYPE_PCI;
-               info->irq_flags = IRQF_SHARED;
-
-               info->init_error = -1; /* assume error, set to 0 on successful init */
-       }
-
-       return info;
-}
-
-static void device_init(int adapter_num, struct pci_dev *pdev)
-{
-       struct slgt_info *port_array[SLGT_MAX_PORTS];
-       int i;
-       int port_count = 1;
-
-       if (pdev->device == SYNCLINK_GT2_DEVICE_ID)
-               port_count = 2;
-       else if (pdev->device == SYNCLINK_GT4_DEVICE_ID)
-               port_count = 4;
-
-       /* allocate device instances for all ports */
-       for (i=0; i < port_count; ++i) {
-               port_array[i] = alloc_dev(adapter_num, i, pdev);
-               if (port_array[i] == NULL) {
-                       for (--i; i >= 0; --i)
-                               kfree(port_array[i]);
-                       return;
-               }
-       }
-
-       /* give copy of port_array to all ports and add to device list  */
-       for (i=0; i < port_count; ++i) {
-               memcpy(port_array[i]->port_array, port_array, sizeof(port_array));
-               add_device(port_array[i]);
-               port_array[i]->port_count = port_count;
-               spin_lock_init(&port_array[i]->lock);
-       }
-
-       /* Allocate and claim adapter resources */
-       if (!claim_resources(port_array[0])) {
-
-               alloc_dma_bufs(port_array[0]);
-
-               /* copy resource information from first port to others */
-               for (i = 1; i < port_count; ++i) {
-                       port_array[i]->irq_level = port_array[0]->irq_level;
-                       port_array[i]->reg_addr  = port_array[0]->reg_addr;
-                       alloc_dma_bufs(port_array[i]);
-               }
-
-               if (request_irq(port_array[0]->irq_level,
-                                       slgt_interrupt,
-                                       port_array[0]->irq_flags,
-                                       port_array[0]->device_name,
-                                       port_array[0]) < 0) {
-                       DBGERR(("%s request_irq failed IRQ=%d\n",
-                               port_array[0]->device_name,
-                               port_array[0]->irq_level));
-               } else {
-                       port_array[0]->irq_requested = true;
-                       adapter_test(port_array[0]);
-                       for (i=1 ; i < port_count ; i++) {
-                               port_array[i]->init_error = port_array[0]->init_error;
-                               port_array[i]->gpio_present = port_array[0]->gpio_present;
-                       }
-               }
-       }
-
-       for (i=0; i < port_count; ++i)
-               tty_register_device(serial_driver, port_array[i]->line, &(port_array[i]->pdev->dev));
-}
-
-static int __devinit init_one(struct pci_dev *dev,
-                             const struct pci_device_id *ent)
-{
-       if (pci_enable_device(dev)) {
-               printk("error enabling pci device %p\n", dev);
-               return -EIO;
-       }
-       pci_set_master(dev);
-       device_init(slgt_device_count, dev);
-       return 0;
-}
-
-static void __devexit remove_one(struct pci_dev *dev)
-{
-}
-
-static const struct tty_operations ops = {
-       .open = open,
-       .close = close,
-       .write = write,
-       .put_char = put_char,
-       .flush_chars = flush_chars,
-       .write_room = write_room,
-       .chars_in_buffer = chars_in_buffer,
-       .flush_buffer = flush_buffer,
-       .ioctl = ioctl,
-       .compat_ioctl = slgt_compat_ioctl,
-       .throttle = throttle,
-       .unthrottle = unthrottle,
-       .send_xchar = send_xchar,
-       .break_ctl = set_break,
-       .wait_until_sent = wait_until_sent,
-       .set_termios = set_termios,
-       .stop = tx_hold,
-       .start = tx_release,
-       .hangup = hangup,
-       .tiocmget = tiocmget,
-       .tiocmset = tiocmset,
-       .get_icount = get_icount,
-       .proc_fops = &synclink_gt_proc_fops,
-};
-
-static void slgt_cleanup(void)
-{
-       int rc;
-       struct slgt_info *info;
-       struct slgt_info *tmp;
-
-       printk(KERN_INFO "unload %s\n", driver_name);
-
-       if (serial_driver) {
-               for (info=slgt_device_list ; info != NULL ; info=info->next_device)
-                       tty_unregister_device(serial_driver, info->line);
-               if ((rc = tty_unregister_driver(serial_driver)))
-                       DBGERR(("tty_unregister_driver error=%d\n", rc));
-               put_tty_driver(serial_driver);
-       }
-
-       /* reset devices */
-       info = slgt_device_list;
-       while(info) {
-               reset_port(info);
-               info = info->next_device;
-       }
-
-       /* release devices */
-       info = slgt_device_list;
-       while(info) {
-#if SYNCLINK_GENERIC_HDLC
-               hdlcdev_exit(info);
-#endif
-               free_dma_bufs(info);
-               free_tmp_rbuf(info);
-               if (info->port_num == 0)
-                       release_resources(info);
-               tmp = info;
-               info = info->next_device;
-               kfree(tmp);
-       }
-
-       if (pci_registered)
-               pci_unregister_driver(&pci_driver);
-}
-
-/*
- *  Driver initialization entry point.
- */
-static int __init slgt_init(void)
-{
-       int rc;
-
-       printk(KERN_INFO "%s\n", driver_name);
-
-       serial_driver = alloc_tty_driver(MAX_DEVICES);
-       if (!serial_driver) {
-               printk("%s can't allocate tty driver\n", driver_name);
-               return -ENOMEM;
-       }
-
-       /* Initialize the tty_driver structure */
-
-       serial_driver->owner = THIS_MODULE;
-       serial_driver->driver_name = tty_driver_name;
-       serial_driver->name = tty_dev_prefix;
-       serial_driver->major = ttymajor;
-       serial_driver->minor_start = 64;
-       serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
-       serial_driver->subtype = SERIAL_TYPE_NORMAL;
-       serial_driver->init_termios = tty_std_termios;
-       serial_driver->init_termios.c_cflag =
-               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-       serial_driver->init_termios.c_ispeed = 9600;
-       serial_driver->init_termios.c_ospeed = 9600;
-       serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
-       tty_set_operations(serial_driver, &ops);
-       if ((rc = tty_register_driver(serial_driver)) < 0) {
-               DBGERR(("%s can't register serial driver\n", driver_name));
-               put_tty_driver(serial_driver);
-               serial_driver = NULL;
-               goto error;
-       }
-
-       printk(KERN_INFO "%s, tty major#%d\n",
-              driver_name, serial_driver->major);
-
-       slgt_device_count = 0;
-       if ((rc = pci_register_driver(&pci_driver)) < 0) {
-               printk("%s pci_register_driver error=%d\n", driver_name, rc);
-               goto error;
-       }
-       pci_registered = true;
-
-       if (!slgt_device_list)
-               printk("%s no devices found\n",driver_name);
-
-       return 0;
-
-error:
-       slgt_cleanup();
-       return rc;
-}
-
-static void __exit slgt_exit(void)
-{
-       slgt_cleanup();
-}
-
-module_init(slgt_init);
-module_exit(slgt_exit);
-
-/*
- * register access routines
- */
-
-#define CALC_REGADDR() \
-       unsigned long reg_addr = ((unsigned long)info->reg_addr) + addr; \
-       if (addr >= 0x80) \
-               reg_addr += (info->port_num) * 32; \
-       else if (addr >= 0x40)  \
-               reg_addr += (info->port_num) * 16;
-
-static __u8 rd_reg8(struct slgt_info *info, unsigned int addr)
-{
-       CALC_REGADDR();
-       return readb((void __iomem *)reg_addr);
-}
-
-static void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value)
-{
-       CALC_REGADDR();
-       writeb(value, (void __iomem *)reg_addr);
-}
-
-static __u16 rd_reg16(struct slgt_info *info, unsigned int addr)
-{
-       CALC_REGADDR();
-       return readw((void __iomem *)reg_addr);
-}
-
-static void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value)
-{
-       CALC_REGADDR();
-       writew(value, (void __iomem *)reg_addr);
-}
-
-static __u32 rd_reg32(struct slgt_info *info, unsigned int addr)
-{
-       CALC_REGADDR();
-       return readl((void __iomem *)reg_addr);
-}
-
-static void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value)
-{
-       CALC_REGADDR();
-       writel(value, (void __iomem *)reg_addr);
-}
-
-static void rdma_reset(struct slgt_info *info)
-{
-       unsigned int i;
-
-       /* set reset bit */
-       wr_reg32(info, RDCSR, BIT1);
-
-       /* wait for enable bit cleared */
-       for(i=0 ; i < 1000 ; i++)
-               if (!(rd_reg32(info, RDCSR) & BIT0))
-                       break;
-}
-
-static void tdma_reset(struct slgt_info *info)
-{
-       unsigned int i;
-
-       /* set reset bit */
-       wr_reg32(info, TDCSR, BIT1);
-
-       /* wait for enable bit cleared */
-       for(i=0 ; i < 1000 ; i++)
-               if (!(rd_reg32(info, TDCSR) & BIT0))
-                       break;
-}
-
-/*
- * enable internal loopback
- * TxCLK and RxCLK are generated from BRG
- * and TxD is looped back to RxD internally.
- */
-static void enable_loopback(struct slgt_info *info)
-{
-       /* SCR (serial control) BIT2=looopback enable */
-       wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT2));
-
-       if (info->params.mode != MGSL_MODE_ASYNC) {
-               /* CCR (clock control)
-                * 07..05  tx clock source (010 = BRG)
-                * 04..02  rx clock source (010 = BRG)
-                * 01      auxclk enable   (0 = disable)
-                * 00      BRG enable      (1 = enable)
-                *
-                * 0100 1001
-                */
-               wr_reg8(info, CCR, 0x49);
-
-               /* set speed if available, otherwise use default */
-               if (info->params.clock_speed)
-                       set_rate(info, info->params.clock_speed);
-               else
-                       set_rate(info, 3686400);
-       }
-}
-
-/*
- *  set baud rate generator to specified rate
- */
-static void set_rate(struct slgt_info *info, u32 rate)
-{
-       unsigned int div;
-       unsigned int osc = info->base_clock;
-
-       /* div = osc/rate - 1
-        *
-        * Round div up if osc/rate is not integer to
-        * force to next slowest rate.
-        */
-
-       if (rate) {
-               div = osc/rate;
-               if (!(osc % rate) && div)
-                       div--;
-               wr_reg16(info, BDR, (unsigned short)div);
-       }
-}
-
-static void rx_stop(struct slgt_info *info)
-{
-       unsigned short val;
-
-       /* disable and reset receiver */
-       val = rd_reg16(info, RCR) & ~BIT1;          /* clear enable bit */
-       wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */
-       wr_reg16(info, RCR, val);                  /* clear reset bit */
-
-       slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA + IRQ_RXIDLE);
-
-       /* clear pending rx interrupts */
-       wr_reg16(info, SSR, IRQ_RXIDLE + IRQ_RXOVER);
-
-       rdma_reset(info);
-
-       info->rx_enabled = false;
-       info->rx_restart = false;
-}
-
-static void rx_start(struct slgt_info *info)
-{
-       unsigned short val;
-
-       slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA);
-
-       /* clear pending rx overrun IRQ */
-       wr_reg16(info, SSR, IRQ_RXOVER);
-
-       /* reset and disable receiver */
-       val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */
-       wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */
-       wr_reg16(info, RCR, val);                  /* clear reset bit */
-
-       rdma_reset(info);
-       reset_rbufs(info);
-
-       if (info->rx_pio) {
-               /* rx request when rx FIFO not empty */
-               wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) & ~BIT14));
-               slgt_irq_on(info, IRQ_RXDATA);
-               if (info->params.mode == MGSL_MODE_ASYNC) {
-                       /* enable saving of rx status */
-                       wr_reg32(info, RDCSR, BIT6);
-               }
-       } else {
-               /* rx request when rx FIFO half full */
-               wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT14));
-               /* set 1st descriptor address */
-               wr_reg32(info, RDDAR, info->rbufs[0].pdesc);
-
-               if (info->params.mode != MGSL_MODE_ASYNC) {
-                       /* enable rx DMA and DMA interrupt */
-                       wr_reg32(info, RDCSR, (BIT2 + BIT0));
-               } else {
-                       /* enable saving of rx status, rx DMA and DMA interrupt */
-                       wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0));
-               }
-       }
-
-       slgt_irq_on(info, IRQ_RXOVER);
-
-       /* enable receiver */
-       wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | BIT1));
-
-       info->rx_restart = false;
-       info->rx_enabled = true;
-}
-
-static void tx_start(struct slgt_info *info)
-{
-       if (!info->tx_enabled) {
-               wr_reg16(info, TCR,
-                        (unsigned short)((rd_reg16(info, TCR) | BIT1) & ~BIT2));
-               info->tx_enabled = true;
-       }
-
-       if (desc_count(info->tbufs[info->tbuf_start])) {
-               info->drop_rts_on_tx_done = false;
-
-               if (info->params.mode != MGSL_MODE_ASYNC) {
-                       if (info->params.flags & HDLC_FLAG_AUTO_RTS) {
-                               get_signals(info);
-                               if (!(info->signals & SerialSignal_RTS)) {
-                                       info->signals |= SerialSignal_RTS;
-                                       set_signals(info);
-                                       info->drop_rts_on_tx_done = true;
-                               }
-                       }
-
-                       slgt_irq_off(info, IRQ_TXDATA);
-                       slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE);
-                       /* clear tx idle and underrun status bits */
-                       wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
-               } else {
-                       slgt_irq_off(info, IRQ_TXDATA);
-                       slgt_irq_on(info, IRQ_TXIDLE);
-                       /* clear tx idle status bit */
-                       wr_reg16(info, SSR, IRQ_TXIDLE);
-               }
-               /* set 1st descriptor address and start DMA */
-               wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
-               wr_reg32(info, TDCSR, BIT2 + BIT0);
-               info->tx_active = true;
-       }
-}
-
-static void tx_stop(struct slgt_info *info)
-{
-       unsigned short val;
-
-       del_timer(&info->tx_timer);
-
-       tdma_reset(info);
-
-       /* reset and disable transmitter */
-       val = rd_reg16(info, TCR) & ~BIT1;          /* clear enable bit */
-       wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */
-
-       slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER);
-
-       /* clear tx idle and underrun status bit */
-       wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
-
-       reset_tbufs(info);
-
-       info->tx_enabled = false;
-       info->tx_active = false;
-}
-
-static void reset_port(struct slgt_info *info)
-{
-       if (!info->reg_addr)
-               return;
-
-       tx_stop(info);
-       rx_stop(info);
-
-       info->signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
-       set_signals(info);
-
-       slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
-}
-
-static void reset_adapter(struct slgt_info *info)
-{
-       int i;
-       for (i=0; i < info->port_count; ++i) {
-               if (info->port_array[i])
-                       reset_port(info->port_array[i]);
-       }
-}
-
-static void async_mode(struct slgt_info *info)
-{
-       unsigned short val;
-
-       slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
-       tx_stop(info);
-       rx_stop(info);
-
-       /* TCR (tx control)
-        *
-        * 15..13  mode, 010=async
-        * 12..10  encoding, 000=NRZ
-        * 09      parity enable
-        * 08      1=odd parity, 0=even parity
-        * 07      1=RTS driver control
-        * 06      1=break enable
-        * 05..04  character length
-        *         00=5 bits
-        *         01=6 bits
-        *         10=7 bits
-        *         11=8 bits
-        * 03      0=1 stop bit, 1=2 stop bits
-        * 02      reset
-        * 01      enable
-        * 00      auto-CTS enable
-        */
-       val = 0x4000;
-
-       if (info->if_mode & MGSL_INTERFACE_RTS_EN)
-               val |= BIT7;
-
-       if (info->params.parity != ASYNC_PARITY_NONE) {
-               val |= BIT9;
-               if (info->params.parity == ASYNC_PARITY_ODD)
-                       val |= BIT8;
-       }
-
-       switch (info->params.data_bits)
-       {
-       case 6: val |= BIT4; break;
-       case 7: val |= BIT5; break;
-       case 8: val |= BIT5 + BIT4; break;
-       }
-
-       if (info->params.stop_bits != 1)
-               val |= BIT3;
-
-       if (info->params.flags & HDLC_FLAG_AUTO_CTS)
-               val |= BIT0;
-
-       wr_reg16(info, TCR, val);
-
-       /* RCR (rx control)
-        *
-        * 15..13  mode, 010=async
-        * 12..10  encoding, 000=NRZ
-        * 09      parity enable
-        * 08      1=odd parity, 0=even parity
-        * 07..06  reserved, must be 0
-        * 05..04  character length
-        *         00=5 bits
-        *         01=6 bits
-        *         10=7 bits
-        *         11=8 bits
-        * 03      reserved, must be zero
-        * 02      reset
-        * 01      enable
-        * 00      auto-DCD enable
-        */
-       val = 0x4000;
-
-       if (info->params.parity != ASYNC_PARITY_NONE) {
-               val |= BIT9;
-               if (info->params.parity == ASYNC_PARITY_ODD)
-                       val |= BIT8;
-       }
-
-       switch (info->params.data_bits)
-       {
-       case 6: val |= BIT4; break;
-       case 7: val |= BIT5; break;
-       case 8: val |= BIT5 + BIT4; break;
-       }
-
-       if (info->params.flags & HDLC_FLAG_AUTO_DCD)
-               val |= BIT0;
-
-       wr_reg16(info, RCR, val);
-
-       /* CCR (clock control)
-        *
-        * 07..05  011 = tx clock source is BRG/16
-        * 04..02  010 = rx clock source is BRG
-        * 01      0 = auxclk disabled
-        * 00      1 = BRG enabled
-        *
-        * 0110 1001
-        */
-       wr_reg8(info, CCR, 0x69);
-
-       msc_set_vcr(info);
-
-       /* SCR (serial control)
-        *
-        * 15  1=tx req on FIFO half empty
-        * 14  1=rx req on FIFO half full
-        * 13  tx data  IRQ enable
-        * 12  tx idle  IRQ enable
-        * 11  rx break on IRQ enable
-        * 10  rx data  IRQ enable
-        * 09  rx break off IRQ enable
-        * 08  overrun  IRQ enable
-        * 07  DSR      IRQ enable
-        * 06  CTS      IRQ enable
-        * 05  DCD      IRQ enable
-        * 04  RI       IRQ enable
-        * 03  0=16x sampling, 1=8x sampling
-        * 02  1=txd->rxd internal loopback enable
-        * 01  reserved, must be zero
-        * 00  1=master IRQ enable
-        */
-       val = BIT15 + BIT14 + BIT0;
-       /* JCR[8] : 1 = x8 async mode feature available */
-       if ((rd_reg32(info, JCR) & BIT8) && info->params.data_rate &&
-           ((info->base_clock < (info->params.data_rate * 16)) ||
-            (info->base_clock % (info->params.data_rate * 16)))) {
-               /* use 8x sampling */
-               val |= BIT3;
-               set_rate(info, info->params.data_rate * 8);
-       } else {
-               /* use 16x sampling */
-               set_rate(info, info->params.data_rate * 16);
-       }
-       wr_reg16(info, SCR, val);
-
-       slgt_irq_on(info, IRQ_RXBREAK | IRQ_RXOVER);
-
-       if (info->params.loopback)
-               enable_loopback(info);
-}
-
-static void sync_mode(struct slgt_info *info)
-{
-       unsigned short val;
-
-       slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
-       tx_stop(info);
-       rx_stop(info);
-
-       /* TCR (tx control)
-        *
-        * 15..13  mode
-        *         000=HDLC/SDLC
-        *         001=raw bit synchronous
-        *         010=asynchronous/isochronous
-        *         011=monosync byte synchronous
-        *         100=bisync byte synchronous
-        *         101=xsync byte synchronous
-        * 12..10  encoding
-        * 09      CRC enable
-        * 08      CRC32
-        * 07      1=RTS driver control
-        * 06      preamble enable
-        * 05..04  preamble length
-        * 03      share open/close flag
-        * 02      reset
-        * 01      enable
-        * 00      auto-CTS enable
-        */
-       val = BIT2;
-
-       switch(info->params.mode) {
-       case MGSL_MODE_XSYNC:
-               val |= BIT15 + BIT13;
-               break;
-       case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break;
-       case MGSL_MODE_BISYNC:   val |= BIT15; break;
-       case MGSL_MODE_RAW:      val |= BIT13; break;
-       }
-       if (info->if_mode & MGSL_INTERFACE_RTS_EN)
-               val |= BIT7;
-
-       switch(info->params.encoding)
-       {
-       case HDLC_ENCODING_NRZB:          val |= BIT10; break;
-       case HDLC_ENCODING_NRZI_MARK:     val |= BIT11; break;
-       case HDLC_ENCODING_NRZI:          val |= BIT11 + BIT10; break;
-       case HDLC_ENCODING_BIPHASE_MARK:  val |= BIT12; break;
-       case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break;
-       case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break;
-       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break;
-       }
-
-       switch (info->params.crc_type & HDLC_CRC_MASK)
-       {
-       case HDLC_CRC_16_CCITT: val |= BIT9; break;
-       case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break;
-       }
-
-       if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE)
-               val |= BIT6;
-
-       switch (info->params.preamble_length)
-       {
-       case HDLC_PREAMBLE_LENGTH_16BITS: val |= BIT5; break;
-       case HDLC_PREAMBLE_LENGTH_32BITS: val |= BIT4; break;
-       case HDLC_PREAMBLE_LENGTH_64BITS: val |= BIT5 + BIT4; break;
-       }
-
-       if (info->params.flags & HDLC_FLAG_AUTO_CTS)
-               val |= BIT0;
-
-       wr_reg16(info, TCR, val);
-
-       /* TPR (transmit preamble) */
-
-       switch (info->params.preamble)
-       {
-       case HDLC_PREAMBLE_PATTERN_FLAGS: val = 0x7e; break;
-       case HDLC_PREAMBLE_PATTERN_ONES:  val = 0xff; break;
-       case HDLC_PREAMBLE_PATTERN_ZEROS: val = 0x00; break;
-       case HDLC_PREAMBLE_PATTERN_10:    val = 0x55; break;
-       case HDLC_PREAMBLE_PATTERN_01:    val = 0xaa; break;
-       default:                          val = 0x7e; break;
-       }
-       wr_reg8(info, TPR, (unsigned char)val);
-
-       /* RCR (rx control)
-        *
-        * 15..13  mode
-        *         000=HDLC/SDLC
-        *         001=raw bit synchronous
-        *         010=asynchronous/isochronous
-        *         011=monosync byte synchronous
-        *         100=bisync byte synchronous
-        *         101=xsync byte synchronous
-        * 12..10  encoding
-        * 09      CRC enable
-        * 08      CRC32
-        * 07..03  reserved, must be 0
-        * 02      reset
-        * 01      enable
-        * 00      auto-DCD enable
-        */
-       val = 0;
-
-       switch(info->params.mode) {
-       case MGSL_MODE_XSYNC:
-               val |= BIT15 + BIT13;
-               break;
-       case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break;
-       case MGSL_MODE_BISYNC:   val |= BIT15; break;
-       case MGSL_MODE_RAW:      val |= BIT13; break;
-       }
-
-       switch(info->params.encoding)
-       {
-       case HDLC_ENCODING_NRZB:          val |= BIT10; break;
-       case HDLC_ENCODING_NRZI_MARK:     val |= BIT11; break;
-       case HDLC_ENCODING_NRZI:          val |= BIT11 + BIT10; break;
-       case HDLC_ENCODING_BIPHASE_MARK:  val |= BIT12; break;
-       case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break;
-       case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break;
-       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break;
-       }
-
-       switch (info->params.crc_type & HDLC_CRC_MASK)
-       {
-       case HDLC_CRC_16_CCITT: val |= BIT9; break;
-       case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break;
-       }
-
-       if (info->params.flags & HDLC_FLAG_AUTO_DCD)
-               val |= BIT0;
-
-       wr_reg16(info, RCR, val);
-
-       /* CCR (clock control)
-        *
-        * 07..05  tx clock source
-        * 04..02  rx clock source
-        * 01      auxclk enable
-        * 00      BRG enable
-        */
-       val = 0;
-
-       if (info->params.flags & HDLC_FLAG_TXC_BRG)
-       {
-               // when RxC source is DPLL, BRG generates 16X DPLL
-               // reference clock, so take TxC from BRG/16 to get
-               // transmit clock at actual data rate
-               if (info->params.flags & HDLC_FLAG_RXC_DPLL)
-                       val |= BIT6 + BIT5;     /* 011, txclk = BRG/16 */
-               else
-                       val |= BIT6;    /* 010, txclk = BRG */
-       }
-       else if (info->params.flags & HDLC_FLAG_TXC_DPLL)
-               val |= BIT7;    /* 100, txclk = DPLL Input */
-       else if (info->params.flags & HDLC_FLAG_TXC_RXCPIN)
-               val |= BIT5;    /* 001, txclk = RXC Input */
-
-       if (info->params.flags & HDLC_FLAG_RXC_BRG)
-               val |= BIT3;    /* 010, rxclk = BRG */
-       else if (info->params.flags & HDLC_FLAG_RXC_DPLL)
-               val |= BIT4;    /* 100, rxclk = DPLL */
-       else if (info->params.flags & HDLC_FLAG_RXC_TXCPIN)
-               val |= BIT2;    /* 001, rxclk = TXC Input */
-
-       if (info->params.clock_speed)
-               val |= BIT1 + BIT0;
-
-       wr_reg8(info, CCR, (unsigned char)val);
-
-       if (info->params.flags & (HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL))
-       {
-               // program DPLL mode
-               switch(info->params.encoding)
-               {
-               case HDLC_ENCODING_BIPHASE_MARK:
-               case HDLC_ENCODING_BIPHASE_SPACE:
-                       val = BIT7; break;
-               case HDLC_ENCODING_BIPHASE_LEVEL:
-               case HDLC_ENCODING_DIFF_BIPHASE_LEVEL:
-                       val = BIT7 + BIT6; break;
-               default: val = BIT6;    // NRZ encodings
-               }
-               wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | val));
-
-               // DPLL requires a 16X reference clock from BRG
-               set_rate(info, info->params.clock_speed * 16);
-       }
-       else
-               set_rate(info, info->params.clock_speed);
-
-       tx_set_idle(info);
-
-       msc_set_vcr(info);
-
-       /* SCR (serial control)
-        *
-        * 15  1=tx req on FIFO half empty
-        * 14  1=rx req on FIFO half full
-        * 13  tx data  IRQ enable
-        * 12  tx idle  IRQ enable
-        * 11  underrun IRQ enable
-        * 10  rx data  IRQ enable
-        * 09  rx idle  IRQ enable
-        * 08  overrun  IRQ enable
-        * 07  DSR      IRQ enable
-        * 06  CTS      IRQ enable
-        * 05  DCD      IRQ enable
-        * 04  RI       IRQ enable
-        * 03  reserved, must be zero
-        * 02  1=txd->rxd internal loopback enable
-        * 01  reserved, must be zero
-        * 00  1=master IRQ enable
-        */
-       wr_reg16(info, SCR, BIT15 + BIT14 + BIT0);
-
-       if (info->params.loopback)
-               enable_loopback(info);
-}
-
-/*
- *  set transmit idle mode
- */
-static void tx_set_idle(struct slgt_info *info)
-{
-       unsigned char val;
-       unsigned short tcr;
-
-       /* if preamble enabled (tcr[6] == 1) then tx idle size = 8 bits
-        * else tcr[5:4] = tx idle size: 00 = 8 bits, 01 = 16 bits
-        */
-       tcr = rd_reg16(info, TCR);
-       if (info->idle_mode & HDLC_TXIDLE_CUSTOM_16) {
-               /* disable preamble, set idle size to 16 bits */
-               tcr = (tcr & ~(BIT6 + BIT5)) | BIT4;
-               /* MSB of 16 bit idle specified in tx preamble register (TPR) */
-               wr_reg8(info, TPR, (unsigned char)((info->idle_mode >> 8) & 0xff));
-       } else if (!(tcr & BIT6)) {
-               /* preamble is disabled, set idle size to 8 bits */
-               tcr &= ~(BIT5 + BIT4);
-       }
-       wr_reg16(info, TCR, tcr);
-
-       if (info->idle_mode & (HDLC_TXIDLE_CUSTOM_8 | HDLC_TXIDLE_CUSTOM_16)) {
-               /* LSB of custom tx idle specified in tx idle register */
-               val = (unsigned char)(info->idle_mode & 0xff);
-       } else {
-               /* standard 8 bit idle patterns */
-               switch(info->idle_mode)
-               {
-               case HDLC_TXIDLE_FLAGS:          val = 0x7e; break;
-               case HDLC_TXIDLE_ALT_ZEROS_ONES:
-               case HDLC_TXIDLE_ALT_MARK_SPACE: val = 0xaa; break;
-               case HDLC_TXIDLE_ZEROS:
-               case HDLC_TXIDLE_SPACE:          val = 0x00; break;
-               default:                         val = 0xff;
-               }
-       }
-
-       wr_reg8(info, TIR, val);
-}
-
-/*
- * get state of V24 status (input) signals
- */
-static void get_signals(struct slgt_info *info)
-{
-       unsigned short status = rd_reg16(info, SSR);
-
-       /* clear all serial signals except DTR and RTS */
-       info->signals &= SerialSignal_DTR + SerialSignal_RTS;
-
-       if (status & BIT3)
-               info->signals |= SerialSignal_DSR;
-       if (status & BIT2)
-               info->signals |= SerialSignal_CTS;
-       if (status & BIT1)
-               info->signals |= SerialSignal_DCD;
-       if (status & BIT0)
-               info->signals |= SerialSignal_RI;
-}
-
-/*
- * set V.24 Control Register based on current configuration
- */
-static void msc_set_vcr(struct slgt_info *info)
-{
-       unsigned char val = 0;
-
-       /* VCR (V.24 control)
-        *
-        * 07..04  serial IF select
-        * 03      DTR
-        * 02      RTS
-        * 01      LL
-        * 00      RL
-        */
-
-       switch(info->if_mode & MGSL_INTERFACE_MASK)
-       {
-       case MGSL_INTERFACE_RS232:
-               val |= BIT5; /* 0010 */
-               break;
-       case MGSL_INTERFACE_V35:
-               val |= BIT7 + BIT6 + BIT5; /* 1110 */
-               break;
-       case MGSL_INTERFACE_RS422:
-               val |= BIT6; /* 0100 */
-               break;
-       }
-
-       if (info->if_mode & MGSL_INTERFACE_MSB_FIRST)
-               val |= BIT4;
-       if (info->signals & SerialSignal_DTR)
-               val |= BIT3;
-       if (info->signals & SerialSignal_RTS)
-               val |= BIT2;
-       if (info->if_mode & MGSL_INTERFACE_LL)
-               val |= BIT1;
-       if (info->if_mode & MGSL_INTERFACE_RL)
-               val |= BIT0;
-       wr_reg8(info, VCR, val);
-}
-
-/*
- * set state of V24 control (output) signals
- */
-static void set_signals(struct slgt_info *info)
-{
-       unsigned char val = rd_reg8(info, VCR);
-       if (info->signals & SerialSignal_DTR)
-               val |= BIT3;
-       else
-               val &= ~BIT3;
-       if (info->signals & SerialSignal_RTS)
-               val |= BIT2;
-       else
-               val &= ~BIT2;
-       wr_reg8(info, VCR, val);
-}
-
-/*
- * free range of receive DMA buffers (i to last)
- */
-static void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last)
-{
-       int done = 0;
-
-       while(!done) {
-               /* reset current buffer for reuse */
-               info->rbufs[i].status = 0;
-               set_desc_count(info->rbufs[i], info->rbuf_fill_level);
-               if (i == last)
-                       done = 1;
-               if (++i == info->rbuf_count)
-                       i = 0;
-       }
-       info->rbuf_current = i;
-}
-
-/*
- * mark all receive DMA buffers as free
- */
-static void reset_rbufs(struct slgt_info *info)
-{
-       free_rbufs(info, 0, info->rbuf_count - 1);
-       info->rbuf_fill_index = 0;
-       info->rbuf_fill_count = 0;
-}
-
-/*
- * pass receive HDLC frame to upper layer
- *
- * return true if frame available, otherwise false
- */
-static bool rx_get_frame(struct slgt_info *info)
-{
-       unsigned int start, end;
-       unsigned short status;
-       unsigned int framesize = 0;
-       unsigned long flags;
-       struct tty_struct *tty = info->port.tty;
-       unsigned char addr_field = 0xff;
-       unsigned int crc_size = 0;
-
-       switch (info->params.crc_type & HDLC_CRC_MASK) {
-       case HDLC_CRC_16_CCITT: crc_size = 2; break;
-       case HDLC_CRC_32_CCITT: crc_size = 4; break;
-       }
-
-check_again:
-
-       framesize = 0;
-       addr_field = 0xff;
-       start = end = info->rbuf_current;
-
-       for (;;) {
-               if (!desc_complete(info->rbufs[end]))
-                       goto cleanup;
-
-               if (framesize == 0 && info->params.addr_filter != 0xff)
-                       addr_field = info->rbufs[end].buf[0];
-
-               framesize += desc_count(info->rbufs[end]);
-
-               if (desc_eof(info->rbufs[end]))
-                       break;
-
-               if (++end == info->rbuf_count)
-                       end = 0;
-
-               if (end == info->rbuf_current) {
-                       if (info->rx_enabled){
-                               spin_lock_irqsave(&info->lock,flags);
-                               rx_start(info);
-                               spin_unlock_irqrestore(&info->lock,flags);
-                       }
-                       goto cleanup;
-               }
-       }
-
-       /* status
-        *
-        * 15      buffer complete
-        * 14..06  reserved
-        * 05..04  residue
-        * 02      eof (end of frame)
-        * 01      CRC error
-        * 00      abort
-        */
-       status = desc_status(info->rbufs[end]);
-
-       /* ignore CRC bit if not using CRC (bit is undefined) */
-       if ((info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_NONE)
-               status &= ~BIT1;
-
-       if (framesize == 0 ||
-                (addr_field != 0xff && addr_field != info->params.addr_filter)) {
-               free_rbufs(info, start, end);
-               goto check_again;
-       }
-
-       if (framesize < (2 + crc_size) || status & BIT0) {
-               info->icount.rxshort++;
-               framesize = 0;
-       } else if (status & BIT1) {
-               info->icount.rxcrc++;
-               if (!(info->params.crc_type & HDLC_CRC_RETURN_EX))
-                       framesize = 0;
-       }
-
-#if SYNCLINK_GENERIC_HDLC
-       if (framesize == 0) {
-               info->netdev->stats.rx_errors++;
-               info->netdev->stats.rx_frame_errors++;
-       }
-#endif
-
-       DBGBH(("%s rx frame status=%04X size=%d\n",
-               info->device_name, status, framesize));
-       DBGDATA(info, info->rbufs[start].buf, min_t(int, framesize, info->rbuf_fill_level), "rx");
-
-       if (framesize) {
-               if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) {
-                       framesize -= crc_size;
-                       crc_size = 0;
-               }
-
-               if (framesize > info->max_frame_size + crc_size)
-                       info->icount.rxlong++;
-               else {
-                       /* copy dma buffer(s) to contiguous temp buffer */
-                       int copy_count = framesize;
-                       int i = start;
-                       unsigned char *p = info->tmp_rbuf;
-                       info->tmp_rbuf_count = framesize;
-
-                       info->icount.rxok++;
-
-                       while(copy_count) {
-                               int partial_count = min_t(int, copy_count, info->rbuf_fill_level);
-                               memcpy(p, info->rbufs[i].buf, partial_count);
-                               p += partial_count;
-                               copy_count -= partial_count;
-                               if (++i == info->rbuf_count)
-                                       i = 0;
-                       }
-
-                       if (info->params.crc_type & HDLC_CRC_RETURN_EX) {
-                               *p = (status & BIT1) ? RX_CRC_ERROR : RX_OK;
-                               framesize++;
-                       }
-
-#if SYNCLINK_GENERIC_HDLC
-                       if (info->netcount)
-                               hdlcdev_rx(info,info->tmp_rbuf, framesize);
-                       else
-#endif
-                               ldisc_receive_buf(tty, info->tmp_rbuf, info->flag_buf, framesize);
-               }
-       }
-       free_rbufs(info, start, end);
-       return true;
-
-cleanup:
-       return false;
-}
-
-/*
- * pass receive buffer (RAW synchronous mode) to tty layer
- * return true if buffer available, otherwise false
- */
-static bool rx_get_buf(struct slgt_info *info)
-{
-       unsigned int i = info->rbuf_current;
-       unsigned int count;
-
-       if (!desc_complete(info->rbufs[i]))
-               return false;
-       count = desc_count(info->rbufs[i]);
-       switch(info->params.mode) {
-       case MGSL_MODE_MONOSYNC:
-       case MGSL_MODE_BISYNC:
-       case MGSL_MODE_XSYNC:
-               /* ignore residue in byte synchronous modes */
-               if (desc_residue(info->rbufs[i]))
-                       count--;
-               break;
-       }
-       DBGDATA(info, info->rbufs[i].buf, count, "rx");
-       DBGINFO(("rx_get_buf size=%d\n", count));
-       if (count)
-               ldisc_receive_buf(info->port.tty, info->rbufs[i].buf,
-                                 info->flag_buf, count);
-       free_rbufs(info, i, i);
-       return true;
-}
-
-static void reset_tbufs(struct slgt_info *info)
-{
-       unsigned int i;
-       info->tbuf_current = 0;
-       for (i=0 ; i < info->tbuf_count ; i++) {
-               info->tbufs[i].status = 0;
-               info->tbufs[i].count  = 0;
-       }
-}
-
-/*
- * return number of free transmit DMA buffers
- */
-static unsigned int free_tbuf_count(struct slgt_info *info)
-{
-       unsigned int count = 0;
-       unsigned int i = info->tbuf_current;
-
-       do
-       {
-               if (desc_count(info->tbufs[i]))
-                       break; /* buffer in use */
-               ++count;
-               if (++i == info->tbuf_count)
-                       i=0;
-       } while (i != info->tbuf_current);
-
-       /* if tx DMA active, last zero count buffer is in use */
-       if (count && (rd_reg32(info, TDCSR) & BIT0))
-               --count;
-
-       return count;
-}
-
-/*
- * return number of bytes in unsent transmit DMA buffers
- * and the serial controller tx FIFO
- */
-static unsigned int tbuf_bytes(struct slgt_info *info)
-{
-       unsigned int total_count = 0;
-       unsigned int i = info->tbuf_current;
-       unsigned int reg_value;
-       unsigned int count;
-       unsigned int active_buf_count = 0;
-
-       /*
-        * Add descriptor counts for all tx DMA buffers.
-        * If count is zero (cleared by DMA controller after read),
-        * the buffer is complete or is actively being read from.
-        *
-        * Record buf_count of last buffer with zero count starting
-        * from current ring position. buf_count is mirror
-        * copy of count and is not cleared by serial controller.
-        * If DMA controller is active, that buffer is actively
-        * being read so add to total.
-        */
-       do {
-               count = desc_count(info->tbufs[i]);
-               if (count)
-                       total_count += count;
-               else if (!total_count)
-                       active_buf_count = info->tbufs[i].buf_count;
-               if (++i == info->tbuf_count)
-                       i = 0;
-       } while (i != info->tbuf_current);
-
-       /* read tx DMA status register */
-       reg_value = rd_reg32(info, TDCSR);
-
-       /* if tx DMA active, last zero count buffer is in use */
-       if (reg_value & BIT0)
-               total_count += active_buf_count;
-
-       /* add tx FIFO count = reg_value[15..8] */
-       total_count += (reg_value >> 8) & 0xff;
-
-       /* if transmitter active add one byte for shift register */
-       if (info->tx_active)
-               total_count++;
-
-       return total_count;
-}
-
-/*
- * load data into transmit DMA buffer ring and start transmitter if needed
- * return true if data accepted, otherwise false (buffers full)
- */
-static bool tx_load(struct slgt_info *info, const char *buf, unsigned int size)
-{
-       unsigned short count;
-       unsigned int i;
-       struct slgt_desc *d;
-
-       /* check required buffer space */
-       if (DIV_ROUND_UP(size, DMABUFSIZE) > free_tbuf_count(info))
-               return false;
-
-       DBGDATA(info, buf, size, "tx");
-
-       /*
-        * copy data to one or more DMA buffers in circular ring
-        * tbuf_start   = first buffer for this data
-        * tbuf_current = next free buffer
-        *
-        * Copy all data before making data visible to DMA controller by
-        * setting descriptor count of the first buffer.
-        * This prevents an active DMA controller from reading the first DMA
-        * buffers of a frame and stopping before the final buffers are filled.
-        */
-
-       info->tbuf_start = i = info->tbuf_current;
-
-       while (size) {
-               d = &info->tbufs[i];
-
-               count = (unsigned short)((size > DMABUFSIZE) ? DMABUFSIZE : size);
-               memcpy(d->buf, buf, count);
-
-               size -= count;
-               buf  += count;
-
-               /*
-                * set EOF bit for last buffer of HDLC frame or
-                * for every buffer in raw mode
-                */
-               if ((!size && info->params.mode == MGSL_MODE_HDLC) ||
-                   info->params.mode == MGSL_MODE_RAW)
-                       set_desc_eof(*d, 1);
-               else
-                       set_desc_eof(*d, 0);
-
-               /* set descriptor count for all but first buffer */
-               if (i != info->tbuf_start)
-                       set_desc_count(*d, count);
-               d->buf_count = count;
-
-               if (++i == info->tbuf_count)
-                       i = 0;
-       }
-
-       info->tbuf_current = i;
-
-       /* set first buffer count to make new data visible to DMA controller */
-       d = &info->tbufs[info->tbuf_start];
-       set_desc_count(*d, d->buf_count);
-
-       /* start transmitter if needed and update transmit timeout */
-       if (!info->tx_active)
-               tx_start(info);
-       update_tx_timer(info);
-
-       return true;
-}
-
-static int register_test(struct slgt_info *info)
-{
-       static unsigned short patterns[] =
-               {0x0000, 0xffff, 0xaaaa, 0x5555, 0x6969, 0x9696};
-       static unsigned int count = ARRAY_SIZE(patterns);
-       unsigned int i;
-       int rc = 0;
-
-       for (i=0 ; i < count ; i++) {
-               wr_reg16(info, TIR, patterns[i]);
-               wr_reg16(info, BDR, patterns[(i+1)%count]);
-               if ((rd_reg16(info, TIR) != patterns[i]) ||
-                   (rd_reg16(info, BDR) != patterns[(i+1)%count])) {
-                       rc = -ENODEV;
-                       break;
-               }
-       }
-       info->gpio_present = (rd_reg32(info, JCR) & BIT5) ? 1 : 0;
-       info->init_error = rc ? 0 : DiagStatus_AddressFailure;
-       return rc;
-}
-
-static int irq_test(struct slgt_info *info)
-{
-       unsigned long timeout;
-       unsigned long flags;
-       struct tty_struct *oldtty = info->port.tty;
-       u32 speed = info->params.data_rate;
-
-       info->params.data_rate = 921600;
-       info->port.tty = NULL;
-
-       spin_lock_irqsave(&info->lock, flags);
-       async_mode(info);
-       slgt_irq_on(info, IRQ_TXIDLE);
-
-       /* enable transmitter */
-       wr_reg16(info, TCR,
-               (unsigned short)(rd_reg16(info, TCR) | BIT1));
-
-       /* write one byte and wait for tx idle */
-       wr_reg16(info, TDR, 0);
-
-       /* assume failure */
-       info->init_error = DiagStatus_IrqFailure;
-       info->irq_occurred = false;
-
-       spin_unlock_irqrestore(&info->lock, flags);
-
-       timeout=100;
-       while(timeout-- && !info->irq_occurred)
-               msleep_interruptible(10);
-
-       spin_lock_irqsave(&info->lock,flags);
-       reset_port(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       info->params.data_rate = speed;
-       info->port.tty = oldtty;
-
-       info->init_error = info->irq_occurred ? 0 : DiagStatus_IrqFailure;
-       return info->irq_occurred ? 0 : -ENODEV;
-}
-
-static int loopback_test_rx(struct slgt_info *info)
-{
-       unsigned char *src, *dest;
-       int count;
-
-       if (desc_complete(info->rbufs[0])) {
-               count = desc_count(info->rbufs[0]);
-               src   = info->rbufs[0].buf;
-               dest  = info->tmp_rbuf;
-
-               for( ; count ; count-=2, src+=2) {
-                       /* src=data byte (src+1)=status byte */
-                       if (!(*(src+1) & (BIT9 + BIT8))) {
-                               *dest = *src;
-                               dest++;
-                               info->tmp_rbuf_count++;
-                       }
-               }
-               DBGDATA(info, info->tmp_rbuf, info->tmp_rbuf_count, "rx");
-               return 1;
-       }
-       return 0;
-}
-
-static int loopback_test(struct slgt_info *info)
-{
-#define TESTFRAMESIZE 20
-
-       unsigned long timeout;
-       u16 count = TESTFRAMESIZE;
-       unsigned char buf[TESTFRAMESIZE];
-       int rc = -ENODEV;
-       unsigned long flags;
-
-       struct tty_struct *oldtty = info->port.tty;
-       MGSL_PARAMS params;
-
-       memcpy(&params, &info->params, sizeof(params));
-
-       info->params.mode = MGSL_MODE_ASYNC;
-       info->params.data_rate = 921600;
-       info->params.loopback = 1;
-       info->port.tty = NULL;
-
-       /* build and send transmit frame */
-       for (count = 0; count < TESTFRAMESIZE; ++count)
-               buf[count] = (unsigned char)count;
-
-       info->tmp_rbuf_count = 0;
-       memset(info->tmp_rbuf, 0, TESTFRAMESIZE);
-
-       /* program hardware for HDLC and enabled receiver */
-       spin_lock_irqsave(&info->lock,flags);
-       async_mode(info);
-       rx_start(info);
-       tx_load(info, buf, count);
-       spin_unlock_irqrestore(&info->lock, flags);
-
-       /* wait for receive complete */
-       for (timeout = 100; timeout; --timeout) {
-               msleep_interruptible(10);
-               if (loopback_test_rx(info)) {
-                       rc = 0;
-                       break;
-               }
-       }
-
-       /* verify received frame length and contents */
-       if (!rc && (info->tmp_rbuf_count != count ||
-                 memcmp(buf, info->tmp_rbuf, count))) {
-               rc = -ENODEV;
-       }
-
-       spin_lock_irqsave(&info->lock,flags);
-       reset_adapter(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       memcpy(&info->params, &params, sizeof(info->params));
-       info->port.tty = oldtty;
-
-       info->init_error = rc ? DiagStatus_DmaFailure : 0;
-       return rc;
-}
-
-static int adapter_test(struct slgt_info *info)
-{
-       DBGINFO(("testing %s\n", info->device_name));
-       if (register_test(info) < 0) {
-               printk("register test failure %s addr=%08X\n",
-                       info->device_name, info->phys_reg_addr);
-       } else if (irq_test(info) < 0) {
-               printk("IRQ test failure %s IRQ=%d\n",
-                       info->device_name, info->irq_level);
-       } else if (loopback_test(info) < 0) {
-               printk("loopback test failure %s\n", info->device_name);
-       }
-       return info->init_error;
-}
-
-/*
- * transmit timeout handler
- */
-static void tx_timeout(unsigned long context)
-{
-       struct slgt_info *info = (struct slgt_info*)context;
-       unsigned long flags;
-
-       DBGINFO(("%s tx_timeout\n", info->device_name));
-       if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) {
-               info->icount.txtimeout++;
-       }
-       spin_lock_irqsave(&info->lock,flags);
-       tx_stop(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-#if SYNCLINK_GENERIC_HDLC
-       if (info->netcount)
-               hdlcdev_tx_done(info);
-       else
-#endif
-               bh_transmit(info);
-}
-
-/*
- * receive buffer polling timer
- */
-static void rx_timeout(unsigned long context)
-{
-       struct slgt_info *info = (struct slgt_info*)context;
-       unsigned long flags;
-
-       DBGINFO(("%s rx_timeout\n", info->device_name));
-       spin_lock_irqsave(&info->lock, flags);
-       info->pending_bh |= BH_RECEIVE;
-       spin_unlock_irqrestore(&info->lock, flags);
-       bh_handler(&info->task);
-}
-
diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c
deleted file mode 100644 (file)
index 3273436..0000000
+++ /dev/null
@@ -1,5600 +0,0 @@
-/*
- * $Id: synclinkmp.c,v 4.38 2005/07/15 13:29:44 paulkf Exp $
- *
- * Device driver for Microgate SyncLink Multiport
- * high speed multiprotocol serial adapter.
- *
- * written by Paul Fulghum for Microgate Corporation
- * paulkf@microgate.com
- *
- * Microgate and SyncLink are trademarks of Microgate Corporation
- *
- * Derived from serial.c written by Theodore Ts'o and Linus Torvalds
- * This code is released under the GNU General Public License (GPL)
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq))
-#if defined(__i386__)
-#  define BREAKPOINT() asm("   int $3");
-#else
-#  define BREAKPOINT() { }
-#endif
-
-#define MAX_DEVICES 12
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/netdevice.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/ioctl.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/dma.h>
-#include <linux/bitops.h>
-#include <asm/types.h>
-#include <linux/termios.h>
-#include <linux/workqueue.h>
-#include <linux/hdlc.h>
-#include <linux/synclink.h>
-
-#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINKMP_MODULE))
-#define SYNCLINK_GENERIC_HDLC 1
-#else
-#define SYNCLINK_GENERIC_HDLC 0
-#endif
-
-#define GET_USER(error,value,addr) error = get_user(value,addr)
-#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0
-#define PUT_USER(error,value,addr) error = put_user(value,addr)
-#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0
-
-#include <asm/uaccess.h>
-
-static MGSL_PARAMS default_params = {
-       MGSL_MODE_HDLC,                 /* unsigned long mode */
-       0,                              /* unsigned char loopback; */
-       HDLC_FLAG_UNDERRUN_ABORT15,     /* unsigned short flags; */
-       HDLC_ENCODING_NRZI_SPACE,       /* unsigned char encoding; */
-       0,                              /* unsigned long clock_speed; */
-       0xff,                           /* unsigned char addr_filter; */
-       HDLC_CRC_16_CCITT,              /* unsigned short crc_type; */
-       HDLC_PREAMBLE_LENGTH_8BITS,     /* unsigned char preamble_length; */
-       HDLC_PREAMBLE_PATTERN_NONE,     /* unsigned char preamble; */
-       9600,                           /* unsigned long data_rate; */
-       8,                              /* unsigned char data_bits; */
-       1,                              /* unsigned char stop_bits; */
-       ASYNC_PARITY_NONE               /* unsigned char parity; */
-};
-
-/* size in bytes of DMA data buffers */
-#define SCABUFSIZE     1024
-#define SCA_MEM_SIZE   0x40000
-#define SCA_BASE_SIZE   512
-#define SCA_REG_SIZE    16
-#define SCA_MAX_PORTS   4
-#define SCAMAXDESC     128
-
-#define        BUFFERLISTSIZE  4096
-
-/* SCA-I style DMA buffer descriptor */
-typedef struct _SCADESC
-{
-       u16     next;           /* lower l6 bits of next descriptor addr */
-       u16     buf_ptr;        /* lower 16 bits of buffer addr */
-       u8      buf_base;       /* upper 8 bits of buffer addr */
-       u8      pad1;
-       u16     length;         /* length of buffer */
-       u8      status;         /* status of buffer */
-       u8      pad2;
-} SCADESC, *PSCADESC;
-
-typedef struct _SCADESC_EX
-{
-       /* device driver bookkeeping section */
-       char    *virt_addr;     /* virtual address of data buffer */
-       u16     phys_entry;     /* lower 16-bits of physical address of this descriptor */
-} SCADESC_EX, *PSCADESC_EX;
-
-/* The queue of BH actions to be performed */
-
-#define BH_RECEIVE  1
-#define BH_TRANSMIT 2
-#define BH_STATUS   4
-
-#define IO_PIN_SHUTDOWN_LIMIT 100
-
-struct _input_signal_events {
-       int     ri_up;
-       int     ri_down;
-       int     dsr_up;
-       int     dsr_down;
-       int     dcd_up;
-       int     dcd_down;
-       int     cts_up;
-       int     cts_down;
-};
-
-/*
- * Device instance data structure
- */
-typedef struct _synclinkmp_info {
-       void *if_ptr;                           /* General purpose pointer (used by SPPP) */
-       int                     magic;
-       struct tty_port         port;
-       int                     line;
-       unsigned short          close_delay;
-       unsigned short          closing_wait;   /* time to wait before closing */
-
-       struct mgsl_icount      icount;
-
-       int                     timeout;
-       int                     x_char;         /* xon/xoff character */
-       u16                     read_status_mask1;  /* break detection (SR1 indications) */
-       u16                     read_status_mask2;  /* parity/framing/overun (SR2 indications) */
-       unsigned char           ignore_status_mask1;  /* break detection (SR1 indications) */
-       unsigned char           ignore_status_mask2;  /* parity/framing/overun (SR2 indications) */
-       unsigned char           *tx_buf;
-       int                     tx_put;
-       int                     tx_get;
-       int                     tx_count;
-
-       wait_queue_head_t       status_event_wait_q;
-       wait_queue_head_t       event_wait_q;
-       struct timer_list       tx_timer;       /* HDLC transmit timeout timer */
-       struct _synclinkmp_info *next_device;   /* device list link */
-       struct timer_list       status_timer;   /* input signal status check timer */
-
-       spinlock_t lock;                /* spinlock for synchronizing with ISR */
-       struct work_struct task;                        /* task structure for scheduling bh */
-
-       u32 max_frame_size;                     /* as set by device config */
-
-       u32 pending_bh;
-
-       bool bh_running;                                /* Protection from multiple */
-       int isr_overflow;
-       bool bh_requested;
-
-       int dcd_chkcount;                       /* check counts to prevent */
-       int cts_chkcount;                       /* too many IRQs if a signal */
-       int dsr_chkcount;                       /* is floating */
-       int ri_chkcount;
-
-       char *buffer_list;                      /* virtual address of Rx & Tx buffer lists */
-       unsigned long buffer_list_phys;
-
-       unsigned int rx_buf_count;              /* count of total allocated Rx buffers */
-       SCADESC *rx_buf_list;                   /* list of receive buffer entries */
-       SCADESC_EX rx_buf_list_ex[SCAMAXDESC]; /* list of receive buffer entries */
-       unsigned int current_rx_buf;
-
-       unsigned int tx_buf_count;              /* count of total allocated Tx buffers */
-       SCADESC *tx_buf_list;           /* list of transmit buffer entries */
-       SCADESC_EX tx_buf_list_ex[SCAMAXDESC]; /* list of transmit buffer entries */
-       unsigned int last_tx_buf;
-
-       unsigned char *tmp_rx_buf;
-       unsigned int tmp_rx_buf_count;
-
-       bool rx_enabled;
-       bool rx_overflow;
-
-       bool tx_enabled;
-       bool tx_active;
-       u32 idle_mode;
-
-       unsigned char ie0_value;
-       unsigned char ie1_value;
-       unsigned char ie2_value;
-       unsigned char ctrlreg_value;
-       unsigned char old_signals;
-
-       char device_name[25];                   /* device instance name */
-
-       int port_count;
-       int adapter_num;
-       int port_num;
-
-       struct _synclinkmp_info *port_array[SCA_MAX_PORTS];
-
-       unsigned int bus_type;                  /* expansion bus type (ISA,EISA,PCI) */
-
-       unsigned int irq_level;                 /* interrupt level */
-       unsigned long irq_flags;
-       bool irq_requested;                     /* true if IRQ requested */
-
-       MGSL_PARAMS params;                     /* communications parameters */
-
-       unsigned char serial_signals;           /* current serial signal states */
-
-       bool irq_occurred;                      /* for diagnostics use */
-       unsigned int init_error;                /* Initialization startup error */
-
-       u32 last_mem_alloc;
-       unsigned char* memory_base;             /* shared memory address (PCI only) */
-       u32 phys_memory_base;
-       int shared_mem_requested;
-
-       unsigned char* sca_base;                /* HD64570 SCA Memory address */
-       u32 phys_sca_base;
-       u32 sca_offset;
-       bool sca_base_requested;
-
-       unsigned char* lcr_base;                /* local config registers (PCI only) */
-       u32 phys_lcr_base;
-       u32 lcr_offset;
-       int lcr_mem_requested;
-
-       unsigned char* statctrl_base;           /* status/control register memory */
-       u32 phys_statctrl_base;
-       u32 statctrl_offset;
-       bool sca_statctrl_requested;
-
-       u32 misc_ctrl_value;
-       char flag_buf[MAX_ASYNC_BUFFER_SIZE];
-       char char_buf[MAX_ASYNC_BUFFER_SIZE];
-       bool drop_rts_on_tx_done;
-
-       struct  _input_signal_events    input_signal_events;
-
-       /* SPPP/Cisco HDLC device parts */
-       int netcount;
-       spinlock_t netlock;
-
-#if SYNCLINK_GENERIC_HDLC
-       struct net_device *netdev;
-#endif
-
-} SLMP_INFO;
-
-#define MGSL_MAGIC 0x5401
-
-/*
- * define serial signal status change macros
- */
-#define        MISCSTATUS_DCD_LATCHED  (SerialSignal_DCD<<8)   /* indicates change in DCD */
-#define MISCSTATUS_RI_LATCHED  (SerialSignal_RI<<8)    /* indicates change in RI */
-#define MISCSTATUS_CTS_LATCHED (SerialSignal_CTS<<8)   /* indicates change in CTS */
-#define MISCSTATUS_DSR_LATCHED (SerialSignal_DSR<<8)   /* change in DSR */
-
-/* Common Register macros */
-#define LPR    0x00
-#define PABR0  0x02
-#define PABR1  0x03
-#define WCRL   0x04
-#define WCRM   0x05
-#define WCRH   0x06
-#define DPCR   0x08
-#define DMER   0x09
-#define ISR0   0x10
-#define ISR1   0x11
-#define ISR2   0x12
-#define IER0   0x14
-#define IER1   0x15
-#define IER2   0x16
-#define ITCR   0x18
-#define INTVR  0x1a
-#define IMVR   0x1c
-
-/* MSCI Register macros */
-#define TRB    0x20
-#define TRBL   0x20
-#define TRBH   0x21
-#define SR0    0x22
-#define SR1    0x23
-#define SR2    0x24
-#define SR3    0x25
-#define FST    0x26
-#define IE0    0x28
-#define IE1    0x29
-#define IE2    0x2a
-#define FIE    0x2b
-#define CMD    0x2c
-#define MD0    0x2e
-#define MD1    0x2f
-#define MD2    0x30
-#define CTL    0x31
-#define SA0    0x32
-#define SA1    0x33
-#define IDL    0x34
-#define TMC    0x35
-#define RXS    0x36
-#define TXS    0x37
-#define TRC0   0x38
-#define TRC1   0x39
-#define RRC    0x3a
-#define CST0   0x3c
-#define CST1   0x3d
-
-/* Timer Register Macros */
-#define TCNT   0x60
-#define TCNTL  0x60
-#define TCNTH  0x61
-#define TCONR  0x62
-#define TCONRL 0x62
-#define TCONRH 0x63
-#define TMCS   0x64
-#define TEPR   0x65
-
-/* DMA Controller Register macros */
-#define DARL   0x80
-#define DARH   0x81
-#define DARB   0x82
-#define BAR    0x80
-#define BARL   0x80
-#define BARH   0x81
-#define BARB   0x82
-#define SAR    0x84
-#define SARL   0x84
-#define SARH   0x85
-#define SARB   0x86
-#define CPB    0x86
-#define CDA    0x88
-#define CDAL   0x88
-#define CDAH   0x89
-#define EDA    0x8a
-#define EDAL   0x8a
-#define EDAH   0x8b
-#define BFL    0x8c
-#define BFLL   0x8c
-#define BFLH   0x8d
-#define BCR    0x8e
-#define BCRL   0x8e
-#define BCRH   0x8f
-#define DSR    0x90
-#define DMR    0x91
-#define FCT    0x93
-#define DIR    0x94
-#define DCMD   0x95
-
-/* combine with timer or DMA register address */
-#define TIMER0 0x00
-#define TIMER1 0x08
-#define TIMER2 0x10
-#define TIMER3 0x18
-#define RXDMA  0x00
-#define TXDMA  0x20
-
-/* SCA Command Codes */
-#define NOOP           0x00
-#define TXRESET                0x01
-#define TXENABLE       0x02
-#define TXDISABLE      0x03
-#define TXCRCINIT      0x04
-#define TXCRCEXCL      0x05
-#define TXEOM          0x06
-#define TXABORT                0x07
-#define MPON           0x08
-#define TXBUFCLR       0x09
-#define RXRESET                0x11
-#define RXENABLE       0x12
-#define RXDISABLE      0x13
-#define RXCRCINIT      0x14
-#define RXREJECT       0x15
-#define SEARCHMP       0x16
-#define RXCRCEXCL      0x17
-#define RXCRCCALC      0x18
-#define CHRESET                0x21
-#define HUNT           0x31
-
-/* DMA command codes */
-#define SWABORT                0x01
-#define FEICLEAR       0x02
-
-/* IE0 */
-#define TXINTE                 BIT7
-#define RXINTE                 BIT6
-#define TXRDYE                 BIT1
-#define RXRDYE                 BIT0
-
-/* IE1 & SR1 */
-#define UDRN           BIT7
-#define IDLE           BIT6
-#define SYNCD          BIT4
-#define FLGD           BIT4
-#define CCTS           BIT3
-#define CDCD           BIT2
-#define BRKD           BIT1
-#define ABTD           BIT1
-#define GAPD           BIT1
-#define BRKE           BIT0
-#define IDLD   BIT0
-
-/* IE2 & SR2 */
-#define EOM    BIT7
-#define PMP    BIT6
-#define SHRT   BIT6
-#define PE     BIT5
-#define ABT    BIT5
-#define FRME   BIT4
-#define RBIT   BIT4
-#define OVRN   BIT3
-#define CRCE   BIT2
-
-
-/*
- * Global linked list of SyncLink devices
- */
-static SLMP_INFO *synclinkmp_device_list = NULL;
-static int synclinkmp_adapter_count = -1;
-static int synclinkmp_device_count = 0;
-
-/*
- * Set this param to non-zero to load eax with the
- * .text section address and breakpoint on module load.
- * This is useful for use with gdb and add-symbol-file command.
- */
-static int break_on_load = 0;
-
-/*
- * Driver major number, defaults to zero to get auto
- * assigned major number. May be forced as module parameter.
- */
-static int ttymajor = 0;
-
-/*
- * Array of user specified options for ISA adapters.
- */
-static int debug_level = 0;
-static int maxframe[MAX_DEVICES] = {0,};
-
-module_param(break_on_load, bool, 0);
-module_param(ttymajor, int, 0);
-module_param(debug_level, int, 0);
-module_param_array(maxframe, int, NULL, 0);
-
-static char *driver_name = "SyncLink MultiPort driver";
-static char *driver_version = "$Revision: 4.38 $";
-
-static int synclinkmp_init_one(struct pci_dev *dev,const struct pci_device_id *ent);
-static void synclinkmp_remove_one(struct pci_dev *dev);
-
-static struct pci_device_id synclinkmp_pci_tbl[] = {
-       { PCI_VENDOR_ID_MICROGATE, PCI_DEVICE_ID_MICROGATE_SCA, PCI_ANY_ID, PCI_ANY_ID, },
-       { 0, }, /* terminate list */
-};
-MODULE_DEVICE_TABLE(pci, synclinkmp_pci_tbl);
-
-MODULE_LICENSE("GPL");
-
-static struct pci_driver synclinkmp_pci_driver = {
-       .name           = "synclinkmp",
-       .id_table       = synclinkmp_pci_tbl,
-       .probe          = synclinkmp_init_one,
-       .remove         = __devexit_p(synclinkmp_remove_one),
-};
-
-
-static struct tty_driver *serial_driver;
-
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
-
-/* tty callbacks */
-
-static int  open(struct tty_struct *tty, struct file * filp);
-static void close(struct tty_struct *tty, struct file * filp);
-static void hangup(struct tty_struct *tty);
-static void set_termios(struct tty_struct *tty, struct ktermios *old_termios);
-
-static int  write(struct tty_struct *tty, const unsigned char *buf, int count);
-static int put_char(struct tty_struct *tty, unsigned char ch);
-static void send_xchar(struct tty_struct *tty, char ch);
-static void wait_until_sent(struct tty_struct *tty, int timeout);
-static int  write_room(struct tty_struct *tty);
-static void flush_chars(struct tty_struct *tty);
-static void flush_buffer(struct tty_struct *tty);
-static void tx_hold(struct tty_struct *tty);
-static void tx_release(struct tty_struct *tty);
-
-static int  ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
-static int  chars_in_buffer(struct tty_struct *tty);
-static void throttle(struct tty_struct * tty);
-static void unthrottle(struct tty_struct * tty);
-static int set_break(struct tty_struct *tty, int break_state);
-
-#if SYNCLINK_GENERIC_HDLC
-#define dev_to_port(D) (dev_to_hdlc(D)->priv)
-static void hdlcdev_tx_done(SLMP_INFO *info);
-static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size);
-static int  hdlcdev_init(SLMP_INFO *info);
-static void hdlcdev_exit(SLMP_INFO *info);
-#endif
-
-/* ioctl handlers */
-
-static int  get_stats(SLMP_INFO *info, struct mgsl_icount __user *user_icount);
-static int  get_params(SLMP_INFO *info, MGSL_PARAMS __user *params);
-static int  set_params(SLMP_INFO *info, MGSL_PARAMS __user *params);
-static int  get_txidle(SLMP_INFO *info, int __user *idle_mode);
-static int  set_txidle(SLMP_INFO *info, int idle_mode);
-static int  tx_enable(SLMP_INFO *info, int enable);
-static int  tx_abort(SLMP_INFO *info);
-static int  rx_enable(SLMP_INFO *info, int enable);
-static int  modem_input_wait(SLMP_INFO *info,int arg);
-static int  wait_mgsl_event(SLMP_INFO *info, int __user *mask_ptr);
-static int  tiocmget(struct tty_struct *tty);
-static int  tiocmset(struct tty_struct *tty,
-                       unsigned int set, unsigned int clear);
-static int  set_break(struct tty_struct *tty, int break_state);
-
-static void add_device(SLMP_INFO *info);
-static void device_init(int adapter_num, struct pci_dev *pdev);
-static int  claim_resources(SLMP_INFO *info);
-static void release_resources(SLMP_INFO *info);
-
-static int  startup(SLMP_INFO *info);
-static int  block_til_ready(struct tty_struct *tty, struct file * filp,SLMP_INFO *info);
-static int carrier_raised(struct tty_port *port);
-static void shutdown(SLMP_INFO *info);
-static void program_hw(SLMP_INFO *info);
-static void change_params(SLMP_INFO *info);
-
-static bool init_adapter(SLMP_INFO *info);
-static bool register_test(SLMP_INFO *info);
-static bool irq_test(SLMP_INFO *info);
-static bool loopback_test(SLMP_INFO *info);
-static int  adapter_test(SLMP_INFO *info);
-static bool memory_test(SLMP_INFO *info);
-
-static void reset_adapter(SLMP_INFO *info);
-static void reset_port(SLMP_INFO *info);
-static void async_mode(SLMP_INFO *info);
-static void hdlc_mode(SLMP_INFO *info);
-
-static void rx_stop(SLMP_INFO *info);
-static void rx_start(SLMP_INFO *info);
-static void rx_reset_buffers(SLMP_INFO *info);
-static void rx_free_frame_buffers(SLMP_INFO *info, unsigned int first, unsigned int last);
-static bool rx_get_frame(SLMP_INFO *info);
-
-static void tx_start(SLMP_INFO *info);
-static void tx_stop(SLMP_INFO *info);
-static void tx_load_fifo(SLMP_INFO *info);
-static void tx_set_idle(SLMP_INFO *info);
-static void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int count);
-
-static void get_signals(SLMP_INFO *info);
-static void set_signals(SLMP_INFO *info);
-static void enable_loopback(SLMP_INFO *info, int enable);
-static void set_rate(SLMP_INFO *info, u32 data_rate);
-
-static int  bh_action(SLMP_INFO *info);
-static void bh_handler(struct work_struct *work);
-static void bh_receive(SLMP_INFO *info);
-static void bh_transmit(SLMP_INFO *info);
-static void bh_status(SLMP_INFO *info);
-static void isr_timer(SLMP_INFO *info);
-static void isr_rxint(SLMP_INFO *info);
-static void isr_rxrdy(SLMP_INFO *info);
-static void isr_txint(SLMP_INFO *info);
-static void isr_txrdy(SLMP_INFO *info);
-static void isr_rxdmaok(SLMP_INFO *info);
-static void isr_rxdmaerror(SLMP_INFO *info);
-static void isr_txdmaok(SLMP_INFO *info);
-static void isr_txdmaerror(SLMP_INFO *info);
-static void isr_io_pin(SLMP_INFO *info, u16 status);
-
-static int  alloc_dma_bufs(SLMP_INFO *info);
-static void free_dma_bufs(SLMP_INFO *info);
-static int  alloc_buf_list(SLMP_INFO *info);
-static int  alloc_frame_bufs(SLMP_INFO *info, SCADESC *list, SCADESC_EX *list_ex,int count);
-static int  alloc_tmp_rx_buf(SLMP_INFO *info);
-static void free_tmp_rx_buf(SLMP_INFO *info);
-
-static void load_pci_memory(SLMP_INFO *info, char* dest, const char* src, unsigned short count);
-static void trace_block(SLMP_INFO *info, const char* data, int count, int xmit);
-static void tx_timeout(unsigned long context);
-static void status_timeout(unsigned long context);
-
-static unsigned char read_reg(SLMP_INFO *info, unsigned char addr);
-static void write_reg(SLMP_INFO *info, unsigned char addr, unsigned char val);
-static u16 read_reg16(SLMP_INFO *info, unsigned char addr);
-static void write_reg16(SLMP_INFO *info, unsigned char addr, u16 val);
-static unsigned char read_status_reg(SLMP_INFO * info);
-static void write_control_reg(SLMP_INFO * info);
-
-
-static unsigned char rx_active_fifo_level = 16;        // rx request FIFO activation level in bytes
-static unsigned char tx_active_fifo_level = 16;        // tx request FIFO activation level in bytes
-static unsigned char tx_negate_fifo_level = 32;        // tx request FIFO negation level in bytes
-
-static u32 misc_ctrl_value = 0x007e4040;
-static u32 lcr1_brdr_value = 0x00800028;
-
-static u32 read_ahead_count = 8;
-
-/* DPCR, DMA Priority Control
- *
- * 07..05  Not used, must be 0
- * 04      BRC, bus release condition: 0=all transfers complete
- *              1=release after 1 xfer on all channels
- * 03      CCC, channel change condition: 0=every cycle
- *              1=after each channel completes all xfers
- * 02..00  PR<2..0>, priority 100=round robin
- *
- * 00000100 = 0x00
- */
-static unsigned char dma_priority = 0x04;
-
-// Number of bytes that can be written to shared RAM
-// in a single write operation
-static u32 sca_pci_load_interval = 64;
-
-/*
- * 1st function defined in .text section. Calling this function in
- * init_module() followed by a breakpoint allows a remote debugger
- * (gdb) to get the .text address for the add-symbol-file command.
- * This allows remote debugging of dynamically loadable modules.
- */
-static void* synclinkmp_get_text_ptr(void);
-static void* synclinkmp_get_text_ptr(void) {return synclinkmp_get_text_ptr;}
-
-static inline int sanity_check(SLMP_INFO *info,
-                              char *name, const char *routine)
-{
-#ifdef SANITY_CHECK
-       static const char *badmagic =
-               "Warning: bad magic number for synclinkmp_struct (%s) in %s\n";
-       static const char *badinfo =
-               "Warning: null synclinkmp_struct for (%s) in %s\n";
-
-       if (!info) {
-               printk(badinfo, name, routine);
-               return 1;
-       }
-       if (info->magic != MGSL_MAGIC) {
-               printk(badmagic, name, routine);
-               return 1;
-       }
-#else
-       if (!info)
-               return 1;
-#endif
-       return 0;
-}
-
-/**
- * line discipline callback wrappers
- *
- * The wrappers maintain line discipline references
- * while calling into the line discipline.
- *
- * ldisc_receive_buf  - pass receive data to line discipline
- */
-
-static void ldisc_receive_buf(struct tty_struct *tty,
-                             const __u8 *data, char *flags, int count)
-{
-       struct tty_ldisc *ld;
-       if (!tty)
-               return;
-       ld = tty_ldisc_ref(tty);
-       if (ld) {
-               if (ld->ops->receive_buf)
-                       ld->ops->receive_buf(tty, data, flags, count);
-               tty_ldisc_deref(ld);
-       }
-}
-
-/* tty callbacks */
-
-/* Called when a port is opened.  Init and enable port.
- */
-static int open(struct tty_struct *tty, struct file *filp)
-{
-       SLMP_INFO *info;
-       int retval, line;
-       unsigned long flags;
-
-       line = tty->index;
-       if ((line < 0) || (line >= synclinkmp_device_count)) {
-               printk("%s(%d): open with invalid line #%d.\n",
-                       __FILE__,__LINE__,line);
-               return -ENODEV;
-       }
-
-       info = synclinkmp_device_list;
-       while(info && info->line != line)
-               info = info->next_device;
-       if (sanity_check(info, tty->name, "open"))
-               return -ENODEV;
-       if ( info->init_error ) {
-               printk("%s(%d):%s device is not allocated, init error=%d\n",
-                       __FILE__,__LINE__,info->device_name,info->init_error);
-               return -ENODEV;
-       }
-
-       tty->driver_data = info;
-       info->port.tty = tty;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s open(), old ref count = %d\n",
-                        __FILE__,__LINE__,tty->driver->name, info->port.count);
-
-       /* If port is closing, signal caller to try again */
-       if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){
-               if (info->port.flags & ASYNC_CLOSING)
-                       interruptible_sleep_on(&info->port.close_wait);
-               retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ?
-                       -EAGAIN : -ERESTARTSYS);
-               goto cleanup;
-       }
-
-       info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-
-       spin_lock_irqsave(&info->netlock, flags);
-       if (info->netcount) {
-               retval = -EBUSY;
-               spin_unlock_irqrestore(&info->netlock, flags);
-               goto cleanup;
-       }
-       info->port.count++;
-       spin_unlock_irqrestore(&info->netlock, flags);
-
-       if (info->port.count == 1) {
-               /* 1st open on this device, init hardware */
-               retval = startup(info);
-               if (retval < 0)
-                       goto cleanup;
-       }
-
-       retval = block_til_ready(tty, filp, info);
-       if (retval) {
-               if (debug_level >= DEBUG_LEVEL_INFO)
-                       printk("%s(%d):%s block_til_ready() returned %d\n",
-                                __FILE__,__LINE__, info->device_name, retval);
-               goto cleanup;
-       }
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s open() success\n",
-                        __FILE__,__LINE__, info->device_name);
-       retval = 0;
-
-cleanup:
-       if (retval) {
-               if (tty->count == 1)
-                       info->port.tty = NULL; /* tty layer will release tty struct */
-               if(info->port.count)
-                       info->port.count--;
-       }
-
-       return retval;
-}
-
-/* Called when port is closed. Wait for remaining data to be
- * sent. Disable port and free resources.
- */
-static void close(struct tty_struct *tty, struct file *filp)
-{
-       SLMP_INFO * info = tty->driver_data;
-
-       if (sanity_check(info, tty->name, "close"))
-               return;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s close() entry, count=%d\n",
-                        __FILE__,__LINE__, info->device_name, info->port.count);
-
-       if (tty_port_close_start(&info->port, tty, filp) == 0)
-               goto cleanup;
-
-       mutex_lock(&info->port.mutex);
-       if (info->port.flags & ASYNC_INITIALIZED)
-               wait_until_sent(tty, info->timeout);
-
-       flush_buffer(tty);
-       tty_ldisc_flush(tty);
-       shutdown(info);
-       mutex_unlock(&info->port.mutex);
-
-       tty_port_close_end(&info->port, tty);
-       info->port.tty = NULL;
-cleanup:
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s close() exit, count=%d\n", __FILE__,__LINE__,
-                       tty->driver->name, info->port.count);
-}
-
-/* Called by tty_hangup() when a hangup is signaled.
- * This is the same as closing all open descriptors for the port.
- */
-static void hangup(struct tty_struct *tty)
-{
-       SLMP_INFO *info = tty->driver_data;
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s hangup()\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       if (sanity_check(info, tty->name, "hangup"))
-               return;
-
-       mutex_lock(&info->port.mutex);
-       flush_buffer(tty);
-       shutdown(info);
-
-       spin_lock_irqsave(&info->port.lock, flags);
-       info->port.count = 0;
-       info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
-       info->port.tty = NULL;
-       spin_unlock_irqrestore(&info->port.lock, flags);
-       mutex_unlock(&info->port.mutex);
-
-       wake_up_interruptible(&info->port.open_wait);
-}
-
-/* Set new termios settings
- */
-static void set_termios(struct tty_struct *tty, struct ktermios *old_termios)
-{
-       SLMP_INFO *info = tty->driver_data;
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s set_termios()\n", __FILE__,__LINE__,
-                       tty->driver->name );
-
-       change_params(info);
-
-       /* Handle transition to B0 status */
-       if (old_termios->c_cflag & CBAUD &&
-           !(tty->termios->c_cflag & CBAUD)) {
-               info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
-               spin_lock_irqsave(&info->lock,flags);
-               set_signals(info);
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
-
-       /* Handle transition away from B0 status */
-       if (!(old_termios->c_cflag & CBAUD) &&
-           tty->termios->c_cflag & CBAUD) {
-               info->serial_signals |= SerialSignal_DTR;
-               if (!(tty->termios->c_cflag & CRTSCTS) ||
-                   !test_bit(TTY_THROTTLED, &tty->flags)) {
-                       info->serial_signals |= SerialSignal_RTS;
-               }
-               spin_lock_irqsave(&info->lock,flags);
-               set_signals(info);
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
-
-       /* Handle turning off CRTSCTS */
-       if (old_termios->c_cflag & CRTSCTS &&
-           !(tty->termios->c_cflag & CRTSCTS)) {
-               tty->hw_stopped = 0;
-               tx_release(tty);
-       }
-}
-
-/* Send a block of data
- *
- * Arguments:
- *
- *     tty             pointer to tty information structure
- *     buf             pointer to buffer containing send data
- *     count           size of send data in bytes
- *
- * Return Value:       number of characters written
- */
-static int write(struct tty_struct *tty,
-                const unsigned char *buf, int count)
-{
-       int     c, ret = 0;
-       SLMP_INFO *info = tty->driver_data;
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s write() count=%d\n",
-                      __FILE__,__LINE__,info->device_name,count);
-
-       if (sanity_check(info, tty->name, "write"))
-               goto cleanup;
-
-       if (!info->tx_buf)
-               goto cleanup;
-
-       if (info->params.mode == MGSL_MODE_HDLC) {
-               if (count > info->max_frame_size) {
-                       ret = -EIO;
-                       goto cleanup;
-               }
-               if (info->tx_active)
-                       goto cleanup;
-               if (info->tx_count) {
-                       /* send accumulated data from send_char() calls */
-                       /* as frame and wait before accepting more data. */
-                       tx_load_dma_buffer(info, info->tx_buf, info->tx_count);
-                       goto start;
-               }
-               ret = info->tx_count = count;
-               tx_load_dma_buffer(info, buf, count);
-               goto start;
-       }
-
-       for (;;) {
-               c = min_t(int, count,
-                       min(info->max_frame_size - info->tx_count - 1,
-                           info->max_frame_size - info->tx_put));
-               if (c <= 0)
-                       break;
-                       
-               memcpy(info->tx_buf + info->tx_put, buf, c);
-
-               spin_lock_irqsave(&info->lock,flags);
-               info->tx_put += c;
-               if (info->tx_put >= info->max_frame_size)
-                       info->tx_put -= info->max_frame_size;
-               info->tx_count += c;
-               spin_unlock_irqrestore(&info->lock,flags);
-
-               buf += c;
-               count -= c;
-               ret += c;
-       }
-
-       if (info->params.mode == MGSL_MODE_HDLC) {
-               if (count) {
-                       ret = info->tx_count = 0;
-                       goto cleanup;
-               }
-               tx_load_dma_buffer(info, info->tx_buf, info->tx_count);
-       }
-start:
-       if (info->tx_count && !tty->stopped && !tty->hw_stopped) {
-               spin_lock_irqsave(&info->lock,flags);
-               if (!info->tx_active)
-                       tx_start(info);
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
-
-cleanup:
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk( "%s(%d):%s write() returning=%d\n",
-                       __FILE__,__LINE__,info->device_name,ret);
-       return ret;
-}
-
-/* Add a character to the transmit buffer.
- */
-static int put_char(struct tty_struct *tty, unsigned char ch)
-{
-       SLMP_INFO *info = tty->driver_data;
-       unsigned long flags;
-       int ret = 0;
-
-       if ( debug_level >= DEBUG_LEVEL_INFO ) {
-               printk( "%s(%d):%s put_char(%d)\n",
-                       __FILE__,__LINE__,info->device_name,ch);
-       }
-
-       if (sanity_check(info, tty->name, "put_char"))
-               return 0;
-
-       if (!info->tx_buf)
-               return 0;
-
-       spin_lock_irqsave(&info->lock,flags);
-
-       if ( (info->params.mode != MGSL_MODE_HDLC) ||
-            !info->tx_active ) {
-
-               if (info->tx_count < info->max_frame_size - 1) {
-                       info->tx_buf[info->tx_put++] = ch;
-                       if (info->tx_put >= info->max_frame_size)
-                               info->tx_put -= info->max_frame_size;
-                       info->tx_count++;
-                       ret = 1;
-               }
-       }
-
-       spin_unlock_irqrestore(&info->lock,flags);
-       return ret;
-}
-
-/* Send a high-priority XON/XOFF character
- */
-static void send_xchar(struct tty_struct *tty, char ch)
-{
-       SLMP_INFO *info = tty->driver_data;
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s send_xchar(%d)\n",
-                        __FILE__,__LINE__, info->device_name, ch );
-
-       if (sanity_check(info, tty->name, "send_xchar"))
-               return;
-
-       info->x_char = ch;
-       if (ch) {
-               /* Make sure transmit interrupts are on */
-               spin_lock_irqsave(&info->lock,flags);
-               if (!info->tx_enabled)
-                       tx_start(info);
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
-}
-
-/* Wait until the transmitter is empty.
- */
-static void wait_until_sent(struct tty_struct *tty, int timeout)
-{
-       SLMP_INFO * info = tty->driver_data;
-       unsigned long orig_jiffies, char_time;
-
-       if (!info )
-               return;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s wait_until_sent() entry\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       if (sanity_check(info, tty->name, "wait_until_sent"))
-               return;
-
-       if (!test_bit(ASYNCB_INITIALIZED, &info->port.flags))
-               goto exit;
-
-       orig_jiffies = jiffies;
-
-       /* Set check interval to 1/5 of estimated time to
-        * send a character, and make it at least 1. The check
-        * interval should also be less than the timeout.
-        * Note: use tight timings here to satisfy the NIST-PCTS.
-        */
-
-       if ( info->params.data_rate ) {
-               char_time = info->timeout/(32 * 5);
-               if (!char_time)
-                       char_time++;
-       } else
-               char_time = 1;
-
-       if (timeout)
-               char_time = min_t(unsigned long, char_time, timeout);
-
-       if ( info->params.mode == MGSL_MODE_HDLC ) {
-               while (info->tx_active) {
-                       msleep_interruptible(jiffies_to_msecs(char_time));
-                       if (signal_pending(current))
-                               break;
-                       if (timeout && time_after(jiffies, orig_jiffies + timeout))
-                               break;
-               }
-       } else {
-               /*
-                * TODO: determine if there is something similar to USC16C32
-                *       TXSTATUS_ALL_SENT status
-                */
-               while ( info->tx_active && info->tx_enabled) {
-                       msleep_interruptible(jiffies_to_msecs(char_time));
-                       if (signal_pending(current))
-                               break;
-                       if (timeout && time_after(jiffies, orig_jiffies + timeout))
-                               break;
-               }
-       }
-
-exit:
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s wait_until_sent() exit\n",
-                        __FILE__,__LINE__, info->device_name );
-}
-
-/* Return the count of free bytes in transmit buffer
- */
-static int write_room(struct tty_struct *tty)
-{
-       SLMP_INFO *info = tty->driver_data;
-       int ret;
-
-       if (sanity_check(info, tty->name, "write_room"))
-               return 0;
-
-       if (info->params.mode == MGSL_MODE_HDLC) {
-               ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE;
-       } else {
-               ret = info->max_frame_size - info->tx_count - 1;
-               if (ret < 0)
-                       ret = 0;
-       }
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s write_room()=%d\n",
-                      __FILE__, __LINE__, info->device_name, ret);
-
-       return ret;
-}
-
-/* enable transmitter and send remaining buffered characters
- */
-static void flush_chars(struct tty_struct *tty)
-{
-       SLMP_INFO *info = tty->driver_data;
-       unsigned long flags;
-
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):%s flush_chars() entry tx_count=%d\n",
-                       __FILE__,__LINE__,info->device_name,info->tx_count);
-
-       if (sanity_check(info, tty->name, "flush_chars"))
-               return;
-
-       if (info->tx_count <= 0 || tty->stopped || tty->hw_stopped ||
-           !info->tx_buf)
-               return;
-
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):%s flush_chars() entry, starting transmitter\n",
-                       __FILE__,__LINE__,info->device_name );
-
-       spin_lock_irqsave(&info->lock,flags);
-
-       if (!info->tx_active) {
-               if ( (info->params.mode == MGSL_MODE_HDLC) &&
-                       info->tx_count ) {
-                       /* operating in synchronous (frame oriented) mode */
-                       /* copy data from circular tx_buf to */
-                       /* transmit DMA buffer. */
-                       tx_load_dma_buffer(info,
-                                info->tx_buf,info->tx_count);
-               }
-               tx_start(info);
-       }
-
-       spin_unlock_irqrestore(&info->lock,flags);
-}
-
-/* Discard all data in the send buffer
- */
-static void flush_buffer(struct tty_struct *tty)
-{
-       SLMP_INFO *info = tty->driver_data;
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s flush_buffer() entry\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       if (sanity_check(info, tty->name, "flush_buffer"))
-               return;
-
-       spin_lock_irqsave(&info->lock,flags);
-       info->tx_count = info->tx_put = info->tx_get = 0;
-       del_timer(&info->tx_timer);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       tty_wakeup(tty);
-}
-
-/* throttle (stop) transmitter
- */
-static void tx_hold(struct tty_struct *tty)
-{
-       SLMP_INFO *info = tty->driver_data;
-       unsigned long flags;
-
-       if (sanity_check(info, tty->name, "tx_hold"))
-               return;
-
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk("%s(%d):%s tx_hold()\n",
-                       __FILE__,__LINE__,info->device_name);
-
-       spin_lock_irqsave(&info->lock,flags);
-       if (info->tx_enabled)
-               tx_stop(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-}
-
-/* release (start) transmitter
- */
-static void tx_release(struct tty_struct *tty)
-{
-       SLMP_INFO *info = tty->driver_data;
-       unsigned long flags;
-
-       if (sanity_check(info, tty->name, "tx_release"))
-               return;
-
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk("%s(%d):%s tx_release()\n",
-                       __FILE__,__LINE__,info->device_name);
-
-       spin_lock_irqsave(&info->lock,flags);
-       if (!info->tx_enabled)
-               tx_start(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-}
-
-/* Service an IOCTL request
- *
- * Arguments:
- *
- *     tty     pointer to tty instance data
- *     cmd     IOCTL command code
- *     arg     command argument/context
- *
- * Return Value:       0 if success, otherwise error code
- */
-static int ioctl(struct tty_struct *tty,
-                unsigned int cmd, unsigned long arg)
-{
-       SLMP_INFO *info = tty->driver_data;
-       void __user *argp = (void __user *)arg;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s ioctl() cmd=%08X\n", __FILE__,__LINE__,
-                       info->device_name, cmd );
-
-       if (sanity_check(info, tty->name, "ioctl"))
-               return -ENODEV;
-
-       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-           (cmd != TIOCMIWAIT)) {
-               if (tty->flags & (1 << TTY_IO_ERROR))
-                   return -EIO;
-       }
-
-       switch (cmd) {
-       case MGSL_IOCGPARAMS:
-               return get_params(info, argp);
-       case MGSL_IOCSPARAMS:
-               return set_params(info, argp);
-       case MGSL_IOCGTXIDLE:
-               return get_txidle(info, argp);
-       case MGSL_IOCSTXIDLE:
-               return set_txidle(info, (int)arg);
-       case MGSL_IOCTXENABLE:
-               return tx_enable(info, (int)arg);
-       case MGSL_IOCRXENABLE:
-               return rx_enable(info, (int)arg);
-       case MGSL_IOCTXABORT:
-               return tx_abort(info);
-       case MGSL_IOCGSTATS:
-               return get_stats(info, argp);
-       case MGSL_IOCWAITEVENT:
-               return wait_mgsl_event(info, argp);
-       case MGSL_IOCLOOPTXDONE:
-               return 0; // TODO: Not supported, need to document
-               /* Wait for modem input (DCD,RI,DSR,CTS) change
-                * as specified by mask in arg (TIOCM_RNG/DSR/CD/CTS)
-                */
-       case TIOCMIWAIT:
-               return modem_input_wait(info,(int)arg);
-               
-               /*
-                * 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.
-                */
-       default:
-               return -ENOIOCTLCMD;
-       }
-       return 0;
-}
-
-static int get_icount(struct tty_struct *tty,
-                               struct serial_icounter_struct *icount)
-{
-       SLMP_INFO *info = tty->driver_data;
-       struct mgsl_icount cnow;        /* kernel counter temps */
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->lock,flags);
-       cnow = info->icount;
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       icount->cts = cnow.cts;
-       icount->dsr = cnow.dsr;
-       icount->rng = cnow.rng;
-       icount->dcd = cnow.dcd;
-       icount->rx = cnow.rx;
-       icount->tx = cnow.tx;
-       icount->frame = cnow.frame;
-       icount->overrun = cnow.overrun;
-       icount->parity = cnow.parity;
-       icount->brk = cnow.brk;
-       icount->buf_overrun = cnow.buf_overrun;
-
-       return 0;
-}
-
-/*
- * /proc fs routines....
- */
-
-static inline void line_info(struct seq_file *m, SLMP_INFO *info)
-{
-       char    stat_buf[30];
-       unsigned long flags;
-
-       seq_printf(m, "%s: SCABase=%08x Mem=%08X StatusControl=%08x LCR=%08X\n"
-                      "\tIRQ=%d MaxFrameSize=%u\n",
-               info->device_name,
-               info->phys_sca_base,
-               info->phys_memory_base,
-               info->phys_statctrl_base,
-               info->phys_lcr_base,
-               info->irq_level,
-               info->max_frame_size );
-
-       /* output current serial signal states */
-       spin_lock_irqsave(&info->lock,flags);
-       get_signals(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       stat_buf[0] = 0;
-       stat_buf[1] = 0;
-       if (info->serial_signals & SerialSignal_RTS)
-               strcat(stat_buf, "|RTS");
-       if (info->serial_signals & SerialSignal_CTS)
-               strcat(stat_buf, "|CTS");
-       if (info->serial_signals & SerialSignal_DTR)
-               strcat(stat_buf, "|DTR");
-       if (info->serial_signals & SerialSignal_DSR)
-               strcat(stat_buf, "|DSR");
-       if (info->serial_signals & SerialSignal_DCD)
-               strcat(stat_buf, "|CD");
-       if (info->serial_signals & SerialSignal_RI)
-               strcat(stat_buf, "|RI");
-
-       if (info->params.mode == MGSL_MODE_HDLC) {
-               seq_printf(m, "\tHDLC txok:%d rxok:%d",
-                             info->icount.txok, info->icount.rxok);
-               if (info->icount.txunder)
-                       seq_printf(m, " txunder:%d", info->icount.txunder);
-               if (info->icount.txabort)
-                       seq_printf(m, " txabort:%d", info->icount.txabort);
-               if (info->icount.rxshort)
-                       seq_printf(m, " rxshort:%d", info->icount.rxshort);
-               if (info->icount.rxlong)
-                       seq_printf(m, " rxlong:%d", info->icount.rxlong);
-               if (info->icount.rxover)
-                       seq_printf(m, " rxover:%d", info->icount.rxover);
-               if (info->icount.rxcrc)
-                       seq_printf(m, " rxlong:%d", info->icount.rxcrc);
-       } else {
-               seq_printf(m, "\tASYNC tx:%d rx:%d",
-                             info->icount.tx, info->icount.rx);
-               if (info->icount.frame)
-                       seq_printf(m, " fe:%d", info->icount.frame);
-               if (info->icount.parity)
-                       seq_printf(m, " pe:%d", info->icount.parity);
-               if (info->icount.brk)
-                       seq_printf(m, " brk:%d", info->icount.brk);
-               if (info->icount.overrun)
-                       seq_printf(m, " oe:%d", info->icount.overrun);
-       }
-
-       /* Append serial signal status to end */
-       seq_printf(m, " %s\n", stat_buf+1);
-
-       seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
-        info->tx_active,info->bh_requested,info->bh_running,
-        info->pending_bh);
-}
-
-/* Called to print information about devices
- */
-static int synclinkmp_proc_show(struct seq_file *m, void *v)
-{
-       SLMP_INFO *info;
-
-       seq_printf(m, "synclinkmp driver:%s\n", driver_version);
-
-       info = synclinkmp_device_list;
-       while( info ) {
-               line_info(m, info);
-               info = info->next_device;
-       }
-       return 0;
-}
-
-static int synclinkmp_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, synclinkmp_proc_show, NULL);
-}
-
-static const struct file_operations synclinkmp_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = synclinkmp_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-/* Return the count of bytes in transmit buffer
- */
-static int chars_in_buffer(struct tty_struct *tty)
-{
-       SLMP_INFO *info = tty->driver_data;
-
-       if (sanity_check(info, tty->name, "chars_in_buffer"))
-               return 0;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s chars_in_buffer()=%d\n",
-                      __FILE__, __LINE__, info->device_name, info->tx_count);
-
-       return info->tx_count;
-}
-
-/* Signal remote device to throttle send data (our receive data)
- */
-static void throttle(struct tty_struct * tty)
-{
-       SLMP_INFO *info = tty->driver_data;
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s throttle() entry\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       if (sanity_check(info, tty->name, "throttle"))
-               return;
-
-       if (I_IXOFF(tty))
-               send_xchar(tty, STOP_CHAR(tty));
-
-       if (tty->termios->c_cflag & CRTSCTS) {
-               spin_lock_irqsave(&info->lock,flags);
-               info->serial_signals &= ~SerialSignal_RTS;
-               set_signals(info);
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
-}
-
-/* Signal remote device to stop throttling send data (our receive data)
- */
-static void unthrottle(struct tty_struct * tty)
-{
-       SLMP_INFO *info = tty->driver_data;
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s unthrottle() entry\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       if (sanity_check(info, tty->name, "unthrottle"))
-               return;
-
-       if (I_IXOFF(tty)) {
-               if (info->x_char)
-                       info->x_char = 0;
-               else
-                       send_xchar(tty, START_CHAR(tty));
-       }
-
-       if (tty->termios->c_cflag & CRTSCTS) {
-               spin_lock_irqsave(&info->lock,flags);
-               info->serial_signals |= SerialSignal_RTS;
-               set_signals(info);
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
-}
-
-/* set or clear transmit break condition
- * break_state -1=set break condition, 0=clear
- */
-static int set_break(struct tty_struct *tty, int break_state)
-{
-       unsigned char RegValue;
-       SLMP_INFO * info = tty->driver_data;
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s set_break(%d)\n",
-                        __FILE__,__LINE__, info->device_name, break_state);
-
-       if (sanity_check(info, tty->name, "set_break"))
-               return -EINVAL;
-
-       spin_lock_irqsave(&info->lock,flags);
-       RegValue = read_reg(info, CTL);
-       if (break_state == -1)
-               RegValue |= BIT3;
-       else
-               RegValue &= ~BIT3;
-       write_reg(info, CTL, RegValue);
-       spin_unlock_irqrestore(&info->lock,flags);
-       return 0;
-}
-
-#if SYNCLINK_GENERIC_HDLC
-
-/**
- * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.)
- * set encoding and frame check sequence (FCS) options
- *
- * dev       pointer to network device structure
- * encoding  serial encoding setting
- * parity    FCS setting
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
-                         unsigned short parity)
-{
-       SLMP_INFO *info = dev_to_port(dev);
-       unsigned char  new_encoding;
-       unsigned short new_crctype;
-
-       /* return error if TTY interface open */
-       if (info->port.count)
-               return -EBUSY;
-
-       switch (encoding)
-       {
-       case ENCODING_NRZ:        new_encoding = HDLC_ENCODING_NRZ; break;
-       case ENCODING_NRZI:       new_encoding = HDLC_ENCODING_NRZI_SPACE; break;
-       case ENCODING_FM_MARK:    new_encoding = HDLC_ENCODING_BIPHASE_MARK; break;
-       case ENCODING_FM_SPACE:   new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break;
-       case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break;
-       default: return -EINVAL;
-       }
-
-       switch (parity)
-       {
-       case PARITY_NONE:            new_crctype = HDLC_CRC_NONE; break;
-       case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break;
-       case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break;
-       default: return -EINVAL;
-       }
-
-       info->params.encoding = new_encoding;
-       info->params.crc_type = new_crctype;
-
-       /* if network interface up, reprogram hardware */
-       if (info->netcount)
-               program_hw(info);
-
-       return 0;
-}
-
-/**
- * called by generic HDLC layer to send frame
- *
- * skb  socket buffer containing HDLC frame
- * dev  pointer to network device structure
- */
-static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
-                                     struct net_device *dev)
-{
-       SLMP_INFO *info = dev_to_port(dev);
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk(KERN_INFO "%s:hdlc_xmit(%s)\n",__FILE__,dev->name);
-
-       /* stop sending until this frame completes */
-       netif_stop_queue(dev);
-
-       /* copy data to device buffers */
-       info->tx_count = skb->len;
-       tx_load_dma_buffer(info, skb->data, skb->len);
-
-       /* update network statistics */
-       dev->stats.tx_packets++;
-       dev->stats.tx_bytes += skb->len;
-
-       /* done with socket buffer, so free it */
-       dev_kfree_skb(skb);
-
-       /* save start time for transmit timeout detection */
-       dev->trans_start = jiffies;
-
-       /* start hardware transmitter if necessary */
-       spin_lock_irqsave(&info->lock,flags);
-       if (!info->tx_active)
-               tx_start(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       return NETDEV_TX_OK;
-}
-
-/**
- * called by network layer when interface enabled
- * claim resources and initialize hardware
- *
- * dev  pointer to network device structure
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_open(struct net_device *dev)
-{
-       SLMP_INFO *info = dev_to_port(dev);
-       int rc;
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s:hdlcdev_open(%s)\n",__FILE__,dev->name);
-
-       /* generic HDLC layer open processing */
-       if ((rc = hdlc_open(dev)))
-               return rc;
-
-       /* arbitrate between network and tty opens */
-       spin_lock_irqsave(&info->netlock, flags);
-       if (info->port.count != 0 || info->netcount != 0) {
-               printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name);
-               spin_unlock_irqrestore(&info->netlock, flags);
-               return -EBUSY;
-       }
-       info->netcount=1;
-       spin_unlock_irqrestore(&info->netlock, flags);
-
-       /* claim resources and init adapter */
-       if ((rc = startup(info)) != 0) {
-               spin_lock_irqsave(&info->netlock, flags);
-               info->netcount=0;
-               spin_unlock_irqrestore(&info->netlock, flags);
-               return rc;
-       }
-
-       /* assert DTR and RTS, apply hardware settings */
-       info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
-       program_hw(info);
-
-       /* enable network layer transmit */
-       dev->trans_start = jiffies;
-       netif_start_queue(dev);
-
-       /* inform generic HDLC layer of current DCD status */
-       spin_lock_irqsave(&info->lock, flags);
-       get_signals(info);
-       spin_unlock_irqrestore(&info->lock, flags);
-       if (info->serial_signals & SerialSignal_DCD)
-               netif_carrier_on(dev);
-       else
-               netif_carrier_off(dev);
-       return 0;
-}
-
-/**
- * called by network layer when interface is disabled
- * shutdown hardware and release resources
- *
- * dev  pointer to network device structure
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_close(struct net_device *dev)
-{
-       SLMP_INFO *info = dev_to_port(dev);
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s:hdlcdev_close(%s)\n",__FILE__,dev->name);
-
-       netif_stop_queue(dev);
-
-       /* shutdown adapter and release resources */
-       shutdown(info);
-
-       hdlc_close(dev);
-
-       spin_lock_irqsave(&info->netlock, flags);
-       info->netcount=0;
-       spin_unlock_irqrestore(&info->netlock, flags);
-
-       return 0;
-}
-
-/**
- * called by network layer to process IOCTL call to network device
- *
- * dev  pointer to network device structure
- * ifr  pointer to network interface request structure
- * cmd  IOCTL command code
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
-       const size_t size = sizeof(sync_serial_settings);
-       sync_serial_settings new_line;
-       sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
-       SLMP_INFO *info = dev_to_port(dev);
-       unsigned int flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s:hdlcdev_ioctl(%s)\n",__FILE__,dev->name);
-
-       /* return error if TTY interface open */
-       if (info->port.count)
-               return -EBUSY;
-
-       if (cmd != SIOCWANDEV)
-               return hdlc_ioctl(dev, ifr, cmd);
-
-       switch(ifr->ifr_settings.type) {
-       case IF_GET_IFACE: /* return current sync_serial_settings */
-
-               ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
-               if (ifr->ifr_settings.size < size) {
-                       ifr->ifr_settings.size = size; /* data size wanted */
-                       return -ENOBUFS;
-               }
-
-               flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
-                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
-                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
-                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
-
-               switch (flags){
-               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break;
-               case (HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_INT; break;
-               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_TXINT; break;
-               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break;
-               default: new_line.clock_type = CLOCK_DEFAULT;
-               }
-
-               new_line.clock_rate = info->params.clock_speed;
-               new_line.loopback   = info->params.loopback ? 1:0;
-
-               if (copy_to_user(line, &new_line, size))
-                       return -EFAULT;
-               return 0;
-
-       case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */
-
-               if(!capable(CAP_NET_ADMIN))
-                       return -EPERM;
-               if (copy_from_user(&new_line, line, size))
-                       return -EFAULT;
-
-               switch (new_line.clock_type)
-               {
-               case CLOCK_EXT:      flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break;
-               case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break;
-               case CLOCK_INT:      flags = HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG;    break;
-               case CLOCK_TXINT:    flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG;    break;
-               case CLOCK_DEFAULT:  flags = info->params.flags &
-                                            (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
-                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
-                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
-                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN); break;
-               default: return -EINVAL;
-               }
-
-               if (new_line.loopback != 0 && new_line.loopback != 1)
-                       return -EINVAL;
-
-               info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
-                                       HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
-                                       HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
-                                       HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
-               info->params.flags |= flags;
-
-               info->params.loopback = new_line.loopback;
-
-               if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG))
-                       info->params.clock_speed = new_line.clock_rate;
-               else
-                       info->params.clock_speed = 0;
-
-               /* if network interface up, reprogram hardware */
-               if (info->netcount)
-                       program_hw(info);
-               return 0;
-
-       default:
-               return hdlc_ioctl(dev, ifr, cmd);
-       }
-}
-
-/**
- * called by network layer when transmit timeout is detected
- *
- * dev  pointer to network device structure
- */
-static void hdlcdev_tx_timeout(struct net_device *dev)
-{
-       SLMP_INFO *info = dev_to_port(dev);
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("hdlcdev_tx_timeout(%s)\n",dev->name);
-
-       dev->stats.tx_errors++;
-       dev->stats.tx_aborted_errors++;
-
-       spin_lock_irqsave(&info->lock,flags);
-       tx_stop(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       netif_wake_queue(dev);
-}
-
-/**
- * called by device driver when transmit completes
- * reenable network layer transmit if stopped
- *
- * info  pointer to device instance information
- */
-static void hdlcdev_tx_done(SLMP_INFO *info)
-{
-       if (netif_queue_stopped(info->netdev))
-               netif_wake_queue(info->netdev);
-}
-
-/**
- * called by device driver when frame received
- * pass frame to network layer
- *
- * info  pointer to device instance information
- * buf   pointer to buffer contianing frame data
- * size  count of data bytes in buf
- */
-static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size)
-{
-       struct sk_buff *skb = dev_alloc_skb(size);
-       struct net_device *dev = info->netdev;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("hdlcdev_rx(%s)\n",dev->name);
-
-       if (skb == NULL) {
-               printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n",
-                      dev->name);
-               dev->stats.rx_dropped++;
-               return;
-       }
-
-       memcpy(skb_put(skb, size), buf, size);
-
-       skb->protocol = hdlc_type_trans(skb, dev);
-
-       dev->stats.rx_packets++;
-       dev->stats.rx_bytes += size;
-
-       netif_rx(skb);
-}
-
-static const struct net_device_ops hdlcdev_ops = {
-       .ndo_open       = hdlcdev_open,
-       .ndo_stop       = hdlcdev_close,
-       .ndo_change_mtu = hdlc_change_mtu,
-       .ndo_start_xmit = hdlc_start_xmit,
-       .ndo_do_ioctl   = hdlcdev_ioctl,
-       .ndo_tx_timeout = hdlcdev_tx_timeout,
-};
-
-/**
- * called by device driver when adding device instance
- * do generic HDLC initialization
- *
- * info  pointer to device instance information
- *
- * returns 0 if success, otherwise error code
- */
-static int hdlcdev_init(SLMP_INFO *info)
-{
-       int rc;
-       struct net_device *dev;
-       hdlc_device *hdlc;
-
-       /* allocate and initialize network and HDLC layer objects */
-
-       if (!(dev = alloc_hdlcdev(info))) {
-               printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__);
-               return -ENOMEM;
-       }
-
-       /* for network layer reporting purposes only */
-       dev->mem_start = info->phys_sca_base;
-       dev->mem_end   = info->phys_sca_base + SCA_BASE_SIZE - 1;
-       dev->irq       = info->irq_level;
-
-       /* network layer callbacks and settings */
-       dev->netdev_ops     = &hdlcdev_ops;
-       dev->watchdog_timeo = 10 * HZ;
-       dev->tx_queue_len   = 50;
-
-       /* generic HDLC layer callbacks and settings */
-       hdlc         = dev_to_hdlc(dev);
-       hdlc->attach = hdlcdev_attach;
-       hdlc->xmit   = hdlcdev_xmit;
-
-       /* register objects with HDLC layer */
-       if ((rc = register_hdlc_device(dev))) {
-               printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__);
-               free_netdev(dev);
-               return rc;
-       }
-
-       info->netdev = dev;
-       return 0;
-}
-
-/**
- * called by device driver when removing device instance
- * do generic HDLC cleanup
- *
- * info  pointer to device instance information
- */
-static void hdlcdev_exit(SLMP_INFO *info)
-{
-       unregister_hdlc_device(info->netdev);
-       free_netdev(info->netdev);
-       info->netdev = NULL;
-}
-
-#endif /* CONFIG_HDLC */
-
-
-/* Return next bottom half action to perform.
- * Return Value:       BH action code or 0 if nothing to do.
- */
-static int bh_action(SLMP_INFO *info)
-{
-       unsigned long flags;
-       int rc = 0;
-
-       spin_lock_irqsave(&info->lock,flags);
-
-       if (info->pending_bh & BH_RECEIVE) {
-               info->pending_bh &= ~BH_RECEIVE;
-               rc = BH_RECEIVE;
-       } else if (info->pending_bh & BH_TRANSMIT) {
-               info->pending_bh &= ~BH_TRANSMIT;
-               rc = BH_TRANSMIT;
-       } else if (info->pending_bh & BH_STATUS) {
-               info->pending_bh &= ~BH_STATUS;
-               rc = BH_STATUS;
-       }
-
-       if (!rc) {
-               /* Mark BH routine as complete */
-               info->bh_running = false;
-               info->bh_requested = false;
-       }
-
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       return rc;
-}
-
-/* Perform bottom half processing of work items queued by ISR.
- */
-static void bh_handler(struct work_struct *work)
-{
-       SLMP_INFO *info = container_of(work, SLMP_INFO, task);
-       int action;
-
-       if (!info)
-               return;
-
-       if ( debug_level >= DEBUG_LEVEL_BH )
-               printk( "%s(%d):%s bh_handler() entry\n",
-                       __FILE__,__LINE__,info->device_name);
-
-       info->bh_running = true;
-
-       while((action = bh_action(info)) != 0) {
-
-               /* Process work item */
-               if ( debug_level >= DEBUG_LEVEL_BH )
-                       printk( "%s(%d):%s bh_handler() work item action=%d\n",
-                               __FILE__,__LINE__,info->device_name, action);
-
-               switch (action) {
-
-               case BH_RECEIVE:
-                       bh_receive(info);
-                       break;
-               case BH_TRANSMIT:
-                       bh_transmit(info);
-                       break;
-               case BH_STATUS:
-                       bh_status(info);
-                       break;
-               default:
-                       /* unknown work item ID */
-                       printk("%s(%d):%s Unknown work item ID=%08X!\n",
-                               __FILE__,__LINE__,info->device_name,action);
-                       break;
-               }
-       }
-
-       if ( debug_level >= DEBUG_LEVEL_BH )
-               printk( "%s(%d):%s bh_handler() exit\n",
-                       __FILE__,__LINE__,info->device_name);
-}
-
-static void bh_receive(SLMP_INFO *info)
-{
-       if ( debug_level >= DEBUG_LEVEL_BH )
-               printk( "%s(%d):%s bh_receive()\n",
-                       __FILE__,__LINE__,info->device_name);
-
-       while( rx_get_frame(info) );
-}
-
-static void bh_transmit(SLMP_INFO *info)
-{
-       struct tty_struct *tty = info->port.tty;
-
-       if ( debug_level >= DEBUG_LEVEL_BH )
-               printk( "%s(%d):%s bh_transmit() entry\n",
-                       __FILE__,__LINE__,info->device_name);
-
-       if (tty)
-               tty_wakeup(tty);
-}
-
-static void bh_status(SLMP_INFO *info)
-{
-       if ( debug_level >= DEBUG_LEVEL_BH )
-               printk( "%s(%d):%s bh_status() entry\n",
-                       __FILE__,__LINE__,info->device_name);
-
-       info->ri_chkcount = 0;
-       info->dsr_chkcount = 0;
-       info->dcd_chkcount = 0;
-       info->cts_chkcount = 0;
-}
-
-static void isr_timer(SLMP_INFO * info)
-{
-       unsigned char timer = (info->port_num & 1) ? TIMER2 : TIMER0;
-
-       /* IER2<7..4> = timer<3..0> interrupt enables (0=disabled) */
-       write_reg(info, IER2, 0);
-
-       /* TMCS, Timer Control/Status Register
-        *
-        * 07      CMF, Compare match flag (read only) 1=match
-        * 06      ECMI, CMF Interrupt Enable: 0=disabled
-        * 05      Reserved, must be 0
-        * 04      TME, Timer Enable
-        * 03..00  Reserved, must be 0
-        *
-        * 0000 0000
-        */
-       write_reg(info, (unsigned char)(timer + TMCS), 0);
-
-       info->irq_occurred = true;
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk("%s(%d):%s isr_timer()\n",
-                       __FILE__,__LINE__,info->device_name);
-}
-
-static void isr_rxint(SLMP_INFO * info)
-{
-       struct tty_struct *tty = info->port.tty;
-       struct  mgsl_icount *icount = &info->icount;
-       unsigned char status = read_reg(info, SR1) & info->ie1_value & (FLGD + IDLD + CDCD + BRKD);
-       unsigned char status2 = read_reg(info, SR2) & info->ie2_value & OVRN;
-
-       /* clear status bits */
-       if (status)
-               write_reg(info, SR1, status);
-
-       if (status2)
-               write_reg(info, SR2, status2);
-       
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk("%s(%d):%s isr_rxint status=%02X %02x\n",
-                       __FILE__,__LINE__,info->device_name,status,status2);
-
-       if (info->params.mode == MGSL_MODE_ASYNC) {
-               if (status & BRKD) {
-                       icount->brk++;
-
-                       /* process break detection if tty control
-                        * is not set to ignore it
-                        */
-                       if ( tty ) {
-                               if (!(status & info->ignore_status_mask1)) {
-                                       if (info->read_status_mask1 & BRKD) {
-                                               tty_insert_flip_char(tty, 0, TTY_BREAK);
-                                               if (info->port.flags & ASYNC_SAK)
-                                                       do_SAK(tty);
-                                       }
-                               }
-                       }
-               }
-       }
-       else {
-               if (status & (FLGD|IDLD)) {
-                       if (status & FLGD)
-                               info->icount.exithunt++;
-                       else if (status & IDLD)
-                               info->icount.rxidle++;
-                       wake_up_interruptible(&info->event_wait_q);
-               }
-       }
-
-       if (status & CDCD) {
-               /* simulate a common modem status change interrupt
-                * for our handler
-                */
-               get_signals( info );
-               isr_io_pin(info,
-                       MISCSTATUS_DCD_LATCHED|(info->serial_signals&SerialSignal_DCD));
-       }
-}
-
-/*
- * handle async rx data interrupts
- */
-static void isr_rxrdy(SLMP_INFO * info)
-{
-       u16 status;
-       unsigned char DataByte;
-       struct tty_struct *tty = info->port.tty;
-       struct  mgsl_icount *icount = &info->icount;
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk("%s(%d):%s isr_rxrdy\n",
-                       __FILE__,__LINE__,info->device_name);
-
-       while((status = read_reg(info,CST0)) & BIT0)
-       {
-               int flag = 0;
-               bool over = false;
-               DataByte = read_reg(info,TRB);
-
-               icount->rx++;
-
-               if ( status & (PE + FRME + OVRN) ) {
-                       printk("%s(%d):%s rxerr=%04X\n",
-                               __FILE__,__LINE__,info->device_name,status);
-
-                       /* update error statistics */
-                       if (status & PE)
-                               icount->parity++;
-                       else if (status & FRME)
-                               icount->frame++;
-                       else if (status & OVRN)
-                               icount->overrun++;
-
-                       /* discard char if tty control flags say so */
-                       if (status & info->ignore_status_mask2)
-                               continue;
-
-                       status &= info->read_status_mask2;
-
-                       if ( tty ) {
-                               if (status & PE)
-                                       flag = TTY_PARITY;
-                               else if (status & FRME)
-                                       flag = TTY_FRAME;
-                               if (status & OVRN) {
-                                       /* Overrun is special, since it's
-                                        * reported immediately, and doesn't
-                                        * affect the current character
-                                        */
-                                       over = true;
-                               }
-                       }
-               }       /* end of if (error) */
-
-               if ( tty ) {
-                       tty_insert_flip_char(tty, DataByte, flag);
-                       if (over)
-                               tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-               }
-       }
-
-       if ( debug_level >= DEBUG_LEVEL_ISR ) {
-               printk("%s(%d):%s rx=%d brk=%d parity=%d frame=%d overrun=%d\n",
-                       __FILE__,__LINE__,info->device_name,
-                       icount->rx,icount->brk,icount->parity,
-                       icount->frame,icount->overrun);
-       }
-
-       if ( tty )
-               tty_flip_buffer_push(tty);
-}
-
-static void isr_txeom(SLMP_INFO * info, unsigned char status)
-{
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk("%s(%d):%s isr_txeom status=%02x\n",
-                       __FILE__,__LINE__,info->device_name,status);
-
-       write_reg(info, TXDMA + DIR, 0x00); /* disable Tx DMA IRQs */
-       write_reg(info, TXDMA + DSR, 0xc0); /* clear IRQs and disable DMA */
-       write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */
-
-       if (status & UDRN) {
-               write_reg(info, CMD, TXRESET);
-               write_reg(info, CMD, TXENABLE);
-       } else
-               write_reg(info, CMD, TXBUFCLR);
-
-       /* disable and clear tx interrupts */
-       info->ie0_value &= ~TXRDYE;
-       info->ie1_value &= ~(IDLE + UDRN);
-       write_reg16(info, IE0, (unsigned short)((info->ie1_value << 8) + info->ie0_value));
-       write_reg(info, SR1, (unsigned char)(UDRN + IDLE));
-
-       if ( info->tx_active ) {
-               if (info->params.mode != MGSL_MODE_ASYNC) {
-                       if (status & UDRN)
-                               info->icount.txunder++;
-                       else if (status & IDLE)
-                               info->icount.txok++;
-               }
-
-               info->tx_active = false;
-               info->tx_count = info->tx_put = info->tx_get = 0;
-
-               del_timer(&info->tx_timer);
-
-               if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done ) {
-                       info->serial_signals &= ~SerialSignal_RTS;
-                       info->drop_rts_on_tx_done = false;
-                       set_signals(info);
-               }
-
-#if SYNCLINK_GENERIC_HDLC
-               if (info->netcount)
-                       hdlcdev_tx_done(info);
-               else
-#endif
-               {
-                       if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) {
-                               tx_stop(info);
-                               return;
-                       }
-                       info->pending_bh |= BH_TRANSMIT;
-               }
-       }
-}
-
-
-/*
- * handle tx status interrupts
- */
-static void isr_txint(SLMP_INFO * info)
-{
-       unsigned char status = read_reg(info, SR1) & info->ie1_value & (UDRN + IDLE + CCTS);
-
-       /* clear status bits */
-       write_reg(info, SR1, status);
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk("%s(%d):%s isr_txint status=%02x\n",
-                       __FILE__,__LINE__,info->device_name,status);
-
-       if (status & (UDRN + IDLE))
-               isr_txeom(info, status);
-
-       if (status & CCTS) {
-               /* simulate a common modem status change interrupt
-                * for our handler
-                */
-               get_signals( info );
-               isr_io_pin(info,
-                       MISCSTATUS_CTS_LATCHED|(info->serial_signals&SerialSignal_CTS));
-
-       }
-}
-
-/*
- * handle async tx data interrupts
- */
-static void isr_txrdy(SLMP_INFO * info)
-{
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk("%s(%d):%s isr_txrdy() tx_count=%d\n",
-                       __FILE__,__LINE__,info->device_name,info->tx_count);
-
-       if (info->params.mode != MGSL_MODE_ASYNC) {
-               /* disable TXRDY IRQ, enable IDLE IRQ */
-               info->ie0_value &= ~TXRDYE;
-               info->ie1_value |= IDLE;
-               write_reg16(info, IE0, (unsigned short)((info->ie1_value << 8) + info->ie0_value));
-               return;
-       }
-
-       if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) {
-               tx_stop(info);
-               return;
-       }
-
-       if ( info->tx_count )
-               tx_load_fifo( info );
-       else {
-               info->tx_active = false;
-               info->ie0_value &= ~TXRDYE;
-               write_reg(info, IE0, info->ie0_value);
-       }
-
-       if (info->tx_count < WAKEUP_CHARS)
-               info->pending_bh |= BH_TRANSMIT;
-}
-
-static void isr_rxdmaok(SLMP_INFO * info)
-{
-       /* BIT7 = EOT (end of transfer)
-        * BIT6 = EOM (end of message/frame)
-        */
-       unsigned char status = read_reg(info,RXDMA + DSR) & 0xc0;
-
-       /* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */
-       write_reg(info, RXDMA + DSR, (unsigned char)(status | 1));
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk("%s(%d):%s isr_rxdmaok(), status=%02x\n",
-                       __FILE__,__LINE__,info->device_name,status);
-
-       info->pending_bh |= BH_RECEIVE;
-}
-
-static void isr_rxdmaerror(SLMP_INFO * info)
-{
-       /* BIT5 = BOF (buffer overflow)
-        * BIT4 = COF (counter overflow)
-        */
-       unsigned char status = read_reg(info,RXDMA + DSR) & 0x30;
-
-       /* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */
-       write_reg(info, RXDMA + DSR, (unsigned char)(status | 1));
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk("%s(%d):%s isr_rxdmaerror(), status=%02x\n",
-                       __FILE__,__LINE__,info->device_name,status);
-
-       info->rx_overflow = true;
-       info->pending_bh |= BH_RECEIVE;
-}
-
-static void isr_txdmaok(SLMP_INFO * info)
-{
-       unsigned char status_reg1 = read_reg(info, SR1);
-
-       write_reg(info, TXDMA + DIR, 0x00);     /* disable Tx DMA IRQs */
-       write_reg(info, TXDMA + DSR, 0xc0); /* clear IRQs and disable DMA */
-       write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk("%s(%d):%s isr_txdmaok(), status=%02x\n",
-                       __FILE__,__LINE__,info->device_name,status_reg1);
-
-       /* program TXRDY as FIFO empty flag, enable TXRDY IRQ */
-       write_reg16(info, TRC0, 0);
-       info->ie0_value |= TXRDYE;
-       write_reg(info, IE0, info->ie0_value);
-}
-
-static void isr_txdmaerror(SLMP_INFO * info)
-{
-       /* BIT5 = BOF (buffer overflow)
-        * BIT4 = COF (counter overflow)
-        */
-       unsigned char status = read_reg(info,TXDMA + DSR) & 0x30;
-
-       /* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */
-       write_reg(info, TXDMA + DSR, (unsigned char)(status | 1));
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk("%s(%d):%s isr_txdmaerror(), status=%02x\n",
-                       __FILE__,__LINE__,info->device_name,status);
-}
-
-/* handle input serial signal changes
- */
-static void isr_io_pin( SLMP_INFO *info, u16 status )
-{
-       struct  mgsl_icount *icount;
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk("%s(%d):isr_io_pin status=%04X\n",
-                       __FILE__,__LINE__,status);
-
-       if (status & (MISCSTATUS_CTS_LATCHED | MISCSTATUS_DCD_LATCHED |
-                     MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) {
-               icount = &info->icount;
-               /* update input line counters */
-               if (status & MISCSTATUS_RI_LATCHED) {
-                       icount->rng++;
-                       if ( status & SerialSignal_RI )
-                               info->input_signal_events.ri_up++;
-                       else
-                               info->input_signal_events.ri_down++;
-               }
-               if (status & MISCSTATUS_DSR_LATCHED) {
-                       icount->dsr++;
-                       if ( status & SerialSignal_DSR )
-                               info->input_signal_events.dsr_up++;
-                       else
-                               info->input_signal_events.dsr_down++;
-               }
-               if (status & MISCSTATUS_DCD_LATCHED) {
-                       if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) {
-                               info->ie1_value &= ~CDCD;
-                               write_reg(info, IE1, info->ie1_value);
-                       }
-                       icount->dcd++;
-                       if (status & SerialSignal_DCD) {
-                               info->input_signal_events.dcd_up++;
-                       } else
-                               info->input_signal_events.dcd_down++;
-#if SYNCLINK_GENERIC_HDLC
-                       if (info->netcount) {
-                               if (status & SerialSignal_DCD)
-                                       netif_carrier_on(info->netdev);
-                               else
-                                       netif_carrier_off(info->netdev);
-                       }
-#endif
-               }
-               if (status & MISCSTATUS_CTS_LATCHED)
-               {
-                       if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) {
-                               info->ie1_value &= ~CCTS;
-                               write_reg(info, IE1, info->ie1_value);
-                       }
-                       icount->cts++;
-                       if ( status & SerialSignal_CTS )
-                               info->input_signal_events.cts_up++;
-                       else
-                               info->input_signal_events.cts_down++;
-               }
-               wake_up_interruptible(&info->status_event_wait_q);
-               wake_up_interruptible(&info->event_wait_q);
-
-               if ( (info->port.flags & ASYNC_CHECK_CD) &&
-                    (status & MISCSTATUS_DCD_LATCHED) ) {
-                       if ( debug_level >= DEBUG_LEVEL_ISR )
-                               printk("%s CD now %s...", info->device_name,
-                                      (status & SerialSignal_DCD) ? "on" : "off");
-                       if (status & SerialSignal_DCD)
-                               wake_up_interruptible(&info->port.open_wait);
-                       else {
-                               if ( debug_level >= DEBUG_LEVEL_ISR )
-                                       printk("doing serial hangup...");
-                               if (info->port.tty)
-                                       tty_hangup(info->port.tty);
-                       }
-               }
-
-               if ( (info->port.flags & ASYNC_CTS_FLOW) &&
-                    (status & MISCSTATUS_CTS_LATCHED) ) {
-                       if ( info->port.tty ) {
-                               if (info->port.tty->hw_stopped) {
-                                       if (status & SerialSignal_CTS) {
-                                               if ( debug_level >= DEBUG_LEVEL_ISR )
-                                                       printk("CTS tx start...");
-                                               info->port.tty->hw_stopped = 0;
-                                               tx_start(info);
-                                               info->pending_bh |= BH_TRANSMIT;
-                                               return;
-                                       }
-                               } else {
-                                       if (!(status & SerialSignal_CTS)) {
-                                               if ( debug_level >= DEBUG_LEVEL_ISR )
-                                                       printk("CTS tx stop...");
-                                               info->port.tty->hw_stopped = 1;
-                                               tx_stop(info);
-                                       }
-                               }
-                       }
-               }
-       }
-
-       info->pending_bh |= BH_STATUS;
-}
-
-/* Interrupt service routine entry point.
- *
- * Arguments:
- *     irq             interrupt number that caused interrupt
- *     dev_id          device ID supplied during interrupt registration
- *     regs            interrupted processor context
- */
-static irqreturn_t synclinkmp_interrupt(int dummy, void *dev_id)
-{
-       SLMP_INFO *info = dev_id;
-       unsigned char status, status0, status1=0;
-       unsigned char dmastatus, dmastatus0, dmastatus1=0;
-       unsigned char timerstatus0, timerstatus1=0;
-       unsigned char shift;
-       unsigned int i;
-       unsigned short tmp;
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk(KERN_DEBUG "%s(%d): synclinkmp_interrupt(%d)entry.\n",
-                       __FILE__, __LINE__, info->irq_level);
-
-       spin_lock(&info->lock);
-
-       for(;;) {
-
-               /* get status for SCA0 (ports 0-1) */
-               tmp = read_reg16(info, ISR0);   /* get ISR0 and ISR1 in one read */
-               status0 = (unsigned char)tmp;
-               dmastatus0 = (unsigned char)(tmp>>8);
-               timerstatus0 = read_reg(info, ISR2);
-
-               if ( debug_level >= DEBUG_LEVEL_ISR )
-                       printk(KERN_DEBUG "%s(%d):%s status0=%02x, dmastatus0=%02x, timerstatus0=%02x\n",
-                               __FILE__, __LINE__, info->device_name,
-                               status0, dmastatus0, timerstatus0);
-
-               if (info->port_count == 4) {
-                       /* get status for SCA1 (ports 2-3) */
-                       tmp = read_reg16(info->port_array[2], ISR0);
-                       status1 = (unsigned char)tmp;
-                       dmastatus1 = (unsigned char)(tmp>>8);
-                       timerstatus1 = read_reg(info->port_array[2], ISR2);
-
-                       if ( debug_level >= DEBUG_LEVEL_ISR )
-                               printk("%s(%d):%s status1=%02x, dmastatus1=%02x, timerstatus1=%02x\n",
-                                       __FILE__,__LINE__,info->device_name,
-                                       status1,dmastatus1,timerstatus1);
-               }
-
-               if (!status0 && !dmastatus0 && !timerstatus0 &&
-                        !status1 && !dmastatus1 && !timerstatus1)
-                       break;
-
-               for(i=0; i < info->port_count ; i++) {
-                       if (info->port_array[i] == NULL)
-                               continue;
-                       if (i < 2) {
-                               status = status0;
-                               dmastatus = dmastatus0;
-                       } else {
-                               status = status1;
-                               dmastatus = dmastatus1;
-                       }
-
-                       shift = i & 1 ? 4 :0;
-
-                       if (status & BIT0 << shift)
-                               isr_rxrdy(info->port_array[i]);
-                       if (status & BIT1 << shift)
-                               isr_txrdy(info->port_array[i]);
-                       if (status & BIT2 << shift)
-                               isr_rxint(info->port_array[i]);
-                       if (status & BIT3 << shift)
-                               isr_txint(info->port_array[i]);
-
-                       if (dmastatus & BIT0 << shift)
-                               isr_rxdmaerror(info->port_array[i]);
-                       if (dmastatus & BIT1 << shift)
-                               isr_rxdmaok(info->port_array[i]);
-                       if (dmastatus & BIT2 << shift)
-                               isr_txdmaerror(info->port_array[i]);
-                       if (dmastatus & BIT3 << shift)
-                               isr_txdmaok(info->port_array[i]);
-               }
-
-               if (timerstatus0 & (BIT5 | BIT4))
-                       isr_timer(info->port_array[0]);
-               if (timerstatus0 & (BIT7 | BIT6))
-                       isr_timer(info->port_array[1]);
-               if (timerstatus1 & (BIT5 | BIT4))
-                       isr_timer(info->port_array[2]);
-               if (timerstatus1 & (BIT7 | BIT6))
-                       isr_timer(info->port_array[3]);
-       }
-
-       for(i=0; i < info->port_count ; i++) {
-               SLMP_INFO * port = info->port_array[i];
-
-               /* Request bottom half processing if there's something
-                * for it to do and the bh is not already running.
-                *
-                * Note: startup adapter diags require interrupts.
-                * do not request bottom half processing if the
-                * device is not open in a normal mode.
-                */
-               if ( port && (port->port.count || port->netcount) &&
-                    port->pending_bh && !port->bh_running &&
-                    !port->bh_requested ) {
-                       if ( debug_level >= DEBUG_LEVEL_ISR )
-                               printk("%s(%d):%s queueing bh task.\n",
-                                       __FILE__,__LINE__,port->device_name);
-                       schedule_work(&port->task);
-                       port->bh_requested = true;
-               }
-       }
-
-       spin_unlock(&info->lock);
-
-       if ( debug_level >= DEBUG_LEVEL_ISR )
-               printk(KERN_DEBUG "%s(%d):synclinkmp_interrupt(%d)exit.\n",
-                       __FILE__, __LINE__, info->irq_level);
-       return IRQ_HANDLED;
-}
-
-/* Initialize and start device.
- */
-static int startup(SLMP_INFO * info)
-{
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk("%s(%d):%s tx_releaseup()\n",__FILE__,__LINE__,info->device_name);
-
-       if (info->port.flags & ASYNC_INITIALIZED)
-               return 0;
-
-       if (!info->tx_buf) {
-               info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL);
-               if (!info->tx_buf) {
-                       printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n",
-                               __FILE__,__LINE__,info->device_name);
-                       return -ENOMEM;
-               }
-       }
-
-       info->pending_bh = 0;
-
-       memset(&info->icount, 0, sizeof(info->icount));
-
-       /* program hardware for current parameters */
-       reset_port(info);
-
-       change_params(info);
-
-       mod_timer(&info->status_timer, jiffies + msecs_to_jiffies(10));
-
-       if (info->port.tty)
-               clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
-
-       info->port.flags |= ASYNC_INITIALIZED;
-
-       return 0;
-}
-
-/* Called by close() and hangup() to shutdown hardware
- */
-static void shutdown(SLMP_INFO * info)
-{
-       unsigned long flags;
-
-       if (!(info->port.flags & ASYNC_INITIALIZED))
-               return;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s synclinkmp_shutdown()\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       /* clear status wait queue because status changes */
-       /* can't happen after shutting down the hardware */
-       wake_up_interruptible(&info->status_event_wait_q);
-       wake_up_interruptible(&info->event_wait_q);
-
-       del_timer(&info->tx_timer);
-       del_timer(&info->status_timer);
-
-       kfree(info->tx_buf);
-       info->tx_buf = NULL;
-
-       spin_lock_irqsave(&info->lock,flags);
-
-       reset_port(info);
-
-       if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) {
-               info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
-               set_signals(info);
-       }
-
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       if (info->port.tty)
-               set_bit(TTY_IO_ERROR, &info->port.tty->flags);
-
-       info->port.flags &= ~ASYNC_INITIALIZED;
-}
-
-static void program_hw(SLMP_INFO *info)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->lock,flags);
-
-       rx_stop(info);
-       tx_stop(info);
-
-       info->tx_count = info->tx_put = info->tx_get = 0;
-
-       if (info->params.mode == MGSL_MODE_HDLC || info->netcount)
-               hdlc_mode(info);
-       else
-               async_mode(info);
-
-       set_signals(info);
-
-       info->dcd_chkcount = 0;
-       info->cts_chkcount = 0;
-       info->ri_chkcount = 0;
-       info->dsr_chkcount = 0;
-
-       info->ie1_value |= (CDCD|CCTS);
-       write_reg(info, IE1, info->ie1_value);
-
-       get_signals(info);
-
-       if (info->netcount || (info->port.tty && info->port.tty->termios->c_cflag & CREAD) )
-               rx_start(info);
-
-       spin_unlock_irqrestore(&info->lock,flags);
-}
-
-/* Reconfigure adapter based on new parameters
- */
-static void change_params(SLMP_INFO *info)
-{
-       unsigned cflag;
-       int bits_per_char;
-
-       if (!info->port.tty || !info->port.tty->termios)
-               return;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s change_params()\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       cflag = info->port.tty->termios->c_cflag;
-
-       /* if B0 rate (hangup) specified then negate DTR and RTS */
-       /* otherwise assert DTR and RTS */
-       if (cflag & CBAUD)
-               info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
-       else
-               info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
-
-       /* byte size and parity */
-
-       switch (cflag & CSIZE) {
-             case CS5: info->params.data_bits = 5; break;
-             case CS6: info->params.data_bits = 6; break;
-             case CS7: info->params.data_bits = 7; break;
-             case CS8: info->params.data_bits = 8; break;
-             /* Never happens, but GCC is too dumb to figure it out */
-             default:  info->params.data_bits = 7; break;
-             }
-
-       if (cflag & CSTOPB)
-               info->params.stop_bits = 2;
-       else
-               info->params.stop_bits = 1;
-
-       info->params.parity = ASYNC_PARITY_NONE;
-       if (cflag & PARENB) {
-               if (cflag & PARODD)
-                       info->params.parity = ASYNC_PARITY_ODD;
-               else
-                       info->params.parity = ASYNC_PARITY_EVEN;
-#ifdef CMSPAR
-               if (cflag & CMSPAR)
-                       info->params.parity = ASYNC_PARITY_SPACE;
-#endif
-       }
-
-       /* calculate number of jiffies to transmit a full
-        * FIFO (32 bytes) at specified data rate
-        */
-       bits_per_char = info->params.data_bits +
-                       info->params.stop_bits + 1;
-
-       /* if port data rate is set to 460800 or less then
-        * allow tty settings to override, otherwise keep the
-        * current data rate.
-        */
-       if (info->params.data_rate <= 460800) {
-               info->params.data_rate = tty_get_baud_rate(info->port.tty);
-       }
-
-       if ( info->params.data_rate ) {
-               info->timeout = (32*HZ*bits_per_char) /
-                               info->params.data_rate;
-       }
-       info->timeout += HZ/50;         /* Add .02 seconds of slop */
-
-       if (cflag & CRTSCTS)
-               info->port.flags |= ASYNC_CTS_FLOW;
-       else
-               info->port.flags &= ~ASYNC_CTS_FLOW;
-
-       if (cflag & CLOCAL)
-               info->port.flags &= ~ASYNC_CHECK_CD;
-       else
-               info->port.flags |= ASYNC_CHECK_CD;
-
-       /* process tty input control flags */
-
-       info->read_status_mask2 = OVRN;
-       if (I_INPCK(info->port.tty))
-               info->read_status_mask2 |= PE | FRME;
-       if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty))
-               info->read_status_mask1 |= BRKD;
-       if (I_IGNPAR(info->port.tty))
-               info->ignore_status_mask2 |= PE | FRME;
-       if (I_IGNBRK(info->port.tty)) {
-               info->ignore_status_mask1 |= BRKD;
-               /* If ignoring parity and break indicators, ignore
-                * overruns too.  (For real raw support).
-                */
-               if (I_IGNPAR(info->port.tty))
-                       info->ignore_status_mask2 |= OVRN;
-       }
-
-       program_hw(info);
-}
-
-static int get_stats(SLMP_INFO * info, struct mgsl_icount __user *user_icount)
-{
-       int err;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s get_params()\n",
-                        __FILE__,__LINE__, info->device_name);
-
-       if (!user_icount) {
-               memset(&info->icount, 0, sizeof(info->icount));
-       } else {
-               mutex_lock(&info->port.mutex);
-               COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
-               mutex_unlock(&info->port.mutex);
-               if (err)
-                       return -EFAULT;
-       }
-
-       return 0;
-}
-
-static int get_params(SLMP_INFO * info, MGSL_PARAMS __user *user_params)
-{
-       int err;
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s get_params()\n",
-                        __FILE__,__LINE__, info->device_name);
-
-       mutex_lock(&info->port.mutex);
-       COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
-       mutex_unlock(&info->port.mutex);
-       if (err) {
-               if ( debug_level >= DEBUG_LEVEL_INFO )
-                       printk( "%s(%d):%s get_params() user buffer copy failed\n",
-                               __FILE__,__LINE__,info->device_name);
-               return -EFAULT;
-       }
-
-       return 0;
-}
-
-static int set_params(SLMP_INFO * info, MGSL_PARAMS __user *new_params)
-{
-       unsigned long flags;
-       MGSL_PARAMS tmp_params;
-       int err;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s set_params\n",
-                       __FILE__,__LINE__,info->device_name );
-       COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS));
-       if (err) {
-               if ( debug_level >= DEBUG_LEVEL_INFO )
-                       printk( "%s(%d):%s set_params() user buffer copy failed\n",
-                               __FILE__,__LINE__,info->device_name);
-               return -EFAULT;
-       }
-
-       mutex_lock(&info->port.mutex);
-       spin_lock_irqsave(&info->lock,flags);
-       memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       change_params(info);
-       mutex_unlock(&info->port.mutex);
-
-       return 0;
-}
-
-static int get_txidle(SLMP_INFO * info, int __user *idle_mode)
-{
-       int err;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s get_txidle()=%d\n",
-                        __FILE__,__LINE__, info->device_name, info->idle_mode);
-
-       COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int));
-       if (err) {
-               if ( debug_level >= DEBUG_LEVEL_INFO )
-                       printk( "%s(%d):%s get_txidle() user buffer copy failed\n",
-                               __FILE__,__LINE__,info->device_name);
-               return -EFAULT;
-       }
-
-       return 0;
-}
-
-static int set_txidle(SLMP_INFO * info, int idle_mode)
-{
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s set_txidle(%d)\n",
-                       __FILE__,__LINE__,info->device_name, idle_mode );
-
-       spin_lock_irqsave(&info->lock,flags);
-       info->idle_mode = idle_mode;
-       tx_set_idle( info );
-       spin_unlock_irqrestore(&info->lock,flags);
-       return 0;
-}
-
-static int tx_enable(SLMP_INFO * info, int enable)
-{
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s tx_enable(%d)\n",
-                       __FILE__,__LINE__,info->device_name, enable);
-
-       spin_lock_irqsave(&info->lock,flags);
-       if ( enable ) {
-               if ( !info->tx_enabled ) {
-                       tx_start(info);
-               }
-       } else {
-               if ( info->tx_enabled )
-                       tx_stop(info);
-       }
-       spin_unlock_irqrestore(&info->lock,flags);
-       return 0;
-}
-
-/* abort send HDLC frame
- */
-static int tx_abort(SLMP_INFO * info)
-{
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s tx_abort()\n",
-                       __FILE__,__LINE__,info->device_name);
-
-       spin_lock_irqsave(&info->lock,flags);
-       if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC ) {
-               info->ie1_value &= ~UDRN;
-               info->ie1_value |= IDLE;
-               write_reg(info, IE1, info->ie1_value);  /* disable tx status interrupts */
-               write_reg(info, SR1, (unsigned char)(IDLE + UDRN));     /* clear pending */
-
-               write_reg(info, TXDMA + DSR, 0);                /* disable DMA channel */
-               write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */
-
-               write_reg(info, CMD, TXABORT);
-       }
-       spin_unlock_irqrestore(&info->lock,flags);
-       return 0;
-}
-
-static int rx_enable(SLMP_INFO * info, int enable)
-{
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s rx_enable(%d)\n",
-                       __FILE__,__LINE__,info->device_name,enable);
-
-       spin_lock_irqsave(&info->lock,flags);
-       if ( enable ) {
-               if ( !info->rx_enabled )
-                       rx_start(info);
-       } else {
-               if ( info->rx_enabled )
-                       rx_stop(info);
-       }
-       spin_unlock_irqrestore(&info->lock,flags);
-       return 0;
-}
-
-/* wait for specified event to occur
- */
-static int wait_mgsl_event(SLMP_INFO * info, int __user *mask_ptr)
-{
-       unsigned long flags;
-       int s;
-       int rc=0;
-       struct mgsl_icount cprev, cnow;
-       int events;
-       int mask;
-       struct  _input_signal_events oldsigs, newsigs;
-       DECLARE_WAITQUEUE(wait, current);
-
-       COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int));
-       if (rc) {
-               return  -EFAULT;
-       }
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s wait_mgsl_event(%d)\n",
-                       __FILE__,__LINE__,info->device_name,mask);
-
-       spin_lock_irqsave(&info->lock,flags);
-
-       /* return immediately if state matches requested events */
-       get_signals(info);
-       s = info->serial_signals;
-
-       events = mask &
-               ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
-                 ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
-                 ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) +
-                 ((s & SerialSignal_RI)  ? MgslEvent_RiActive :MgslEvent_RiInactive) );
-       if (events) {
-               spin_unlock_irqrestore(&info->lock,flags);
-               goto exit;
-       }
-
-       /* save current irq counts */
-       cprev = info->icount;
-       oldsigs = info->input_signal_events;
-
-       /* enable hunt and idle irqs if needed */
-       if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) {
-               unsigned char oldval = info->ie1_value;
-               unsigned char newval = oldval +
-                        (mask & MgslEvent_ExitHuntMode ? FLGD:0) +
-                        (mask & MgslEvent_IdleReceived ? IDLD:0);
-               if ( oldval != newval ) {
-                       info->ie1_value = newval;
-                       write_reg(info, IE1, info->ie1_value);
-               }
-       }
-
-       set_current_state(TASK_INTERRUPTIBLE);
-       add_wait_queue(&info->event_wait_q, &wait);
-
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       for(;;) {
-               schedule();
-               if (signal_pending(current)) {
-                       rc = -ERESTARTSYS;
-                       break;
-               }
-
-               /* get current irq counts */
-               spin_lock_irqsave(&info->lock,flags);
-               cnow = info->icount;
-               newsigs = info->input_signal_events;
-               set_current_state(TASK_INTERRUPTIBLE);
-               spin_unlock_irqrestore(&info->lock,flags);
-
-               /* if no change, wait aborted for some reason */
-               if (newsigs.dsr_up   == oldsigs.dsr_up   &&
-                   newsigs.dsr_down == oldsigs.dsr_down &&
-                   newsigs.dcd_up   == oldsigs.dcd_up   &&
-                   newsigs.dcd_down == oldsigs.dcd_down &&
-                   newsigs.cts_up   == oldsigs.cts_up   &&
-                   newsigs.cts_down == oldsigs.cts_down &&
-                   newsigs.ri_up    == oldsigs.ri_up    &&
-                   newsigs.ri_down  == oldsigs.ri_down  &&
-                   cnow.exithunt    == cprev.exithunt   &&
-                   cnow.rxidle      == cprev.rxidle) {
-                       rc = -EIO;
-                       break;
-               }
-
-               events = mask &
-                       ( (newsigs.dsr_up   != oldsigs.dsr_up   ? MgslEvent_DsrActive:0)   +
-                         (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) +
-                         (newsigs.dcd_up   != oldsigs.dcd_up   ? MgslEvent_DcdActive:0)   +
-                         (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) +
-                         (newsigs.cts_up   != oldsigs.cts_up   ? MgslEvent_CtsActive:0)   +
-                         (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) +
-                         (newsigs.ri_up    != oldsigs.ri_up    ? MgslEvent_RiActive:0)    +
-                         (newsigs.ri_down  != oldsigs.ri_down  ? MgslEvent_RiInactive:0)  +
-                         (cnow.exithunt    != cprev.exithunt   ? MgslEvent_ExitHuntMode:0) +
-                         (cnow.rxidle      != cprev.rxidle     ? MgslEvent_IdleReceived:0) );
-               if (events)
-                       break;
-
-               cprev = cnow;
-               oldsigs = newsigs;
-       }
-
-       remove_wait_queue(&info->event_wait_q, &wait);
-       set_current_state(TASK_RUNNING);
-
-
-       if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
-               spin_lock_irqsave(&info->lock,flags);
-               if (!waitqueue_active(&info->event_wait_q)) {
-                       /* disable enable exit hunt mode/idle rcvd IRQs */
-                       info->ie1_value &= ~(FLGD|IDLD);
-                       write_reg(info, IE1, info->ie1_value);
-               }
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
-exit:
-       if ( rc == 0 )
-               PUT_USER(rc, events, mask_ptr);
-
-       return rc;
-}
-
-static int modem_input_wait(SLMP_INFO *info,int arg)
-{
-       unsigned long flags;
-       int rc;
-       struct mgsl_icount cprev, cnow;
-       DECLARE_WAITQUEUE(wait, current);
-
-       /* save current irq counts */
-       spin_lock_irqsave(&info->lock,flags);
-       cprev = info->icount;
-       add_wait_queue(&info->status_event_wait_q, &wait);
-       set_current_state(TASK_INTERRUPTIBLE);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       for(;;) {
-               schedule();
-               if (signal_pending(current)) {
-                       rc = -ERESTARTSYS;
-                       break;
-               }
-
-               /* get new irq counts */
-               spin_lock_irqsave(&info->lock,flags);
-               cnow = info->icount;
-               set_current_state(TASK_INTERRUPTIBLE);
-               spin_unlock_irqrestore(&info->lock,flags);
-
-               /* if no change, wait aborted for some reason */
-               if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
-                   cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
-                       rc = -EIO;
-                       break;
-               }
-
-               /* check for change in caller specified modem input */
-               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)) {
-                       rc = 0;
-                       break;
-               }
-
-               cprev = cnow;
-       }
-       remove_wait_queue(&info->status_event_wait_q, &wait);
-       set_current_state(TASK_RUNNING);
-       return rc;
-}
-
-/* return the state of the serial control and status signals
- */
-static int tiocmget(struct tty_struct *tty)
-{
-       SLMP_INFO *info = tty->driver_data;
-       unsigned int result;
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->lock,flags);
-       get_signals(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) +
-               ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) +
-               ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) +
-               ((info->serial_signals & SerialSignal_RI)  ? TIOCM_RNG:0) +
-               ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) +
-               ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0);
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s tiocmget() value=%08X\n",
-                        __FILE__,__LINE__, info->device_name, result );
-       return result;
-}
-
-/* set modem control signals (DTR/RTS)
- */
-static int tiocmset(struct tty_struct *tty,
-                                       unsigned int set, unsigned int clear)
-{
-       SLMP_INFO *info = tty->driver_data;
-       unsigned long flags;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s tiocmset(%x,%x)\n",
-                       __FILE__,__LINE__,info->device_name, set, clear);
-
-       if (set & TIOCM_RTS)
-               info->serial_signals |= SerialSignal_RTS;
-       if (set & TIOCM_DTR)
-               info->serial_signals |= SerialSignal_DTR;
-       if (clear & TIOCM_RTS)
-               info->serial_signals &= ~SerialSignal_RTS;
-       if (clear & TIOCM_DTR)
-               info->serial_signals &= ~SerialSignal_DTR;
-
-       spin_lock_irqsave(&info->lock,flags);
-       set_signals(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       return 0;
-}
-
-static int carrier_raised(struct tty_port *port)
-{
-       SLMP_INFO *info = container_of(port, SLMP_INFO, port);
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->lock,flags);
-       get_signals(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       return (info->serial_signals & SerialSignal_DCD) ? 1 : 0;
-}
-
-static void dtr_rts(struct tty_port *port, int on)
-{
-       SLMP_INFO *info = container_of(port, SLMP_INFO, port);
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->lock,flags);
-       if (on)
-               info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
-       else
-               info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
-       set_signals(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-}
-
-/* Block the current process until the specified port is ready to open.
- */
-static int block_til_ready(struct tty_struct *tty, struct file *filp,
-                          SLMP_INFO *info)
-{
-       DECLARE_WAITQUEUE(wait, current);
-       int             retval;
-       bool            do_clocal = false;
-       bool            extra_count = false;
-       unsigned long   flags;
-       int             cd;
-       struct tty_port *port = &info->port;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s block_til_ready()\n",
-                        __FILE__,__LINE__, tty->driver->name );
-
-       if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){
-               /* nonblock mode is set or port is not enabled */
-               /* just verify that callout device is not active */
-               port->flags |= ASYNC_NORMAL_ACTIVE;
-               return 0;
-       }
-
-       if (tty->termios->c_cflag & CLOCAL)
-               do_clocal = true;
-
-       /* Wait for carrier detect and the line to become
-        * free (i.e., not in use by the callout).  While we are in
-        * this loop, port->count is dropped by one, so that
-        * close() knows when to free things.  We restore it upon
-        * exit, either normal or abnormal.
-        */
-
-       retval = 0;
-       add_wait_queue(&port->open_wait, &wait);
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s block_til_ready() before block, count=%d\n",
-                        __FILE__,__LINE__, tty->driver->name, port->count );
-
-       spin_lock_irqsave(&info->lock, flags);
-       if (!tty_hung_up_p(filp)) {
-               extra_count = true;
-               port->count--;
-       }
-       spin_unlock_irqrestore(&info->lock, flags);
-       port->blocked_open++;
-
-       while (1) {
-               if (tty->termios->c_cflag & CBAUD)
-                       tty_port_raise_dtr_rts(port);
-
-               set_current_state(TASK_INTERRUPTIBLE);
-
-               if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){
-                       retval = (port->flags & ASYNC_HUP_NOTIFY) ?
-                                       -EAGAIN : -ERESTARTSYS;
-                       break;
-               }
-
-               cd = tty_port_carrier_raised(port);
-
-               if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd))
-                       break;
-
-               if (signal_pending(current)) {
-                       retval = -ERESTARTSYS;
-                       break;
-               }
-
-               if (debug_level >= DEBUG_LEVEL_INFO)
-                       printk("%s(%d):%s block_til_ready() count=%d\n",
-                                __FILE__,__LINE__, tty->driver->name, port->count );
-
-               tty_unlock();
-               schedule();
-               tty_lock();
-       }
-
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&port->open_wait, &wait);
-
-       if (extra_count)
-               port->count++;
-       port->blocked_open--;
-
-       if (debug_level >= DEBUG_LEVEL_INFO)
-               printk("%s(%d):%s block_til_ready() after, count=%d\n",
-                        __FILE__,__LINE__, tty->driver->name, port->count );
-
-       if (!retval)
-               port->flags |= ASYNC_NORMAL_ACTIVE;
-
-       return retval;
-}
-
-static int alloc_dma_bufs(SLMP_INFO *info)
-{
-       unsigned short BuffersPerFrame;
-       unsigned short BufferCount;
-
-       // Force allocation to start at 64K boundary for each port.
-       // This is necessary because *all* buffer descriptors for a port
-       // *must* be in the same 64K block. All descriptors on a port
-       // share a common 'base' address (upper 8 bits of 24 bits) programmed
-       // into the CBP register.
-       info->port_array[0]->last_mem_alloc = (SCA_MEM_SIZE/4) * info->port_num;
-
-       /* Calculate the number of DMA buffers necessary to hold the */
-       /* largest allowable frame size. Note: If the max frame size is */
-       /* not an even multiple of the DMA buffer size then we need to */
-       /* round the buffer count per frame up one. */
-
-       BuffersPerFrame = (unsigned short)(info->max_frame_size/SCABUFSIZE);
-       if ( info->max_frame_size % SCABUFSIZE )
-               BuffersPerFrame++;
-
-       /* calculate total number of data buffers (SCABUFSIZE) possible
-        * in one ports memory (SCA_MEM_SIZE/4) after allocating memory
-        * for the descriptor list (BUFFERLISTSIZE).
-        */
-       BufferCount = (SCA_MEM_SIZE/4 - BUFFERLISTSIZE)/SCABUFSIZE;
-
-       /* limit number of buffers to maximum amount of descriptors */
-       if (BufferCount > BUFFERLISTSIZE/sizeof(SCADESC))
-               BufferCount = BUFFERLISTSIZE/sizeof(SCADESC);
-
-       /* use enough buffers to transmit one max size frame */
-       info->tx_buf_count = BuffersPerFrame + 1;
-
-       /* never use more than half the available buffers for transmit */
-       if (info->tx_buf_count > (BufferCount/2))
-               info->tx_buf_count = BufferCount/2;
-
-       if (info->tx_buf_count > SCAMAXDESC)
-               info->tx_buf_count = SCAMAXDESC;
-
-       /* use remaining buffers for receive */
-       info->rx_buf_count = BufferCount - info->tx_buf_count;
-
-       if (info->rx_buf_count > SCAMAXDESC)
-               info->rx_buf_count = SCAMAXDESC;
-
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk("%s(%d):%s Allocating %d TX and %d RX DMA buffers.\n",
-                       __FILE__,__LINE__, info->device_name,
-                       info->tx_buf_count,info->rx_buf_count);
-
-       if ( alloc_buf_list( info ) < 0 ||
-               alloc_frame_bufs(info,
-                                       info->rx_buf_list,
-                                       info->rx_buf_list_ex,
-                                       info->rx_buf_count) < 0 ||
-               alloc_frame_bufs(info,
-                                       info->tx_buf_list,
-                                       info->tx_buf_list_ex,
-                                       info->tx_buf_count) < 0 ||
-               alloc_tmp_rx_buf(info) < 0 ) {
-               printk("%s(%d):%s Can't allocate DMA buffer memory\n",
-                       __FILE__,__LINE__, info->device_name);
-               return -ENOMEM;
-       }
-
-       rx_reset_buffers( info );
-
-       return 0;
-}
-
-/* Allocate DMA buffers for the transmit and receive descriptor lists.
- */
-static int alloc_buf_list(SLMP_INFO *info)
-{
-       unsigned int i;
-
-       /* build list in adapter shared memory */
-       info->buffer_list = info->memory_base + info->port_array[0]->last_mem_alloc;
-       info->buffer_list_phys = info->port_array[0]->last_mem_alloc;
-       info->port_array[0]->last_mem_alloc += BUFFERLISTSIZE;
-
-       memset(info->buffer_list, 0, BUFFERLISTSIZE);
-
-       /* Save virtual address pointers to the receive and */
-       /* transmit buffer lists. (Receive 1st). These pointers will */
-       /* be used by the processor to access the lists. */
-       info->rx_buf_list = (SCADESC *)info->buffer_list;
-
-       info->tx_buf_list = (SCADESC *)info->buffer_list;
-       info->tx_buf_list += info->rx_buf_count;
-
-       /* Build links for circular buffer entry lists (tx and rx)
-        *
-        * Note: links are physical addresses read by the SCA device
-        * to determine the next buffer entry to use.
-        */
-
-       for ( i = 0; i < info->rx_buf_count; i++ ) {
-               /* calculate and store physical address of this buffer entry */
-               info->rx_buf_list_ex[i].phys_entry =
-                       info->buffer_list_phys + (i * sizeof(SCABUFSIZE));
-
-               /* calculate and store physical address of */
-               /* next entry in cirular list of entries */
-               info->rx_buf_list[i].next = info->buffer_list_phys;
-               if ( i < info->rx_buf_count - 1 )
-                       info->rx_buf_list[i].next += (i + 1) * sizeof(SCADESC);
-
-               info->rx_buf_list[i].length = SCABUFSIZE;
-       }
-
-       for ( i = 0; i < info->tx_buf_count; i++ ) {
-               /* calculate and store physical address of this buffer entry */
-               info->tx_buf_list_ex[i].phys_entry = info->buffer_list_phys +
-                       ((info->rx_buf_count + i) * sizeof(SCADESC));
-
-               /* calculate and store physical address of */
-               /* next entry in cirular list of entries */
-
-               info->tx_buf_list[i].next = info->buffer_list_phys +
-                       info->rx_buf_count * sizeof(SCADESC);
-
-               if ( i < info->tx_buf_count - 1 )
-                       info->tx_buf_list[i].next += (i + 1) * sizeof(SCADESC);
-       }
-
-       return 0;
-}
-
-/* Allocate the frame DMA buffers used by the specified buffer list.
- */
-static int alloc_frame_bufs(SLMP_INFO *info, SCADESC *buf_list,SCADESC_EX *buf_list_ex,int count)
-{
-       int i;
-       unsigned long phys_addr;
-
-       for ( i = 0; i < count; i++ ) {
-               buf_list_ex[i].virt_addr = info->memory_base + info->port_array[0]->last_mem_alloc;
-               phys_addr = info->port_array[0]->last_mem_alloc;
-               info->port_array[0]->last_mem_alloc += SCABUFSIZE;
-
-               buf_list[i].buf_ptr  = (unsigned short)phys_addr;
-               buf_list[i].buf_base = (unsigned char)(phys_addr >> 16);
-       }
-
-       return 0;
-}
-
-static void free_dma_bufs(SLMP_INFO *info)
-{
-       info->buffer_list = NULL;
-       info->rx_buf_list = NULL;
-       info->tx_buf_list = NULL;
-}
-
-/* allocate buffer large enough to hold max_frame_size.
- * This buffer is used to pass an assembled frame to the line discipline.
- */
-static int alloc_tmp_rx_buf(SLMP_INFO *info)
-{
-       info->tmp_rx_buf = kmalloc(info->max_frame_size, GFP_KERNEL);
-       if (info->tmp_rx_buf == NULL)
-               return -ENOMEM;
-       return 0;
-}
-
-static void free_tmp_rx_buf(SLMP_INFO *info)
-{
-       kfree(info->tmp_rx_buf);
-       info->tmp_rx_buf = NULL;
-}
-
-static int claim_resources(SLMP_INFO *info)
-{
-       if (request_mem_region(info->phys_memory_base,SCA_MEM_SIZE,"synclinkmp") == NULL) {
-               printk( "%s(%d):%s mem addr conflict, Addr=%08X\n",
-                       __FILE__,__LINE__,info->device_name, info->phys_memory_base);
-               info->init_error = DiagStatus_AddressConflict;
-               goto errout;
-       }
-       else
-               info->shared_mem_requested = true;
-
-       if (request_mem_region(info->phys_lcr_base + info->lcr_offset,128,"synclinkmp") == NULL) {
-               printk( "%s(%d):%s lcr mem addr conflict, Addr=%08X\n",
-                       __FILE__,__LINE__,info->device_name, info->phys_lcr_base);
-               info->init_error = DiagStatus_AddressConflict;
-               goto errout;
-       }
-       else
-               info->lcr_mem_requested = true;
-
-       if (request_mem_region(info->phys_sca_base + info->sca_offset,SCA_BASE_SIZE,"synclinkmp") == NULL) {
-               printk( "%s(%d):%s sca mem addr conflict, Addr=%08X\n",
-                       __FILE__,__LINE__,info->device_name, info->phys_sca_base);
-               info->init_error = DiagStatus_AddressConflict;
-               goto errout;
-       }
-       else
-               info->sca_base_requested = true;
-
-       if (request_mem_region(info->phys_statctrl_base + info->statctrl_offset,SCA_REG_SIZE,"synclinkmp") == NULL) {
-               printk( "%s(%d):%s stat/ctrl mem addr conflict, Addr=%08X\n",
-                       __FILE__,__LINE__,info->device_name, info->phys_statctrl_base);
-               info->init_error = DiagStatus_AddressConflict;
-               goto errout;
-       }
-       else
-               info->sca_statctrl_requested = true;
-
-       info->memory_base = ioremap_nocache(info->phys_memory_base,
-                                                               SCA_MEM_SIZE);
-       if (!info->memory_base) {
-               printk( "%s(%d):%s Cant map shared memory, MemAddr=%08X\n",
-                       __FILE__,__LINE__,info->device_name, info->phys_memory_base );
-               info->init_error = DiagStatus_CantAssignPciResources;
-               goto errout;
-       }
-
-       info->lcr_base = ioremap_nocache(info->phys_lcr_base, PAGE_SIZE);
-       if (!info->lcr_base) {
-               printk( "%s(%d):%s Cant map LCR memory, MemAddr=%08X\n",
-                       __FILE__,__LINE__,info->device_name, info->phys_lcr_base );
-               info->init_error = DiagStatus_CantAssignPciResources;
-               goto errout;
-       }
-       info->lcr_base += info->lcr_offset;
-
-       info->sca_base = ioremap_nocache(info->phys_sca_base, PAGE_SIZE);
-       if (!info->sca_base) {
-               printk( "%s(%d):%s Cant map SCA memory, MemAddr=%08X\n",
-                       __FILE__,__LINE__,info->device_name, info->phys_sca_base );
-               info->init_error = DiagStatus_CantAssignPciResources;
-               goto errout;
-       }
-       info->sca_base += info->sca_offset;
-
-       info->statctrl_base = ioremap_nocache(info->phys_statctrl_base,
-                                                               PAGE_SIZE);
-       if (!info->statctrl_base) {
-               printk( "%s(%d):%s Cant map SCA Status/Control memory, MemAddr=%08X\n",
-                       __FILE__,__LINE__,info->device_name, info->phys_statctrl_base );
-               info->init_error = DiagStatus_CantAssignPciResources;
-               goto errout;
-       }
-       info->statctrl_base += info->statctrl_offset;
-
-       if ( !memory_test(info) ) {
-               printk( "%s(%d):Shared Memory Test failed for device %s MemAddr=%08X\n",
-                       __FILE__,__LINE__,info->device_name, info->phys_memory_base );
-               info->init_error = DiagStatus_MemoryError;
-               goto errout;
-       }
-
-       return 0;
-
-errout:
-       release_resources( info );
-       return -ENODEV;
-}
-
-static void release_resources(SLMP_INFO *info)
-{
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):%s release_resources() entry\n",
-                       __FILE__,__LINE__,info->device_name );
-
-       if ( info->irq_requested ) {
-               free_irq(info->irq_level, info);
-               info->irq_requested = false;
-       }
-
-       if ( info->shared_mem_requested ) {
-               release_mem_region(info->phys_memory_base,SCA_MEM_SIZE);
-               info->shared_mem_requested = false;
-       }
-       if ( info->lcr_mem_requested ) {
-               release_mem_region(info->phys_lcr_base + info->lcr_offset,128);
-               info->lcr_mem_requested = false;
-       }
-       if ( info->sca_base_requested ) {
-               release_mem_region(info->phys_sca_base + info->sca_offset,SCA_BASE_SIZE);
-               info->sca_base_requested = false;
-       }
-       if ( info->sca_statctrl_requested ) {
-               release_mem_region(info->phys_statctrl_base + info->statctrl_offset,SCA_REG_SIZE);
-               info->sca_statctrl_requested = false;
-       }
-
-       if (info->memory_base){
-               iounmap(info->memory_base);
-               info->memory_base = NULL;
-       }
-
-       if (info->sca_base) {
-               iounmap(info->sca_base - info->sca_offset);
-               info->sca_base=NULL;
-       }
-
-       if (info->statctrl_base) {
-               iounmap(info->statctrl_base - info->statctrl_offset);
-               info->statctrl_base=NULL;
-       }
-
-       if (info->lcr_base){
-               iounmap(info->lcr_base - info->lcr_offset);
-               info->lcr_base = NULL;
-       }
-
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):%s release_resources() exit\n",
-                       __FILE__,__LINE__,info->device_name );
-}
-
-/* Add the specified device instance data structure to the
- * global linked list of devices and increment the device count.
- */
-static void add_device(SLMP_INFO *info)
-{
-       info->next_device = NULL;
-       info->line = synclinkmp_device_count;
-       sprintf(info->device_name,"ttySLM%dp%d",info->adapter_num,info->port_num);
-
-       if (info->line < MAX_DEVICES) {
-               if (maxframe[info->line])
-                       info->max_frame_size = maxframe[info->line];
-       }
-
-       synclinkmp_device_count++;
-
-       if ( !synclinkmp_device_list )
-               synclinkmp_device_list = info;
-       else {
-               SLMP_INFO *current_dev = synclinkmp_device_list;
-               while( current_dev->next_device )
-                       current_dev = current_dev->next_device;
-               current_dev->next_device = info;
-       }
-
-       if ( info->max_frame_size < 4096 )
-               info->max_frame_size = 4096;
-       else if ( info->max_frame_size > 65535 )
-               info->max_frame_size = 65535;
-
-       printk( "SyncLink MultiPort %s: "
-               "Mem=(%08x %08X %08x %08X) IRQ=%d MaxFrameSize=%u\n",
-               info->device_name,
-               info->phys_sca_base,
-               info->phys_memory_base,
-               info->phys_statctrl_base,
-               info->phys_lcr_base,
-               info->irq_level,
-               info->max_frame_size );
-
-#if SYNCLINK_GENERIC_HDLC
-       hdlcdev_init(info);
-#endif
-}
-
-static const struct tty_port_operations port_ops = {
-       .carrier_raised = carrier_raised,
-       .dtr_rts = dtr_rts,
-};
-
-/* Allocate and initialize a device instance structure
- *
- * Return Value:       pointer to SLMP_INFO if success, otherwise NULL
- */
-static SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev)
-{
-       SLMP_INFO *info;
-
-       info = kzalloc(sizeof(SLMP_INFO),
-                GFP_KERNEL);
-
-       if (!info) {
-               printk("%s(%d) Error can't allocate device instance data for adapter %d, port %d\n",
-                       __FILE__,__LINE__, adapter_num, port_num);
-       } else {
-               tty_port_init(&info->port);
-               info->port.ops = &port_ops;
-               info->magic = MGSL_MAGIC;
-               INIT_WORK(&info->task, bh_handler);
-               info->max_frame_size = 4096;
-               info->port.close_delay = 5*HZ/10;
-               info->port.closing_wait = 30*HZ;
-               init_waitqueue_head(&info->status_event_wait_q);
-               init_waitqueue_head(&info->event_wait_q);
-               spin_lock_init(&info->netlock);
-               memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
-               info->idle_mode = HDLC_TXIDLE_FLAGS;
-               info->adapter_num = adapter_num;
-               info->port_num = port_num;
-
-               /* Copy configuration info to device instance data */
-               info->irq_level = pdev->irq;
-               info->phys_lcr_base = pci_resource_start(pdev,0);
-               info->phys_sca_base = pci_resource_start(pdev,2);
-               info->phys_memory_base = pci_resource_start(pdev,3);
-               info->phys_statctrl_base = pci_resource_start(pdev,4);
-
-               /* Because veremap only works on page boundaries we must map
-                * a larger area than is actually implemented for the LCR
-                * memory range. We map a full page starting at the page boundary.
-                */
-               info->lcr_offset    = info->phys_lcr_base & (PAGE_SIZE-1);
-               info->phys_lcr_base &= ~(PAGE_SIZE-1);
-
-               info->sca_offset    = info->phys_sca_base & (PAGE_SIZE-1);
-               info->phys_sca_base &= ~(PAGE_SIZE-1);
-
-               info->statctrl_offset    = info->phys_statctrl_base & (PAGE_SIZE-1);
-               info->phys_statctrl_base &= ~(PAGE_SIZE-1);
-
-               info->bus_type = MGSL_BUS_TYPE_PCI;
-               info->irq_flags = IRQF_SHARED;
-
-               setup_timer(&info->tx_timer, tx_timeout, (unsigned long)info);
-               setup_timer(&info->status_timer, status_timeout,
-                               (unsigned long)info);
-
-               /* Store the PCI9050 misc control register value because a flaw
-                * in the PCI9050 prevents LCR registers from being read if
-                * BIOS assigns an LCR base address with bit 7 set.
-                *
-                * Only the misc control register is accessed for which only
-                * write access is needed, so set an initial value and change
-                * bits to the device instance data as we write the value
-                * to the actual misc control register.
-                */
-               info->misc_ctrl_value = 0x087e4546;
-
-               /* initial port state is unknown - if startup errors
-                * occur, init_error will be set to indicate the
-                * problem. Once the port is fully initialized,
-                * this value will be set to 0 to indicate the
-                * port is available.
-                */
-               info->init_error = -1;
-       }
-
-       return info;
-}
-
-static void device_init(int adapter_num, struct pci_dev *pdev)
-{
-       SLMP_INFO *port_array[SCA_MAX_PORTS];
-       int port;
-
-       /* allocate device instances for up to SCA_MAX_PORTS devices */
-       for ( port = 0; port < SCA_MAX_PORTS; ++port ) {
-               port_array[port] = alloc_dev(adapter_num,port,pdev);
-               if( port_array[port] == NULL ) {
-                       for ( --port; port >= 0; --port )
-                               kfree(port_array[port]);
-                       return;
-               }
-       }
-
-       /* give copy of port_array to all ports and add to device list  */
-       for ( port = 0; port < SCA_MAX_PORTS; ++port ) {
-               memcpy(port_array[port]->port_array,port_array,sizeof(port_array));
-               add_device( port_array[port] );
-               spin_lock_init(&port_array[port]->lock);
-       }
-
-       /* Allocate and claim adapter resources */
-       if ( !claim_resources(port_array[0]) ) {
-
-               alloc_dma_bufs(port_array[0]);
-
-               /* copy resource information from first port to others */
-               for ( port = 1; port < SCA_MAX_PORTS; ++port ) {
-                       port_array[port]->lock  = port_array[0]->lock;
-                       port_array[port]->irq_level     = port_array[0]->irq_level;
-                       port_array[port]->memory_base   = port_array[0]->memory_base;
-                       port_array[port]->sca_base      = port_array[0]->sca_base;
-                       port_array[port]->statctrl_base = port_array[0]->statctrl_base;
-                       port_array[port]->lcr_base      = port_array[0]->lcr_base;
-                       alloc_dma_bufs(port_array[port]);
-               }
-
-               if ( request_irq(port_array[0]->irq_level,
-                                       synclinkmp_interrupt,
-                                       port_array[0]->irq_flags,
-                                       port_array[0]->device_name,
-                                       port_array[0]) < 0 ) {
-                       printk( "%s(%d):%s Cant request interrupt, IRQ=%d\n",
-                               __FILE__,__LINE__,
-                               port_array[0]->device_name,
-                               port_array[0]->irq_level );
-               }
-               else {
-                       port_array[0]->irq_requested = true;
-                       adapter_test(port_array[0]);
-               }
-       }
-}
-
-static const struct tty_operations ops = {
-       .open = open,
-       .close = close,
-       .write = write,
-       .put_char = put_char,
-       .flush_chars = flush_chars,
-       .write_room = write_room,
-       .chars_in_buffer = chars_in_buffer,
-       .flush_buffer = flush_buffer,
-       .ioctl = ioctl,
-       .throttle = throttle,
-       .unthrottle = unthrottle,
-       .send_xchar = send_xchar,
-       .break_ctl = set_break,
-       .wait_until_sent = wait_until_sent,
-       .set_termios = set_termios,
-       .stop = tx_hold,
-       .start = tx_release,
-       .hangup = hangup,
-       .tiocmget = tiocmget,
-       .tiocmset = tiocmset,
-       .get_icount = get_icount,
-       .proc_fops = &synclinkmp_proc_fops,
-};
-
-
-static void synclinkmp_cleanup(void)
-{
-       int rc;
-       SLMP_INFO *info;
-       SLMP_INFO *tmp;
-
-       printk("Unloading %s %s\n", driver_name, driver_version);
-
-       if (serial_driver) {
-               if ((rc = tty_unregister_driver(serial_driver)))
-                       printk("%s(%d) failed to unregister tty driver err=%d\n",
-                              __FILE__,__LINE__,rc);
-               put_tty_driver(serial_driver);
-       }
-
-       /* reset devices */
-       info = synclinkmp_device_list;
-       while(info) {
-               reset_port(info);
-               info = info->next_device;
-       }
-
-       /* release devices */
-       info = synclinkmp_device_list;
-       while(info) {
-#if SYNCLINK_GENERIC_HDLC
-               hdlcdev_exit(info);
-#endif
-               free_dma_bufs(info);
-               free_tmp_rx_buf(info);
-               if ( info->port_num == 0 ) {
-                       if (info->sca_base)
-                               write_reg(info, LPR, 1); /* set low power mode */
-                       release_resources(info);
-               }
-               tmp = info;
-               info = info->next_device;
-               kfree(tmp);
-       }
-
-       pci_unregister_driver(&synclinkmp_pci_driver);
-}
-
-/* Driver initialization entry point.
- */
-
-static int __init synclinkmp_init(void)
-{
-       int rc;
-
-       if (break_on_load) {
-               synclinkmp_get_text_ptr();
-               BREAKPOINT();
-       }
-
-       printk("%s %s\n", driver_name, driver_version);
-
-       if ((rc = pci_register_driver(&synclinkmp_pci_driver)) < 0) {
-               printk("%s:failed to register PCI driver, error=%d\n",__FILE__,rc);
-               return rc;
-       }
-
-       serial_driver = alloc_tty_driver(128);
-       if (!serial_driver) {
-               rc = -ENOMEM;
-               goto error;
-       }
-
-       /* Initialize the tty_driver structure */
-
-       serial_driver->owner = THIS_MODULE;
-       serial_driver->driver_name = "synclinkmp";
-       serial_driver->name = "ttySLM";
-       serial_driver->major = ttymajor;
-       serial_driver->minor_start = 64;
-       serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
-       serial_driver->subtype = SERIAL_TYPE_NORMAL;
-       serial_driver->init_termios = tty_std_termios;
-       serial_driver->init_termios.c_cflag =
-               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-       serial_driver->init_termios.c_ispeed = 9600;
-       serial_driver->init_termios.c_ospeed = 9600;
-       serial_driver->flags = TTY_DRIVER_REAL_RAW;
-       tty_set_operations(serial_driver, &ops);
-       if ((rc = tty_register_driver(serial_driver)) < 0) {
-               printk("%s(%d):Couldn't register serial driver\n",
-                       __FILE__,__LINE__);
-               put_tty_driver(serial_driver);
-               serial_driver = NULL;
-               goto error;
-       }
-
-       printk("%s %s, tty major#%d\n",
-               driver_name, driver_version,
-               serial_driver->major);
-
-       return 0;
-
-error:
-       synclinkmp_cleanup();
-       return rc;
-}
-
-static void __exit synclinkmp_exit(void)
-{
-       synclinkmp_cleanup();
-}
-
-module_init(synclinkmp_init);
-module_exit(synclinkmp_exit);
-
-/* Set the port for internal loopback mode.
- * The TxCLK and RxCLK signals are generated from the BRG and
- * the TxD is looped back to the RxD internally.
- */
-static void enable_loopback(SLMP_INFO *info, int enable)
-{
-       if (enable) {
-               /* MD2 (Mode Register 2)
-                * 01..00  CNCT<1..0> Channel Connection 11=Local Loopback
-                */
-               write_reg(info, MD2, (unsigned char)(read_reg(info, MD2) | (BIT1 + BIT0)));
-
-               /* degate external TxC clock source */
-               info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2));
-               write_control_reg(info);
-
-               /* RXS/TXS (Rx/Tx clock source)
-                * 07      Reserved, must be 0
-                * 06..04  Clock Source, 100=BRG
-                * 03..00  Clock Divisor, 0000=1
-                */
-               write_reg(info, RXS, 0x40);
-               write_reg(info, TXS, 0x40);
-
-       } else {
-               /* MD2 (Mode Register 2)
-                * 01..00  CNCT<1..0> Channel connection, 0=normal
-                */
-               write_reg(info, MD2, (unsigned char)(read_reg(info, MD2) & ~(BIT1 + BIT0)));
-
-               /* RXS/TXS (Rx/Tx clock source)
-                * 07      Reserved, must be 0
-                * 06..04  Clock Source, 000=RxC/TxC Pin
-                * 03..00  Clock Divisor, 0000=1
-                */
-               write_reg(info, RXS, 0x00);
-               write_reg(info, TXS, 0x00);
-       }
-
-       /* set LinkSpeed if available, otherwise default to 2Mbps */
-       if (info->params.clock_speed)
-               set_rate(info, info->params.clock_speed);
-       else
-               set_rate(info, 3686400);
-}
-
-/* Set the baud rate register to the desired speed
- *
- *     data_rate       data rate of clock in bits per second
- *                     A data rate of 0 disables the AUX clock.
- */
-static void set_rate( SLMP_INFO *info, u32 data_rate )
-{
-               u32 TMCValue;
-               unsigned char BRValue;
-       u32 Divisor=0;
-
-       /* fBRG = fCLK/(TMC * 2^BR)
-        */
-       if (data_rate != 0) {
-               Divisor = 14745600/data_rate;
-               if (!Divisor)
-                       Divisor = 1;
-
-               TMCValue = Divisor;
-
-               BRValue = 0;
-               if (TMCValue != 1 && TMCValue != 2) {
-                       /* BRValue of 0 provides 50/50 duty cycle *only* when
-                        * TMCValue is 1 or 2. BRValue of 1 to 9 always provides
-                        * 50/50 duty cycle.
-                        */
-                       BRValue = 1;
-                       TMCValue >>= 1;
-               }
-
-               /* while TMCValue is too big for TMC register, divide
-                * by 2 and increment BR exponent.
-                */
-               for(; TMCValue > 256 && BRValue < 10; BRValue++)
-                       TMCValue >>= 1;
-
-               write_reg(info, TXS,
-                       (unsigned char)((read_reg(info, TXS) & 0xf0) | BRValue));
-               write_reg(info, RXS,
-                       (unsigned char)((read_reg(info, RXS) & 0xf0) | BRValue));
-               write_reg(info, TMC, (unsigned char)TMCValue);
-       }
-       else {
-               write_reg(info, TXS,0);
-               write_reg(info, RXS,0);
-               write_reg(info, TMC, 0);
-       }
-}
-
-/* Disable receiver
- */
-static void rx_stop(SLMP_INFO *info)
-{
-       if (debug_level >= DEBUG_LEVEL_ISR)
-               printk("%s(%d):%s rx_stop()\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       write_reg(info, CMD, RXRESET);
-
-       info->ie0_value &= ~RXRDYE;
-       write_reg(info, IE0, info->ie0_value);  /* disable Rx data interrupts */
-
-       write_reg(info, RXDMA + DSR, 0);        /* disable Rx DMA */
-       write_reg(info, RXDMA + DCMD, SWABORT); /* reset/init Rx DMA */
-       write_reg(info, RXDMA + DIR, 0);        /* disable Rx DMA interrupts */
-
-       info->rx_enabled = false;
-       info->rx_overflow = false;
-}
-
-/* enable the receiver
- */
-static void rx_start(SLMP_INFO *info)
-{
-       int i;
-
-       if (debug_level >= DEBUG_LEVEL_ISR)
-               printk("%s(%d):%s rx_start()\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       write_reg(info, CMD, RXRESET);
-
-       if ( info->params.mode == MGSL_MODE_HDLC ) {
-               /* HDLC, disabe IRQ on rxdata */
-               info->ie0_value &= ~RXRDYE;
-               write_reg(info, IE0, info->ie0_value);
-
-               /* Reset all Rx DMA buffers and program rx dma */
-               write_reg(info, RXDMA + DSR, 0);                /* disable Rx DMA */
-               write_reg(info, RXDMA + DCMD, SWABORT); /* reset/init Rx DMA */
-
-               for (i = 0; i < info->rx_buf_count; i++) {
-                       info->rx_buf_list[i].status = 0xff;
-
-                       // throttle to 4 shared memory writes at a time to prevent
-                       // hogging local bus (keep latency time for DMA requests low).
-                       if (!(i % 4))
-                               read_status_reg(info);
-               }
-               info->current_rx_buf = 0;
-
-               /* set current/1st descriptor address */
-               write_reg16(info, RXDMA + CDA,
-                       info->rx_buf_list_ex[0].phys_entry);
-
-               /* set new last rx descriptor address */
-               write_reg16(info, RXDMA + EDA,
-                       info->rx_buf_list_ex[info->rx_buf_count - 1].phys_entry);
-
-               /* set buffer length (shared by all rx dma data buffers) */
-               write_reg16(info, RXDMA + BFL, SCABUFSIZE);
-
-               write_reg(info, RXDMA + DIR, 0x60);     /* enable Rx DMA interrupts (EOM/BOF) */
-               write_reg(info, RXDMA + DSR, 0xf2);     /* clear Rx DMA IRQs, enable Rx DMA */
-       } else {
-               /* async, enable IRQ on rxdata */
-               info->ie0_value |= RXRDYE;
-               write_reg(info, IE0, info->ie0_value);
-       }
-
-       write_reg(info, CMD, RXENABLE);
-
-       info->rx_overflow = false;
-       info->rx_enabled = true;
-}
-
-/* Enable the transmitter and send a transmit frame if
- * one is loaded in the DMA buffers.
- */
-static void tx_start(SLMP_INFO *info)
-{
-       if (debug_level >= DEBUG_LEVEL_ISR)
-               printk("%s(%d):%s tx_start() tx_count=%d\n",
-                        __FILE__,__LINE__, info->device_name,info->tx_count );
-
-       if (!info->tx_enabled ) {
-               write_reg(info, CMD, TXRESET);
-               write_reg(info, CMD, TXENABLE);
-               info->tx_enabled = true;
-       }
-
-       if ( info->tx_count ) {
-
-               /* If auto RTS enabled and RTS is inactive, then assert */
-               /* RTS and set a flag indicating that the driver should */
-               /* negate RTS when the transmission completes. */
-
-               info->drop_rts_on_tx_done = false;
-
-               if (info->params.mode != MGSL_MODE_ASYNC) {
-
-                       if ( info->params.flags & HDLC_FLAG_AUTO_RTS ) {
-                               get_signals( info );
-                               if ( !(info->serial_signals & SerialSignal_RTS) ) {
-                                       info->serial_signals |= SerialSignal_RTS;
-                                       set_signals( info );
-                                       info->drop_rts_on_tx_done = true;
-                               }
-                       }
-
-                       write_reg16(info, TRC0,
-                               (unsigned short)(((tx_negate_fifo_level-1)<<8) + tx_active_fifo_level));
-
-                       write_reg(info, TXDMA + DSR, 0);                /* disable DMA channel */
-                       write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */
-       
-                       /* set TX CDA (current descriptor address) */
-                       write_reg16(info, TXDMA + CDA,
-                               info->tx_buf_list_ex[0].phys_entry);
-       
-                       /* set TX EDA (last descriptor address) */
-                       write_reg16(info, TXDMA + EDA,
-                               info->tx_buf_list_ex[info->last_tx_buf].phys_entry);
-       
-                       /* enable underrun IRQ */
-                       info->ie1_value &= ~IDLE;
-                       info->ie1_value |= UDRN;
-                       write_reg(info, IE1, info->ie1_value);
-                       write_reg(info, SR1, (unsigned char)(IDLE + UDRN));
-       
-                       write_reg(info, TXDMA + DIR, 0x40);             /* enable Tx DMA interrupts (EOM) */
-                       write_reg(info, TXDMA + DSR, 0xf2);             /* clear Tx DMA IRQs, enable Tx DMA */
-       
-                       mod_timer(&info->tx_timer, jiffies +
-                                       msecs_to_jiffies(5000));
-               }
-               else {
-                       tx_load_fifo(info);
-                       /* async, enable IRQ on txdata */
-                       info->ie0_value |= TXRDYE;
-                       write_reg(info, IE0, info->ie0_value);
-               }
-
-               info->tx_active = true;
-       }
-}
-
-/* stop the transmitter and DMA
- */
-static void tx_stop( SLMP_INFO *info )
-{
-       if (debug_level >= DEBUG_LEVEL_ISR)
-               printk("%s(%d):%s tx_stop()\n",
-                        __FILE__,__LINE__, info->device_name );
-
-       del_timer(&info->tx_timer);
-
-       write_reg(info, TXDMA + DSR, 0);                /* disable DMA channel */
-       write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */
-
-       write_reg(info, CMD, TXRESET);
-
-       info->ie1_value &= ~(UDRN + IDLE);
-       write_reg(info, IE1, info->ie1_value);  /* disable tx status interrupts */
-       write_reg(info, SR1, (unsigned char)(IDLE + UDRN));     /* clear pending */
-
-       info->ie0_value &= ~TXRDYE;
-       write_reg(info, IE0, info->ie0_value);  /* disable tx data interrupts */
-
-       info->tx_enabled = false;
-       info->tx_active = false;
-}
-
-/* Fill the transmit FIFO until the FIFO is full or
- * there is no more data to load.
- */
-static void tx_load_fifo(SLMP_INFO *info)
-{
-       u8 TwoBytes[2];
-
-       /* do nothing is now tx data available and no XON/XOFF pending */
-
-       if ( !info->tx_count && !info->x_char )
-               return;
-
-       /* load the Transmit FIFO until FIFOs full or all data sent */
-
-       while( info->tx_count && (read_reg(info,SR0) & BIT1) ) {
-
-               /* there is more space in the transmit FIFO and */
-               /* there is more data in transmit buffer */
-
-               if ( (info->tx_count > 1) && !info->x_char ) {
-                       /* write 16-bits */
-                       TwoBytes[0] = info->tx_buf[info->tx_get++];
-                       if (info->tx_get >= info->max_frame_size)
-                               info->tx_get -= info->max_frame_size;
-                       TwoBytes[1] = info->tx_buf[info->tx_get++];
-                       if (info->tx_get >= info->max_frame_size)
-                               info->tx_get -= info->max_frame_size;
-
-                       write_reg16(info, TRB, *((u16 *)TwoBytes));
-
-                       info->tx_count -= 2;
-                       info->icount.tx += 2;
-               } else {
-                       /* only 1 byte left to transmit or 1 FIFO slot left */
-
-                       if (info->x_char) {
-                               /* transmit pending high priority char */
-                               write_reg(info, TRB, info->x_char);
-                               info->x_char = 0;
-                       } else {
-                               write_reg(info, TRB, info->tx_buf[info->tx_get++]);
-                               if (info->tx_get >= info->max_frame_size)
-                                       info->tx_get -= info->max_frame_size;
-                               info->tx_count--;
-                       }
-                       info->icount.tx++;
-               }
-       }
-}
-
-/* Reset a port to a known state
- */
-static void reset_port(SLMP_INFO *info)
-{
-       if (info->sca_base) {
-
-               tx_stop(info);
-               rx_stop(info);
-
-               info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
-               set_signals(info);
-
-               /* disable all port interrupts */
-               info->ie0_value = 0;
-               info->ie1_value = 0;
-               info->ie2_value = 0;
-               write_reg(info, IE0, info->ie0_value);
-               write_reg(info, IE1, info->ie1_value);
-               write_reg(info, IE2, info->ie2_value);
-
-               write_reg(info, CMD, CHRESET);
-       }
-}
-
-/* Reset all the ports to a known state.
- */
-static void reset_adapter(SLMP_INFO *info)
-{
-       int i;
-
-       for ( i=0; i < SCA_MAX_PORTS; ++i) {
-               if (info->port_array[i])
-                       reset_port(info->port_array[i]);
-       }
-}
-
-/* Program port for asynchronous communications.
- */
-static void async_mode(SLMP_INFO *info)
-{
-
-       unsigned char RegValue;
-
-       tx_stop(info);
-       rx_stop(info);
-
-       /* MD0, Mode Register 0
-        *
-        * 07..05  PRCTL<2..0>, Protocol Mode, 000=async
-        * 04      AUTO, Auto-enable (RTS/CTS/DCD)
-        * 03      Reserved, must be 0
-        * 02      CRCCC, CRC Calculation, 0=disabled
-        * 01..00  STOP<1..0> Stop bits (00=1,10=2)
-        *
-        * 0000 0000
-        */
-       RegValue = 0x00;
-       if (info->params.stop_bits != 1)
-               RegValue |= BIT1;
-       write_reg(info, MD0, RegValue);
-
-       /* MD1, Mode Register 1
-        *
-        * 07..06  BRATE<1..0>, bit rate, 00=1/1 01=1/16 10=1/32 11=1/64
-        * 05..04  TXCHR<1..0>, tx char size, 00=8 bits,01=7,10=6,11=5
-        * 03..02  RXCHR<1..0>, rx char size
-        * 01..00  PMPM<1..0>, Parity mode, 00=none 10=even 11=odd
-        *
-        * 0100 0000
-        */
-       RegValue = 0x40;
-       switch (info->params.data_bits) {
-       case 7: RegValue |= BIT4 + BIT2; break;
-       case 6: RegValue |= BIT5 + BIT3; break;
-       case 5: RegValue |= BIT5 + BIT4 + BIT3 + BIT2; break;
-       }
-       if (info->params.parity != ASYNC_PARITY_NONE) {
-               RegValue |= BIT1;
-               if (info->params.parity == ASYNC_PARITY_ODD)
-                       RegValue |= BIT0;
-       }
-       write_reg(info, MD1, RegValue);
-
-       /* MD2, Mode Register 2
-        *
-        * 07..02  Reserved, must be 0
-        * 01..00  CNCT<1..0> Channel connection, 00=normal 11=local loopback
-        *
-        * 0000 0000
-        */
-       RegValue = 0x00;
-       if (info->params.loopback)
-               RegValue |= (BIT1 + BIT0);
-       write_reg(info, MD2, RegValue);
-
-       /* RXS, Receive clock source
-        *
-        * 07      Reserved, must be 0
-        * 06..04  RXCS<2..0>, clock source, 000=RxC Pin, 100=BRG, 110=DPLL
-        * 03..00  RXBR<3..0>, rate divisor, 0000=1
-        */
-       RegValue=BIT6;
-       write_reg(info, RXS, RegValue);
-
-       /* TXS, Transmit clock source
-        *
-        * 07      Reserved, must be 0
-        * 06..04  RXCS<2..0>, clock source, 000=TxC Pin, 100=BRG, 110=Receive Clock
-        * 03..00  RXBR<3..0>, rate divisor, 0000=1
-        */
-       RegValue=BIT6;
-       write_reg(info, TXS, RegValue);
-
-       /* Control Register
-        *
-        * 6,4,2,0  CLKSEL<3..0>, 0 = TcCLK in, 1 = Auxclk out
-        */
-       info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2));
-       write_control_reg(info);
-
-       tx_set_idle(info);
-
-       /* RRC Receive Ready Control 0
-        *
-        * 07..05  Reserved, must be 0
-        * 04..00  RRC<4..0> Rx FIFO trigger active 0x00 = 1 byte
-        */
-       write_reg(info, RRC, 0x00);
-
-       /* TRC0 Transmit Ready Control 0
-        *
-        * 07..05  Reserved, must be 0
-        * 04..00  TRC<4..0> Tx FIFO trigger active 0x10 = 16 bytes
-        */
-       write_reg(info, TRC0, 0x10);
-
-       /* TRC1 Transmit Ready Control 1
-        *
-        * 07..05  Reserved, must be 0
-        * 04..00  TRC<4..0> Tx FIFO trigger inactive 0x1e = 31 bytes (full-1)
-        */
-       write_reg(info, TRC1, 0x1e);
-
-       /* CTL, MSCI control register
-        *
-        * 07..06  Reserved, set to 0
-        * 05      UDRNC, underrun control, 0=abort 1=CRC+flag (HDLC/BSC)
-        * 04      IDLC, idle control, 0=mark 1=idle register
-        * 03      BRK, break, 0=off 1 =on (async)
-        * 02      SYNCLD, sync char load enable (BSC) 1=enabled
-        * 01      GOP, go active on poll (LOOP mode) 1=enabled
-        * 00      RTS, RTS output control, 0=active 1=inactive
-        *
-        * 0001 0001
-        */
-       RegValue = 0x10;
-       if (!(info->serial_signals & SerialSignal_RTS))
-               RegValue |= 0x01;
-       write_reg(info, CTL, RegValue);
-
-       /* enable status interrupts */
-       info->ie0_value |= TXINTE + RXINTE;
-       write_reg(info, IE0, info->ie0_value);
-
-       /* enable break detect interrupt */
-       info->ie1_value = BRKD;
-       write_reg(info, IE1, info->ie1_value);
-
-       /* enable rx overrun interrupt */
-       info->ie2_value = OVRN;
-       write_reg(info, IE2, info->ie2_value);
-
-       set_rate( info, info->params.data_rate * 16 );
-}
-
-/* Program the SCA for HDLC communications.
- */
-static void hdlc_mode(SLMP_INFO *info)
-{
-       unsigned char RegValue;
-       u32 DpllDivisor;
-
-       // Can't use DPLL because SCA outputs recovered clock on RxC when
-       // DPLL mode selected. This causes output contention with RxC receiver.
-       // Use of DPLL would require external hardware to disable RxC receiver
-       // when DPLL mode selected.
-       info->params.flags &= ~(HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL);
-
-       /* disable DMA interrupts */
-       write_reg(info, TXDMA + DIR, 0);
-       write_reg(info, RXDMA + DIR, 0);
-
-       /* MD0, Mode Register 0
-        *
-        * 07..05  PRCTL<2..0>, Protocol Mode, 100=HDLC
-        * 04      AUTO, Auto-enable (RTS/CTS/DCD)
-        * 03      Reserved, must be 0
-        * 02      CRCCC, CRC Calculation, 1=enabled
-        * 01      CRC1, CRC selection, 0=CRC-16,1=CRC-CCITT-16
-        * 00      CRC0, CRC initial value, 1 = all 1s
-        *
-        * 1000 0001
-        */
-       RegValue = 0x81;
-       if (info->params.flags & HDLC_FLAG_AUTO_CTS)
-               RegValue |= BIT4;
-       if (info->params.flags & HDLC_FLAG_AUTO_DCD)
-               RegValue |= BIT4;
-       if (info->params.crc_type == HDLC_CRC_16_CCITT)
-               RegValue |= BIT2 + BIT1;
-       write_reg(info, MD0, RegValue);
-
-       /* MD1, Mode Register 1
-        *
-        * 07..06  ADDRS<1..0>, Address detect, 00=no addr check
-        * 05..04  TXCHR<1..0>, tx char size, 00=8 bits
-        * 03..02  RXCHR<1..0>, rx char size, 00=8 bits
-        * 01..00  PMPM<1..0>, Parity mode, 00=no parity
-        *
-        * 0000 0000
-        */
-       RegValue = 0x00;
-       write_reg(info, MD1, RegValue);
-
-       /* MD2, Mode Register 2
-        *
-        * 07      NRZFM, 0=NRZ, 1=FM
-        * 06..05  CODE<1..0> Encoding, 00=NRZ
-        * 04..03  DRATE<1..0> DPLL Divisor, 00=8
-        * 02      Reserved, must be 0
-        * 01..00  CNCT<1..0> Channel connection, 0=normal
-        *
-        * 0000 0000
-        */
-       RegValue = 0x00;
-       switch(info->params.encoding) {
-       case HDLC_ENCODING_NRZI:          RegValue |= BIT5; break;
-       case HDLC_ENCODING_BIPHASE_MARK:  RegValue |= BIT7 + BIT5; break; /* aka FM1 */
-       case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT7 + BIT6; break; /* aka FM0 */
-       case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT7; break;      /* aka Manchester */
-#if 0
-       case HDLC_ENCODING_NRZB:                                        /* not supported */
-       case HDLC_ENCODING_NRZI_MARK:                                   /* not supported */
-       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL:                          /* not supported */
-#endif
-       }
-       if ( info->params.flags & HDLC_FLAG_DPLL_DIV16 ) {
-               DpllDivisor = 16;
-               RegValue |= BIT3;
-       } else if ( info->params.flags & HDLC_FLAG_DPLL_DIV8 ) {
-               DpllDivisor = 8;
-       } else {
-               DpllDivisor = 32;
-               RegValue |= BIT4;
-       }
-       write_reg(info, MD2, RegValue);
-
-
-       /* RXS, Receive clock source
-        *
-        * 07      Reserved, must be 0
-        * 06..04  RXCS<2..0>, clock source, 000=RxC Pin, 100=BRG, 110=DPLL
-        * 03..00  RXBR<3..0>, rate divisor, 0000=1
-        */
-       RegValue=0;
-       if (info->params.flags & HDLC_FLAG_RXC_BRG)
-               RegValue |= BIT6;
-       if (info->params.flags & HDLC_FLAG_RXC_DPLL)
-               RegValue |= BIT6 + BIT5;
-       write_reg(info, RXS, RegValue);
-
-       /* TXS, Transmit clock source
-        *
-        * 07      Reserved, must be 0
-        * 06..04  RXCS<2..0>, clock source, 000=TxC Pin, 100=BRG, 110=Receive Clock
-        * 03..00  RXBR<3..0>, rate divisor, 0000=1
-        */
-       RegValue=0;
-       if (info->params.flags & HDLC_FLAG_TXC_BRG)
-               RegValue |= BIT6;
-       if (info->params.flags & HDLC_FLAG_TXC_DPLL)
-               RegValue |= BIT6 + BIT5;
-       write_reg(info, TXS, RegValue);
-
-       if (info->params.flags & HDLC_FLAG_RXC_DPLL)
-               set_rate(info, info->params.clock_speed * DpllDivisor);
-       else
-               set_rate(info, info->params.clock_speed);
-
-       /* GPDATA (General Purpose I/O Data Register)
-        *
-        * 6,4,2,0  CLKSEL<3..0>, 0 = TcCLK in, 1 = Auxclk out
-        */
-       if (info->params.flags & HDLC_FLAG_TXC_BRG)
-               info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2));
-       else
-               info->port_array[0]->ctrlreg_value &= ~(BIT0 << (info->port_num * 2));
-       write_control_reg(info);
-
-       /* RRC Receive Ready Control 0
-        *
-        * 07..05  Reserved, must be 0
-        * 04..00  RRC<4..0> Rx FIFO trigger active
-        */
-       write_reg(info, RRC, rx_active_fifo_level);
-
-       /* TRC0 Transmit Ready Control 0
-        *
-        * 07..05  Reserved, must be 0
-        * 04..00  TRC<4..0> Tx FIFO trigger active
-        */
-       write_reg(info, TRC0, tx_active_fifo_level);
-
-       /* TRC1 Transmit Ready Control 1
-        *
-        * 07..05  Reserved, must be 0
-        * 04..00  TRC<4..0> Tx FIFO trigger inactive 0x1f = 32 bytes (full)
-        */
-       write_reg(info, TRC1, (unsigned char)(tx_negate_fifo_level - 1));
-
-       /* DMR, DMA Mode Register
-        *
-        * 07..05  Reserved, must be 0
-        * 04      TMOD, Transfer Mode: 1=chained-block
-        * 03      Reserved, must be 0
-        * 02      NF, Number of Frames: 1=multi-frame
-        * 01      CNTE, Frame End IRQ Counter enable: 0=disabled
-        * 00      Reserved, must be 0
-        *
-        * 0001 0100
-        */
-       write_reg(info, TXDMA + DMR, 0x14);
-       write_reg(info, RXDMA + DMR, 0x14);
-
-       /* Set chain pointer base (upper 8 bits of 24 bit addr) */
-       write_reg(info, RXDMA + CPB,
-               (unsigned char)(info->buffer_list_phys >> 16));
-
-       /* Set chain pointer base (upper 8 bits of 24 bit addr) */
-       write_reg(info, TXDMA + CPB,
-               (unsigned char)(info->buffer_list_phys >> 16));
-
-       /* enable status interrupts. other code enables/disables
-        * the individual sources for these two interrupt classes.
-        */
-       info->ie0_value |= TXINTE + RXINTE;
-       write_reg(info, IE0, info->ie0_value);
-
-       /* CTL, MSCI control register
-        *
-        * 07..06  Reserved, set to 0
-        * 05      UDRNC, underrun control, 0=abort 1=CRC+flag (HDLC/BSC)
-        * 04      IDLC, idle control, 0=mark 1=idle register
-        * 03      BRK, break, 0=off 1 =on (async)
-        * 02      SYNCLD, sync char load enable (BSC) 1=enabled
-        * 01      GOP, go active on poll (LOOP mode) 1=enabled
-        * 00      RTS, RTS output control, 0=active 1=inactive
-        *
-        * 0001 0001
-        */
-       RegValue = 0x10;
-       if (!(info->serial_signals & SerialSignal_RTS))
-               RegValue |= 0x01;
-       write_reg(info, CTL, RegValue);
-
-       /* preamble not supported ! */
-
-       tx_set_idle(info);
-       tx_stop(info);
-       rx_stop(info);
-
-       set_rate(info, info->params.clock_speed);
-
-       if (info->params.loopback)
-               enable_loopback(info,1);
-}
-
-/* Set the transmit HDLC idle mode
- */
-static void tx_set_idle(SLMP_INFO *info)
-{
-       unsigned char RegValue = 0xff;
-
-       /* Map API idle mode to SCA register bits */
-       switch(info->idle_mode) {
-       case HDLC_TXIDLE_FLAGS:                 RegValue = 0x7e; break;
-       case HDLC_TXIDLE_ALT_ZEROS_ONES:        RegValue = 0xaa; break;
-       case HDLC_TXIDLE_ZEROS:                 RegValue = 0x00; break;
-       case HDLC_TXIDLE_ONES:                  RegValue = 0xff; break;
-       case HDLC_TXIDLE_ALT_MARK_SPACE:        RegValue = 0xaa; break;
-       case HDLC_TXIDLE_SPACE:                 RegValue = 0x00; break;
-       case HDLC_TXIDLE_MARK:                  RegValue = 0xff; break;
-       }
-
-       write_reg(info, IDL, RegValue);
-}
-
-/* Query the adapter for the state of the V24 status (input) signals.
- */
-static void get_signals(SLMP_INFO *info)
-{
-       u16 status = read_reg(info, SR3);
-       u16 gpstatus = read_status_reg(info);
-       u16 testbit;
-
-       /* clear all serial signals except DTR and RTS */
-       info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS;
-
-       /* set serial signal bits to reflect MISR */
-
-       if (!(status & BIT3))
-               info->serial_signals |= SerialSignal_CTS;
-
-       if ( !(status & BIT2))
-               info->serial_signals |= SerialSignal_DCD;
-
-       testbit = BIT1 << (info->port_num * 2); // Port 0..3 RI is GPDATA<1,3,5,7>
-       if (!(gpstatus & testbit))
-               info->serial_signals |= SerialSignal_RI;
-
-       testbit = BIT0 << (info->port_num * 2); // Port 0..3 DSR is GPDATA<0,2,4,6>
-       if (!(gpstatus & testbit))
-               info->serial_signals |= SerialSignal_DSR;
-}
-
-/* Set the state of DTR and RTS based on contents of
- * serial_signals member of device context.
- */
-static void set_signals(SLMP_INFO *info)
-{
-       unsigned char RegValue;
-       u16 EnableBit;
-
-       RegValue = read_reg(info, CTL);
-       if (info->serial_signals & SerialSignal_RTS)
-               RegValue &= ~BIT0;
-       else
-               RegValue |= BIT0;
-       write_reg(info, CTL, RegValue);
-
-       // Port 0..3 DTR is ctrl reg <1,3,5,7>
-       EnableBit = BIT1 << (info->port_num*2);
-       if (info->serial_signals & SerialSignal_DTR)
-               info->port_array[0]->ctrlreg_value &= ~EnableBit;
-       else
-               info->port_array[0]->ctrlreg_value |= EnableBit;
-       write_control_reg(info);
-}
-
-/*******************/
-/* DMA Buffer Code */
-/*******************/
-
-/* Set the count for all receive buffers to SCABUFSIZE
- * and set the current buffer to the first buffer. This effectively
- * makes all buffers free and discards any data in buffers.
- */
-static void rx_reset_buffers(SLMP_INFO *info)
-{
-       rx_free_frame_buffers(info, 0, info->rx_buf_count - 1);
-}
-
-/* Free the buffers used by a received frame
- *
- * info   pointer to device instance data
- * first  index of 1st receive buffer of frame
- * last   index of last receive buffer of frame
- */
-static void rx_free_frame_buffers(SLMP_INFO *info, unsigned int first, unsigned int last)
-{
-       bool done = false;
-
-       while(!done) {
-               /* reset current buffer for reuse */
-               info->rx_buf_list[first].status = 0xff;
-
-               if (first == last) {
-                       done = true;
-                       /* set new last rx descriptor address */
-                       write_reg16(info, RXDMA + EDA, info->rx_buf_list_ex[first].phys_entry);
-               }
-
-               first++;
-               if (first == info->rx_buf_count)
-                       first = 0;
-       }
-
-       /* set current buffer to next buffer after last buffer of frame */
-       info->current_rx_buf = first;
-}
-
-/* Return a received frame from the receive DMA buffers.
- * Only frames received without errors are returned.
- *
- * Return Value:       true if frame returned, otherwise false
- */
-static bool rx_get_frame(SLMP_INFO *info)
-{
-       unsigned int StartIndex, EndIndex;      /* index of 1st and last buffers of Rx frame */
-       unsigned short status;
-       unsigned int framesize = 0;
-       bool ReturnCode = false;
-       unsigned long flags;
-       struct tty_struct *tty = info->port.tty;
-       unsigned char addr_field = 0xff;
-       SCADESC *desc;
-       SCADESC_EX *desc_ex;
-
-CheckAgain:
-       /* assume no frame returned, set zero length */
-       framesize = 0;
-       addr_field = 0xff;
-
-       /*
-        * current_rx_buf points to the 1st buffer of the next available
-        * receive frame. To find the last buffer of the frame look for
-        * a non-zero status field in the buffer entries. (The status
-        * field is set by the 16C32 after completing a receive frame.
-        */
-       StartIndex = EndIndex = info->current_rx_buf;
-
-       for ( ;; ) {
-               desc = &info->rx_buf_list[EndIndex];
-               desc_ex = &info->rx_buf_list_ex[EndIndex];
-
-               if (desc->status == 0xff)
-                       goto Cleanup;   /* current desc still in use, no frames available */
-
-               if (framesize == 0 && info->params.addr_filter != 0xff)
-                       addr_field = desc_ex->virt_addr[0];
-
-               framesize += desc->length;
-
-               /* Status != 0 means last buffer of frame */
-               if (desc->status)
-                       break;
-
-               EndIndex++;
-               if (EndIndex == info->rx_buf_count)
-                       EndIndex = 0;
-
-               if (EndIndex == info->current_rx_buf) {
-                       /* all buffers have been 'used' but none mark      */
-                       /* the end of a frame. Reset buffers and receiver. */
-                       if ( info->rx_enabled ){
-                               spin_lock_irqsave(&info->lock,flags);
-                               rx_start(info);
-                               spin_unlock_irqrestore(&info->lock,flags);
-                       }
-                       goto Cleanup;
-               }
-
-       }
-
-       /* check status of receive frame */
-
-       /* frame status is byte stored after frame data
-        *
-        * 7 EOM (end of msg), 1 = last buffer of frame
-        * 6 Short Frame, 1 = short frame
-        * 5 Abort, 1 = frame aborted
-        * 4 Residue, 1 = last byte is partial
-        * 3 Overrun, 1 = overrun occurred during frame reception
-        * 2 CRC,     1 = CRC error detected
-        *
-        */
-       status = desc->status;
-
-       /* ignore CRC bit if not using CRC (bit is undefined) */
-       /* Note:CRC is not save to data buffer */
-       if (info->params.crc_type == HDLC_CRC_NONE)
-               status &= ~BIT2;
-
-       if (framesize == 0 ||
-                (addr_field != 0xff && addr_field != info->params.addr_filter)) {
-               /* discard 0 byte frames, this seems to occur sometime
-                * when remote is idling flags.
-                */
-               rx_free_frame_buffers(info, StartIndex, EndIndex);
-               goto CheckAgain;
-       }
-
-       if (framesize < 2)
-               status |= BIT6;
-
-       if (status & (BIT6+BIT5+BIT3+BIT2)) {
-               /* received frame has errors,
-                * update counts and mark frame size as 0
-                */
-               if (status & BIT6)
-                       info->icount.rxshort++;
-               else if (status & BIT5)
-                       info->icount.rxabort++;
-               else if (status & BIT3)
-                       info->icount.rxover++;
-               else
-                       info->icount.rxcrc++;
-
-               framesize = 0;
-#if SYNCLINK_GENERIC_HDLC
-               {
-                       info->netdev->stats.rx_errors++;
-                       info->netdev->stats.rx_frame_errors++;
-               }
-#endif
-       }
-
-       if ( debug_level >= DEBUG_LEVEL_BH )
-               printk("%s(%d):%s rx_get_frame() status=%04X size=%d\n",
-                       __FILE__,__LINE__,info->device_name,status,framesize);
-
-       if ( debug_level >= DEBUG_LEVEL_DATA )
-               trace_block(info,info->rx_buf_list_ex[StartIndex].virt_addr,
-                       min_t(int, framesize,SCABUFSIZE),0);
-
-       if (framesize) {
-               if (framesize > info->max_frame_size)
-                       info->icount.rxlong++;
-               else {
-                       /* copy dma buffer(s) to contiguous intermediate buffer */
-                       int copy_count = framesize;
-                       int index = StartIndex;
-                       unsigned char *ptmp = info->tmp_rx_buf;
-                       info->tmp_rx_buf_count = framesize;
-
-                       info->icount.rxok++;
-
-                       while(copy_count) {
-                               int partial_count = min(copy_count,SCABUFSIZE);
-                               memcpy( ptmp,
-                                       info->rx_buf_list_ex[index].virt_addr,
-                                       partial_count );
-                               ptmp += partial_count;
-                               copy_count -= partial_count;
-
-                               if ( ++index == info->rx_buf_count )
-                                       index = 0;
-                       }
-
-#if SYNCLINK_GENERIC_HDLC
-                       if (info->netcount)
-                               hdlcdev_rx(info,info->tmp_rx_buf,framesize);
-                       else
-#endif
-                               ldisc_receive_buf(tty,info->tmp_rx_buf,
-                                                 info->flag_buf, framesize);
-               }
-       }
-       /* Free the buffers used by this frame. */
-       rx_free_frame_buffers( info, StartIndex, EndIndex );
-
-       ReturnCode = true;
-
-Cleanup:
-       if ( info->rx_enabled && info->rx_overflow ) {
-               /* Receiver is enabled, but needs to restarted due to
-                * rx buffer overflow. If buffers are empty, restart receiver.
-                */
-               if (info->rx_buf_list[EndIndex].status == 0xff) {
-                       spin_lock_irqsave(&info->lock,flags);
-                       rx_start(info);
-                       spin_unlock_irqrestore(&info->lock,flags);
-               }
-       }
-
-       return ReturnCode;
-}
-
-/* load the transmit DMA buffer with data
- */
-static void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int count)
-{
-       unsigned short copy_count;
-       unsigned int i = 0;
-       SCADESC *desc;
-       SCADESC_EX *desc_ex;
-
-       if ( debug_level >= DEBUG_LEVEL_DATA )
-               trace_block(info,buf, min_t(int, count,SCABUFSIZE), 1);
-
-       /* Copy source buffer to one or more DMA buffers, starting with
-        * the first transmit dma buffer.
-        */
-       for(i=0;;)
-       {
-               copy_count = min_t(unsigned short,count,SCABUFSIZE);
-
-               desc = &info->tx_buf_list[i];
-               desc_ex = &info->tx_buf_list_ex[i];
-
-               load_pci_memory(info, desc_ex->virt_addr,buf,copy_count);
-
-               desc->length = copy_count;
-               desc->status = 0;
-
-               buf += copy_count;
-               count -= copy_count;
-
-               if (!count)
-                       break;
-
-               i++;
-               if (i >= info->tx_buf_count)
-                       i = 0;
-       }
-
-       info->tx_buf_list[i].status = 0x81;     /* set EOM and EOT status */
-       info->last_tx_buf = ++i;
-}
-
-static bool register_test(SLMP_INFO *info)
-{
-       static unsigned char testval[] = {0x00, 0xff, 0xaa, 0x55, 0x69, 0x96};
-       static unsigned int count = ARRAY_SIZE(testval);
-       unsigned int i;
-       bool rc = true;
-       unsigned long flags;
-
-       spin_lock_irqsave(&info->lock,flags);
-       reset_port(info);
-
-       /* assume failure */
-       info->init_error = DiagStatus_AddressFailure;
-
-       /* Write bit patterns to various registers but do it out of */
-       /* sync, then read back and verify values. */
-
-       for (i = 0 ; i < count ; i++) {
-               write_reg(info, TMC, testval[i]);
-               write_reg(info, IDL, testval[(i+1)%count]);
-               write_reg(info, SA0, testval[(i+2)%count]);
-               write_reg(info, SA1, testval[(i+3)%count]);
-
-               if ( (read_reg(info, TMC) != testval[i]) ||
-                         (read_reg(info, IDL) != testval[(i+1)%count]) ||
-                         (read_reg(info, SA0) != testval[(i+2)%count]) ||
-                         (read_reg(info, SA1) != testval[(i+3)%count]) )
-               {
-                       rc = false;
-                       break;
-               }
-       }
-
-       reset_port(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       return rc;
-}
-
-static bool irq_test(SLMP_INFO *info)
-{
-       unsigned long timeout;
-       unsigned long flags;
-
-       unsigned char timer = (info->port_num & 1) ? TIMER2 : TIMER0;
-
-       spin_lock_irqsave(&info->lock,flags);
-       reset_port(info);
-
-       /* assume failure */
-       info->init_error = DiagStatus_IrqFailure;
-       info->irq_occurred = false;
-
-       /* setup timer0 on SCA0 to interrupt */
-
-       /* IER2<7..4> = timer<3..0> interrupt enables (1=enabled) */
-       write_reg(info, IER2, (unsigned char)((info->port_num & 1) ? BIT6 : BIT4));
-
-       write_reg(info, (unsigned char)(timer + TEPR), 0);      /* timer expand prescale */
-       write_reg16(info, (unsigned char)(timer + TCONR), 1);   /* timer constant */
-
-
-       /* TMCS, Timer Control/Status Register
-        *
-        * 07      CMF, Compare match flag (read only) 1=match
-        * 06      ECMI, CMF Interrupt Enable: 1=enabled
-        * 05      Reserved, must be 0
-        * 04      TME, Timer Enable
-        * 03..00  Reserved, must be 0
-        *
-        * 0101 0000
-        */
-       write_reg(info, (unsigned char)(timer + TMCS), 0x50);
-
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       timeout=100;
-       while( timeout-- && !info->irq_occurred ) {
-               msleep_interruptible(10);
-       }
-
-       spin_lock_irqsave(&info->lock,flags);
-       reset_port(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       return info->irq_occurred;
-}
-
-/* initialize individual SCA device (2 ports)
- */
-static bool sca_init(SLMP_INFO *info)
-{
-       /* set wait controller to single mem partition (low), no wait states */
-       write_reg(info, PABR0, 0);      /* wait controller addr boundary 0 */
-       write_reg(info, PABR1, 0);      /* wait controller addr boundary 1 */
-       write_reg(info, WCRL, 0);       /* wait controller low range */
-       write_reg(info, WCRM, 0);       /* wait controller mid range */
-       write_reg(info, WCRH, 0);       /* wait controller high range */
-
-       /* DPCR, DMA Priority Control
-        *
-        * 07..05  Not used, must be 0
-        * 04      BRC, bus release condition: 0=all transfers complete
-        * 03      CCC, channel change condition: 0=every cycle
-        * 02..00  PR<2..0>, priority 100=round robin
-        *
-        * 00000100 = 0x04
-        */
-       write_reg(info, DPCR, dma_priority);
-
-       /* DMA Master Enable, BIT7: 1=enable all channels */
-       write_reg(info, DMER, 0x80);
-
-       /* enable all interrupt classes */
-       write_reg(info, IER0, 0xff);    /* TxRDY,RxRDY,TxINT,RxINT (ports 0-1) */
-       write_reg(info, IER1, 0xff);    /* DMIB,DMIA (channels 0-3) */
-       write_reg(info, IER2, 0xf0);    /* TIRQ (timers 0-3) */
-
-       /* ITCR, interrupt control register
-        * 07      IPC, interrupt priority, 0=MSCI->DMA
-        * 06..05  IAK<1..0>, Acknowledge cycle, 00=non-ack cycle
-        * 04      VOS, Vector Output, 0=unmodified vector
-        * 03..00  Reserved, must be 0
-        */
-       write_reg(info, ITCR, 0);
-
-       return true;
-}
-
-/* initialize adapter hardware
- */
-static bool init_adapter(SLMP_INFO *info)
-{
-       int i;
-
-       /* Set BIT30 of Local Control Reg 0x50 to reset SCA */
-       volatile u32 *MiscCtrl = (u32 *)(info->lcr_base + 0x50);
-       u32 readval;
-
-       info->misc_ctrl_value |= BIT30;
-       *MiscCtrl = info->misc_ctrl_value;
-
-       /*
-        * Force at least 170ns delay before clearing
-        * reset bit. Each read from LCR takes at least
-        * 30ns so 10 times for 300ns to be safe.
-        */
-       for(i=0;i<10;i++)
-               readval = *MiscCtrl;
-
-       info->misc_ctrl_value &= ~BIT30;
-       *MiscCtrl = info->misc_ctrl_value;
-
-       /* init control reg (all DTRs off, all clksel=input) */
-       info->ctrlreg_value = 0xaa;
-       write_control_reg(info);
-
-       {
-               volatile u32 *LCR1BRDR = (u32 *)(info->lcr_base + 0x2c);
-               lcr1_brdr_value &= ~(BIT5 + BIT4 + BIT3);
-
-               switch(read_ahead_count)
-               {
-               case 16:
-                       lcr1_brdr_value |= BIT5 + BIT4 + BIT3;
-                       break;
-               case 8:
-                       lcr1_brdr_value |= BIT5 + BIT4;
-                       break;
-               case 4:
-                       lcr1_brdr_value |= BIT5 + BIT3;
-                       break;
-               case 0:
-                       lcr1_brdr_value |= BIT5;
-                       break;
-               }
-
-               *LCR1BRDR = lcr1_brdr_value;
-               *MiscCtrl = misc_ctrl_value;
-       }
-
-       sca_init(info->port_array[0]);
-       sca_init(info->port_array[2]);
-
-       return true;
-}
-
-/* Loopback an HDLC frame to test the hardware
- * interrupt and DMA functions.
- */
-static bool loopback_test(SLMP_INFO *info)
-{
-#define TESTFRAMESIZE 20
-
-       unsigned long timeout;
-       u16 count = TESTFRAMESIZE;
-       unsigned char buf[TESTFRAMESIZE];
-       bool rc = false;
-       unsigned long flags;
-
-       struct tty_struct *oldtty = info->port.tty;
-       u32 speed = info->params.clock_speed;
-
-       info->params.clock_speed = 3686400;
-       info->port.tty = NULL;
-
-       /* assume failure */
-       info->init_error = DiagStatus_DmaFailure;
-
-       /* build and send transmit frame */
-       for (count = 0; count < TESTFRAMESIZE;++count)
-               buf[count] = (unsigned char)count;
-
-       memset(info->tmp_rx_buf,0,TESTFRAMESIZE);
-
-       /* program hardware for HDLC and enabled receiver */
-       spin_lock_irqsave(&info->lock,flags);
-       hdlc_mode(info);
-       enable_loopback(info,1);
-               rx_start(info);
-       info->tx_count = count;
-       tx_load_dma_buffer(info,buf,count);
-       tx_start(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       /* wait for receive complete */
-       /* Set a timeout for waiting for interrupt. */
-       for ( timeout = 100; timeout; --timeout ) {
-               msleep_interruptible(10);
-
-               if (rx_get_frame(info)) {
-                       rc = true;
-                       break;
-               }
-       }
-
-       /* verify received frame length and contents */
-       if (rc &&
-           ( info->tmp_rx_buf_count != count ||
-             memcmp(buf, info->tmp_rx_buf,count))) {
-               rc = false;
-       }
-
-       spin_lock_irqsave(&info->lock,flags);
-       reset_adapter(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       info->params.clock_speed = speed;
-       info->port.tty = oldtty;
-
-       return rc;
-}
-
-/* Perform diagnostics on hardware
- */
-static int adapter_test( SLMP_INFO *info )
-{
-       unsigned long flags;
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):Testing device %s\n",
-                       __FILE__,__LINE__,info->device_name );
-
-       spin_lock_irqsave(&info->lock,flags);
-       init_adapter(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       info->port_array[0]->port_count = 0;
-
-       if ( register_test(info->port_array[0]) &&
-               register_test(info->port_array[1])) {
-
-               info->port_array[0]->port_count = 2;
-
-               if ( register_test(info->port_array[2]) &&
-                       register_test(info->port_array[3]) )
-                       info->port_array[0]->port_count += 2;
-       }
-       else {
-               printk( "%s(%d):Register test failure for device %s Addr=%08lX\n",
-                       __FILE__,__LINE__,info->device_name, (unsigned long)(info->phys_sca_base));
-               return -ENODEV;
-       }
-
-       if ( !irq_test(info->port_array[0]) ||
-               !irq_test(info->port_array[1]) ||
-                (info->port_count == 4 && !irq_test(info->port_array[2])) ||
-                (info->port_count == 4 && !irq_test(info->port_array[3]))) {
-               printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n",
-                       __FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) );
-               return -ENODEV;
-       }
-
-       if (!loopback_test(info->port_array[0]) ||
-               !loopback_test(info->port_array[1]) ||
-                (info->port_count == 4 && !loopback_test(info->port_array[2])) ||
-                (info->port_count == 4 && !loopback_test(info->port_array[3]))) {
-               printk( "%s(%d):DMA test failure for device %s\n",
-                       __FILE__,__LINE__,info->device_name);
-               return -ENODEV;
-       }
-
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):device %s passed diagnostics\n",
-                       __FILE__,__LINE__,info->device_name );
-
-       info->port_array[0]->init_error = 0;
-       info->port_array[1]->init_error = 0;
-       if ( info->port_count > 2 ) {
-               info->port_array[2]->init_error = 0;
-               info->port_array[3]->init_error = 0;
-       }
-
-       return 0;
-}
-
-/* Test the shared memory on a PCI adapter.
- */
-static bool memory_test(SLMP_INFO *info)
-{
-       static unsigned long testval[] = { 0x0, 0x55555555, 0xaaaaaaaa,
-               0x66666666, 0x99999999, 0xffffffff, 0x12345678 };
-       unsigned long count = ARRAY_SIZE(testval);
-       unsigned long i;
-       unsigned long limit = SCA_MEM_SIZE/sizeof(unsigned long);
-       unsigned long * addr = (unsigned long *)info->memory_base;
-
-       /* Test data lines with test pattern at one location. */
-
-       for ( i = 0 ; i < count ; i++ ) {
-               *addr = testval[i];
-               if ( *addr != testval[i] )
-                       return false;
-       }
-
-       /* Test address lines with incrementing pattern over */
-       /* entire address range. */
-
-       for ( i = 0 ; i < limit ; i++ ) {
-               *addr = i * 4;
-               addr++;
-       }
-
-       addr = (unsigned long *)info->memory_base;
-
-       for ( i = 0 ; i < limit ; i++ ) {
-               if ( *addr != i * 4 )
-                       return false;
-               addr++;
-       }
-
-       memset( info->memory_base, 0, SCA_MEM_SIZE );
-       return true;
-}
-
-/* Load data into PCI adapter shared memory.
- *
- * The PCI9050 releases control of the local bus
- * after completing the current read or write operation.
- *
- * While the PCI9050 write FIFO not empty, the
- * PCI9050 treats all of the writes as a single transaction
- * and does not release the bus. This causes DMA latency problems
- * at high speeds when copying large data blocks to the shared memory.
- *
- * This function breaks a write into multiple transations by
- * interleaving a read which flushes the write FIFO and 'completes'
- * the write transation. This allows any pending DMA request to gain control
- * of the local bus in a timely fasion.
- */
-static void load_pci_memory(SLMP_INFO *info, char* dest, const char* src, unsigned short count)
-{
-       /* A load interval of 16 allows for 4 32-bit writes at */
-       /* 136ns each for a maximum latency of 542ns on the local bus.*/
-
-       unsigned short interval = count / sca_pci_load_interval;
-       unsigned short i;
-
-       for ( i = 0 ; i < interval ; i++ )
-       {
-               memcpy(dest, src, sca_pci_load_interval);
-               read_status_reg(info);
-               dest += sca_pci_load_interval;
-               src += sca_pci_load_interval;
-       }
-
-       memcpy(dest, src, count % sca_pci_load_interval);
-}
-
-static void trace_block(SLMP_INFO *info,const char* data, int count, int xmit)
-{
-       int i;
-       int linecount;
-       if (xmit)
-               printk("%s tx data:\n",info->device_name);
-       else
-               printk("%s rx data:\n",info->device_name);
-
-       while(count) {
-               if (count > 16)
-                       linecount = 16;
-               else
-                       linecount = count;
-
-               for(i=0;i<linecount;i++)
-                       printk("%02X ",(unsigned char)data[i]);
-               for(;i<17;i++)
-                       printk("   ");
-               for(i=0;i<linecount;i++) {
-                       if (data[i]>=040 && data[i]<=0176)
-                               printk("%c",data[i]);
-                       else
-                               printk(".");
-               }
-               printk("\n");
-
-               data  += linecount;
-               count -= linecount;
-       }
-}      /* end of trace_block() */
-
-/* called when HDLC frame times out
- * update stats and do tx completion processing
- */
-static void tx_timeout(unsigned long context)
-{
-       SLMP_INFO *info = (SLMP_INFO*)context;
-       unsigned long flags;
-
-       if ( debug_level >= DEBUG_LEVEL_INFO )
-               printk( "%s(%d):%s tx_timeout()\n",
-                       __FILE__,__LINE__,info->device_name);
-       if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) {
-               info->icount.txtimeout++;
-       }
-       spin_lock_irqsave(&info->lock,flags);
-       info->tx_active = false;
-       info->tx_count = info->tx_put = info->tx_get = 0;
-
-       spin_unlock_irqrestore(&info->lock,flags);
-
-#if SYNCLINK_GENERIC_HDLC
-       if (info->netcount)
-               hdlcdev_tx_done(info);
-       else
-#endif
-               bh_transmit(info);
-}
-
-/* called to periodically check the DSR/RI modem signal input status
- */
-static void status_timeout(unsigned long context)
-{
-       u16 status = 0;
-       SLMP_INFO *info = (SLMP_INFO*)context;
-       unsigned long flags;
-       unsigned char delta;
-
-
-       spin_lock_irqsave(&info->lock,flags);
-       get_signals(info);
-       spin_unlock_irqrestore(&info->lock,flags);
-
-       /* check for DSR/RI state change */
-
-       delta = info->old_signals ^ info->serial_signals;
-       info->old_signals = info->serial_signals;
-
-       if (delta & SerialSignal_DSR)
-               status |= MISCSTATUS_DSR_LATCHED|(info->serial_signals&SerialSignal_DSR);
-
-       if (delta & SerialSignal_RI)
-               status |= MISCSTATUS_RI_LATCHED|(info->serial_signals&SerialSignal_RI);
-
-       if (delta & SerialSignal_DCD)
-               status |= MISCSTATUS_DCD_LATCHED|(info->serial_signals&SerialSignal_DCD);
-
-       if (delta & SerialSignal_CTS)
-               status |= MISCSTATUS_CTS_LATCHED|(info->serial_signals&SerialSignal_CTS);
-
-       if (status)
-               isr_io_pin(info,status);
-
-       mod_timer(&info->status_timer, jiffies + msecs_to_jiffies(10));
-}
-
-
-/* Register Access Routines -
- * All registers are memory mapped
- */
-#define CALC_REGADDR() \
-       unsigned char * RegAddr = (unsigned char*)(info->sca_base + Addr); \
-       if (info->port_num > 1) \
-               RegAddr += 256;                 /* port 0-1 SCA0, 2-3 SCA1 */ \
-       if ( info->port_num & 1) { \
-               if (Addr > 0x7f) \
-                       RegAddr += 0x40;        /* DMA access */ \
-               else if (Addr > 0x1f && Addr < 0x60) \
-                       RegAddr += 0x20;        /* MSCI access */ \
-       }
-
-
-static unsigned char read_reg(SLMP_INFO * info, unsigned char Addr)
-{
-       CALC_REGADDR();
-       return *RegAddr;
-}
-static void write_reg(SLMP_INFO * info, unsigned char Addr, unsigned char Value)
-{
-       CALC_REGADDR();
-       *RegAddr = Value;
-}
-
-static u16 read_reg16(SLMP_INFO * info, unsigned char Addr)
-{
-       CALC_REGADDR();
-       return *((u16 *)RegAddr);
-}
-
-static void write_reg16(SLMP_INFO * info, unsigned char Addr, u16 Value)
-{
-       CALC_REGADDR();
-       *((u16 *)RegAddr) = Value;
-}
-
-static unsigned char read_status_reg(SLMP_INFO * info)
-{
-       unsigned char *RegAddr = (unsigned char *)info->statctrl_base;
-       return *RegAddr;
-}
-
-static void write_control_reg(SLMP_INFO * info)
-{
-       unsigned char *RegAddr = (unsigned char *)info->statctrl_base;
-       *RegAddr = info->port_array[0]->ctrlreg_value;
-}
-
-
-static int __devinit synclinkmp_init_one (struct pci_dev *dev,
-                                         const struct pci_device_id *ent)
-{
-       if (pci_enable_device(dev)) {
-               printk("error enabling pci device %p\n", dev);
-               return -EIO;
-       }
-       device_init( ++synclinkmp_adapter_count, dev );
-       return 0;
-}
-
-static void __devexit synclinkmp_remove_one (struct pci_dev *dev)
-{
-}
index 9cfbdb318ed9dc24fc4a140d9d2fb9b62b916416..3fd7199301b649797bb4f757b9f5040e68365811 100644 (file)
@@ -147,4 +147,175 @@ config LEGACY_PTY_COUNT
          When not in use, each legacy PTY occupies 12 bytes on 32-bit
          architectures and 24 bytes on 64-bit architectures.
 
+config BFIN_JTAG_COMM
+       tristate "Blackfin JTAG Communication"
+       depends on BLACKFIN
+       help
+         Add support for emulating a TTY device over the Blackfin JTAG.
+
+         To compile this driver as a module, choose M here: the
+         module will be called bfin_jtag_comm.
+
+config BFIN_JTAG_COMM_CONSOLE
+       bool "Console on Blackfin JTAG"
+       depends on BFIN_JTAG_COMM=y
+
+config SERIAL_NONSTANDARD
+       bool "Non-standard serial port support"
+       depends on HAS_IOMEM
+       ---help---
+         Say Y here if you have any non-standard serial boards -- boards
+         which aren't supported using the standard "dumb" serial driver.
+         This includes intelligent serial boards such as Cyclades,
+         Digiboards, etc. These are usually used for systems that need many
+         serial ports because they serve many terminals or dial-in
+         connections.
+
+         Note that the answer to this question won't directly affect the
+         kernel: saying N will just cause the configurator to skip all
+         the questions about non-standard serial boards.
+
+         Most people can say N here.
+
+config ROCKETPORT
+       tristate "Comtrol RocketPort support"
+       depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
+       help
+         This driver supports Comtrol RocketPort and RocketModem PCI boards.   
+          These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or
+          modems.  For information about the RocketPort/RocketModem  boards
+          and this driver read <file:Documentation/serial/rocket.txt>.
+
+         To compile this driver as a module, choose M here: the
+         module will be called rocket.
+
+         If you want to compile this driver into the kernel, say Y here.  If
+          you don't have a Comtrol RocketPort/RocketModem card installed, say N.
+
+config CYCLADES
+       tristate "Cyclades async mux support"
+       depends on SERIAL_NONSTANDARD && (PCI || ISA)
+       select FW_LOADER
+       ---help---
+         This driver supports Cyclades Z and Y multiserial boards.
+         You would need something like this to connect more than two modems to
+         your Linux box, for instance in order to become a dial-in server.
+
+         For information about the Cyclades-Z card, read
+         <file:Documentation/serial/README.cycladesZ>.
+
+         To compile this driver as a module, choose M here: the
+         module will be called cyclades.
+
+         If you haven't heard about it, it's safe to say N.
+
+config CYZ_INTR
+       bool "Cyclades-Z interrupt mode operation (EXPERIMENTAL)"
+       depends on EXPERIMENTAL && CYCLADES
+       help
+         The Cyclades-Z family of multiport cards allows 2 (two) driver op
+         modes: polling and interrupt. In polling mode, the driver will check
+         the status of the Cyclades-Z ports every certain amount of time
+         (which is called polling cycle and is configurable). In interrupt
+         mode, it will use an interrupt line (IRQ) in order to check the
+         status of the Cyclades-Z ports. The default op mode is polling. If
+         unsure, say N.
+
+config MOXA_INTELLIO
+       tristate "Moxa Intellio support"
+       depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
+       select FW_LOADER
+       help
+         Say Y here if you have a Moxa Intellio multiport serial card.
+
+         To compile this driver as a module, choose M here: the
+         module will be called moxa.
+
+config MOXA_SMARTIO
+       tristate "Moxa SmartIO support v. 2.0"
+       depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA)
+       help
+         Say Y here if you have a Moxa SmartIO multiport serial card and/or
+         want to help develop a new version of this driver.
+
+         This is upgraded (1.9.1) driver from original Moxa drivers with
+         changes finally resulting in PCI probing.
+
+         This driver can also be built as a module. The module will be called
+         mxser. If you want to do that, say M here.
+
+config SYNCLINK
+       tristate "Microgate SyncLink card support"
+       depends on SERIAL_NONSTANDARD && PCI && ISA_DMA_API
+       help
+         Provides support for the SyncLink ISA and PCI multiprotocol serial
+         adapters. These adapters support asynchronous and HDLC bit
+         synchronous communication up to 10Mbps (PCI adapter).
+
+         This driver can only be built as a module ( = code which can be
+         inserted in and removed from the running kernel whenever you want).
+         The module will be called synclink.  If you want to do that, say M
+         here.
+
+config SYNCLINKMP
+       tristate "SyncLink Multiport support"
+       depends on SERIAL_NONSTANDARD && PCI
+       help
+         Enable support for the SyncLink Multiport (2 or 4 ports)
+         serial adapter, running asynchronous and HDLC communications up
+         to 2.048Mbps. Each ports is independently selectable for
+         RS-232, V.35, RS-449, RS-530, and X.21
+
+         This driver may be built as a module ( = code which can be
+         inserted in and removed from the running kernel whenever you want).
+         The module will be called synclinkmp.  If you want to do that, say M
+         here.
+
+config SYNCLINK_GT
+       tristate "SyncLink GT/AC support"
+       depends on SERIAL_NONSTANDARD && PCI
+       help
+         Support for SyncLink GT and SyncLink AC families of
+         synchronous and asynchronous serial adapters
+         manufactured by Microgate Systems, Ltd. (www.microgate.com)
+
+config NOZOMI
+       tristate "HSDPA Broadband Wireless Data Card - Globe Trotter"
+       depends on PCI && EXPERIMENTAL
+       help
+         If you have a HSDPA driver Broadband Wireless Data Card -
+         Globe Trotter PCMCIA card, say Y here.
+
+         To compile this driver as a module, choose M here, the module
+         will be called nozomi.
+
+config ISI
+       tristate "Multi-Tech multiport card support (EXPERIMENTAL)"
+       depends on SERIAL_NONSTANDARD && PCI
+       select FW_LOADER
+       help
+         This is a driver for the Multi-Tech cards which provide several
+         serial ports.  The driver is experimental and can currently only be
+         built as a module. The module will be called isicom.
+         If you want to do that, choose M here.
+
+config N_HDLC
+       tristate "HDLC line discipline support"
+       depends on SERIAL_NONSTANDARD
+       help
+         Allows synchronous HDLC communications with tty device drivers that
+         support synchronous HDLC such as the Microgate SyncLink adapter.
+
+         This driver can be built as a module ( = code which can be
+         inserted in and removed from the running kernel whenever you want).
+         The module will be called n_hdlc. If you want to do that, say M
+         here.
+
+config N_GSM
+       tristate "GSM MUX line discipline support (EXPERIMENTAL)"
+       depends on EXPERIMENTAL
+       depends on NET
+       help
+         This line discipline provides support for the GSM MUX protocol and
+         presents the mux as a set of 61 individual tty devices.
 
index 396277216e4ffdddf37e48d595e5a865b6546e46..e549da348a0425de4e34bf91d02ff77536665556 100644 (file)
@@ -11,3 +11,16 @@ obj-$(CONFIG_R3964)          += n_r3964.o
 obj-y                          += vt/
 obj-$(CONFIG_HVC_DRIVER)       += hvc/
 obj-y                          += serial/
+
+# tty drivers
+obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o
+obj-$(CONFIG_BFIN_JTAG_COMM)   += bfin_jtag_comm.o
+obj-$(CONFIG_CYCLADES)         += cyclades.o
+obj-$(CONFIG_ISI)              += isicom.o
+obj-$(CONFIG_MOXA_INTELLIO)    += moxa.o
+obj-$(CONFIG_MOXA_SMARTIO)     += mxser.o
+obj-$(CONFIG_NOZOMI)           += nozomi.o
+obj-$(CONFIG_ROCKETPORT)       += rocket.o
+obj-$(CONFIG_SYNCLINK_GT)      += synclink_gt.o
+obj-$(CONFIG_SYNCLINKMP)       += synclinkmp.o
+obj-$(CONFIG_SYNCLINK)         += synclink.o
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
new file mode 100644 (file)
index 0000000..f214e50
--- /dev/null
@@ -0,0 +1,2178 @@
+/*
+ *  linux/drivers/char/amiserial.c
+ *
+ * Serial driver for the amiga builtin port.
+ *
+ * This code was created by taking serial.c version 4.30 from kernel
+ * release 2.3.22, replacing all hardware related stuff with the
+ * corresponding amiga hardware actions, and removing all irrelevant
+ * code. As a consequence, it uses many of the constants and names
+ * associated with the registers and bits of 16550 compatible UARTS -
+ * but only to keep track of status, etc in the state variables. It
+ * was done this was to make it easier to keep the code in line with
+ * (non hardware specific) changes to serial.c.
+ *
+ * The port is registered with the tty driver as minor device 64, and
+ * therefore other ports should should only use 65 upwards.
+ *
+ * Richard Lucock 28/12/99
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *  Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 
+ *             1998, 1999  Theodore Ts'o
+ *
+ */
+
+/*
+ * Serial driver configuration section.  Here are the various options:
+ *
+ * SERIAL_PARANOIA_CHECK
+ *             Check the magic number for the async_structure where
+ *             ever possible.
+ */
+
+#include <linux/delay.h>
+
+#undef SERIAL_PARANOIA_CHECK
+#define SERIAL_DO_RESTART
+
+/* Set of debugging defines */
+
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+
+/* Sanity checks */
+
+#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
+#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
+ tty->name, (info->flags), serial_driver->refcount,info->count,tty->count,s)
+#else
+#define DBG_CNT(s)
+#endif
+
+/*
+ * End of serial driver configuration section.
+ */
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+#include <linux/serial_reg.h>
+static char *serial_version = "4.30";
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/console.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+
+#include <asm/setup.h>
+
+#include <asm/system.h>
+
+#include <asm/irq.h>
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+#define custom amiga_custom
+static char *serial_name = "Amiga-builtin serial driver";
+
+static struct tty_driver *serial_driver;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+static struct async_struct *IRQ_ports;
+
+static unsigned char current_ctl_bits;
+
+static void change_speed(struct async_struct *info, struct ktermios *old);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+
+
+static struct serial_state rs_table[1];
+
+#define NR_PORTS ARRAY_SIZE(rs_table)
+
+#include <asm/uaccess.h>
+
+#define serial_isroot()        (capable(CAP_SYS_ADMIN))
+
+
+static inline int serial_paranoia_check(struct async_struct *info,
+                                       char *name, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+       static const char *badmagic =
+               "Warning: bad magic number for serial struct (%s) in %s\n";
+       static const char *badinfo =
+               "Warning: null async_struct for (%s) in %s\n";
+
+       if (!info) {
+               printk(badinfo, name, routine);
+               return 1;
+       }
+       if (info->magic != SERIAL_MAGIC) {
+               printk(badmagic, name, routine);
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+/* some serial hardware definitions */
+#define SDR_OVRUN   (1<<15)
+#define SDR_RBF     (1<<14)
+#define SDR_TBE     (1<<13)
+#define SDR_TSRE    (1<<12)
+
+#define SERPER_PARENB    (1<<15)
+
+#define AC_SETCLR   (1<<15)
+#define AC_UARTBRK  (1<<11)
+
+#define SER_DTR     (1<<7)
+#define SER_RTS     (1<<6)
+#define SER_DCD     (1<<5)
+#define SER_CTS     (1<<4)
+#define SER_DSR     (1<<3)
+
+static __inline__ void rtsdtr_ctrl(int bits)
+{
+    ciab.pra = ((bits & (SER_RTS | SER_DTR)) ^ (SER_RTS | SER_DTR)) | (ciab.pra & ~(SER_RTS | SER_DTR));
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+       struct async_struct *info = tty->driver_data;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->name, "rs_stop"))
+               return;
+
+       local_irq_save(flags);
+       if (info->IER & UART_IER_THRI) {
+               info->IER &= ~UART_IER_THRI;
+               /* disable Tx interrupt and remove any pending interrupts */
+               custom.intena = IF_TBE;
+               mb();
+               custom.intreq = IF_TBE;
+               mb();
+       }
+       local_irq_restore(flags);
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+       struct async_struct *info = tty->driver_data;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->name, "rs_start"))
+               return;
+
+       local_irq_save(flags);
+       if (info->xmit.head != info->xmit.tail
+           && info->xmit.buf
+           && !(info->IER & UART_IER_THRI)) {
+               info->IER |= UART_IER_THRI;
+               custom.intena = IF_SETCLR | IF_TBE;
+               mb();
+               /* set a pending Tx Interrupt, transmitter should restart now */
+               custom.intreq = IF_SETCLR | IF_TBE;
+               mb();
+       }
+       local_irq_restore(flags);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt().  They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off.  People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible.  After you are done making modifications, it is not a bad
+ * idea to do:
+ * 
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ *                             - Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static void rs_sched_event(struct async_struct *info,
+                          int event)
+{
+       info->event |= 1 << event;
+       tasklet_schedule(&info->tlet);
+}
+
+static void receive_chars(struct async_struct *info)
+{
+        int status;
+       int serdatr;
+       struct tty_struct *tty = info->tty;
+       unsigned char ch, flag;
+       struct  async_icount *icount;
+       int oe = 0;
+
+       icount = &info->state->icount;
+
+       status = UART_LSR_DR; /* We obviously have a character! */
+       serdatr = custom.serdatr;
+       mb();
+       custom.intreq = IF_RBF;
+       mb();
+
+       if((serdatr & 0x1ff) == 0)
+           status |= UART_LSR_BI;
+       if(serdatr & SDR_OVRUN)
+           status |= UART_LSR_OE;
+
+       ch = serdatr & 0xff;
+       icount->rx++;
+
+#ifdef SERIAL_DEBUG_INTR
+       printk("DR%02x:%02x...", ch, status);
+#endif
+       flag = TTY_NORMAL;
+
+       /*
+        * We don't handle parity or frame errors - but I have left
+        * the code in, since I'm not sure that the errors can't be
+        * detected.
+        */
+
+       if (status & (UART_LSR_BI | UART_LSR_PE |
+                     UART_LSR_FE | UART_LSR_OE)) {
+         /*
+          * For statistics only
+          */
+         if (status & UART_LSR_BI) {
+           status &= ~(UART_LSR_FE | UART_LSR_PE);
+           icount->brk++;
+         } else if (status & UART_LSR_PE)
+           icount->parity++;
+         else if (status & UART_LSR_FE)
+           icount->frame++;
+         if (status & UART_LSR_OE)
+           icount->overrun++;
+
+         /*
+          * Now check to see if character should be
+          * ignored, and mask off conditions which
+          * should be ignored.
+          */
+         if (status & info->ignore_status_mask)
+           goto out;
+
+         status &= info->read_status_mask;
+
+         if (status & (UART_LSR_BI)) {
+#ifdef SERIAL_DEBUG_INTR
+           printk("handling break....");
+#endif
+           flag = TTY_BREAK;
+           if (info->flags & ASYNC_SAK)
+             do_SAK(tty);
+         } else if (status & UART_LSR_PE)
+           flag = TTY_PARITY;
+         else if (status & UART_LSR_FE)
+           flag = TTY_FRAME;
+         if (status & UART_LSR_OE) {
+           /*
+            * Overrun is special, since it's
+            * reported immediately, and doesn't
+            * affect the current character
+            */
+            oe = 1;
+         }
+       }
+       tty_insert_flip_char(tty, ch, flag);
+       if (oe == 1)
+               tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+       tty_flip_buffer_push(tty);
+out:
+       return;
+}
+
+static void transmit_chars(struct async_struct *info)
+{
+       custom.intreq = IF_TBE;
+       mb();
+       if (info->x_char) {
+               custom.serdat = info->x_char | 0x100;
+               mb();
+               info->state->icount.tx++;
+               info->x_char = 0;
+               return;
+       }
+       if (info->xmit.head == info->xmit.tail
+           || info->tty->stopped
+           || info->tty->hw_stopped) {
+               info->IER &= ~UART_IER_THRI;
+               custom.intena = IF_TBE;
+               mb();
+               return;
+       }
+
+       custom.serdat = info->xmit.buf[info->xmit.tail++] | 0x100;
+       mb();
+       info->xmit.tail = info->xmit.tail & (SERIAL_XMIT_SIZE-1);
+       info->state->icount.tx++;
+
+       if (CIRC_CNT(info->xmit.head,
+                    info->xmit.tail,
+                    SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
+               rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+#ifdef SERIAL_DEBUG_INTR
+       printk("THRE...");
+#endif
+       if (info->xmit.head == info->xmit.tail) {
+               custom.intena = IF_TBE;
+               mb();
+               info->IER &= ~UART_IER_THRI;
+       }
+}
+
+static void check_modem_status(struct async_struct *info)
+{
+       unsigned char status = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
+       unsigned char dstatus;
+       struct  async_icount *icount;
+
+       /* Determine bits that have changed */
+       dstatus = status ^ current_ctl_bits;
+       current_ctl_bits = status;
+
+       if (dstatus) {
+               icount = &info->state->icount;
+               /* update input line counters */
+               if (dstatus & SER_DSR)
+                       icount->dsr++;
+               if (dstatus & SER_DCD) {
+                       icount->dcd++;
+#ifdef CONFIG_HARD_PPS
+                       if ((info->flags & ASYNC_HARDPPS_CD) &&
+                           !(status & SER_DCD))
+                               hardpps();
+#endif
+               }
+               if (dstatus & SER_CTS)
+                       icount->cts++;
+               wake_up_interruptible(&info->delta_msr_wait);
+       }
+
+       if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+               printk("ttyS%d CD now %s...", info->line,
+                      (!(status & SER_DCD)) ? "on" : "off");
+#endif
+               if (!(status & SER_DCD))
+                       wake_up_interruptible(&info->open_wait);
+               else {
+#ifdef SERIAL_DEBUG_OPEN
+                       printk("doing serial hangup...");
+#endif
+                       if (info->tty)
+                               tty_hangup(info->tty);
+               }
+       }
+       if (info->flags & ASYNC_CTS_FLOW) {
+               if (info->tty->hw_stopped) {
+                       if (!(status & SER_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+                               printk("CTS tx start...");
+#endif
+                               info->tty->hw_stopped = 0;
+                               info->IER |= UART_IER_THRI;
+                               custom.intena = IF_SETCLR | IF_TBE;
+                               mb();
+                               /* set a pending Tx Interrupt, transmitter should restart now */
+                               custom.intreq = IF_SETCLR | IF_TBE;
+                               mb();
+                               rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+                               return;
+                       }
+               } else {
+                       if ((status & SER_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+                               printk("CTS tx stop...");
+#endif
+                               info->tty->hw_stopped = 1;
+                               info->IER &= ~UART_IER_THRI;
+                               /* disable Tx interrupt and remove any pending interrupts */
+                               custom.intena = IF_TBE;
+                               mb();
+                               custom.intreq = IF_TBE;
+                               mb();
+                       }
+               }
+       }
+}
+
+static irqreturn_t ser_vbl_int( int irq, void *data)
+{
+        /* vbl is just a periodic interrupt we tie into to update modem status */
+       struct async_struct * info = IRQ_ports;
+       /*
+        * TBD - is it better to unregister from this interrupt or to
+        * ignore it if MSI is clear ?
+        */
+       if(info->IER & UART_IER_MSI)
+         check_modem_status(info);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t ser_rx_int(int irq, void *dev_id)
+{
+       struct async_struct * info;
+
+#ifdef SERIAL_DEBUG_INTR
+       printk("ser_rx_int...");
+#endif
+
+       info = IRQ_ports;
+       if (!info || !info->tty)
+               return IRQ_NONE;
+
+       receive_chars(info);
+       info->last_active = jiffies;
+#ifdef SERIAL_DEBUG_INTR
+       printk("end.\n");
+#endif
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t ser_tx_int(int irq, void *dev_id)
+{
+       struct async_struct * info;
+
+       if (custom.serdatr & SDR_TBE) {
+#ifdef SERIAL_DEBUG_INTR
+         printk("ser_tx_int...");
+#endif
+
+         info = IRQ_ports;
+         if (!info || !info->tty)
+               return IRQ_NONE;
+
+         transmit_chars(info);
+         info->last_active = jiffies;
+#ifdef SERIAL_DEBUG_INTR
+         printk("end.\n");
+#endif
+       }
+       return IRQ_HANDLED;
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * 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
+ * rs_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 rs_sched_event(), and they get done here.
+ */
+
+static void do_softint(unsigned long private_)
+{
+       struct async_struct     *info = (struct async_struct *) private_;
+       struct tty_struct       *tty;
+
+       tty = info->tty;
+       if (!tty)
+               return;
+
+       if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event))
+               tty_wakeup(tty);
+}
+
+/*
+ * ---------------------------------------------------------------
+ * Low level utility subroutines for the serial driver:  routines to
+ * figure out the appropriate timeout for an interrupt chain, routines
+ * to initialize and startup a serial port, and routines to shutdown a
+ * serial port.  Useful stuff like that.
+ * ---------------------------------------------------------------
+ */
+
+static int startup(struct async_struct * info)
+{
+       unsigned long flags;
+       int     retval=0;
+       unsigned long page;
+
+       page = get_zeroed_page(GFP_KERNEL);
+       if (!page)
+               return -ENOMEM;
+
+       local_irq_save(flags);
+
+       if (info->flags & ASYNC_INITIALIZED) {
+               free_page(page);
+               goto errout;
+       }
+
+       if (info->xmit.buf)
+               free_page(page);
+       else
+               info->xmit.buf = (unsigned char *) page;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("starting up ttys%d ...", info->line);
+#endif
+
+       /* Clear anything in the input buffer */
+
+       custom.intreq = IF_RBF;
+       mb();
+
+       retval = request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0, "serial status", info);
+       if (retval) {
+         if (serial_isroot()) {
+           if (info->tty)
+             set_bit(TTY_IO_ERROR,
+                     &info->tty->flags);
+           retval = 0;
+         }
+         goto errout;
+       }
+
+       /* enable both Rx and Tx interrupts */
+       custom.intena = IF_SETCLR | IF_RBF | IF_TBE;
+       mb();
+       info->IER = UART_IER_MSI;
+
+       /* remember current state of the DCD and CTS bits */
+       current_ctl_bits = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
+
+       IRQ_ports = info;
+
+       info->MCR = 0;
+       if (info->tty->termios->c_cflag & CBAUD)
+         info->MCR = SER_DTR | SER_RTS;
+       rtsdtr_ctrl(info->MCR);
+
+       if (info->tty)
+               clear_bit(TTY_IO_ERROR, &info->tty->flags);
+       info->xmit.head = info->xmit.tail = 0;
+
+       /*
+        * Set up the tty->alt_speed kludge
+        */
+       if (info->tty) {
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                       info->tty->alt_speed = 57600;
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                       info->tty->alt_speed = 115200;
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                       info->tty->alt_speed = 230400;
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                       info->tty->alt_speed = 460800;
+       }
+
+       /*
+        * and set the speed of the serial port
+        */
+       change_speed(info, NULL);
+
+       info->flags |= ASYNC_INITIALIZED;
+       local_irq_restore(flags);
+       return 0;
+
+errout:
+       local_irq_restore(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 shutdown(struct async_struct * info)
+{
+       unsigned long   flags;
+       struct serial_state *state;
+
+       if (!(info->flags & ASYNC_INITIALIZED))
+               return;
+
+       state = info->state;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("Shutting down serial port %d ....\n", info->line);
+#endif
+
+       local_irq_save(flags); /* 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(&info->delta_msr_wait);
+
+       IRQ_ports = NULL;
+
+       /*
+        * Free the IRQ, if necessary
+        */
+       free_irq(IRQ_AMIGA_VERTB, info);
+
+       if (info->xmit.buf) {
+               free_page((unsigned long) info->xmit.buf);
+               info->xmit.buf = NULL;
+       }
+
+       info->IER = 0;
+       custom.intena = IF_RBF | IF_TBE;
+       mb();
+
+       /* disable break condition */
+       custom.adkcon = AC_UARTBRK;
+       mb();
+
+       if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
+               info->MCR &= ~(SER_DTR|SER_RTS);
+       rtsdtr_ctrl(info->MCR);
+
+       if (info->tty)
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       info->flags &= ~ASYNC_INITIALIZED;
+       local_irq_restore(flags);
+}
+
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct async_struct *info,
+                        struct ktermios *old_termios)
+{
+       int     quot = 0, baud_base, baud;
+       unsigned cflag, cval = 0;
+       int     bits;
+       unsigned long   flags;
+
+       if (!info->tty || !info->tty->termios)
+               return;
+       cflag = info->tty->termios->c_cflag;
+
+       /* Byte size is always 8 bits plus parity bit if requested */
+
+       cval = 3; bits = 10;
+       if (cflag & CSTOPB) {
+               cval |= 0x04;
+               bits++;
+       }
+       if (cflag & PARENB) {
+               cval |= UART_LCR_PARITY;
+               bits++;
+       }
+       if (!(cflag & PARODD))
+               cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+       if (cflag & CMSPAR)
+               cval |= UART_LCR_SPAR;
+#endif
+
+       /* Determine divisor based on baud rate */
+       baud = tty_get_baud_rate(info->tty);
+       if (!baud)
+               baud = 9600;    /* B0 transition handled in rs_set_termios */
+       baud_base = info->state->baud_base;
+       if (baud == 38400 &&
+           ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+               quot = info->state->custom_divisor;
+       else {
+               if (baud == 134)
+                       /* Special case since 134 is really 134.5 */
+                       quot = (2*baud_base / 269);
+               else if (baud)
+                       quot = baud_base / baud;
+       }
+       /* If the quotient is zero refuse the change */
+       if (!quot && old_termios) {
+               /* FIXME: Will need updating for new tty in the end */
+               info->tty->termios->c_cflag &= ~CBAUD;
+               info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
+               baud = tty_get_baud_rate(info->tty);
+               if (!baud)
+                       baud = 9600;
+               if (baud == 38400 &&
+                   ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+                       quot = info->state->custom_divisor;
+               else {
+                       if (baud == 134)
+                               /* Special case since 134 is really 134.5 */
+                               quot = (2*baud_base / 269);
+                       else if (baud)
+                               quot = baud_base / baud;
+               }
+       }
+       /* As a last resort, if the quotient is zero, default to 9600 bps */
+       if (!quot)
+               quot = baud_base / 9600;
+       info->quot = quot;
+       info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
+       info->timeout += HZ/50;         /* Add .02 seconds of slop */
+
+       /* CTS flow control flag and modem status interrupts */
+       info->IER &= ~UART_IER_MSI;
+       if (info->flags & ASYNC_HARDPPS_CD)
+               info->IER |= UART_IER_MSI;
+       if (cflag & CRTSCTS) {
+               info->flags |= ASYNC_CTS_FLOW;
+               info->IER |= UART_IER_MSI;
+       } else
+               info->flags &= ~ASYNC_CTS_FLOW;
+       if (cflag & CLOCAL)
+               info->flags &= ~ASYNC_CHECK_CD;
+       else {
+               info->flags |= ASYNC_CHECK_CD;
+               info->IER |= UART_IER_MSI;
+       }
+       /* TBD:
+        * Does clearing IER_MSI imply that we should disable the VBL interrupt ?
+        */
+
+       /*
+        * Set up parity check flag
+        */
+
+       info->read_status_mask = UART_LSR_OE | UART_LSR_DR;
+       if (I_INPCK(info->tty))
+               info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+       if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+               info->read_status_mask |= UART_LSR_BI;
+
+       /*
+        * Characters to ignore
+        */
+       info->ignore_status_mask = 0;
+       if (I_IGNPAR(info->tty))
+               info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+       if (I_IGNBRK(info->tty)) {
+               info->ignore_status_mask |= UART_LSR_BI;
+               /*
+                * If we're ignore parity and break indicators, ignore 
+                * overruns too.  (For real raw support).
+                */
+               if (I_IGNPAR(info->tty))
+                       info->ignore_status_mask |= UART_LSR_OE;
+       }
+       /*
+        * !!! ignore all characters if CREAD is not set
+        */
+       if ((cflag & CREAD) == 0)
+               info->ignore_status_mask |= UART_LSR_DR;
+       local_irq_save(flags);
+
+       {
+         short serper;
+
+       /* Set up the baud rate */
+         serper = quot - 1;
+
+       /* Enable or disable parity bit */
+
+       if(cval & UART_LCR_PARITY)
+         serper |= (SERPER_PARENB);
+
+       custom.serper = serper;
+       mb();
+       }
+
+       info->LCR = cval;                               /* Save LCR */
+       local_irq_restore(flags);
+}
+
+static int rs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct async_struct *info;
+       unsigned long flags;
+
+       info = tty->driver_data;
+
+       if (serial_paranoia_check(info, tty->name, "rs_put_char"))
+               return 0;
+
+       if (!info->xmit.buf)
+               return 0;
+
+       local_irq_save(flags);
+       if (CIRC_SPACE(info->xmit.head,
+                      info->xmit.tail,
+                      SERIAL_XMIT_SIZE) == 0) {
+               local_irq_restore(flags);
+               return 0;
+       }
+
+       info->xmit.buf[info->xmit.head++] = ch;
+       info->xmit.head &= SERIAL_XMIT_SIZE-1;
+       local_irq_restore(flags);
+       return 1;
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+       struct async_struct *info = tty->driver_data;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->name, "rs_flush_chars"))
+               return;
+
+       if (info->xmit.head == info->xmit.tail
+           || tty->stopped
+           || tty->hw_stopped
+           || !info->xmit.buf)
+               return;
+
+       local_irq_save(flags);
+       info->IER |= UART_IER_THRI;
+       custom.intena = IF_SETCLR | IF_TBE;
+       mb();
+       /* set a pending Tx Interrupt, transmitter should restart now */
+       custom.intreq = IF_SETCLR | IF_TBE;
+       mb();
+       local_irq_restore(flags);
+}
+
+static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count)
+{
+       int     c, ret = 0;
+       struct async_struct *info;
+       unsigned long flags;
+
+       info = tty->driver_data;
+
+       if (serial_paranoia_check(info, tty->name, "rs_write"))
+               return 0;
+
+       if (!info->xmit.buf)
+               return 0;
+
+       local_irq_save(flags);
+       while (1) {
+               c = CIRC_SPACE_TO_END(info->xmit.head,
+                                     info->xmit.tail,
+                                     SERIAL_XMIT_SIZE);
+               if (count < c)
+                       c = count;
+               if (c <= 0) {
+                       break;
+               }
+               memcpy(info->xmit.buf + info->xmit.head, buf, c);
+               info->xmit.head = ((info->xmit.head + c) &
+                                  (SERIAL_XMIT_SIZE-1));
+               buf += c;
+               count -= c;
+               ret += c;
+       }
+       local_irq_restore(flags);
+
+       if (info->xmit.head != info->xmit.tail
+           && !tty->stopped
+           && !tty->hw_stopped
+           && !(info->IER & UART_IER_THRI)) {
+               info->IER |= UART_IER_THRI;
+               local_irq_disable();
+               custom.intena = IF_SETCLR | IF_TBE;
+               mb();
+               /* set a pending Tx Interrupt, transmitter should restart now */
+               custom.intreq = IF_SETCLR | IF_TBE;
+               mb();
+               local_irq_restore(flags);
+       }
+       return ret;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+       struct async_struct *info = tty->driver_data;
+
+       if (serial_paranoia_check(info, tty->name, "rs_write_room"))
+               return 0;
+       return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+       struct async_struct *info = tty->driver_data;
+
+       if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer"))
+               return 0;
+       return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+       struct async_struct *info = tty->driver_data;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->name, "rs_flush_buffer"))
+               return;
+       local_irq_save(flags);
+       info->xmit.head = info->xmit.tail = 0;
+       local_irq_restore(flags);
+       tty_wakeup(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void rs_send_xchar(struct tty_struct *tty, char ch)
+{
+       struct async_struct *info = tty->driver_data;
+        unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->name, "rs_send_char"))
+               return;
+
+       info->x_char = ch;
+       if (ch) {
+               /* Make sure transmit interrupts are on */
+
+               /* Check this ! */
+               local_irq_save(flags);
+               if(!(custom.intenar & IF_TBE)) {
+                   custom.intena = IF_SETCLR | IF_TBE;
+                   mb();
+                   /* set a pending Tx Interrupt, transmitter should restart now */
+                   custom.intreq = IF_SETCLR | IF_TBE;
+                   mb();
+               }
+               local_irq_restore(flags);
+
+               info->IER |= UART_IER_THRI;
+       }
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ * 
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+       struct async_struct *info = tty->driver_data;
+       unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+       char    buf[64];
+
+       printk("throttle %s: %d....\n", tty_name(tty, buf),
+              tty->ldisc.chars_in_buffer(tty));
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "rs_throttle"))
+               return;
+
+       if (I_IXOFF(tty))
+               rs_send_xchar(tty, STOP_CHAR(tty));
+
+       if (tty->termios->c_cflag & CRTSCTS)
+               info->MCR &= ~SER_RTS;
+
+       local_irq_save(flags);
+       rtsdtr_ctrl(info->MCR);
+       local_irq_restore(flags);
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+       struct async_struct *info = tty->driver_data;
+       unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+       char    buf[64];
+
+       printk("unthrottle %s: %d....\n", tty_name(tty, buf),
+              tty->ldisc.chars_in_buffer(tty));
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "rs_unthrottle"))
+               return;
+
+       if (I_IXOFF(tty)) {
+               if (info->x_char)
+                       info->x_char = 0;
+               else
+                       rs_send_xchar(tty, START_CHAR(tty));
+       }
+       if (tty->termios->c_cflag & CRTSCTS)
+               info->MCR |= SER_RTS;
+       local_irq_save(flags);
+       rtsdtr_ctrl(info->MCR);
+       local_irq_restore(flags);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct async_struct * info,
+                          struct serial_struct __user * retinfo)
+{
+       struct serial_struct tmp;
+       struct serial_state *state = info->state;
+   
+       if (!retinfo)
+               return -EFAULT;
+       memset(&tmp, 0, sizeof(tmp));
+       tty_lock();
+       tmp.type = state->type;
+       tmp.line = state->line;
+       tmp.port = state->port;
+       tmp.irq = state->irq;
+       tmp.flags = state->flags;
+       tmp.xmit_fifo_size = state->xmit_fifo_size;
+       tmp.baud_base = state->baud_base;
+       tmp.close_delay = state->close_delay;
+       tmp.closing_wait = state->closing_wait;
+       tmp.custom_divisor = state->custom_divisor;
+       tty_unlock();
+       if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+               return -EFAULT;
+       return 0;
+}
+
+static int set_serial_info(struct async_struct * info,
+                          struct serial_struct __user * new_info)
+{
+       struct serial_struct new_serial;
+       struct serial_state old_state, *state;
+       unsigned int            change_irq,change_port;
+       int                     retval = 0;
+
+       if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+               return -EFAULT;
+
+       tty_lock();
+       state = info->state;
+       old_state = *state;
+  
+       change_irq = new_serial.irq != state->irq;
+       change_port = (new_serial.port != state->port);
+       if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size)) {
+         tty_unlock();
+         return -EINVAL;
+       }
+  
+       if (!serial_isroot()) {
+               if ((new_serial.baud_base != state->baud_base) ||
+                   (new_serial.close_delay != state->close_delay) ||
+                   (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
+                   ((new_serial.flags & ~ASYNC_USR_MASK) !=
+                    (state->flags & ~ASYNC_USR_MASK)))
+                       return -EPERM;
+               state->flags = ((state->flags & ~ASYNC_USR_MASK) |
+                              (new_serial.flags & ASYNC_USR_MASK));
+               info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+                              (new_serial.flags & ASYNC_USR_MASK));
+               state->custom_divisor = new_serial.custom_divisor;
+               goto check_and_exit;
+       }
+
+       if (new_serial.baud_base < 9600) {
+               tty_unlock();
+               return -EINVAL;
+       }
+
+       /*
+        * OK, past this point, all the error checking has been done.
+        * At this point, we start making changes.....
+        */
+
+       state->baud_base = new_serial.baud_base;
+       state->flags = ((state->flags & ~ASYNC_FLAGS) |
+                       (new_serial.flags & ASYNC_FLAGS));
+       info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) |
+                      (info->flags & ASYNC_INTERNAL_FLAGS));
+       state->custom_divisor = new_serial.custom_divisor;
+       state->close_delay = new_serial.close_delay * HZ/100;
+       state->closing_wait = new_serial.closing_wait * HZ/100;
+       info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+check_and_exit:
+       if (info->flags & ASYNC_INITIALIZED) {
+               if (((old_state.flags & ASYNC_SPD_MASK) !=
+                    (state->flags & ASYNC_SPD_MASK)) ||
+                   (old_state.custom_divisor != state->custom_divisor)) {
+                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                               info->tty->alt_speed = 57600;
+                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                               info->tty->alt_speed = 115200;
+                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                               info->tty->alt_speed = 230400;
+                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                               info->tty->alt_speed = 460800;
+                       change_speed(info, NULL);
+               }
+       } else
+               retval = startup(info);
+       tty_unlock();
+       return retval;
+}
+
+
+/*
+ * 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 get_lsr_info(struct async_struct * info, unsigned int __user *value)
+{
+       unsigned char status;
+       unsigned int result;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       status = custom.serdatr;
+       mb();
+       local_irq_restore(flags);
+       result = ((status & SDR_TSRE) ? TIOCSER_TEMT : 0);
+       if (copy_to_user(value, &result, sizeof(int)))
+               return -EFAULT;
+       return 0;
+}
+
+
+static int rs_tiocmget(struct tty_struct *tty)
+{
+       struct async_struct * info = tty->driver_data;
+       unsigned char control, status;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
+               return -ENODEV;
+       if (tty->flags & (1 << TTY_IO_ERROR))
+               return -EIO;
+
+       control = info->MCR;
+       local_irq_save(flags);
+       status = ciab.pra;
+       local_irq_restore(flags);
+       return    ((control & SER_RTS) ? TIOCM_RTS : 0)
+               | ((control & SER_DTR) ? TIOCM_DTR : 0)
+               | (!(status  & SER_DCD) ? TIOCM_CAR : 0)
+               | (!(status  & SER_DSR) ? TIOCM_DSR : 0)
+               | (!(status  & SER_CTS) ? TIOCM_CTS : 0);
+}
+
+static int rs_tiocmset(struct tty_struct *tty, unsigned int set,
+                                               unsigned int clear)
+{
+       struct async_struct * info = tty->driver_data;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
+               return -ENODEV;
+       if (tty->flags & (1 << TTY_IO_ERROR))
+               return -EIO;
+
+       local_irq_save(flags);
+       if (set & TIOCM_RTS)
+               info->MCR |= SER_RTS;
+       if (set & TIOCM_DTR)
+               info->MCR |= SER_DTR;
+       if (clear & TIOCM_RTS)
+               info->MCR &= ~SER_RTS;
+       if (clear & TIOCM_DTR)
+               info->MCR &= ~SER_DTR;
+       rtsdtr_ctrl(info->MCR);
+       local_irq_restore(flags);
+       return 0;
+}
+
+/*
+ * rs_break() --- routine which turns the break handling on or off
+ */
+static int rs_break(struct tty_struct *tty, int break_state)
+{
+       struct async_struct * info = tty->driver_data;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->name, "rs_break"))
+               return -EINVAL;
+
+       local_irq_save(flags);
+       if (break_state == -1)
+         custom.adkcon = AC_SETCLR | AC_UARTBRK;
+       else
+         custom.adkcon = AC_UARTBRK;
+       mb();
+       local_irq_restore(flags);
+       return 0;
+}
+
+/*
+ * 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.
+ */
+static int rs_get_icount(struct tty_struct *tty,
+                               struct serial_icounter_struct *icount)
+{
+       struct async_struct *info = tty->driver_data;
+       struct async_icount cnow;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       cnow = info->state->icount;
+       local_irq_restore(flags);
+       icount->cts = cnow.cts;
+       icount->dsr = cnow.dsr;
+       icount->rng = cnow.rng;
+       icount->dcd = cnow.dcd;
+       icount->rx = cnow.rx;
+       icount->tx = cnow.tx;
+       icount->frame = cnow.frame;
+       icount->overrun = cnow.overrun;
+       icount->parity = cnow.parity;
+       icount->brk = cnow.brk;
+       icount->buf_overrun = cnow.buf_overrun;
+
+       return 0;
+}
+
+static int rs_ioctl(struct tty_struct *tty,
+                   unsigned int cmd, unsigned long arg)
+{
+       struct async_struct * info = tty->driver_data;
+       struct async_icount cprev, cnow;        /* kernel counter temps */
+       void __user *argp = (void __user *)arg;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
+               return -ENODEV;
+
+       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+           (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
+           (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+               if (tty->flags & (1 << TTY_IO_ERROR))
+                   return -EIO;
+       }
+
+       switch (cmd) {
+               case TIOCGSERIAL:
+                       return get_serial_info(info, argp);
+               case TIOCSSERIAL:
+                       return set_serial_info(info, argp);
+               case TIOCSERCONFIG:
+                       return 0;
+
+               case TIOCSERGETLSR: /* Get line status register */
+                       return get_lsr_info(info, argp);
+
+               case TIOCSERGSTRUCT:
+                       if (copy_to_user(argp,
+                                        info, sizeof(struct async_struct)))
+                               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:
+                       local_irq_save(flags);
+                       /* note the counters on entry */
+                       cprev = info->state->icount;
+                       local_irq_restore(flags);
+                       while (1) {
+                               interruptible_sleep_on(&info->delta_msr_wait);
+                               /* see if a signal did it */
+                               if (signal_pending(current))
+                                       return -ERESTARTSYS;
+                               local_irq_save(flags);
+                               cnow = info->state->icount; /* atomic copy */
+                               local_irq_restore(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 */
+
+               case TIOCSERGWILD:
+               case TIOCSERSWILD:
+                       /* "setserial -W" is called in Debian boot */
+                       printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
+                       return 0;
+
+               default:
+                       return -ENOIOCTLCMD;
+               }
+       return 0;
+}
+
+static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+       struct async_struct *info = tty->driver_data;
+       unsigned long flags;
+       unsigned int cflag = tty->termios->c_cflag;
+
+       change_speed(info, old_termios);
+
+       /* Handle transition to B0 status */
+       if ((old_termios->c_cflag & CBAUD) &&
+           !(cflag & CBAUD)) {
+               info->MCR &= ~(SER_DTR|SER_RTS);
+               local_irq_save(flags);
+               rtsdtr_ctrl(info->MCR);
+               local_irq_restore(flags);
+       }
+
+       /* Handle transition away from B0 status */
+       if (!(old_termios->c_cflag & CBAUD) &&
+           (cflag & CBAUD)) {
+               info->MCR |= SER_DTR;
+               if (!(tty->termios->c_cflag & CRTSCTS) || 
+                   !test_bit(TTY_THROTTLED, &tty->flags)) {
+                       info->MCR |= SER_RTS;
+               }
+               local_irq_save(flags);
+               rtsdtr_ctrl(info->MCR);
+               local_irq_restore(flags);
+       }
+
+       /* Handle turning off CRTSCTS */
+       if ((old_termios->c_cflag & CRTSCTS) &&
+           !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               rs_start(tty);
+       }
+
+#if 0
+       /*
+        * No need to wake up processes in open wait, since they
+        * sample the CLOCAL flag once, and don't recheck it.
+        * XXX  It's not clear whether the current behavior is correct
+        * or not.  Hence, this may change.....
+        */
+       if (!(old_termios->c_cflag & CLOCAL) &&
+           (tty->termios->c_cflag & CLOCAL))
+               wake_up_interruptible(&info->open_wait);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_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 rs_close(struct tty_struct *tty, struct file * filp)
+{
+       struct async_struct * info = tty->driver_data;
+       struct serial_state *state;
+       unsigned long flags;
+
+       if (!info || serial_paranoia_check(info, tty->name, "rs_close"))
+               return;
+
+       state = info->state;
+
+       local_irq_save(flags);
+
+       if (tty_hung_up_p(filp)) {
+               DBG_CNT("before DEC-hung");
+               local_irq_restore(flags);
+               return;
+       }
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("rs_close ttys%d, count = %d\n", info->line, state->count);
+#endif
+       if ((tty->count == 1) && (state->count != 1)) {
+               /*
+                * Uh, oh.  tty->count is 1, which means that the tty
+                * structure will be freed.  state->count should always
+                * be one in these conditions.  If it's greater than
+                * one, we've got real problems, since it means the
+                * serial port won't be shutdown.
+                */
+               printk("rs_close: bad serial port count; tty->count is 1, "
+                      "state->count is %d\n", state->count);
+               state->count = 1;
+       }
+       if (--state->count < 0) {
+               printk("rs_close: bad serial port count for ttys%d: %d\n",
+                      info->line, state->count);
+               state->count = 0;
+       }
+       if (state->count) {
+               DBG_CNT("before DEC-2");
+               local_irq_restore(flags);
+               return;
+       }
+       info->flags |= ASYNC_CLOSING;
+       /*
+        * 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 (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+               tty_wait_until_sent(tty, info->closing_wait);
+       /*
+        * At this point we stop accepting input.  To do this, we
+        * disable the receive line status interrupts, and tell the
+        * interrupt driver to stop checking the data ready bit in the
+        * line status register.
+        */
+       info->read_status_mask &= ~UART_LSR_DR;
+       if (info->flags & ASYNC_INITIALIZED) {
+               /* disable receive interrupts */
+               custom.intena = IF_RBF;
+               mb();
+               /* clear any pending receive interrupt */
+               custom.intreq = IF_RBF;
+               mb();
+
+               /*
+                * Before we drop DTR, make sure the UART transmitter
+                * has completely drained; this is especially
+                * important if there is a transmit FIFO!
+                */
+               rs_wait_until_sent(tty, info->timeout);
+       }
+       shutdown(info);
+       rs_flush_buffer(tty);
+               
+       tty_ldisc_flush(tty);
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = NULL;
+       if (info->blocked_open) {
+               if (info->close_delay) {
+                       msleep_interruptible(jiffies_to_msecs(info->close_delay));
+               }
+               wake_up_interruptible(&info->open_wait);
+       }
+       info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
+       wake_up_interruptible(&info->close_wait);
+       local_irq_restore(flags);
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       struct async_struct * info = tty->driver_data;
+       unsigned long orig_jiffies, char_time;
+       int tty_was_locked = tty_locked();
+       int lsr;
+
+       if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
+               return;
+
+       if (info->xmit_fifo_size == 0)
+               return; /* Just in case.... */
+
+       orig_jiffies = jiffies;
+
+       /*
+        * tty_wait_until_sent is called from lots of places,
+        * with or without the BTM.
+        */
+       if (!tty_was_locked)
+               tty_lock();
+       /*
+        * Set the check interval to be 1/5 of the estimated time to
+        * send a single character, and make it at least 1.  The check
+        * interval should also be less than the timeout.
+        * 
+        * Note: we have to use pretty tight timings here to satisfy
+        * the NIST-PCTS.
+        */
+       char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
+       char_time = char_time / 5;
+       if (char_time == 0)
+               char_time = 1;
+       if (timeout)
+         char_time = min_t(unsigned long, char_time, timeout);
+       /*
+        * If the transmitter hasn't cleared in twice the approximate
+        * amount of time to send the entire FIFO, it probably won't
+        * ever clear.  This assumes the UART isn't doing flow
+        * control, which is currently the case.  Hence, if it ever
+        * takes longer than info->timeout, this is probably due to a
+        * UART bug of some kind.  So, we clamp the timeout parameter at
+        * 2*info->timeout.
+        */
+       if (!timeout || timeout > 2*info->timeout)
+               timeout = 2*info->timeout;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+       printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
+       printk("jiff=%lu...", jiffies);
+#endif
+       while(!((lsr = custom.serdatr) & SDR_TSRE)) {
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+               printk("serdatr = %d (jiff=%lu)...", lsr, jiffies);
+#endif
+               msleep_interruptible(jiffies_to_msecs(char_time));
+               if (signal_pending(current))
+                       break;
+               if (timeout && time_after(jiffies, orig_jiffies + timeout))
+                       break;
+       }
+       __set_current_state(TASK_RUNNING);
+       if (!tty_was_locked)
+               tty_unlock();
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+       printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+#endif
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rs_hangup(struct tty_struct *tty)
+{
+       struct async_struct * info = tty->driver_data;
+       struct serial_state *state = info->state;
+
+       if (serial_paranoia_check(info, tty->name, "rs_hangup"))
+               return;
+
+       state = info->state;
+
+       rs_flush_buffer(tty);
+       shutdown(info);
+       info->event = 0;
+       state->count = 0;
+       info->flags &= ~ASYNC_NORMAL_ACTIVE;
+       info->tty = NULL;
+       wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+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;
+       unsigned long   flags;
+
+       /*
+        * If the device is in the middle of being closed, then block
+        * until it's done, and then try again.
+        */
+       if (tty_hung_up_p(filp) ||
+           (info->flags & ASYNC_CLOSING)) {
+               if (info->flags & ASYNC_CLOSING)
+                       interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+               return ((info->flags & ASYNC_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS);
+#else
+               return -EAGAIN;
+#endif
+       }
+
+       /*
+        * If non-blocking mode is set, or the port is not enabled,
+        * then make the check up front and then exit.
+        */
+       if ((filp->f_flags & O_NONBLOCK) ||
+           (tty->flags & (1 << TTY_IO_ERROR))) {
+               info->flags |= ASYNC_NORMAL_ACTIVE;
+               return 0;
+       }
+
+       if (tty->termios->c_cflag & CLOCAL)
+               do_clocal = 1;
+
+       /*
+        * Block waiting for the carrier detect and the line to become
+        * free (i.e., not in use by the callout).  While we are in
+        * this loop, state->count is dropped by one, so that
+        * rs_close() knows when to free things.  We restore it upon
+        * exit, either normal or abnormal.
+        */
+       retval = 0;
+       add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+       printk("block_til_ready before block: ttys%d, count = %d\n",
+              state->line, state->count);
+#endif
+       local_irq_save(flags);
+       if (!tty_hung_up_p(filp)) {
+               extra_count = 1;
+               state->count--;
+       }
+       local_irq_restore(flags);
+       info->blocked_open++;
+       while (1) {
+               local_irq_save(flags);
+               if (tty->termios->c_cflag & CBAUD)
+                       rtsdtr_ctrl(SER_DTR|SER_RTS);
+               local_irq_restore(flags);
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (tty_hung_up_p(filp) ||
+                   !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+                       if (info->flags & ASYNC_HUP_NOTIFY)
+                               retval = -EAGAIN;
+                       else
+                               retval = -ERESTARTSYS;
+#else
+                       retval = -EAGAIN;
+#endif
+                       break;
+               }
+               if (!(info->flags & ASYNC_CLOSING) &&
+                   (do_clocal || (!(ciab.pra & SER_DCD)) ))
+                       break;
+               if (signal_pending(current)) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+#ifdef SERIAL_DEBUG_OPEN
+               printk("block_til_ready blocking: ttys%d, count = %d\n",
+                      info->line, state->count);
+#endif
+               tty_unlock();
+               schedule();
+               tty_lock();
+       }
+       __set_current_state(TASK_RUNNING);
+       remove_wait_queue(&info->open_wait, &wait);
+       if (extra_count)
+               state->count++;
+       info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+       printk("block_til_ready after blocking: ttys%d, count = %d\n",
+              info->line, state->count);
+#endif
+       if (retval)
+               return retval;
+       info->flags |= ASYNC_NORMAL_ACTIVE;
+       return 0;
+}
+
+static int get_async_struct(int line, struct async_struct **ret_info)
+{
+       struct async_struct *info;
+       struct serial_state *sstate;
+
+       sstate = rs_table + line;
+       sstate->count++;
+       if (sstate->info) {
+               *ret_info = sstate->info;
+               return 0;
+       }
+       info = kzalloc(sizeof(struct async_struct), GFP_KERNEL);
+       if (!info) {
+               sstate->count--;
+               return -ENOMEM;
+       }
+#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->xmit_fifo_size = sstate->xmit_fifo_size;
+       info->line = line;
+       tasklet_init(&info->tlet, do_softint, (unsigned long)info);
+       info->state = sstate;
+       if (sstate->info) {
+               kfree(info);
+               *ret_info = sstate->info;
+               return 0;
+       }
+       *ret_info = sstate->info = info;
+       return 0;
+}
+
+/*
+ * 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 rs_open(struct tty_struct *tty, struct file * filp)
+{
+       struct async_struct     *info;
+       int                     retval, line;
+
+       line = tty->index;
+       if ((line < 0) || (line >= NR_PORTS)) {
+               return -ENODEV;
+       }
+       retval = get_async_struct(line, &info);
+       if (retval) {
+               return retval;
+       }
+       tty->driver_data = info;
+       info->tty = tty;
+       if (serial_paranoia_check(info, tty->name, "rs_open"))
+               return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("rs_open %s, count = %d\n", tty->name, info->state->count);
+#endif
+       info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+       /*
+        * If the port is the middle of closing, bail out now
+        */
+       if (tty_hung_up_p(filp) ||
+           (info->flags & ASYNC_CLOSING)) {
+               if (info->flags & ASYNC_CLOSING)
+                       interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+               return ((info->flags & ASYNC_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS);
+#else
+               return -EAGAIN;
+#endif
+       }
+
+       /*
+        * Start up serial port
+        */
+       retval = startup(info);
+       if (retval) {
+               return retval;
+       }
+
+       retval = block_til_ready(tty, filp, info);
+       if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+               printk("rs_open returning after block_til_ready with %d\n",
+                      retval);
+#endif
+               return retval;
+       }
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("rs_open %s successful...", tty->name);
+#endif
+       return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static inline void line_info(struct seq_file *m, struct serial_state *state)
+{
+       struct async_struct *info = state->info, scr_info;
+       char    stat_buf[30], control, status;
+       unsigned long flags;
+
+       seq_printf(m, "%d: uart:amiga_builtin",state->line);
+
+       /*
+        * Figure out the current RS-232 lines
+        */
+       if (!info) {
+               info = &scr_info;       /* This is just for serial_{in,out} */
+
+               info->magic = SERIAL_MAGIC;
+               info->flags = state->flags;
+               info->quot = 0;
+               info->tty = NULL;
+       }
+       local_irq_save(flags);
+       status = ciab.pra;
+       control = info ? info->MCR : status;
+       local_irq_restore(flags);
+
+       stat_buf[0] = 0;
+       stat_buf[1] = 0;
+       if(!(control & SER_RTS))
+               strcat(stat_buf, "|RTS");
+       if(!(status & SER_CTS))
+               strcat(stat_buf, "|CTS");
+       if(!(control & SER_DTR))
+               strcat(stat_buf, "|DTR");
+       if(!(status & SER_DSR))
+               strcat(stat_buf, "|DSR");
+       if(!(status & SER_DCD))
+               strcat(stat_buf, "|CD");
+
+       if (info->quot) {
+               seq_printf(m, " baud:%d", state->baud_base / info->quot);
+       }
+
+       seq_printf(m, " tx:%d rx:%d", state->icount.tx, state->icount.rx);
+
+       if (state->icount.frame)
+               seq_printf(m, " fe:%d", state->icount.frame);
+
+       if (state->icount.parity)
+               seq_printf(m, " pe:%d", state->icount.parity);
+
+       if (state->icount.brk)
+               seq_printf(m, " brk:%d", state->icount.brk);
+
+       if (state->icount.overrun)
+               seq_printf(m, " oe:%d", state->icount.overrun);
+
+       /*
+        * Last thing is the RS-232 status lines
+        */
+       seq_printf(m, " %s\n", stat_buf+1);
+}
+
+static int rs_proc_show(struct seq_file *m, void *v)
+{
+       seq_printf(m, "serinfo:1.0 driver:%s\n", serial_version);
+       line_info(m, &rs_table[0]);
+       return 0;
+}
+
+static int rs_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, rs_proc_show, NULL);
+}
+
+static const struct file_operations rs_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = rs_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/*
+ * ---------------------------------------------------------------------
+ * rs_init() and friends
+ *
+ * rs_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static void show_serial_version(void)
+{
+       printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
+}
+
+
+static const struct tty_operations serial_ops = {
+       .open = rs_open,
+       .close = rs_close,
+       .write = rs_write,
+       .put_char = rs_put_char,
+       .flush_chars = rs_flush_chars,
+       .write_room = rs_write_room,
+       .chars_in_buffer = rs_chars_in_buffer,
+       .flush_buffer = rs_flush_buffer,
+       .ioctl = rs_ioctl,
+       .throttle = rs_throttle,
+       .unthrottle = rs_unthrottle,
+       .set_termios = rs_set_termios,
+       .stop = rs_stop,
+       .start = rs_start,
+       .hangup = rs_hangup,
+       .break_ctl = rs_break,
+       .send_xchar = rs_send_xchar,
+       .wait_until_sent = rs_wait_until_sent,
+       .tiocmget = rs_tiocmget,
+       .tiocmset = rs_tiocmset,
+       .get_icount = rs_get_icount,
+       .proc_fops = &rs_proc_fops,
+};
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+static int __init amiga_serial_probe(struct platform_device *pdev)
+{
+       unsigned long flags;
+       struct serial_state * state;
+       int error;
+
+       serial_driver = alloc_tty_driver(1);
+       if (!serial_driver)
+               return -ENOMEM;
+
+       IRQ_ports = NULL;
+
+       show_serial_version();
+
+       /* Initialize the tty_driver structure */
+
+       serial_driver->owner = THIS_MODULE;
+       serial_driver->driver_name = "amiserial";
+       serial_driver->name = "ttyS";
+       serial_driver->major = TTY_MAJOR;
+       serial_driver->minor_start = 64;
+       serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
+       serial_driver->subtype = SERIAL_TYPE_NORMAL;
+       serial_driver->init_termios = tty_std_termios;
+       serial_driver->init_termios.c_cflag =
+               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       serial_driver->flags = TTY_DRIVER_REAL_RAW;
+       tty_set_operations(serial_driver, &serial_ops);
+
+       error = tty_register_driver(serial_driver);
+       if (error)
+               goto fail_put_tty_driver;
+
+       state = rs_table;
+       state->magic = SSTATE_MAGIC;
+       state->port = (int)&custom.serdatr; /* Just to give it a value */
+       state->line = 0;
+       state->custom_divisor = 0;
+       state->close_delay = 5*HZ/10;
+       state->closing_wait = 30*HZ;
+       state->icount.cts = state->icount.dsr = 
+         state->icount.rng = state->icount.dcd = 0;
+       state->icount.rx = state->icount.tx = 0;
+       state->icount.frame = state->icount.parity = 0;
+       state->icount.overrun = state->icount.brk = 0;
+
+       printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n",
+                      state->line);
+
+       /* Hardware set up */
+
+       state->baud_base = amiga_colorclock;
+       state->xmit_fifo_size = 1;
+
+       /* set ISRs, and then disable the rx interrupts */
+       error = request_irq(IRQ_AMIGA_TBE, ser_tx_int, 0, "serial TX", state);
+       if (error)
+               goto fail_unregister;
+
+       error = request_irq(IRQ_AMIGA_RBF, ser_rx_int, IRQF_DISABLED,
+                           "serial RX", state);
+       if (error)
+               goto fail_free_irq;
+
+       local_irq_save(flags);
+
+       /* turn off Rx and Tx interrupts */
+       custom.intena = IF_RBF | IF_TBE;
+       mb();
+
+       /* clear any pending interrupt */
+       custom.intreq = IF_RBF | IF_TBE;
+       mb();
+
+       local_irq_restore(flags);
+
+       /*
+        * set the appropriate directions for the modem control flags,
+        * and clear RTS and DTR
+        */
+       ciab.ddra |= (SER_DTR | SER_RTS);   /* outputs */
+       ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR);  /* inputs */
+
+       platform_set_drvdata(pdev, state);
+
+       return 0;
+
+fail_free_irq:
+       free_irq(IRQ_AMIGA_TBE, state);
+fail_unregister:
+       tty_unregister_driver(serial_driver);
+fail_put_tty_driver:
+       put_tty_driver(serial_driver);
+       return error;
+}
+
+static int __exit amiga_serial_remove(struct platform_device *pdev)
+{
+       int error;
+       struct serial_state *state = platform_get_drvdata(pdev);
+       struct async_struct *info = state->info;
+
+       /* printk("Unloading %s: version %s\n", serial_name, serial_version); */
+       tasklet_kill(&info->tlet);
+       if ((error = tty_unregister_driver(serial_driver)))
+               printk("SERIAL: failed to unregister serial driver (%d)\n",
+                      error);
+       put_tty_driver(serial_driver);
+
+       rs_table[0].info = NULL;
+       kfree(info);
+
+       free_irq(IRQ_AMIGA_TBE, rs_table);
+       free_irq(IRQ_AMIGA_RBF, rs_table);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return error;
+}
+
+static struct platform_driver amiga_serial_driver = {
+       .remove = __exit_p(amiga_serial_remove),
+       .driver   = {
+               .name   = "amiga-serial",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init amiga_serial_init(void)
+{
+       return platform_driver_probe(&amiga_serial_driver, amiga_serial_probe);
+}
+
+module_init(amiga_serial_init);
+
+static void __exit amiga_serial_exit(void)
+{
+       platform_driver_unregister(&amiga_serial_driver);
+}
+
+module_exit(amiga_serial_exit);
+
+
+#if defined(CONFIG_SERIAL_CONSOLE) && !defined(MODULE)
+
+/*
+ * ------------------------------------------------------------
+ * Serial console driver
+ * ------------------------------------------------------------
+ */
+
+static void amiga_serial_putc(char c)
+{
+       custom.serdat = (unsigned char)c | 0x100;
+       while (!(custom.serdatr & 0x2000))
+               barrier();
+}
+
+/*
+ *     Print a string to the serial port trying not to disturb
+ *     any possible real use of the port...
+ *
+ *     The console must be locked when we get here.
+ */
+static void serial_console_write(struct console *co, const char *s,
+                               unsigned count)
+{
+       unsigned short intena = custom.intenar;
+
+       custom.intena = IF_TBE;
+
+       while (count--) {
+               if (*s == '\n')
+                       amiga_serial_putc('\r');
+               amiga_serial_putc(*s++);
+       }
+
+       custom.intena = IF_SETCLR | (intena & IF_TBE);
+}
+
+static struct tty_driver *serial_console_device(struct console *c, int *index)
+{
+       *index = 0;
+       return serial_driver;
+}
+
+static struct console sercons = {
+       .name =         "ttyS",
+       .write =        serial_console_write,
+       .device =       serial_console_device,
+       .flags =        CON_PRINTBUFFER,
+       .index =        -1,
+};
+
+/*
+ *     Register console.
+ */
+static int __init amiserial_console_init(void)
+{
+       register_console(&sercons);
+       return 0;
+}
+console_initcall(amiserial_console_init);
+
+#endif /* CONFIG_SERIAL_CONSOLE && !MODULE */
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:amiga-serial");
diff --git a/drivers/tty/bfin_jtag_comm.c b/drivers/tty/bfin_jtag_comm.c
new file mode 100644 (file)
index 0000000..1640244
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * TTY over Blackfin JTAG Communication
+ *
+ * Copyright 2008-2009 Analog Devices Inc.
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#define DRV_NAME "bfin-jtag-comm"
+#define DEV_NAME "ttyBFJC"
+#define pr_fmt(fmt) DRV_NAME ": " fmt
+
+#include <linux/circ_buf.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <asm/atomic.h>
+
+#define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); })
+
+/* See the Debug/Emulation chapter in the HRM */
+#define EMUDOF   0x00000001    /* EMUDAT_OUT full & valid */
+#define EMUDIF   0x00000002    /* EMUDAT_IN full & valid */
+#define EMUDOOVF 0x00000004    /* EMUDAT_OUT overflow */
+#define EMUDIOVF 0x00000008    /* EMUDAT_IN overflow */
+
+static inline uint32_t bfin_write_emudat(uint32_t emudat)
+{
+       __asm__ __volatile__("emudat = %0;" : : "d"(emudat));
+       return emudat;
+}
+
+static inline uint32_t bfin_read_emudat(void)
+{
+       uint32_t emudat;
+       __asm__ __volatile__("%0 = emudat;" : "=d"(emudat));
+       return emudat;
+}
+
+static inline uint32_t bfin_write_emudat_chars(char a, char b, char c, char d)
+{
+       return bfin_write_emudat((a << 0) | (b << 8) | (c << 16) | (d << 24));
+}
+
+#define CIRC_SIZE 2048 /* see comment in tty_io.c:do_tty_write() */
+#define CIRC_MASK (CIRC_SIZE - 1)
+#define circ_empty(circ)     ((circ)->head == (circ)->tail)
+#define circ_free(circ)      CIRC_SPACE((circ)->head, (circ)->tail, CIRC_SIZE)
+#define circ_cnt(circ)       CIRC_CNT((circ)->head, (circ)->tail, CIRC_SIZE)
+#define circ_byte(circ, idx) ((circ)->buf[(idx) & CIRC_MASK])
+
+static struct tty_driver *bfin_jc_driver;
+static struct task_struct *bfin_jc_kthread;
+static struct tty_struct * volatile bfin_jc_tty;
+static unsigned long bfin_jc_count;
+static DEFINE_MUTEX(bfin_jc_tty_mutex);
+static volatile struct circ_buf bfin_jc_write_buf;
+
+static int
+bfin_jc_emudat_manager(void *arg)
+{
+       uint32_t inbound_len = 0, outbound_len = 0;
+
+       while (!kthread_should_stop()) {
+               /* no one left to give data to, so sleep */
+               if (bfin_jc_tty == NULL && circ_empty(&bfin_jc_write_buf)) {
+                       pr_debug("waiting for readers\n");
+                       __set_current_state(TASK_UNINTERRUPTIBLE);
+                       schedule();
+                       __set_current_state(TASK_RUNNING);
+               }
+
+               /* no data available, so just chill */
+               if (!(bfin_read_DBGSTAT() & EMUDIF) && circ_empty(&bfin_jc_write_buf)) {
+                       pr_debug("waiting for data (in_len = %i) (circ: %i %i)\n",
+                               inbound_len, bfin_jc_write_buf.tail, bfin_jc_write_buf.head);
+                       if (inbound_len)
+                               schedule();
+                       else
+                               schedule_timeout_interruptible(HZ);
+                       continue;
+               }
+
+               /* if incoming data is ready, eat it */
+               if (bfin_read_DBGSTAT() & EMUDIF) {
+                       struct tty_struct *tty;
+                       mutex_lock(&bfin_jc_tty_mutex);
+                       tty = (struct tty_struct *)bfin_jc_tty;
+                       if (tty != NULL) {
+                               uint32_t emudat = bfin_read_emudat();
+                               if (inbound_len == 0) {
+                                       pr_debug("incoming length: 0x%08x\n", emudat);
+                                       inbound_len = emudat;
+                               } else {
+                                       size_t num_chars = (4 <= inbound_len ? 4 : inbound_len);
+                                       pr_debug("  incoming data: 0x%08x (pushing %zu)\n", emudat, num_chars);
+                                       inbound_len -= num_chars;
+                                       tty_insert_flip_string(tty, (unsigned char *)&emudat, num_chars);
+                                       tty_flip_buffer_push(tty);
+                               }
+                       }
+                       mutex_unlock(&bfin_jc_tty_mutex);
+               }
+
+               /* if outgoing data is ready, post it */
+               if (!(bfin_read_DBGSTAT() & EMUDOF) && !circ_empty(&bfin_jc_write_buf)) {
+                       if (outbound_len == 0) {
+                               outbound_len = circ_cnt(&bfin_jc_write_buf);
+                               bfin_write_emudat(outbound_len);
+                               pr_debug("outgoing length: 0x%08x\n", outbound_len);
+                       } else {
+                               struct tty_struct *tty;
+                               int tail = bfin_jc_write_buf.tail;
+                               size_t ate = (4 <= outbound_len ? 4 : outbound_len);
+                               uint32_t emudat =
+                               bfin_write_emudat_chars(
+                                       circ_byte(&bfin_jc_write_buf, tail + 0),
+                                       circ_byte(&bfin_jc_write_buf, tail + 1),
+                                       circ_byte(&bfin_jc_write_buf, tail + 2),
+                                       circ_byte(&bfin_jc_write_buf, tail + 3)
+                               );
+                               bfin_jc_write_buf.tail += ate;
+                               outbound_len -= ate;
+                               mutex_lock(&bfin_jc_tty_mutex);
+                               tty = (struct tty_struct *)bfin_jc_tty;
+                               if (tty)
+                                       tty_wakeup(tty);
+                               mutex_unlock(&bfin_jc_tty_mutex);
+                               pr_debug("  outgoing data: 0x%08x (pushing %zu)\n", emudat, ate);
+                       }
+               }
+       }
+
+       __set_current_state(TASK_RUNNING);
+       return 0;
+}
+
+static int
+bfin_jc_open(struct tty_struct *tty, struct file *filp)
+{
+       mutex_lock(&bfin_jc_tty_mutex);
+       pr_debug("open %lu\n", bfin_jc_count);
+       ++bfin_jc_count;
+       bfin_jc_tty = tty;
+       wake_up_process(bfin_jc_kthread);
+       mutex_unlock(&bfin_jc_tty_mutex);
+       return 0;
+}
+
+static void
+bfin_jc_close(struct tty_struct *tty, struct file *filp)
+{
+       mutex_lock(&bfin_jc_tty_mutex);
+       pr_debug("close %lu\n", bfin_jc_count);
+       if (--bfin_jc_count == 0)
+               bfin_jc_tty = NULL;
+       wake_up_process(bfin_jc_kthread);
+       mutex_unlock(&bfin_jc_tty_mutex);
+}
+
+/* XXX: we dont handle the put_char() case where we must handle count = 1 */
+static int
+bfin_jc_circ_write(const unsigned char *buf, int count)
+{
+       int i;
+       count = min(count, circ_free(&bfin_jc_write_buf));
+       pr_debug("going to write chunk of %i bytes\n", count);
+       for (i = 0; i < count; ++i)
+               circ_byte(&bfin_jc_write_buf, bfin_jc_write_buf.head + i) = buf[i];
+       bfin_jc_write_buf.head += i;
+       return i;
+}
+
+#ifndef CONFIG_BFIN_JTAG_COMM_CONSOLE
+# define console_lock()
+# define console_unlock()
+#endif
+static int
+bfin_jc_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+       int i;
+       console_lock();
+       i = bfin_jc_circ_write(buf, count);
+       console_unlock();
+       wake_up_process(bfin_jc_kthread);
+       return i;
+}
+
+static void
+bfin_jc_flush_chars(struct tty_struct *tty)
+{
+       wake_up_process(bfin_jc_kthread);
+}
+
+static int
+bfin_jc_write_room(struct tty_struct *tty)
+{
+       return circ_free(&bfin_jc_write_buf);
+}
+
+static int
+bfin_jc_chars_in_buffer(struct tty_struct *tty)
+{
+       return circ_cnt(&bfin_jc_write_buf);
+}
+
+static void
+bfin_jc_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       unsigned long expire = jiffies + timeout;
+       while (!circ_empty(&bfin_jc_write_buf)) {
+               if (signal_pending(current))
+                       break;
+               if (time_after(jiffies, expire))
+                       break;
+       }
+}
+
+static const struct tty_operations bfin_jc_ops = {
+       .open            = bfin_jc_open,
+       .close           = bfin_jc_close,
+       .write           = bfin_jc_write,
+       /*.put_char        = bfin_jc_put_char,*/
+       .flush_chars     = bfin_jc_flush_chars,
+       .write_room      = bfin_jc_write_room,
+       .chars_in_buffer = bfin_jc_chars_in_buffer,
+       .wait_until_sent = bfin_jc_wait_until_sent,
+};
+
+static int __init bfin_jc_init(void)
+{
+       int ret;
+
+       bfin_jc_kthread = kthread_create(bfin_jc_emudat_manager, NULL, DRV_NAME);
+       if (IS_ERR(bfin_jc_kthread))
+               return PTR_ERR(bfin_jc_kthread);
+
+       ret = -ENOMEM;
+
+       bfin_jc_write_buf.head = bfin_jc_write_buf.tail = 0;
+       bfin_jc_write_buf.buf = kmalloc(CIRC_SIZE, GFP_KERNEL);
+       if (!bfin_jc_write_buf.buf)
+               goto err;
+
+       bfin_jc_driver = alloc_tty_driver(1);
+       if (!bfin_jc_driver)
+               goto err;
+
+       bfin_jc_driver->owner        = THIS_MODULE;
+       bfin_jc_driver->driver_name  = DRV_NAME;
+       bfin_jc_driver->name         = DEV_NAME;
+       bfin_jc_driver->type         = TTY_DRIVER_TYPE_SERIAL;
+       bfin_jc_driver->subtype      = SERIAL_TYPE_NORMAL;
+       bfin_jc_driver->init_termios = tty_std_termios;
+       tty_set_operations(bfin_jc_driver, &bfin_jc_ops);
+
+       ret = tty_register_driver(bfin_jc_driver);
+       if (ret)
+               goto err;
+
+       pr_init(KERN_INFO DRV_NAME ": initialized\n");
+
+       return 0;
+
+ err:
+       put_tty_driver(bfin_jc_driver);
+       kfree(bfin_jc_write_buf.buf);
+       kthread_stop(bfin_jc_kthread);
+       return ret;
+}
+module_init(bfin_jc_init);
+
+static void __exit bfin_jc_exit(void)
+{
+       kthread_stop(bfin_jc_kthread);
+       kfree(bfin_jc_write_buf.buf);
+       tty_unregister_driver(bfin_jc_driver);
+       put_tty_driver(bfin_jc_driver);
+}
+module_exit(bfin_jc_exit);
+
+#if defined(CONFIG_BFIN_JTAG_COMM_CONSOLE) || defined(CONFIG_EARLY_PRINTK)
+static void
+bfin_jc_straight_buffer_write(const char *buf, unsigned count)
+{
+       unsigned ate = 0;
+       while (bfin_read_DBGSTAT() & EMUDOF)
+               continue;
+       bfin_write_emudat(count);
+       while (ate < count) {
+               while (bfin_read_DBGSTAT() & EMUDOF)
+                       continue;
+               bfin_write_emudat_chars(buf[ate], buf[ate+1], buf[ate+2], buf[ate+3]);
+               ate += 4;
+       }
+}
+#endif
+
+#ifdef CONFIG_BFIN_JTAG_COMM_CONSOLE
+static void
+bfin_jc_console_write(struct console *co, const char *buf, unsigned count)
+{
+       if (bfin_jc_kthread == NULL)
+               bfin_jc_straight_buffer_write(buf, count);
+       else
+               bfin_jc_circ_write(buf, count);
+}
+
+static struct tty_driver *
+bfin_jc_console_device(struct console *co, int *index)
+{
+       *index = co->index;
+       return bfin_jc_driver;
+}
+
+static struct console bfin_jc_console = {
+       .name    = DEV_NAME,
+       .write   = bfin_jc_console_write,
+       .device  = bfin_jc_console_device,
+       .flags   = CON_ANYTIME | CON_PRINTBUFFER,
+       .index   = -1,
+};
+
+static int __init bfin_jc_console_init(void)
+{
+       register_console(&bfin_jc_console);
+       return 0;
+}
+console_initcall(bfin_jc_console_init);
+#endif
+
+#ifdef CONFIG_EARLY_PRINTK
+static void __init
+bfin_jc_early_write(struct console *co, const char *buf, unsigned int count)
+{
+       bfin_jc_straight_buffer_write(buf, count);
+}
+
+static struct __initdata console bfin_jc_early_console = {
+       .name   = "early_BFJC",
+       .write   = bfin_jc_early_write,
+       .flags   = CON_ANYTIME | CON_PRINTBUFFER,
+       .index   = -1,
+};
+
+struct console * __init
+bfin_jc_early_init(unsigned int port, unsigned int cflag)
+{
+       return &bfin_jc_early_console;
+}
+#endif
+
+MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
+MODULE_DESCRIPTION("TTY over Blackfin JTAG Communication");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c
new file mode 100644 (file)
index 0000000..c99728f
--- /dev/null
@@ -0,0 +1,4200 @@
+#undef BLOCKMOVE
+#define        Z_WAKE
+#undef Z_EXT_CHARS_IN_BUFFER
+
+/*
+ *  linux/drivers/char/cyclades.c
+ *
+ * This file contains the driver for the Cyclades async multiport
+ * serial boards.
+ *
+ * Initially written by Randolph Bentson <bentson@grieg.seaslug.org>.
+ * Modified and maintained by Marcio Saito <marcio@cyclades.com>.
+ *
+ * Copyright (C) 2007-2009 Jiri Slaby <jirislaby@gmail.com>
+ *
+ * Much of the design and some of the code came from serial.c
+ * which was copyright (C) 1991, 1992  Linus Torvalds.  It was
+ * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
+ * and then fixed as suggested by Michael K. Johnson 12/12/92.
+ * Converted to pci probing and cleaned up by Jiri Slaby.
+ *
+ */
+
+#define CY_VERSION     "2.6"
+
+/* If you need to install more boards than NR_CARDS, change the constant
+   in the definition below. No other change is necessary to support up to
+   eight boards. Beyond that you'll have to extend cy_isa_addresses. */
+
+#define NR_CARDS       4
+
+/*
+   If the total number of ports is larger than NR_PORTS, change this
+   constant in the definition below. No other change is necessary to
+   support more boards/ports. */
+
+#define NR_PORTS       256
+
+#define ZO_V1  0
+#define ZO_V2  1
+#define ZE_V1  2
+
+#define        SERIAL_PARANOIA_CHECK
+#undef CY_DEBUG_OPEN
+#undef CY_DEBUG_THROTTLE
+#undef CY_DEBUG_OTHER
+#undef CY_DEBUG_IO
+#undef CY_DEBUG_COUNT
+#undef CY_DEBUG_DTR
+#undef CY_DEBUG_WAIT_UNTIL_SENT
+#undef CY_DEBUG_INTERRUPTS
+#undef CY_16Y_HACK
+#undef CY_ENABLE_MONITORING
+#undef CY_PCI_DEBUG
+
+/*
+ * Include section
+ */
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/cyclades.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+static void cy_send_xchar(struct tty_struct *tty, char ch);
+
+#ifndef SERIAL_XMIT_SIZE
+#define        SERIAL_XMIT_SIZE        (min(PAGE_SIZE, 4096))
+#endif
+
+#define STD_COM_FLAGS (0)
+
+/* firmware stuff */
+#define ZL_MAX_BLOCKS  16
+#define DRIVER_VERSION 0x02010203
+#define RAM_SIZE 0x80000
+
+enum zblock_type {
+       ZBLOCK_PRG = 0,
+       ZBLOCK_FPGA = 1
+};
+
+struct zfile_header {
+       char name[64];
+       char date[32];
+       char aux[32];
+       u32 n_config;
+       u32 config_offset;
+       u32 n_blocks;
+       u32 block_offset;
+       u32 reserved[9];
+} __attribute__ ((packed));
+
+struct zfile_config {
+       char name[64];
+       u32 mailbox;
+       u32 function;
+       u32 n_blocks;
+       u32 block_list[ZL_MAX_BLOCKS];
+} __attribute__ ((packed));
+
+struct zfile_block {
+       u32 type;
+       u32 file_offset;
+       u32 ram_offset;
+       u32 size;
+} __attribute__ ((packed));
+
+static struct tty_driver *cy_serial_driver;
+
+#ifdef CONFIG_ISA
+/* This is the address lookup table. The driver will probe for
+   Cyclom-Y/ISA boards at all addresses in here. If you want the
+   driver to probe addresses at a different address, add it to
+   this table.  If the driver is probing some other board and
+   causing problems, remove the offending address from this table.
+*/
+
+static unsigned int cy_isa_addresses[] = {
+       0xD0000,
+       0xD2000,
+       0xD4000,
+       0xD6000,
+       0xD8000,
+       0xDA000,
+       0xDC000,
+       0xDE000,
+       0, 0, 0, 0, 0, 0, 0, 0
+};
+
+#define NR_ISA_ADDRS ARRAY_SIZE(cy_isa_addresses)
+
+static long maddr[NR_CARDS];
+static int irq[NR_CARDS];
+
+module_param_array(maddr, long, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+
+#endif                         /* CONFIG_ISA */
+
+/* This is the per-card data structure containing address, irq, number of
+   channels, etc. This driver supports a maximum of NR_CARDS cards.
+*/
+static struct cyclades_card cy_card[NR_CARDS];
+
+static int cy_next_channel;    /* next minor available */
+
+/*
+ * This is used to look up the divisor speeds and the timeouts
+ * We're normally limited to 15 distinct baud rates.  The extra
+ * are accessed via settings in info->port.flags.
+ *      0,     1,     2,     3,     4,     5,     6,     7,     8,     9,
+ *     10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
+ *                                               HI            VHI
+ *     20
+ */
+static const int baud_table[] = {
+       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
+       1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000,
+       230400, 0
+};
+
+static const char baud_co_25[] = {     /* 25 MHz clock option table */
+       /* value =>    00    01   02    03    04 */
+       /* divide by    8    32   128   512  2048 */
+       0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02,
+       0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const char baud_bpr_25[] = {    /* 25 MHz baud rate period table */
+       0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3,
+       0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15
+};
+
+static const char baud_co_60[] = {     /* 60 MHz clock option table (CD1400 J) */
+       /* value =>    00    01   02    03    04 */
+       /* divide by    8    32   128   512  2048 */
+       0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03,
+       0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00
+};
+
+static const char baud_bpr_60[] = {    /* 60 MHz baud rate period table (CD1400 J) */
+       0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62,
+       0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32,
+       0x21
+};
+
+static const char baud_cor3[] = {      /* receive threshold */
+       0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+       0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07,
+       0x07
+};
+
+/*
+ * The Cyclades driver implements HW flow control as any serial driver.
+ * The cyclades_port structure member rflow and the vector rflow_thr
+ * allows us to take advantage of a special feature in the CD1400 to avoid
+ * data loss even when the system interrupt latency is too high. These flags
+ * are to be used only with very special applications. Setting these flags
+ * requires the use of a special cable (DTR and RTS reversed). In the new
+ * CD1400-based boards (rev. 6.00 or later), there is no need for special
+ * cables.
+ */
+
+static const char rflow_thr[] = {      /* rflow threshold */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+       0x0a
+};
+
+/*  The Cyclom-Ye has placed the sequential chips in non-sequential
+ *  address order.  This look-up table overcomes that problem.
+ */
+static const unsigned int cy_chip_offset[] = { 0x0000,
+       0x0400,
+       0x0800,
+       0x0C00,
+       0x0200,
+       0x0600,
+       0x0A00,
+       0x0E00
+};
+
+/* PCI related definitions */
+
+#ifdef CONFIG_PCI
+static const struct pci_device_id cy_pci_dev_id[] = {
+       /* PCI < 1Mb */
+       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) },
+       /* PCI > 1Mb */
+       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Hi) },
+       /* 4Y PCI < 1Mb */
+       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Lo) },
+       /* 4Y PCI > 1Mb */
+       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_4Y_Hi) },
+       /* 8Y PCI < 1Mb */
+       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Lo) },
+       /* 8Y PCI > 1Mb */
+       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_8Y_Hi) },
+       /* Z PCI < 1Mb */
+       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Lo) },
+       /* Z PCI > 1Mb */
+       { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Z_Hi) },
+       { }                     /* end of table */
+};
+MODULE_DEVICE_TABLE(pci, cy_pci_dev_id);
+#endif
+
+static void cy_start(struct tty_struct *);
+static void cy_set_line_char(struct cyclades_port *, struct tty_struct *);
+static int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32);
+#ifdef CONFIG_ISA
+static unsigned detect_isa_irq(void __iomem *);
+#endif                         /* CONFIG_ISA */
+
+#ifndef CONFIG_CYZ_INTR
+static void cyz_poll(unsigned long);
+
+/* The Cyclades-Z polling cycle is defined by this variable */
+static long cyz_polling_cycle = CZ_DEF_POLL;
+
+static DEFINE_TIMER(cyz_timerlist, cyz_poll, 0, 0);
+
+#else                          /* CONFIG_CYZ_INTR */
+static void cyz_rx_restart(unsigned long);
+static struct timer_list cyz_rx_full_timer[NR_PORTS];
+#endif                         /* CONFIG_CYZ_INTR */
+
+static inline void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val)
+{
+       struct cyclades_card *card = port->card;
+
+       cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val);
+}
+
+static inline u8 cyy_readb(struct cyclades_port *port, u32 reg)
+{
+       struct cyclades_card *card = port->card;
+
+       return readb(port->u.cyy.base_addr + (reg << card->bus_index));
+}
+
+static inline bool cy_is_Z(struct cyclades_card *card)
+{
+       return card->num_chips == (unsigned int)-1;
+}
+
+static inline bool __cyz_fpga_loaded(struct RUNTIME_9060 __iomem *ctl_addr)
+{
+       return readl(&ctl_addr->init_ctrl) & (1 << 17);
+}
+
+static inline bool cyz_fpga_loaded(struct cyclades_card *card)
+{
+       return __cyz_fpga_loaded(card->ctl_addr.p9060);
+}
+
+static inline bool cyz_is_loaded(struct cyclades_card *card)
+{
+       struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS;
+
+       return (card->hw_ver == ZO_V1 || cyz_fpga_loaded(card)) &&
+                       readl(&fw_id->signature) == ZFIRM_ID;
+}
+
+static inline int serial_paranoia_check(struct cyclades_port *info,
+               const char *name, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+       if (!info) {
+               printk(KERN_WARNING "cyc Warning: null cyclades_port for (%s) "
+                               "in %s\n", name, routine);
+               return 1;
+       }
+
+       if (info->magic != CYCLADES_MAGIC) {
+               printk(KERN_WARNING "cyc Warning: bad magic number for serial "
+                               "struct (%s) in %s\n", name, routine);
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+/***********************************************************/
+/********* Start of block of Cyclom-Y specific code ********/
+
+/* This routine waits up to 1000 micro-seconds for the previous
+   command to the Cirrus chip to complete and then issues the
+   new command.  An error is returned if the previous command
+   didn't finish within the time limit.
+
+   This function is only called from inside spinlock-protected code.
+ */
+static int __cyy_issue_cmd(void __iomem *base_addr, u8 cmd, int index)
+{
+       void __iomem *ccr = base_addr + (CyCCR << index);
+       unsigned int i;
+
+       /* Check to see that the previous command has completed */
+       for (i = 0; i < 100; i++) {
+               if (readb(ccr) == 0)
+                       break;
+               udelay(10L);
+       }
+       /* if the CCR never cleared, the previous command
+          didn't finish within the "reasonable time" */
+       if (i == 100)
+               return -1;
+
+       /* Issue the new command */
+       cy_writeb(ccr, cmd);
+
+       return 0;
+}
+
+static inline int cyy_issue_cmd(struct cyclades_port *port, u8 cmd)
+{
+       return __cyy_issue_cmd(port->u.cyy.base_addr, cmd,
+                       port->card->bus_index);
+}
+
+#ifdef CONFIG_ISA
+/* ISA interrupt detection code */
+static unsigned detect_isa_irq(void __iomem *address)
+{
+       int irq;
+       unsigned long irqs, flags;
+       int save_xir, save_car;
+       int index = 0;          /* IRQ probing is only for ISA */
+
+       /* forget possible initially masked and pending IRQ */
+       irq = probe_irq_off(probe_irq_on());
+
+       /* Clear interrupts on the board first */
+       cy_writeb(address + (Cy_ClrIntr << index), 0);
+       /* Cy_ClrIntr is 0x1800 */
+
+       irqs = probe_irq_on();
+       /* Wait ... */
+       msleep(5);
+
+       /* Enable the Tx interrupts on the CD1400 */
+       local_irq_save(flags);
+       cy_writeb(address + (CyCAR << index), 0);
+       __cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index);
+
+       cy_writeb(address + (CyCAR << index), 0);
+       cy_writeb(address + (CySRER << index),
+                 readb(address + (CySRER << index)) | CyTxRdy);
+       local_irq_restore(flags);
+
+       /* Wait ... */
+       msleep(5);
+
+       /* Check which interrupt is in use */
+       irq = probe_irq_off(irqs);
+
+       /* Clean up */
+       save_xir = (u_char) readb(address + (CyTIR << index));
+       save_car = readb(address + (CyCAR << index));
+       cy_writeb(address + (CyCAR << index), (save_xir & 0x3));
+       cy_writeb(address + (CySRER << index),
+                 readb(address + (CySRER << index)) & ~CyTxRdy);
+       cy_writeb(address + (CyTIR << index), (save_xir & 0x3f));
+       cy_writeb(address + (CyCAR << index), (save_car));
+       cy_writeb(address + (Cy_ClrIntr << index), 0);
+       /* Cy_ClrIntr is 0x1800 */
+
+       return (irq > 0) ? irq : 0;
+}
+#endif                         /* CONFIG_ISA */
+
+static void cyy_chip_rx(struct cyclades_card *cinfo, int chip,
+               void __iomem *base_addr)
+{
+       struct cyclades_port *info;
+       struct tty_struct *tty;
+       int len, index = cinfo->bus_index;
+       u8 ivr, save_xir, channel, save_car, data, char_count;
+
+#ifdef CY_DEBUG_INTERRUPTS
+       printk(KERN_DEBUG "cyy_interrupt: rcvd intr, chip %d\n", chip);
+#endif
+       /* determine the channel & change to that context */
+       save_xir = readb(base_addr + (CyRIR << index));
+       channel = save_xir & CyIRChannel;
+       info = &cinfo->ports[channel + chip * 4];
+       save_car = cyy_readb(info, CyCAR);
+       cyy_writeb(info, CyCAR, save_xir);
+       ivr = cyy_readb(info, CyRIVR) & CyIVRMask;
+
+       tty = tty_port_tty_get(&info->port);
+       /* if there is nowhere to put the data, discard it */
+       if (tty == NULL) {
+               if (ivr == CyIVRRxEx) { /* exception */
+                       data = cyy_readb(info, CyRDSR);
+               } else {        /* normal character reception */
+                       char_count = cyy_readb(info, CyRDCR);
+                       while (char_count--)
+                               data = cyy_readb(info, CyRDSR);
+               }
+               goto end;
+       }
+       /* there is an open port for this data */
+       if (ivr == CyIVRRxEx) { /* exception */
+               data = cyy_readb(info, CyRDSR);
+
+               /* For statistics only */
+               if (data & CyBREAK)
+                       info->icount.brk++;
+               else if (data & CyFRAME)
+                       info->icount.frame++;
+               else if (data & CyPARITY)
+                       info->icount.parity++;
+               else if (data & CyOVERRUN)
+                       info->icount.overrun++;
+
+               if (data & info->ignore_status_mask) {
+                       info->icount.rx++;
+                       tty_kref_put(tty);
+                       return;
+               }
+               if (tty_buffer_request_room(tty, 1)) {
+                       if (data & info->read_status_mask) {
+                               if (data & CyBREAK) {
+                                       tty_insert_flip_char(tty,
+                                               cyy_readb(info, CyRDSR),
+                                               TTY_BREAK);
+                                       info->icount.rx++;
+                                       if (info->port.flags & ASYNC_SAK)
+                                               do_SAK(tty);
+                               } else if (data & CyFRAME) {
+                                       tty_insert_flip_char(tty,
+                                               cyy_readb(info, CyRDSR),
+                                               TTY_FRAME);
+                                       info->icount.rx++;
+                                       info->idle_stats.frame_errs++;
+                               } else if (data & CyPARITY) {
+                                       /* Pieces of seven... */
+                                       tty_insert_flip_char(tty,
+                                               cyy_readb(info, CyRDSR),
+                                               TTY_PARITY);
+                                       info->icount.rx++;
+                                       info->idle_stats.parity_errs++;
+                               } else if (data & CyOVERRUN) {
+                                       tty_insert_flip_char(tty, 0,
+                                                       TTY_OVERRUN);
+                                       info->icount.rx++;
+                                       /* If the flip buffer itself is
+                                          overflowing, we still lose
+                                          the next incoming character.
+                                        */
+                                       tty_insert_flip_char(tty,
+                                               cyy_readb(info, CyRDSR),
+                                               TTY_FRAME);
+                                       info->icount.rx++;
+                                       info->idle_stats.overruns++;
+                               /* These two conditions may imply */
+                               /* a normal read should be done. */
+                               /* } else if(data & CyTIMEOUT) { */
+                               /* } else if(data & CySPECHAR) { */
+                               } else {
+                                       tty_insert_flip_char(tty, 0,
+                                                       TTY_NORMAL);
+                                       info->icount.rx++;
+                               }
+                       } else {
+                               tty_insert_flip_char(tty, 0, TTY_NORMAL);
+                               info->icount.rx++;
+                       }
+               } else {
+                       /* there was a software buffer overrun and nothing
+                        * could be done about it!!! */
+                       info->icount.buf_overrun++;
+                       info->idle_stats.overruns++;
+               }
+       } else {        /* normal character reception */
+               /* load # chars available from the chip */
+               char_count = cyy_readb(info, CyRDCR);
+
+#ifdef CY_ENABLE_MONITORING
+               ++info->mon.int_count;
+               info->mon.char_count += char_count;
+               if (char_count > info->mon.char_max)
+                       info->mon.char_max = char_count;
+               info->mon.char_last = char_count;
+#endif
+               len = tty_buffer_request_room(tty, char_count);
+               while (len--) {
+                       data = cyy_readb(info, CyRDSR);
+                       tty_insert_flip_char(tty, data, TTY_NORMAL);
+                       info->idle_stats.recv_bytes++;
+                       info->icount.rx++;
+#ifdef CY_16Y_HACK
+                       udelay(10L);
+#endif
+               }
+               info->idle_stats.recv_idle = jiffies;
+       }
+       tty_schedule_flip(tty);
+       tty_kref_put(tty);
+end:
+       /* end of service */
+       cyy_writeb(info, CyRIR, save_xir & 0x3f);
+       cyy_writeb(info, CyCAR, save_car);
+}
+
+static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip,
+               void __iomem *base_addr)
+{
+       struct cyclades_port *info;
+       struct tty_struct *tty;
+       int char_count, index = cinfo->bus_index;
+       u8 save_xir, channel, save_car, outch;
+
+       /* Since we only get here when the transmit buffer
+          is empty, we know we can always stuff a dozen
+          characters. */
+#ifdef CY_DEBUG_INTERRUPTS
+       printk(KERN_DEBUG "cyy_interrupt: xmit intr, chip %d\n", chip);
+#endif
+
+       /* determine the channel & change to that context */
+       save_xir = readb(base_addr + (CyTIR << index));
+       channel = save_xir & CyIRChannel;
+       save_car = readb(base_addr + (CyCAR << index));
+       cy_writeb(base_addr + (CyCAR << index), save_xir);
+
+       info = &cinfo->ports[channel + chip * 4];
+       tty = tty_port_tty_get(&info->port);
+       if (tty == NULL) {
+               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy);
+               goto end;
+       }
+
+       /* load the on-chip space for outbound data */
+       char_count = info->xmit_fifo_size;
+
+       if (info->x_char) {     /* send special char */
+               outch = info->x_char;
+               cyy_writeb(info, CyTDR, outch);
+               char_count--;
+               info->icount.tx++;
+               info->x_char = 0;
+       }
+
+       if (info->breakon || info->breakoff) {
+               if (info->breakon) {
+                       cyy_writeb(info, CyTDR, 0);
+                       cyy_writeb(info, CyTDR, 0x81);
+                       info->breakon = 0;
+                       char_count -= 2;
+               }
+               if (info->breakoff) {
+                       cyy_writeb(info, CyTDR, 0);
+                       cyy_writeb(info, CyTDR, 0x83);
+                       info->breakoff = 0;
+                       char_count -= 2;
+               }
+       }
+
+       while (char_count-- > 0) {
+               if (!info->xmit_cnt) {
+                       if (cyy_readb(info, CySRER) & CyTxMpty) {
+                               cyy_writeb(info, CySRER,
+                                       cyy_readb(info, CySRER) & ~CyTxMpty);
+                       } else {
+                               cyy_writeb(info, CySRER, CyTxMpty |
+                                       (cyy_readb(info, CySRER) & ~CyTxRdy));
+                       }
+                       goto done;
+               }
+               if (info->port.xmit_buf == NULL) {
+                       cyy_writeb(info, CySRER,
+                               cyy_readb(info, CySRER) & ~CyTxRdy);
+                       goto done;
+               }
+               if (tty->stopped || tty->hw_stopped) {
+                       cyy_writeb(info, CySRER,
+                               cyy_readb(info, CySRER) & ~CyTxRdy);
+                       goto done;
+               }
+               /* Because the Embedded Transmit Commands have been enabled,
+                * we must check to see if the escape character, NULL, is being
+                * sent. If it is, we must ensure that there is room for it to
+                * be doubled in the output stream.  Therefore we no longer
+                * advance the pointer when the character is fetched, but
+                * rather wait until after the check for a NULL output
+                * character. This is necessary because there may not be room
+                * for the two chars needed to send a NULL.)
+                */
+               outch = info->port.xmit_buf[info->xmit_tail];
+               if (outch) {
+                       info->xmit_cnt--;
+                       info->xmit_tail = (info->xmit_tail + 1) &
+                                       (SERIAL_XMIT_SIZE - 1);
+                       cyy_writeb(info, CyTDR, outch);
+                       info->icount.tx++;
+               } else {
+                       if (char_count > 1) {
+                               info->xmit_cnt--;
+                               info->xmit_tail = (info->xmit_tail + 1) &
+                                       (SERIAL_XMIT_SIZE - 1);
+                               cyy_writeb(info, CyTDR, outch);
+                               cyy_writeb(info, CyTDR, 0);
+                               info->icount.tx++;
+                               char_count--;
+                       }
+               }
+       }
+
+done:
+       tty_wakeup(tty);
+       tty_kref_put(tty);
+end:
+       /* end of service */
+       cyy_writeb(info, CyTIR, save_xir & 0x3f);
+       cyy_writeb(info, CyCAR, save_car);
+}
+
+static void cyy_chip_modem(struct cyclades_card *cinfo, int chip,
+               void __iomem *base_addr)
+{
+       struct cyclades_port *info;
+       struct tty_struct *tty;
+       int index = cinfo->bus_index;
+       u8 save_xir, channel, save_car, mdm_change, mdm_status;
+
+       /* determine the channel & change to that context */
+       save_xir = readb(base_addr + (CyMIR << index));
+       channel = save_xir & CyIRChannel;
+       info = &cinfo->ports[channel + chip * 4];
+       save_car = cyy_readb(info, CyCAR);
+       cyy_writeb(info, CyCAR, save_xir);
+
+       mdm_change = cyy_readb(info, CyMISR);
+       mdm_status = cyy_readb(info, CyMSVR1);
+
+       tty = tty_port_tty_get(&info->port);
+       if (!tty)
+               goto end;
+
+       if (mdm_change & CyANY_DELTA) {
+               /* For statistics only */
+               if (mdm_change & CyDCD)
+                       info->icount.dcd++;
+               if (mdm_change & CyCTS)
+                       info->icount.cts++;
+               if (mdm_change & CyDSR)
+                       info->icount.dsr++;
+               if (mdm_change & CyRI)
+                       info->icount.rng++;
+
+               wake_up_interruptible(&info->port.delta_msr_wait);
+       }
+
+       if ((mdm_change & CyDCD) && (info->port.flags & ASYNC_CHECK_CD)) {
+               if (mdm_status & CyDCD)
+                       wake_up_interruptible(&info->port.open_wait);
+               else
+                       tty_hangup(tty);
+       }
+       if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) {
+               if (tty->hw_stopped) {
+                       if (mdm_status & CyCTS) {
+                               /* cy_start isn't used
+                                  because... !!! */
+                               tty->hw_stopped = 0;
+                               cyy_writeb(info, CySRER,
+                                       cyy_readb(info, CySRER) | CyTxRdy);
+                               tty_wakeup(tty);
+                       }
+               } else {
+                       if (!(mdm_status & CyCTS)) {
+                               /* cy_stop isn't used
+                                  because ... !!! */
+                               tty->hw_stopped = 1;
+                               cyy_writeb(info, CySRER,
+                                       cyy_readb(info, CySRER) & ~CyTxRdy);
+                       }
+               }
+       }
+/*     if (mdm_change & CyDSR) {
+       }
+       if (mdm_change & CyRI) {
+       }*/
+       tty_kref_put(tty);
+end:
+       /* end of service */
+       cyy_writeb(info, CyMIR, save_xir & 0x3f);
+       cyy_writeb(info, CyCAR, save_car);
+}
+
+/* The real interrupt service routine is called
+   whenever the card wants its hand held--chars
+   received, out buffer empty, modem change, etc.
+ */
+static irqreturn_t cyy_interrupt(int irq, void *dev_id)
+{
+       int status;
+       struct cyclades_card *cinfo = dev_id;
+       void __iomem *base_addr, *card_base_addr;
+       unsigned int chip, too_many, had_work;
+       int index;
+
+       if (unlikely(cinfo == NULL)) {
+#ifdef CY_DEBUG_INTERRUPTS
+               printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n",
+                               irq);
+#endif
+               return IRQ_NONE;        /* spurious interrupt */
+       }
+
+       card_base_addr = cinfo->base_addr;
+       index = cinfo->bus_index;
+
+       /* card was not initialized yet (e.g. DEBUG_SHIRQ) */
+       if (unlikely(card_base_addr == NULL))
+               return IRQ_HANDLED;
+
+       /* This loop checks all chips in the card.  Make a note whenever
+          _any_ chip had some work to do, as this is considered an
+          indication that there will be more to do.  Only when no chip
+          has any work does this outermost loop exit.
+        */
+       do {
+               had_work = 0;
+               for (chip = 0; chip < cinfo->num_chips; chip++) {
+                       base_addr = cinfo->base_addr +
+                                       (cy_chip_offset[chip] << index);
+                       too_many = 0;
+                       while ((status = readb(base_addr +
+                                               (CySVRR << index))) != 0x00) {
+                               had_work++;
+                       /* The purpose of the following test is to ensure that
+                          no chip can monopolize the driver.  This forces the
+                          chips to be checked in a round-robin fashion (after
+                          draining each of a bunch (1000) of characters).
+                        */
+                               if (1000 < too_many++)
+                                       break;
+                               spin_lock(&cinfo->card_lock);
+                               if (status & CySRReceive) /* rx intr */
+                                       cyy_chip_rx(cinfo, chip, base_addr);
+                               if (status & CySRTransmit) /* tx intr */
+                                       cyy_chip_tx(cinfo, chip, base_addr);
+                               if (status & CySRModem) /* modem intr */
+                                       cyy_chip_modem(cinfo, chip, base_addr);
+                               spin_unlock(&cinfo->card_lock);
+                       }
+               }
+       } while (had_work);
+
+       /* clear interrupts */
+       spin_lock(&cinfo->card_lock);
+       cy_writeb(card_base_addr + (Cy_ClrIntr << index), 0);
+       /* Cy_ClrIntr is 0x1800 */
+       spin_unlock(&cinfo->card_lock);
+       return IRQ_HANDLED;
+}                              /* cyy_interrupt */
+
+static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set,
+               unsigned int clear)
+{
+       struct cyclades_card *card = info->card;
+       int channel = info->line - card->first_line;
+       u32 rts, dtr, msvrr, msvrd;
+
+       channel &= 0x03;
+
+       if (info->rtsdtr_inv) {
+               msvrr = CyMSVR2;
+               msvrd = CyMSVR1;
+               rts = CyDTR;
+               dtr = CyRTS;
+       } else {
+               msvrr = CyMSVR1;
+               msvrd = CyMSVR2;
+               rts = CyRTS;
+               dtr = CyDTR;
+       }
+       if (set & TIOCM_RTS) {
+               cyy_writeb(info, CyCAR, channel);
+               cyy_writeb(info, msvrr, rts);
+       }
+       if (clear & TIOCM_RTS) {
+               cyy_writeb(info, CyCAR, channel);
+               cyy_writeb(info, msvrr, ~rts);
+       }
+       if (set & TIOCM_DTR) {
+               cyy_writeb(info, CyCAR, channel);
+               cyy_writeb(info, msvrd, dtr);
+#ifdef CY_DEBUG_DTR
+               printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n");
+               printk(KERN_DEBUG "     status: 0x%x, 0x%x\n",
+                       cyy_readb(info, CyMSVR1),
+                       cyy_readb(info, CyMSVR2));
+#endif
+       }
+       if (clear & TIOCM_DTR) {
+               cyy_writeb(info, CyCAR, channel);
+               cyy_writeb(info, msvrd, ~dtr);
+#ifdef CY_DEBUG_DTR
+               printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n");
+               printk(KERN_DEBUG "     status: 0x%x, 0x%x\n",
+                       cyy_readb(info, CyMSVR1),
+                       cyy_readb(info, CyMSVR2));
+#endif
+       }
+}
+
+/***********************************************************/
+/********* End of block of Cyclom-Y specific code **********/
+/******** Start of block of Cyclades-Z specific code *******/
+/***********************************************************/
+
+static int
+cyz_fetch_msg(struct cyclades_card *cinfo,
+               __u32 *channel, __u8 *cmd, __u32 *param)
+{
+       struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
+       unsigned long loc_doorbell;
+
+       loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell);
+       if (loc_doorbell) {
+               *cmd = (char)(0xff & loc_doorbell);
+               *channel = readl(&board_ctrl->fwcmd_channel);
+               *param = (__u32) readl(&board_ctrl->fwcmd_param);
+               cy_writel(&cinfo->ctl_addr.p9060->loc_doorbell, 0xffffffff);
+               return 1;
+       }
+       return 0;
+}                              /* cyz_fetch_msg */
+
+static int
+cyz_issue_cmd(struct cyclades_card *cinfo,
+               __u32 channel, __u8 cmd, __u32 param)
+{
+       struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
+       __u32 __iomem *pci_doorbell;
+       unsigned int index;
+
+       if (!cyz_is_loaded(cinfo))
+               return -1;
+
+       index = 0;
+       pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell;
+       while ((readl(pci_doorbell) & 0xff) != 0) {
+               if (index++ == 1000)
+                       return (int)(readl(pci_doorbell) & 0xff);
+               udelay(50L);
+       }
+       cy_writel(&board_ctrl->hcmd_channel, channel);
+       cy_writel(&board_ctrl->hcmd_param, param);
+       cy_writel(pci_doorbell, (long)cmd);
+
+       return 0;
+}                              /* cyz_issue_cmd */
+
+static void cyz_handle_rx(struct cyclades_port *info, struct tty_struct *tty)
+{
+       struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl;
+       struct cyclades_card *cinfo = info->card;
+       unsigned int char_count;
+       int len;
+#ifdef BLOCKMOVE
+       unsigned char *buf;
+#else
+       char data;
+#endif
+       __u32 rx_put, rx_get, new_rx_get, rx_bufsize, rx_bufaddr;
+
+       rx_get = new_rx_get = readl(&buf_ctrl->rx_get);
+       rx_put = readl(&buf_ctrl->rx_put);
+       rx_bufsize = readl(&buf_ctrl->rx_bufsize);
+       rx_bufaddr = readl(&buf_ctrl->rx_bufaddr);
+       if (rx_put >= rx_get)
+               char_count = rx_put - rx_get;
+       else
+               char_count = rx_put - rx_get + rx_bufsize;
+
+       if (char_count) {
+#ifdef CY_ENABLE_MONITORING
+               info->mon.int_count++;
+               info->mon.char_count += char_count;
+               if (char_count > info->mon.char_max)
+                       info->mon.char_max = char_count;
+               info->mon.char_last = char_count;
+#endif
+               if (tty == NULL) {
+                       /* flush received characters */
+                       new_rx_get = (new_rx_get + char_count) &
+                                       (rx_bufsize - 1);
+                       info->rflush_count++;
+               } else {
+#ifdef BLOCKMOVE
+               /* we'd like to use memcpy(t, f, n) and memset(s, c, count)
+                  for performance, but because of buffer boundaries, there
+                  may be several steps to the operation */
+                       while (1) {
+                               len = tty_prepare_flip_string(tty, &buf,
+                                               char_count);
+                               if (!len)
+                                       break;
+
+                               len = min_t(unsigned int, min(len, char_count),
+                                               rx_bufsize - new_rx_get);
+
+                               memcpy_fromio(buf, cinfo->base_addr +
+                                               rx_bufaddr + new_rx_get, len);
+
+                               new_rx_get = (new_rx_get + len) &
+                                               (rx_bufsize - 1);
+                               char_count -= len;
+                               info->icount.rx += len;
+                               info->idle_stats.recv_bytes += len;
+                       }
+#else
+                       len = tty_buffer_request_room(tty, char_count);
+                       while (len--) {
+                               data = readb(cinfo->base_addr + rx_bufaddr +
+                                               new_rx_get);
+                               new_rx_get = (new_rx_get + 1) &
+                                                       (rx_bufsize - 1);
+                               tty_insert_flip_char(tty, data, TTY_NORMAL);
+                               info->idle_stats.recv_bytes++;
+                               info->icount.rx++;
+                       }
+#endif
+#ifdef CONFIG_CYZ_INTR
+               /* Recalculate the number of chars in the RX buffer and issue
+                  a cmd in case it's higher than the RX high water mark */
+                       rx_put = readl(&buf_ctrl->rx_put);
+                       if (rx_put >= rx_get)
+                               char_count = rx_put - rx_get;
+                       else
+                               char_count = rx_put - rx_get + rx_bufsize;
+                       if (char_count >= readl(&buf_ctrl->rx_threshold) &&
+                                       !timer_pending(&cyz_rx_full_timer[
+                                                       info->line]))
+                               mod_timer(&cyz_rx_full_timer[info->line],
+                                               jiffies + 1);
+#endif
+                       info->idle_stats.recv_idle = jiffies;
+                       tty_schedule_flip(tty);
+               }
+               /* Update rx_get */
+               cy_writel(&buf_ctrl->rx_get, new_rx_get);
+       }
+}
+
+static void cyz_handle_tx(struct cyclades_port *info, struct tty_struct *tty)
+{
+       struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl;
+       struct cyclades_card *cinfo = info->card;
+       u8 data;
+       unsigned int char_count;
+#ifdef BLOCKMOVE
+       int small_count;
+#endif
+       __u32 tx_put, tx_get, tx_bufsize, tx_bufaddr;
+
+       if (info->xmit_cnt <= 0)        /* Nothing to transmit */
+               return;
+
+       tx_get = readl(&buf_ctrl->tx_get);
+       tx_put = readl(&buf_ctrl->tx_put);
+       tx_bufsize = readl(&buf_ctrl->tx_bufsize);
+       tx_bufaddr = readl(&buf_ctrl->tx_bufaddr);
+       if (tx_put >= tx_get)
+               char_count = tx_get - tx_put - 1 + tx_bufsize;
+       else
+               char_count = tx_get - tx_put - 1;
+
+       if (char_count) {
+
+               if (tty == NULL)
+                       goto ztxdone;
+
+               if (info->x_char) {     /* send special char */
+                       data = info->x_char;
+
+                       cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data);
+                       tx_put = (tx_put + 1) & (tx_bufsize - 1);
+                       info->x_char = 0;
+                       char_count--;
+                       info->icount.tx++;
+               }
+#ifdef BLOCKMOVE
+               while (0 < (small_count = min_t(unsigned int,
+                               tx_bufsize - tx_put, min_t(unsigned int,
+                                       (SERIAL_XMIT_SIZE - info->xmit_tail),
+                                       min_t(unsigned int, info->xmit_cnt,
+                                               char_count))))) {
+
+                       memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr +
+                                       tx_put),
+                                       &info->port.xmit_buf[info->xmit_tail],
+                                       small_count);
+
+                       tx_put = (tx_put + small_count) & (tx_bufsize - 1);
+                       char_count -= small_count;
+                       info->icount.tx += small_count;
+                       info->xmit_cnt -= small_count;
+                       info->xmit_tail = (info->xmit_tail + small_count) &
+                                       (SERIAL_XMIT_SIZE - 1);
+               }
+#else
+               while (info->xmit_cnt && char_count) {
+                       data = info->port.xmit_buf[info->xmit_tail];
+                       info->xmit_cnt--;
+                       info->xmit_tail = (info->xmit_tail + 1) &
+                                       (SERIAL_XMIT_SIZE - 1);
+
+                       cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data);
+                       tx_put = (tx_put + 1) & (tx_bufsize - 1);
+                       char_count--;
+                       info->icount.tx++;
+               }
+#endif
+               tty_wakeup(tty);
+ztxdone:
+               /* Update tx_put */
+               cy_writel(&buf_ctrl->tx_put, tx_put);
+       }
+}
+
+static void cyz_handle_cmd(struct cyclades_card *cinfo)
+{
+       struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl;
+       struct tty_struct *tty;
+       struct cyclades_port *info;
+       __u32 channel, param, fw_ver;
+       __u8 cmd;
+       int special_count;
+       int delta_count;
+
+       fw_ver = readl(&board_ctrl->fw_version);
+
+       while (cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1) {
+               special_count = 0;
+               delta_count = 0;
+               info = &cinfo->ports[channel];
+               tty = tty_port_tty_get(&info->port);
+               if (tty == NULL)
+                       continue;
+
+               switch (cmd) {
+               case C_CM_PR_ERROR:
+                       tty_insert_flip_char(tty, 0, TTY_PARITY);
+                       info->icount.rx++;
+                       special_count++;
+                       break;
+               case C_CM_FR_ERROR:
+                       tty_insert_flip_char(tty, 0, TTY_FRAME);
+                       info->icount.rx++;
+                       special_count++;
+                       break;
+               case C_CM_RXBRK:
+                       tty_insert_flip_char(tty, 0, TTY_BREAK);
+                       info->icount.rx++;
+                       special_count++;
+                       break;
+               case C_CM_MDCD:
+                       info->icount.dcd++;
+                       delta_count++;
+                       if (info->port.flags & ASYNC_CHECK_CD) {
+                               u32 dcd = fw_ver > 241 ? param :
+                                       readl(&info->u.cyz.ch_ctrl->rs_status);
+                               if (dcd & C_RS_DCD)
+                                       wake_up_interruptible(&info->port.open_wait);
+                               else
+                                       tty_hangup(tty);
+                       }
+                       break;
+               case C_CM_MCTS:
+                       info->icount.cts++;
+                       delta_count++;
+                       break;
+               case C_CM_MRI:
+                       info->icount.rng++;
+                       delta_count++;
+                       break;
+               case C_CM_MDSR:
+                       info->icount.dsr++;
+                       delta_count++;
+                       break;
+#ifdef Z_WAKE
+               case C_CM_IOCTLW:
+                       complete(&info->shutdown_wait);
+                       break;
+#endif
+#ifdef CONFIG_CYZ_INTR
+               case C_CM_RXHIWM:
+               case C_CM_RXNNDT:
+               case C_CM_INTBACK2:
+                       /* Reception Interrupt */
+#ifdef CY_DEBUG_INTERRUPTS
+                       printk(KERN_DEBUG "cyz_interrupt: rcvd intr, card %d, "
+                                       "port %ld\n", info->card, channel);
+#endif
+                       cyz_handle_rx(info, tty);
+                       break;
+               case C_CM_TXBEMPTY:
+               case C_CM_TXLOWWM:
+               case C_CM_INTBACK:
+                       /* Transmission Interrupt */
+#ifdef CY_DEBUG_INTERRUPTS
+                       printk(KERN_DEBUG "cyz_interrupt: xmit intr, card %d, "
+                                       "port %ld\n", info->card, channel);
+#endif
+                       cyz_handle_tx(info, tty);
+                       break;
+#endif                         /* CONFIG_CYZ_INTR */
+               case C_CM_FATAL:
+                       /* should do something with this !!! */
+                       break;
+               default:
+                       break;
+               }
+               if (delta_count)
+                       wake_up_interruptible(&info->port.delta_msr_wait);
+               if (special_count)
+                       tty_schedule_flip(tty);
+               tty_kref_put(tty);
+       }
+}
+
+#ifdef CONFIG_CYZ_INTR
+static irqreturn_t cyz_interrupt(int irq, void *dev_id)
+{
+       struct cyclades_card *cinfo = dev_id;
+
+       if (unlikely(!cyz_is_loaded(cinfo))) {
+#ifdef CY_DEBUG_INTERRUPTS
+               printk(KERN_DEBUG "cyz_interrupt: board not yet loaded "
+                               "(IRQ%d).\n", irq);
+#endif
+               return IRQ_NONE;
+       }
+
+       /* Handle the interrupts */
+       cyz_handle_cmd(cinfo);
+
+       return IRQ_HANDLED;
+}                              /* cyz_interrupt */
+
+static void cyz_rx_restart(unsigned long arg)
+{
+       struct cyclades_port *info = (struct cyclades_port *)arg;
+       struct cyclades_card *card = info->card;
+       int retval;
+       __u32 channel = info->line - card->first_line;
+       unsigned long flags;
+
+       spin_lock_irqsave(&card->card_lock, flags);
+       retval = cyz_issue_cmd(card, channel, C_CM_INTBACK2, 0L);
+       if (retval != 0) {
+               printk(KERN_ERR "cyc:cyz_rx_restart retval on ttyC%d was %x\n",
+                       info->line, retval);
+       }
+       spin_unlock_irqrestore(&card->card_lock, flags);
+}
+
+#else                          /* CONFIG_CYZ_INTR */
+
+static void cyz_poll(unsigned long arg)
+{
+       struct cyclades_card *cinfo;
+       struct cyclades_port *info;
+       unsigned long expires = jiffies + HZ;
+       unsigned int port, card;
+
+       for (card = 0; card < NR_CARDS; card++) {
+               cinfo = &cy_card[card];
+
+               if (!cy_is_Z(cinfo))
+                       continue;
+               if (!cyz_is_loaded(cinfo))
+                       continue;
+
+       /* Skip first polling cycle to avoid racing conditions with the FW */
+               if (!cinfo->intr_enabled) {
+                       cinfo->intr_enabled = 1;
+                       continue;
+               }
+
+               cyz_handle_cmd(cinfo);
+
+               for (port = 0; port < cinfo->nports; port++) {
+                       struct tty_struct *tty;
+
+                       info = &cinfo->ports[port];
+                       tty = tty_port_tty_get(&info->port);
+                       /* OK to pass NULL to the handle functions below.
+                          They need to drop the data in that case. */
+
+                       if (!info->throttle)
+                               cyz_handle_rx(info, tty);
+                       cyz_handle_tx(info, tty);
+                       tty_kref_put(tty);
+               }
+               /* poll every 'cyz_polling_cycle' period */
+               expires = jiffies + cyz_polling_cycle;
+       }
+       mod_timer(&cyz_timerlist, expires);
+}                              /* cyz_poll */
+
+#endif                         /* CONFIG_CYZ_INTR */
+
+/********** End of block of Cyclades-Z specific code *********/
+/***********************************************************/
+
+/* This is called whenever a port becomes active;
+   interrupts are enabled and DTR & RTS are turned on.
+ */
+static int cy_startup(struct cyclades_port *info, struct tty_struct *tty)
+{
+       struct cyclades_card *card;
+       unsigned long flags;
+       int retval = 0;
+       int channel;
+       unsigned long page;
+
+       card = info->card;
+       channel = info->line - card->first_line;
+
+       page = get_zeroed_page(GFP_KERNEL);
+       if (!page)
+               return -ENOMEM;
+
+       spin_lock_irqsave(&card->card_lock, flags);
+
+       if (info->port.flags & ASYNC_INITIALIZED)
+               goto errout;
+
+       if (!info->type) {
+               set_bit(TTY_IO_ERROR, &tty->flags);
+               goto errout;
+       }
+
+       if (info->port.xmit_buf)
+               free_page(page);
+       else
+               info->port.xmit_buf = (unsigned char *)page;
+
+       spin_unlock_irqrestore(&card->card_lock, flags);
+
+       cy_set_line_char(info, tty);
+
+       if (!cy_is_Z(card)) {
+               channel &= 0x03;
+
+               spin_lock_irqsave(&card->card_lock, flags);
+
+               cyy_writeb(info, CyCAR, channel);
+
+               cyy_writeb(info, CyRTPR,
+                       (info->default_timeout ? info->default_timeout : 0x02));
+               /* 10ms rx timeout */
+
+               cyy_issue_cmd(info, CyCHAN_CTL | CyENB_RCVR | CyENB_XMTR);
+
+               cyy_change_rts_dtr(info, TIOCM_RTS | TIOCM_DTR, 0);
+
+               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyRxData);
+       } else {
+               struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
+
+               if (!cyz_is_loaded(card))
+                       return -ENODEV;
+
+#ifdef CY_DEBUG_OPEN
+               printk(KERN_DEBUG "cyc startup Z card %d, channel %d, "
+                       "base_addr %p\n", card, channel, card->base_addr);
+#endif
+               spin_lock_irqsave(&card->card_lock, flags);
+
+               cy_writel(&ch_ctrl->op_mode, C_CH_ENABLE);
+#ifdef Z_WAKE
+#ifdef CONFIG_CYZ_INTR
+               cy_writel(&ch_ctrl->intr_enable,
+                         C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM |
+                         C_IN_RXNNDT | C_IN_IOCTLW | C_IN_MDCD);
+#else
+               cy_writel(&ch_ctrl->intr_enable,
+                         C_IN_IOCTLW | C_IN_MDCD);
+#endif                         /* CONFIG_CYZ_INTR */
+#else
+#ifdef CONFIG_CYZ_INTR
+               cy_writel(&ch_ctrl->intr_enable,
+                         C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM |
+                         C_IN_RXNNDT | C_IN_MDCD);
+#else
+               cy_writel(&ch_ctrl->intr_enable, C_IN_MDCD);
+#endif                         /* CONFIG_CYZ_INTR */
+#endif                         /* Z_WAKE */
+
+               retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L);
+               if (retval != 0) {
+                       printk(KERN_ERR "cyc:startup(1) retval on ttyC%d was "
+                               "%x\n", info->line, retval);
+               }
+
+               /* Flush RX buffers before raising DTR and RTS */
+               retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_RX, 0L);
+               if (retval != 0) {
+                       printk(KERN_ERR "cyc:startup(2) retval on ttyC%d was "
+                               "%x\n", info->line, retval);
+               }
+
+               /* set timeout !!! */
+               /* set RTS and DTR !!! */
+               tty_port_raise_dtr_rts(&info->port);
+
+               /* enable send, recv, modem !!! */
+       }
+
+       info->port.flags |= ASYNC_INITIALIZED;
+
+       clear_bit(TTY_IO_ERROR, &tty->flags);
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       info->breakon = info->breakoff = 0;
+       memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats));
+       info->idle_stats.in_use =
+       info->idle_stats.recv_idle =
+       info->idle_stats.xmit_idle = jiffies;
+
+       spin_unlock_irqrestore(&card->card_lock, flags);
+
+#ifdef CY_DEBUG_OPEN
+       printk(KERN_DEBUG "cyc startup done\n");
+#endif
+       return 0;
+
+errout:
+       spin_unlock_irqrestore(&card->card_lock, flags);
+       free_page(page);
+       return retval;
+}                              /* startup */
+
+static void start_xmit(struct cyclades_port *info)
+{
+       struct cyclades_card *card = info->card;
+       unsigned long flags;
+       int channel = info->line - card->first_line;
+
+       if (!cy_is_Z(card)) {
+               spin_lock_irqsave(&card->card_lock, flags);
+               cyy_writeb(info, CyCAR, channel & 0x03);
+               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy);
+               spin_unlock_irqrestore(&card->card_lock, flags);
+       } else {
+#ifdef CONFIG_CYZ_INTR
+               int retval;
+
+               spin_lock_irqsave(&card->card_lock, flags);
+               retval = cyz_issue_cmd(card, channel, C_CM_INTBACK, 0L);
+               if (retval != 0) {
+                       printk(KERN_ERR "cyc:start_xmit retval on ttyC%d was "
+                               "%x\n", info->line, retval);
+               }
+               spin_unlock_irqrestore(&card->card_lock, flags);
+#else                          /* CONFIG_CYZ_INTR */
+               /* Don't have to do anything at this time */
+#endif                         /* CONFIG_CYZ_INTR */
+       }
+}                              /* start_xmit */
+
+/*
+ * This routine shuts down a serial port; interrupts are disabled,
+ * and DTR is dropped if the hangup on close termio flag is on.
+ */
+static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty)
+{
+       struct cyclades_card *card;
+       unsigned long flags;
+       int channel;
+
+       if (!(info->port.flags & ASYNC_INITIALIZED))
+               return;
+
+       card = info->card;
+       channel = info->line - card->first_line;
+       if (!cy_is_Z(card)) {
+               spin_lock_irqsave(&card->card_lock, flags);
+
+               /* Clear delta_msr_wait queue to avoid mem leaks. */
+               wake_up_interruptible(&info->port.delta_msr_wait);
+
+               if (info->port.xmit_buf) {
+                       unsigned char *temp;
+                       temp = info->port.xmit_buf;
+                       info->port.xmit_buf = NULL;
+                       free_page((unsigned long)temp);
+               }
+               if (tty->termios->c_cflag & HUPCL)
+                       cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR);
+
+               cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR);
+               /* it may be appropriate to clear _XMIT at
+                  some later date (after testing)!!! */
+
+               set_bit(TTY_IO_ERROR, &tty->flags);
+               info->port.flags &= ~ASYNC_INITIALIZED;
+               spin_unlock_irqrestore(&card->card_lock, flags);
+       } else {
+#ifdef CY_DEBUG_OPEN
+               printk(KERN_DEBUG "cyc shutdown Z card %d, channel %d, "
+                       "base_addr %p\n", card, channel, card->base_addr);
+#endif
+
+               if (!cyz_is_loaded(card))
+                       return;
+
+               spin_lock_irqsave(&card->card_lock, flags);
+
+               if (info->port.xmit_buf) {
+                       unsigned char *temp;
+                       temp = info->port.xmit_buf;
+                       info->port.xmit_buf = NULL;
+                       free_page((unsigned long)temp);
+               }
+
+               if (tty->termios->c_cflag & HUPCL)
+                       tty_port_lower_dtr_rts(&info->port);
+
+               set_bit(TTY_IO_ERROR, &tty->flags);
+               info->port.flags &= ~ASYNC_INITIALIZED;
+
+               spin_unlock_irqrestore(&card->card_lock, flags);
+       }
+
+#ifdef CY_DEBUG_OPEN
+       printk(KERN_DEBUG "cyc shutdown done\n");
+#endif
+}                              /* shutdown */
+
+/*
+ * ------------------------------------------------------------
+ * cy_open() and friends
+ * ------------------------------------------------------------
+ */
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * performs the serial-specific initialization for the tty structure.
+ */
+static int cy_open(struct tty_struct *tty, struct file *filp)
+{
+       struct cyclades_port *info;
+       unsigned int i, line;
+       int retval;
+
+       line = tty->index;
+       if (tty->index < 0 || NR_PORTS <= line)
+               return -ENODEV;
+
+       for (i = 0; i < NR_CARDS; i++)
+               if (line < cy_card[i].first_line + cy_card[i].nports &&
+                               line >= cy_card[i].first_line)
+                       break;
+       if (i >= NR_CARDS)
+               return -ENODEV;
+       info = &cy_card[i].ports[line - cy_card[i].first_line];
+       if (info->line < 0)
+               return -ENODEV;
+
+       /* If the card's firmware hasn't been loaded,
+          treat it as absent from the system.  This
+          will make the user pay attention.
+        */
+       if (cy_is_Z(info->card)) {
+               struct cyclades_card *cinfo = info->card;
+               struct FIRM_ID __iomem *firm_id = cinfo->base_addr + ID_ADDRESS;
+
+               if (!cyz_is_loaded(cinfo)) {
+                       if (cinfo->hw_ver == ZE_V1 && cyz_fpga_loaded(cinfo) &&
+                                       readl(&firm_id->signature) ==
+                                       ZFIRM_HLT) {
+                               printk(KERN_ERR "cyc:Cyclades-Z Error: you "
+                                       "need an external power supply for "
+                                       "this number of ports.\nFirmware "
+                                       "halted.\n");
+                       } else {
+                               printk(KERN_ERR "cyc:Cyclades-Z firmware not "
+                                       "yet loaded\n");
+                       }
+                       return -ENODEV;
+               }
+#ifdef CONFIG_CYZ_INTR
+               else {
+               /* In case this Z board is operating in interrupt mode, its
+                  interrupts should be enabled as soon as the first open
+                  happens to one of its ports. */
+                       if (!cinfo->intr_enabled) {
+                               u16 intr;
+
+                               /* Enable interrupts on the PLX chip */
+                               intr = readw(&cinfo->ctl_addr.p9060->
+                                               intr_ctrl_stat) | 0x0900;
+                               cy_writew(&cinfo->ctl_addr.p9060->
+                                               intr_ctrl_stat, intr);
+                               /* Enable interrupts on the FW */
+                               retval = cyz_issue_cmd(cinfo, 0,
+                                               C_CM_IRQ_ENBL, 0L);
+                               if (retval != 0) {
+                                       printk(KERN_ERR "cyc:IRQ enable retval "
+                                               "was %x\n", retval);
+                               }
+                               cinfo->intr_enabled = 1;
+                       }
+               }
+#endif                         /* CONFIG_CYZ_INTR */
+               /* Make sure this Z port really exists in hardware */
+               if (info->line > (cinfo->first_line + cinfo->nports - 1))
+                       return -ENODEV;
+       }
+#ifdef CY_DEBUG_OTHER
+       printk(KERN_DEBUG "cyc:cy_open ttyC%d\n", info->line);
+#endif
+       tty->driver_data = info;
+       if (serial_paranoia_check(info, tty->name, "cy_open"))
+               return -ENODEV;
+
+#ifdef CY_DEBUG_OPEN
+       printk(KERN_DEBUG "cyc:cy_open ttyC%d, count = %d\n", info->line,
+                       info->port.count);
+#endif
+       info->port.count++;
+#ifdef CY_DEBUG_COUNT
+       printk(KERN_DEBUG "cyc:cy_open (%d): incrementing count to %d\n",
+               current->pid, info->port.count);
+#endif
+
+       /*
+        * If the port is the middle of closing, bail out now
+        */
+       if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) {
+               wait_event_interruptible_tty(info->port.close_wait,
+                               !(info->port.flags & ASYNC_CLOSING));
+               return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS;
+       }
+
+       /*
+        * Start up serial port
+        */
+       retval = cy_startup(info, tty);
+       if (retval)
+               return retval;
+
+       retval = tty_port_block_til_ready(&info->port, tty, filp);
+       if (retval) {
+#ifdef CY_DEBUG_OPEN
+               printk(KERN_DEBUG "cyc:cy_open returning after block_til_ready "
+                       "with %d\n", retval);
+#endif
+               return retval;
+       }
+
+       info->throttle = 0;
+       tty_port_tty_set(&info->port, tty);
+
+#ifdef CY_DEBUG_OPEN
+       printk(KERN_DEBUG "cyc:cy_open done\n");
+#endif
+       return 0;
+}                              /* cy_open */
+
+/*
+ * cy_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void cy_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       struct cyclades_card *card;
+       struct cyclades_port *info = tty->driver_data;
+       unsigned long orig_jiffies;
+       int char_time;
+
+       if (serial_paranoia_check(info, tty->name, "cy_wait_until_sent"))
+               return;
+
+       if (info->xmit_fifo_size == 0)
+               return;         /* Just in case.... */
+
+       orig_jiffies = jiffies;
+       /*
+        * Set the check interval to be 1/5 of the estimated time to
+        * send a single character, and make it at least 1.  The check
+        * interval should also be less than the timeout.
+        *
+        * Note: we have to use pretty tight timings here to satisfy
+        * the NIST-PCTS.
+        */
+       char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size;
+       char_time = char_time / 5;
+       if (char_time <= 0)
+               char_time = 1;
+       if (timeout < 0)
+               timeout = 0;
+       if (timeout)
+               char_time = min(char_time, timeout);
+       /*
+        * If the transmitter hasn't cleared in twice the approximate
+        * amount of time to send the entire FIFO, it probably won't
+        * ever clear.  This assumes the UART isn't doing flow
+        * control, which is currently the case.  Hence, if it ever
+        * takes longer than info->timeout, this is probably due to a
+        * UART bug of some kind.  So, we clamp the timeout parameter at
+        * 2*info->timeout.
+        */
+       if (!timeout || timeout > 2 * info->timeout)
+               timeout = 2 * info->timeout;
+#ifdef CY_DEBUG_WAIT_UNTIL_SENT
+       printk(KERN_DEBUG "In cy_wait_until_sent(%d) check=%d, jiff=%lu...",
+               timeout, char_time, jiffies);
+#endif
+       card = info->card;
+       if (!cy_is_Z(card)) {
+               while (cyy_readb(info, CySRER) & CyTxRdy) {
+#ifdef CY_DEBUG_WAIT_UNTIL_SENT
+                       printk(KERN_DEBUG "Not clean (jiff=%lu)...", jiffies);
+#endif
+                       if (msleep_interruptible(jiffies_to_msecs(char_time)))
+                               break;
+                       if (timeout && time_after(jiffies, orig_jiffies +
+                                       timeout))
+                               break;
+               }
+       }
+       /* Run one more char cycle */
+       msleep_interruptible(jiffies_to_msecs(char_time * 5));
+#ifdef CY_DEBUG_WAIT_UNTIL_SENT
+       printk(KERN_DEBUG "Clean (jiff=%lu)...done\n", jiffies);
+#endif
+}
+
+static void cy_flush_buffer(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+       struct cyclades_card *card;
+       int channel, retval;
+       unsigned long flags;
+
+#ifdef CY_DEBUG_IO
+       printk(KERN_DEBUG "cyc:cy_flush_buffer ttyC%d\n", info->line);
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_flush_buffer"))
+               return;
+
+       card = info->card;
+       channel = info->line - card->first_line;
+
+       spin_lock_irqsave(&card->card_lock, flags);
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       spin_unlock_irqrestore(&card->card_lock, flags);
+
+       if (cy_is_Z(card)) {    /* If it is a Z card, flush the on-board
+                                          buffers as well */
+               spin_lock_irqsave(&card->card_lock, flags);
+               retval = cyz_issue_cmd(card, channel, C_CM_FLUSH_TX, 0L);
+               if (retval != 0) {
+                       printk(KERN_ERR "cyc: flush_buffer retval on ttyC%d "
+                               "was %x\n", info->line, retval);
+               }
+               spin_unlock_irqrestore(&card->card_lock, flags);
+       }
+       tty_wakeup(tty);
+}                              /* cy_flush_buffer */
+
+
+static void cy_do_close(struct tty_port *port)
+{
+       struct cyclades_port *info = container_of(port, struct cyclades_port,
+                                                               port);
+       struct cyclades_card *card;
+       unsigned long flags;
+       int channel;
+
+       card = info->card;
+       channel = info->line - card->first_line;
+       spin_lock_irqsave(&card->card_lock, flags);
+
+       if (!cy_is_Z(card)) {
+               /* Stop accepting input */
+               cyy_writeb(info, CyCAR, channel & 0x03);
+               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyRxData);
+               if (info->port.flags & ASYNC_INITIALIZED) {
+                       /* Waiting for on-board buffers to be empty before
+                          closing the port */
+                       spin_unlock_irqrestore(&card->card_lock, flags);
+                       cy_wait_until_sent(port->tty, info->timeout);
+                       spin_lock_irqsave(&card->card_lock, flags);
+               }
+       } else {
+#ifdef Z_WAKE
+               /* Waiting for on-board buffers to be empty before closing
+                  the port */
+               struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
+               int retval;
+
+               if (readl(&ch_ctrl->flow_status) != C_FS_TXIDLE) {
+                       retval = cyz_issue_cmd(card, channel, C_CM_IOCTLW, 0L);
+                       if (retval != 0) {
+                               printk(KERN_DEBUG "cyc:cy_close retval on "
+                                       "ttyC%d was %x\n", info->line, retval);
+                       }
+                       spin_unlock_irqrestore(&card->card_lock, flags);
+                       wait_for_completion_interruptible(&info->shutdown_wait);
+                       spin_lock_irqsave(&card->card_lock, flags);
+               }
+#endif
+       }
+       spin_unlock_irqrestore(&card->card_lock, flags);
+       cy_shutdown(info, port->tty);
+}
+
+/*
+ * This routine is called when a particular tty device is closed.
+ */
+static void cy_close(struct tty_struct *tty, struct file *filp)
+{
+       struct cyclades_port *info = tty->driver_data;
+       if (!info || serial_paranoia_check(info, tty->name, "cy_close"))
+               return;
+       tty_port_close(&info->port, tty, filp);
+}                              /* cy_close */
+
+/* This routine gets called when tty_write has put something into
+ * the write_queue.  The characters may come from user space or
+ * kernel space.
+ *
+ * This routine will return the number of characters actually
+ * accepted for writing.
+ *
+ * If the port is not already transmitting stuff, start it off by
+ * enabling interrupts.  The interrupt service routine will then
+ * ensure that the characters are sent.
+ * If the port is already active, there is no need to kick it.
+ *
+ */
+static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+       struct cyclades_port *info = tty->driver_data;
+       unsigned long flags;
+       int c, ret = 0;
+
+#ifdef CY_DEBUG_IO
+       printk(KERN_DEBUG "cyc:cy_write ttyC%d\n", info->line);
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_write"))
+               return 0;
+
+       if (!info->port.xmit_buf)
+               return 0;
+
+       spin_lock_irqsave(&info->card->card_lock, flags);
+       while (1) {
+               c = min(count, (int)(SERIAL_XMIT_SIZE - info->xmit_cnt - 1));
+               c = min(c, (int)(SERIAL_XMIT_SIZE - info->xmit_head));
+
+               if (c <= 0)
+                       break;
+
+               memcpy(info->port.xmit_buf + info->xmit_head, buf, c);
+               info->xmit_head = (info->xmit_head + c) &
+                       (SERIAL_XMIT_SIZE - 1);
+               info->xmit_cnt += c;
+               buf += c;
+               count -= c;
+               ret += c;
+       }
+       spin_unlock_irqrestore(&info->card->card_lock, flags);
+
+       info->idle_stats.xmit_bytes += ret;
+       info->idle_stats.xmit_idle = jiffies;
+
+       if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped)
+               start_xmit(info);
+
+       return ret;
+}                              /* cy_write */
+
+/*
+ * This routine is called by the kernel to write a single
+ * character to the tty device.  If the kernel uses this routine,
+ * it must call the flush_chars() routine (if defined) when it is
+ * done stuffing characters into the driver.  If there is no room
+ * in the queue, the character is ignored.
+ */
+static int cy_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct cyclades_port *info = tty->driver_data;
+       unsigned long flags;
+
+#ifdef CY_DEBUG_IO
+       printk(KERN_DEBUG "cyc:cy_put_char ttyC%d\n", info->line);
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_put_char"))
+               return 0;
+
+       if (!info->port.xmit_buf)
+               return 0;
+
+       spin_lock_irqsave(&info->card->card_lock, flags);
+       if (info->xmit_cnt >= (int)(SERIAL_XMIT_SIZE - 1)) {
+               spin_unlock_irqrestore(&info->card->card_lock, flags);
+               return 0;
+       }
+
+       info->port.xmit_buf[info->xmit_head++] = ch;
+       info->xmit_head &= SERIAL_XMIT_SIZE - 1;
+       info->xmit_cnt++;
+       info->idle_stats.xmit_bytes++;
+       info->idle_stats.xmit_idle = jiffies;
+       spin_unlock_irqrestore(&info->card->card_lock, flags);
+       return 1;
+}                              /* cy_put_char */
+
+/*
+ * This routine is called by the kernel after it has written a
+ * series of characters to the tty device using put_char().
+ */
+static void cy_flush_chars(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+
+#ifdef CY_DEBUG_IO
+       printk(KERN_DEBUG "cyc:cy_flush_chars ttyC%d\n", info->line);
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_flush_chars"))
+               return;
+
+       if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+                       !info->port.xmit_buf)
+               return;
+
+       start_xmit(info);
+}                              /* cy_flush_chars */
+
+/*
+ * This routine returns the numbers of characters the tty driver
+ * will accept for queuing to be written.  This number is subject
+ * to change as output buffers get emptied, or if the output flow
+ * control is activated.
+ */
+static int cy_write_room(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+       int ret;
+
+#ifdef CY_DEBUG_IO
+       printk(KERN_DEBUG "cyc:cy_write_room ttyC%d\n", info->line);
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_write_room"))
+               return 0;
+       ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+       if (ret < 0)
+               ret = 0;
+       return ret;
+}                              /* cy_write_room */
+
+static int cy_chars_in_buffer(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+
+       if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer"))
+               return 0;
+
+#ifdef Z_EXT_CHARS_IN_BUFFER
+       if (!cy_is_Z(info->card)) {
+#endif                         /* Z_EXT_CHARS_IN_BUFFER */
+#ifdef CY_DEBUG_IO
+               printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n",
+                       info->line, info->xmit_cnt);
+#endif
+               return info->xmit_cnt;
+#ifdef Z_EXT_CHARS_IN_BUFFER
+       } else {
+               struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl;
+               int char_count;
+               __u32 tx_put, tx_get, tx_bufsize;
+
+               tx_get = readl(&buf_ctrl->tx_get);
+               tx_put = readl(&buf_ctrl->tx_put);
+               tx_bufsize = readl(&buf_ctrl->tx_bufsize);
+               if (tx_put >= tx_get)
+                       char_count = tx_put - tx_get;
+               else
+                       char_count = tx_put - tx_get + tx_bufsize;
+#ifdef CY_DEBUG_IO
+               printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n",
+                       info->line, info->xmit_cnt + char_count);
+#endif
+               return info->xmit_cnt + char_count;
+       }
+#endif                         /* Z_EXT_CHARS_IN_BUFFER */
+}                              /* cy_chars_in_buffer */
+
+/*
+ * ------------------------------------------------------------
+ * cy_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static void cyy_baud_calc(struct cyclades_port *info, __u32 baud)
+{
+       int co, co_val, bpr;
+       __u32 cy_clock = ((info->chip_rev >= CD1400_REV_J) ? 60000000 :
+                       25000000);
+
+       if (baud == 0) {
+               info->tbpr = info->tco = info->rbpr = info->rco = 0;
+               return;
+       }
+
+       /* determine which prescaler to use */
+       for (co = 4, co_val = 2048; co; co--, co_val >>= 2) {
+               if (cy_clock / co_val / baud > 63)
+                       break;
+       }
+
+       bpr = (cy_clock / co_val * 2 / baud + 1) / 2;
+       if (bpr > 255)
+               bpr = 255;
+
+       info->tbpr = info->rbpr = bpr;
+       info->tco = info->rco = co;
+}
+
+/*
+ * This routine finds or computes the various line characteristics.
+ * It used to be called config_setup
+ */
+static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty)
+{
+       struct cyclades_card *card;
+       unsigned long flags;
+       int channel;
+       unsigned cflag, iflag;
+       int baud, baud_rate = 0;
+       int i;
+
+       if (!tty->termios) /* XXX can this happen at all? */
+               return;
+
+       if (info->line == -1)
+               return;
+
+       cflag = tty->termios->c_cflag;
+       iflag = tty->termios->c_iflag;
+
+       /*
+        * Set up the tty->alt_speed kludge
+        */
+       if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+               tty->alt_speed = 57600;
+       if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+               tty->alt_speed = 115200;
+       if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+               tty->alt_speed = 230400;
+       if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+               tty->alt_speed = 460800;
+
+       card = info->card;
+       channel = info->line - card->first_line;
+
+       if (!cy_is_Z(card)) {
+               u32 cflags;
+
+               /* baud rate */
+               baud = tty_get_baud_rate(tty);
+               if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
+                               ASYNC_SPD_CUST) {
+                       if (info->custom_divisor)
+                               baud_rate = info->baud / info->custom_divisor;
+                       else
+                               baud_rate = info->baud;
+               } else if (baud > CD1400_MAX_SPEED) {
+                       baud = CD1400_MAX_SPEED;
+               }
+               /* find the baud index */
+               for (i = 0; i < 20; i++) {
+                       if (baud == baud_table[i])
+                               break;
+               }
+               if (i == 20)
+                       i = 19; /* CD1400_MAX_SPEED */
+
+               if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
+                               ASYNC_SPD_CUST) {
+                       cyy_baud_calc(info, baud_rate);
+               } else {
+                       if (info->chip_rev >= CD1400_REV_J) {
+                               /* It is a CD1400 rev. J or later */
+                               info->tbpr = baud_bpr_60[i];    /* Tx BPR */
+                               info->tco = baud_co_60[i];      /* Tx CO */
+                               info->rbpr = baud_bpr_60[i];    /* Rx BPR */
+                               info->rco = baud_co_60[i];      /* Rx CO */
+                       } else {
+                               info->tbpr = baud_bpr_25[i];    /* Tx BPR */
+                               info->tco = baud_co_25[i];      /* Tx CO */
+                               info->rbpr = baud_bpr_25[i];    /* Rx BPR */
+                               info->rco = baud_co_25[i];      /* Rx CO */
+                       }
+               }
+               if (baud_table[i] == 134) {
+                       /* get it right for 134.5 baud */
+                       info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) +
+                                       2;
+               } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
+                               ASYNC_SPD_CUST) {
+                       info->timeout = (info->xmit_fifo_size * HZ * 15 /
+                                       baud_rate) + 2;
+               } else if (baud_table[i]) {
+                       info->timeout = (info->xmit_fifo_size * HZ * 15 /
+                                       baud_table[i]) + 2;
+                       /* this needs to be propagated into the card info */
+               } else {
+                       info->timeout = 0;
+               }
+               /* By tradition (is it a standard?) a baud rate of zero
+                  implies the line should be/has been closed.  A bit
+                  later in this routine such a test is performed. */
+
+               /* byte size and parity */
+               info->cor5 = 0;
+               info->cor4 = 0;
+               /* receive threshold */
+               info->cor3 = (info->default_threshold ?
+                               info->default_threshold : baud_cor3[i]);
+               info->cor2 = CyETC;
+               switch (cflag & CSIZE) {
+               case CS5:
+                       info->cor1 = Cy_5_BITS;
+                       break;
+               case CS6:
+                       info->cor1 = Cy_6_BITS;
+                       break;
+               case CS7:
+                       info->cor1 = Cy_7_BITS;
+                       break;
+               case CS8:
+                       info->cor1 = Cy_8_BITS;
+                       break;
+               }
+               if (cflag & CSTOPB)
+                       info->cor1 |= Cy_2_STOP;
+
+               if (cflag & PARENB) {
+                       if (cflag & PARODD)
+                               info->cor1 |= CyPARITY_O;
+                       else
+                               info->cor1 |= CyPARITY_E;
+               } else
+                       info->cor1 |= CyPARITY_NONE;
+
+               /* CTS flow control flag */
+               if (cflag & CRTSCTS) {
+                       info->port.flags |= ASYNC_CTS_FLOW;
+                       info->cor2 |= CyCtsAE;
+               } else {
+                       info->port.flags &= ~ASYNC_CTS_FLOW;
+                       info->cor2 &= ~CyCtsAE;
+               }
+               if (cflag & CLOCAL)
+                       info->port.flags &= ~ASYNC_CHECK_CD;
+               else
+                       info->port.flags |= ASYNC_CHECK_CD;
+
+        /***********************************************
+           The hardware option, CyRtsAO, presents RTS when
+           the chip has characters to send.  Since most modems
+           use RTS as reverse (inbound) flow control, this
+           option is not used.  If inbound flow control is
+           necessary, DTR can be programmed to provide the
+           appropriate signals for use with a non-standard
+           cable.  Contact Marcio Saito for details.
+        ***********************************************/
+
+               channel &= 0x03;
+
+               spin_lock_irqsave(&card->card_lock, flags);
+               cyy_writeb(info, CyCAR, channel);
+
+               /* tx and rx baud rate */
+
+               cyy_writeb(info, CyTCOR, info->tco);
+               cyy_writeb(info, CyTBPR, info->tbpr);
+               cyy_writeb(info, CyRCOR, info->rco);
+               cyy_writeb(info, CyRBPR, info->rbpr);
+
+               /* set line characteristics  according configuration */
+
+               cyy_writeb(info, CySCHR1, START_CHAR(tty));
+               cyy_writeb(info, CySCHR2, STOP_CHAR(tty));
+               cyy_writeb(info, CyCOR1, info->cor1);
+               cyy_writeb(info, CyCOR2, info->cor2);
+               cyy_writeb(info, CyCOR3, info->cor3);
+               cyy_writeb(info, CyCOR4, info->cor4);
+               cyy_writeb(info, CyCOR5, info->cor5);
+
+               cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR1ch | CyCOR2ch |
+                               CyCOR3ch);
+
+               /* !!! Is this needed? */
+               cyy_writeb(info, CyCAR, channel);
+               cyy_writeb(info, CyRTPR,
+                       (info->default_timeout ? info->default_timeout : 0x02));
+               /* 10ms rx timeout */
+
+               cflags = CyCTS;
+               if (!C_CLOCAL(tty))
+                       cflags |= CyDSR | CyRI | CyDCD;
+               /* without modem intr */
+               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyMdmCh);
+               /* act on 1->0 modem transitions */
+               if ((cflag & CRTSCTS) && info->rflow)
+                       cyy_writeb(info, CyMCOR1, cflags | rflow_thr[i]);
+               else
+                       cyy_writeb(info, CyMCOR1, cflags);
+               /* act on 0->1 modem transitions */
+               cyy_writeb(info, CyMCOR2, cflags);
+
+               if (i == 0)     /* baud rate is zero, turn off line */
+                       cyy_change_rts_dtr(info, 0, TIOCM_DTR);
+               else
+                       cyy_change_rts_dtr(info, TIOCM_DTR, 0);
+
+               clear_bit(TTY_IO_ERROR, &tty->flags);
+               spin_unlock_irqrestore(&card->card_lock, flags);
+
+       } else {
+               struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
+               __u32 sw_flow;
+               int retval;
+
+               if (!cyz_is_loaded(card))
+                       return;
+
+               /* baud rate */
+               baud = tty_get_baud_rate(tty);
+               if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
+                               ASYNC_SPD_CUST) {
+                       if (info->custom_divisor)
+                               baud_rate = info->baud / info->custom_divisor;
+                       else
+                               baud_rate = info->baud;
+               } else if (baud > CYZ_MAX_SPEED) {
+                       baud = CYZ_MAX_SPEED;
+               }
+               cy_writel(&ch_ctrl->comm_baud, baud);
+
+               if (baud == 134) {
+                       /* get it right for 134.5 baud */
+                       info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) +
+                                       2;
+               } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) ==
+                               ASYNC_SPD_CUST) {
+                       info->timeout = (info->xmit_fifo_size * HZ * 15 /
+                                       baud_rate) + 2;
+               } else if (baud) {
+                       info->timeout = (info->xmit_fifo_size * HZ * 15 /
+                                       baud) + 2;
+                       /* this needs to be propagated into the card info */
+               } else {
+                       info->timeout = 0;
+               }
+
+               /* byte size and parity */
+               switch (cflag & CSIZE) {
+               case CS5:
+                       cy_writel(&ch_ctrl->comm_data_l, C_DL_CS5);
+                       break;
+               case CS6:
+                       cy_writel(&ch_ctrl->comm_data_l, C_DL_CS6);
+                       break;
+               case CS7:
+                       cy_writel(&ch_ctrl->comm_data_l, C_DL_CS7);
+                       break;
+               case CS8:
+                       cy_writel(&ch_ctrl->comm_data_l, C_DL_CS8);
+                       break;
+               }
+               if (cflag & CSTOPB) {
+                       cy_writel(&ch_ctrl->comm_data_l,
+                                 readl(&ch_ctrl->comm_data_l) | C_DL_2STOP);
+               } else {
+                       cy_writel(&ch_ctrl->comm_data_l,
+                                 readl(&ch_ctrl->comm_data_l) | C_DL_1STOP);
+               }
+               if (cflag & PARENB) {
+                       if (cflag & PARODD)
+                               cy_writel(&ch_ctrl->comm_parity, C_PR_ODD);
+                       else
+                               cy_writel(&ch_ctrl->comm_parity, C_PR_EVEN);
+               } else
+                       cy_writel(&ch_ctrl->comm_parity, C_PR_NONE);
+
+               /* CTS flow control flag */
+               if (cflag & CRTSCTS) {
+                       cy_writel(&ch_ctrl->hw_flow,
+                               readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS);
+               } else {
+                       cy_writel(&ch_ctrl->hw_flow, readl(&ch_ctrl->hw_flow) &
+                                       ~(C_RS_CTS | C_RS_RTS));
+               }
+               /* As the HW flow control is done in firmware, the driver
+                  doesn't need to care about it */
+               info->port.flags &= ~ASYNC_CTS_FLOW;
+
+               /* XON/XOFF/XANY flow control flags */
+               sw_flow = 0;
+               if (iflag & IXON) {
+                       sw_flow |= C_FL_OXX;
+                       if (iflag & IXANY)
+                               sw_flow |= C_FL_OIXANY;
+               }
+               cy_writel(&ch_ctrl->sw_flow, sw_flow);
+
+               retval = cyz_issue_cmd(card, channel, C_CM_IOCTL, 0L);
+               if (retval != 0) {
+                       printk(KERN_ERR "cyc:set_line_char retval on ttyC%d "
+                               "was %x\n", info->line, retval);
+               }
+
+               /* CD sensitivity */
+               if (cflag & CLOCAL)
+                       info->port.flags &= ~ASYNC_CHECK_CD;
+               else
+                       info->port.flags |= ASYNC_CHECK_CD;
+
+               if (baud == 0) {        /* baud rate is zero, turn off line */
+                       cy_writel(&ch_ctrl->rs_control,
+                                 readl(&ch_ctrl->rs_control) & ~C_RS_DTR);
+#ifdef CY_DEBUG_DTR
+                       printk(KERN_DEBUG "cyc:set_line_char dropping Z DTR\n");
+#endif
+               } else {
+                       cy_writel(&ch_ctrl->rs_control,
+                                 readl(&ch_ctrl->rs_control) | C_RS_DTR);
+#ifdef CY_DEBUG_DTR
+                       printk(KERN_DEBUG "cyc:set_line_char raising Z DTR\n");
+#endif
+               }
+
+               retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L);
+               if (retval != 0) {
+                       printk(KERN_ERR "cyc:set_line_char(2) retval on ttyC%d "
+                               "was %x\n", info->line, retval);
+               }
+
+               clear_bit(TTY_IO_ERROR, &tty->flags);
+       }
+}                              /* set_line_char */
+
+static int cy_get_serial_info(struct cyclades_port *info,
+               struct serial_struct __user *retinfo)
+{
+       struct cyclades_card *cinfo = info->card;
+       struct serial_struct tmp = {
+               .type = info->type,
+               .line = info->line,
+               .port = (info->card - cy_card) * 0x100 + info->line -
+                       cinfo->first_line,
+               .irq = cinfo->irq,
+               .flags = info->port.flags,
+               .close_delay = info->port.close_delay,
+               .closing_wait = info->port.closing_wait,
+               .baud_base = info->baud,
+               .custom_divisor = info->custom_divisor,
+               .hub6 = 0,              /*!!! */
+       };
+       return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;
+}
+
+static int
+cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty,
+               struct serial_struct __user *new_info)
+{
+       struct serial_struct new_serial;
+       int ret;
+
+       if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
+               return -EFAULT;
+
+       mutex_lock(&info->port.mutex);
+       if (!capable(CAP_SYS_ADMIN)) {
+               if (new_serial.close_delay != info->port.close_delay ||
+                               new_serial.baud_base != info->baud ||
+                               (new_serial.flags & ASYNC_FLAGS &
+                                       ~ASYNC_USR_MASK) !=
+                               (info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))
+               {
+                       mutex_unlock(&info->port.mutex);
+                       return -EPERM;
+               }
+               info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) |
+                               (new_serial.flags & ASYNC_USR_MASK);
+               info->baud = new_serial.baud_base;
+               info->custom_divisor = new_serial.custom_divisor;
+               goto check_and_exit;
+       }
+
+       /*
+        * OK, past this point, all the error checking has been done.
+        * At this point, we start making changes.....
+        */
+
+       info->baud = new_serial.baud_base;
+       info->custom_divisor = new_serial.custom_divisor;
+       info->port.flags = (info->port.flags & ~ASYNC_FLAGS) |
+                       (new_serial.flags & ASYNC_FLAGS);
+       info->port.close_delay = new_serial.close_delay * HZ / 100;
+       info->port.closing_wait = new_serial.closing_wait * HZ / 100;
+
+check_and_exit:
+       if (info->port.flags & ASYNC_INITIALIZED) {
+               cy_set_line_char(info, tty);
+               ret = 0;
+       } else {
+               ret = cy_startup(info, tty);
+       }
+       mutex_unlock(&info->port.mutex);
+       return ret;
+}                              /* set_serial_info */
+
+/*
+ * 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 get_lsr_info(struct cyclades_port *info, unsigned int __user *value)
+{
+       struct cyclades_card *card = info->card;
+       unsigned int result;
+       unsigned long flags;
+       u8 status;
+
+       if (!cy_is_Z(card)) {
+               spin_lock_irqsave(&card->card_lock, flags);
+               status = cyy_readb(info, CySRER) & (CyTxRdy | CyTxMpty);
+               spin_unlock_irqrestore(&card->card_lock, flags);
+               result = (status ? 0 : TIOCSER_TEMT);
+       } else {
+               /* Not supported yet */
+               return -EINVAL;
+       }
+       return put_user(result, (unsigned long __user *)value);
+}
+
+static int cy_tiocmget(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+       struct cyclades_card *card;
+       int result;
+
+       if (serial_paranoia_check(info, tty->name, __func__))
+               return -ENODEV;
+
+       card = info->card;
+
+       if (!cy_is_Z(card)) {
+               unsigned long flags;
+               int channel = info->line - card->first_line;
+               u8 status;
+
+               spin_lock_irqsave(&card->card_lock, flags);
+               cyy_writeb(info, CyCAR, channel & 0x03);
+               status = cyy_readb(info, CyMSVR1);
+               status |= cyy_readb(info, CyMSVR2);
+               spin_unlock_irqrestore(&card->card_lock, flags);
+
+               if (info->rtsdtr_inv) {
+                       result = ((status & CyRTS) ? TIOCM_DTR : 0) |
+                               ((status & CyDTR) ? TIOCM_RTS : 0);
+               } else {
+                       result = ((status & CyRTS) ? TIOCM_RTS : 0) |
+                               ((status & CyDTR) ? TIOCM_DTR : 0);
+               }
+               result |= ((status & CyDCD) ? TIOCM_CAR : 0) |
+                       ((status & CyRI) ? TIOCM_RNG : 0) |
+                       ((status & CyDSR) ? TIOCM_DSR : 0) |
+                       ((status & CyCTS) ? TIOCM_CTS : 0);
+       } else {
+               u32 lstatus;
+
+               if (!cyz_is_loaded(card)) {
+                       result = -ENODEV;
+                       goto end;
+               }
+
+               lstatus = readl(&info->u.cyz.ch_ctrl->rs_status);
+               result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) |
+                       ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) |
+                       ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) |
+                       ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) |
+                       ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) |
+                       ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0);
+       }
+end:
+       return result;
+}                              /* cy_tiomget */
+
+static int
+cy_tiocmset(struct tty_struct *tty,
+               unsigned int set, unsigned int clear)
+{
+       struct cyclades_port *info = tty->driver_data;
+       struct cyclades_card *card;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->name, __func__))
+               return -ENODEV;
+
+       card = info->card;
+       if (!cy_is_Z(card)) {
+               spin_lock_irqsave(&card->card_lock, flags);
+               cyy_change_rts_dtr(info, set, clear);
+               spin_unlock_irqrestore(&card->card_lock, flags);
+       } else {
+               struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
+               int retval, channel = info->line - card->first_line;
+               u32 rs;
+
+               if (!cyz_is_loaded(card))
+                       return -ENODEV;
+
+               spin_lock_irqsave(&card->card_lock, flags);
+               rs = readl(&ch_ctrl->rs_control);
+               if (set & TIOCM_RTS)
+                       rs |= C_RS_RTS;
+               if (clear & TIOCM_RTS)
+                       rs &= ~C_RS_RTS;
+               if (set & TIOCM_DTR) {
+                       rs |= C_RS_DTR;
+#ifdef CY_DEBUG_DTR
+                       printk(KERN_DEBUG "cyc:set_modem_info raising Z DTR\n");
+#endif
+               }
+               if (clear & TIOCM_DTR) {
+                       rs &= ~C_RS_DTR;
+#ifdef CY_DEBUG_DTR
+                       printk(KERN_DEBUG "cyc:set_modem_info clearing "
+                               "Z DTR\n");
+#endif
+               }
+               cy_writel(&ch_ctrl->rs_control, rs);
+               retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L);
+               spin_unlock_irqrestore(&card->card_lock, flags);
+               if (retval != 0) {
+                       printk(KERN_ERR "cyc:set_modem_info retval on ttyC%d "
+                               "was %x\n", info->line, retval);
+               }
+       }
+       return 0;
+}
+
+/*
+ * cy_break() --- routine which turns the break handling on or off
+ */
+static int cy_break(struct tty_struct *tty, int break_state)
+{
+       struct cyclades_port *info = tty->driver_data;
+       struct cyclades_card *card;
+       unsigned long flags;
+       int retval = 0;
+
+       if (serial_paranoia_check(info, tty->name, "cy_break"))
+               return -EINVAL;
+
+       card = info->card;
+
+       spin_lock_irqsave(&card->card_lock, flags);
+       if (!cy_is_Z(card)) {
+               /* Let the transmit ISR take care of this (since it
+                  requires stuffing characters into the output stream).
+                */
+               if (break_state == -1) {
+                       if (!info->breakon) {
+                               info->breakon = 1;
+                               if (!info->xmit_cnt) {
+                                       spin_unlock_irqrestore(&card->card_lock, flags);
+                                       start_xmit(info);
+                                       spin_lock_irqsave(&card->card_lock, flags);
+                               }
+                       }
+               } else {
+                       if (!info->breakoff) {
+                               info->breakoff = 1;
+                               if (!info->xmit_cnt) {
+                                       spin_unlock_irqrestore(&card->card_lock, flags);
+                                       start_xmit(info);
+                                       spin_lock_irqsave(&card->card_lock, flags);
+                               }
+                       }
+               }
+       } else {
+               if (break_state == -1) {
+                       retval = cyz_issue_cmd(card,
+                               info->line - card->first_line,
+                               C_CM_SET_BREAK, 0L);
+                       if (retval != 0) {
+                               printk(KERN_ERR "cyc:cy_break (set) retval on "
+                                       "ttyC%d was %x\n", info->line, retval);
+                       }
+               } else {
+                       retval = cyz_issue_cmd(card,
+                               info->line - card->first_line,
+                               C_CM_CLR_BREAK, 0L);
+                       if (retval != 0) {
+                               printk(KERN_DEBUG "cyc:cy_break (clr) retval "
+                                       "on ttyC%d was %x\n", info->line,
+                                       retval);
+                       }
+               }
+       }
+       spin_unlock_irqrestore(&card->card_lock, flags);
+       return retval;
+}                              /* cy_break */
+
+static int set_threshold(struct cyclades_port *info, unsigned long value)
+{
+       struct cyclades_card *card = info->card;
+       unsigned long flags;
+
+       if (!cy_is_Z(card)) {
+               info->cor3 &= ~CyREC_FIFO;
+               info->cor3 |= value & CyREC_FIFO;
+
+               spin_lock_irqsave(&card->card_lock, flags);
+               cyy_writeb(info, CyCOR3, info->cor3);
+               cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR3ch);
+               spin_unlock_irqrestore(&card->card_lock, flags);
+       }
+       return 0;
+}                              /* set_threshold */
+
+static int get_threshold(struct cyclades_port *info,
+                                               unsigned long __user *value)
+{
+       struct cyclades_card *card = info->card;
+
+       if (!cy_is_Z(card)) {
+               u8 tmp = cyy_readb(info, CyCOR3) & CyREC_FIFO;
+               return put_user(tmp, value);
+       }
+       return 0;
+}                              /* get_threshold */
+
+static int set_timeout(struct cyclades_port *info, unsigned long value)
+{
+       struct cyclades_card *card = info->card;
+       unsigned long flags;
+
+       if (!cy_is_Z(card)) {
+               spin_lock_irqsave(&card->card_lock, flags);
+               cyy_writeb(info, CyRTPR, value & 0xff);
+               spin_unlock_irqrestore(&card->card_lock, flags);
+       }
+       return 0;
+}                              /* set_timeout */
+
+static int get_timeout(struct cyclades_port *info,
+                                               unsigned long __user *value)
+{
+       struct cyclades_card *card = info->card;
+
+       if (!cy_is_Z(card)) {
+               u8 tmp = cyy_readb(info, CyRTPR);
+               return put_user(tmp, value);
+       }
+       return 0;
+}                              /* get_timeout */
+
+static int cy_cflags_changed(struct cyclades_port *info, unsigned long arg,
+               struct cyclades_icount *cprev)
+{
+       struct cyclades_icount cnow;
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&info->card->card_lock, flags);
+       cnow = info->icount;    /* atomic copy */
+       spin_unlock_irqrestore(&info->card->card_lock, flags);
+
+       ret =   ((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));
+
+       *cprev = cnow;
+
+       return ret;
+}
+
+/*
+ * This routine allows the tty driver to implement device-
+ * specific ioctl's.  If the ioctl number passed in cmd is
+ * not recognized by the driver, it should return ENOIOCTLCMD.
+ */
+static int
+cy_ioctl(struct tty_struct *tty,
+        unsigned int cmd, unsigned long arg)
+{
+       struct cyclades_port *info = tty->driver_data;
+       struct cyclades_icount cnow;    /* kernel counter temps */
+       int ret_val = 0;
+       unsigned long flags;
+       void __user *argp = (void __user *)arg;
+
+       if (serial_paranoia_check(info, tty->name, "cy_ioctl"))
+               return -ENODEV;
+
+#ifdef CY_DEBUG_OTHER
+       printk(KERN_DEBUG "cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n",
+               info->line, cmd, arg);
+#endif
+
+       switch (cmd) {
+       case CYGETMON:
+               if (copy_to_user(argp, &info->mon, sizeof(info->mon))) {
+                       ret_val = -EFAULT;
+                       break;
+               }
+               memset(&info->mon, 0, sizeof(info->mon));
+               break;
+       case CYGETTHRESH:
+               ret_val = get_threshold(info, argp);
+               break;
+       case CYSETTHRESH:
+               ret_val = set_threshold(info, arg);
+               break;
+       case CYGETDEFTHRESH:
+               ret_val = put_user(info->default_threshold,
+                               (unsigned long __user *)argp);
+               break;
+       case CYSETDEFTHRESH:
+               info->default_threshold = arg & 0x0f;
+               break;
+       case CYGETTIMEOUT:
+               ret_val = get_timeout(info, argp);
+               break;
+       case CYSETTIMEOUT:
+               ret_val = set_timeout(info, arg);
+               break;
+       case CYGETDEFTIMEOUT:
+               ret_val = put_user(info->default_timeout,
+                               (unsigned long __user *)argp);
+               break;
+       case CYSETDEFTIMEOUT:
+               info->default_timeout = arg & 0xff;
+               break;
+       case CYSETRFLOW:
+               info->rflow = (int)arg;
+               break;
+       case CYGETRFLOW:
+               ret_val = info->rflow;
+               break;
+       case CYSETRTSDTR_INV:
+               info->rtsdtr_inv = (int)arg;
+               break;
+       case CYGETRTSDTR_INV:
+               ret_val = info->rtsdtr_inv;
+               break;
+       case CYGETCD1400VER:
+               ret_val = info->chip_rev;
+               break;
+#ifndef CONFIG_CYZ_INTR
+       case CYZSETPOLLCYCLE:
+               cyz_polling_cycle = (arg * HZ) / 1000;
+               break;
+       case CYZGETPOLLCYCLE:
+               ret_val = (cyz_polling_cycle * 1000) / HZ;
+               break;
+#endif                         /* CONFIG_CYZ_INTR */
+       case CYSETWAIT:
+               info->port.closing_wait = (unsigned short)arg * HZ / 100;
+               break;
+       case CYGETWAIT:
+               ret_val = info->port.closing_wait / (HZ / 100);
+               break;
+       case TIOCGSERIAL:
+               ret_val = cy_get_serial_info(info, argp);
+               break;
+       case TIOCSSERIAL:
+               ret_val = cy_set_serial_info(info, tty, argp);
+               break;
+       case TIOCSERGETLSR:     /* Get line status register */
+               ret_val = get_lsr_info(info, argp);
+               break;
+               /*
+                * 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:
+               spin_lock_irqsave(&info->card->card_lock, flags);
+               /* note the counters on entry */
+               cnow = info->icount;
+               spin_unlock_irqrestore(&info->card->card_lock, flags);
+               ret_val = wait_event_interruptible(info->port.delta_msr_wait,
+                               cy_cflags_changed(info, arg, &cnow));
+               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.
+                */
+       default:
+               ret_val = -ENOIOCTLCMD;
+       }
+
+#ifdef CY_DEBUG_OTHER
+       printk(KERN_DEBUG "cyc:cy_ioctl done\n");
+#endif
+       return ret_val;
+}                              /* cy_ioctl */
+
+static int cy_get_icount(struct tty_struct *tty,
+                               struct serial_icounter_struct *sic)
+{
+       struct cyclades_port *info = tty->driver_data;
+       struct cyclades_icount cnow;    /* Used to snapshot */
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->card->card_lock, flags);
+       cnow = info->icount;
+       spin_unlock_irqrestore(&info->card->card_lock, flags);
+
+       sic->cts = cnow.cts;
+       sic->dsr = cnow.dsr;
+       sic->rng = cnow.rng;
+       sic->dcd = cnow.dcd;
+       sic->rx = cnow.rx;
+       sic->tx = cnow.tx;
+       sic->frame = cnow.frame;
+       sic->overrun = cnow.overrun;
+       sic->parity = cnow.parity;
+       sic->brk = cnow.brk;
+       sic->buf_overrun = cnow.buf_overrun;
+       return 0;
+}
+
+/*
+ * This routine allows the tty driver to be notified when
+ * device's termios settings have changed.  Note that a
+ * well-designed tty driver should be prepared to accept the case
+ * where old == NULL, and try to do something rational.
+ */
+static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+       struct cyclades_port *info = tty->driver_data;
+
+#ifdef CY_DEBUG_OTHER
+       printk(KERN_DEBUG "cyc:cy_set_termios ttyC%d\n", info->line);
+#endif
+
+       cy_set_line_char(info, tty);
+
+       if ((old_termios->c_cflag & CRTSCTS) &&
+                       !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               cy_start(tty);
+       }
+#if 0
+       /*
+        * No need to wake up processes in open wait, since they
+        * sample the CLOCAL flag once, and don't recheck it.
+        * XXX  It's not clear whether the current behavior is correct
+        * or not.  Hence, this may change.....
+        */
+       if (!(old_termios->c_cflag & CLOCAL) &&
+           (tty->termios->c_cflag & CLOCAL))
+               wake_up_interruptible(&info->port.open_wait);
+#endif
+}                              /* cy_set_termios */
+
+/* This function is used to send a high-priority XON/XOFF character to
+   the device.
+*/
+static void cy_send_xchar(struct tty_struct *tty, char ch)
+{
+       struct cyclades_port *info = tty->driver_data;
+       struct cyclades_card *card;
+       int channel;
+
+       if (serial_paranoia_check(info, tty->name, "cy_send_xchar"))
+               return;
+
+       info->x_char = ch;
+
+       if (ch)
+               cy_start(tty);
+
+       card = info->card;
+       channel = info->line - card->first_line;
+
+       if (cy_is_Z(card)) {
+               if (ch == STOP_CHAR(tty))
+                       cyz_issue_cmd(card, channel, C_CM_SENDXOFF, 0L);
+               else if (ch == START_CHAR(tty))
+                       cyz_issue_cmd(card, channel, C_CM_SENDXON, 0L);
+       }
+}
+
+/* This routine is called by the upper-layer tty layer to signal
+   that incoming characters should be throttled because the input
+   buffers are close to full.
+ */
+static void cy_throttle(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+       struct cyclades_card *card;
+       unsigned long flags;
+
+#ifdef CY_DEBUG_THROTTLE
+       char buf[64];
+
+       printk(KERN_DEBUG "cyc:throttle %s: %ld...ttyC%d\n", tty_name(tty, buf),
+                       tty->ldisc.chars_in_buffer(tty), info->line);
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_throttle"))
+               return;
+
+       card = info->card;
+
+       if (I_IXOFF(tty)) {
+               if (!cy_is_Z(card))
+                       cy_send_xchar(tty, STOP_CHAR(tty));
+               else
+                       info->throttle = 1;
+       }
+
+       if (tty->termios->c_cflag & CRTSCTS) {
+               if (!cy_is_Z(card)) {
+                       spin_lock_irqsave(&card->card_lock, flags);
+                       cyy_change_rts_dtr(info, 0, TIOCM_RTS);
+                       spin_unlock_irqrestore(&card->card_lock, flags);
+               } else {
+                       info->throttle = 1;
+               }
+       }
+}                              /* cy_throttle */
+
+/*
+ * This routine notifies the tty driver that it should signal
+ * that characters can now be sent to the tty without fear of
+ * overrunning the input buffers of the line disciplines.
+ */
+static void cy_unthrottle(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+       struct cyclades_card *card;
+       unsigned long flags;
+
+#ifdef CY_DEBUG_THROTTLE
+       char buf[64];
+
+       printk(KERN_DEBUG "cyc:unthrottle %s: %ld...ttyC%d\n",
+               tty_name(tty, buf), tty_chars_in_buffer(tty), info->line);
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_unthrottle"))
+               return;
+
+       if (I_IXOFF(tty)) {
+               if (info->x_char)
+                       info->x_char = 0;
+               else
+                       cy_send_xchar(tty, START_CHAR(tty));
+       }
+
+       if (tty->termios->c_cflag & CRTSCTS) {
+               card = info->card;
+               if (!cy_is_Z(card)) {
+                       spin_lock_irqsave(&card->card_lock, flags);
+                       cyy_change_rts_dtr(info, TIOCM_RTS, 0);
+                       spin_unlock_irqrestore(&card->card_lock, flags);
+               } else {
+                       info->throttle = 0;
+               }
+       }
+}                              /* cy_unthrottle */
+
+/* cy_start and cy_stop provide software output flow control as a
+   function of XON/XOFF, software CTS, and other such stuff.
+*/
+static void cy_stop(struct tty_struct *tty)
+{
+       struct cyclades_card *cinfo;
+       struct cyclades_port *info = tty->driver_data;
+       int channel;
+       unsigned long flags;
+
+#ifdef CY_DEBUG_OTHER
+       printk(KERN_DEBUG "cyc:cy_stop ttyC%d\n", info->line);
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_stop"))
+               return;
+
+       cinfo = info->card;
+       channel = info->line - cinfo->first_line;
+       if (!cy_is_Z(cinfo)) {
+               spin_lock_irqsave(&cinfo->card_lock, flags);
+               cyy_writeb(info, CyCAR, channel & 0x03);
+               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy);
+               spin_unlock_irqrestore(&cinfo->card_lock, flags);
+       }
+}                              /* cy_stop */
+
+static void cy_start(struct tty_struct *tty)
+{
+       struct cyclades_card *cinfo;
+       struct cyclades_port *info = tty->driver_data;
+       int channel;
+       unsigned long flags;
+
+#ifdef CY_DEBUG_OTHER
+       printk(KERN_DEBUG "cyc:cy_start ttyC%d\n", info->line);
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_start"))
+               return;
+
+       cinfo = info->card;
+       channel = info->line - cinfo->first_line;
+       if (!cy_is_Z(cinfo)) {
+               spin_lock_irqsave(&cinfo->card_lock, flags);
+               cyy_writeb(info, CyCAR, channel & 0x03);
+               cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy);
+               spin_unlock_irqrestore(&cinfo->card_lock, flags);
+       }
+}                              /* cy_start */
+
+/*
+ * cy_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void cy_hangup(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+
+#ifdef CY_DEBUG_OTHER
+       printk(KERN_DEBUG "cyc:cy_hangup ttyC%d\n", info->line);
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_hangup"))
+               return;
+
+       cy_flush_buffer(tty);
+       cy_shutdown(info, tty);
+       tty_port_hangup(&info->port);
+}                              /* cy_hangup */
+
+static int cyy_carrier_raised(struct tty_port *port)
+{
+       struct cyclades_port *info = container_of(port, struct cyclades_port,
+                       port);
+       struct cyclades_card *cinfo = info->card;
+       unsigned long flags;
+       int channel = info->line - cinfo->first_line;
+       u32 cd;
+
+       spin_lock_irqsave(&cinfo->card_lock, flags);
+       cyy_writeb(info, CyCAR, channel & 0x03);
+       cd = cyy_readb(info, CyMSVR1) & CyDCD;
+       spin_unlock_irqrestore(&cinfo->card_lock, flags);
+
+       return cd;
+}
+
+static void cyy_dtr_rts(struct tty_port *port, int raise)
+{
+       struct cyclades_port *info = container_of(port, struct cyclades_port,
+                       port);
+       struct cyclades_card *cinfo = info->card;
+       unsigned long flags;
+
+       spin_lock_irqsave(&cinfo->card_lock, flags);
+       cyy_change_rts_dtr(info, raise ? TIOCM_RTS | TIOCM_DTR : 0,
+                       raise ? 0 : TIOCM_RTS | TIOCM_DTR);
+       spin_unlock_irqrestore(&cinfo->card_lock, flags);
+}
+
+static int cyz_carrier_raised(struct tty_port *port)
+{
+       struct cyclades_port *info = container_of(port, struct cyclades_port,
+                       port);
+
+       return readl(&info->u.cyz.ch_ctrl->rs_status) & C_RS_DCD;
+}
+
+static void cyz_dtr_rts(struct tty_port *port, int raise)
+{
+       struct cyclades_port *info = container_of(port, struct cyclades_port,
+                       port);
+       struct cyclades_card *cinfo = info->card;
+       struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl;
+       int ret, channel = info->line - cinfo->first_line;
+       u32 rs;
+
+       rs = readl(&ch_ctrl->rs_control);
+       if (raise)
+               rs |= C_RS_RTS | C_RS_DTR;
+       else
+               rs &= ~(C_RS_RTS | C_RS_DTR);
+       cy_writel(&ch_ctrl->rs_control, rs);
+       ret = cyz_issue_cmd(cinfo, channel, C_CM_IOCTLM, 0L);
+       if (ret != 0)
+               printk(KERN_ERR "%s: retval on ttyC%d was %x\n",
+                               __func__, info->line, ret);
+#ifdef CY_DEBUG_DTR
+       printk(KERN_DEBUG "%s: raising Z DTR\n", __func__);
+#endif
+}
+
+static const struct tty_port_operations cyy_port_ops = {
+       .carrier_raised = cyy_carrier_raised,
+       .dtr_rts = cyy_dtr_rts,
+       .shutdown = cy_do_close,
+};
+
+static const struct tty_port_operations cyz_port_ops = {
+       .carrier_raised = cyz_carrier_raised,
+       .dtr_rts = cyz_dtr_rts,
+       .shutdown = cy_do_close,
+};
+
+/*
+ * ---------------------------------------------------------------------
+ * cy_init() and friends
+ *
+ * cy_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+static int __devinit cy_init_card(struct cyclades_card *cinfo)
+{
+       struct cyclades_port *info;
+       unsigned int channel, port;
+
+       spin_lock_init(&cinfo->card_lock);
+       cinfo->intr_enabled = 0;
+
+       cinfo->ports = kcalloc(cinfo->nports, sizeof(*cinfo->ports),
+                       GFP_KERNEL);
+       if (cinfo->ports == NULL) {
+               printk(KERN_ERR "Cyclades: cannot allocate ports\n");
+               return -ENOMEM;
+       }
+
+       for (channel = 0, port = cinfo->first_line; channel < cinfo->nports;
+                       channel++, port++) {
+               info = &cinfo->ports[channel];
+               tty_port_init(&info->port);
+               info->magic = CYCLADES_MAGIC;
+               info->card = cinfo;
+               info->line = port;
+
+               info->port.closing_wait = CLOSING_WAIT_DELAY;
+               info->port.close_delay = 5 * HZ / 10;
+               info->port.flags = STD_COM_FLAGS;
+               init_completion(&info->shutdown_wait);
+
+               if (cy_is_Z(cinfo)) {
+                       struct FIRM_ID *firm_id = cinfo->base_addr + ID_ADDRESS;
+                       struct ZFW_CTRL *zfw_ctrl;
+
+                       info->port.ops = &cyz_port_ops;
+                       info->type = PORT_STARTECH;
+
+                       zfw_ctrl = cinfo->base_addr +
+                               (readl(&firm_id->zfwctrl_addr) & 0xfffff);
+                       info->u.cyz.ch_ctrl = &zfw_ctrl->ch_ctrl[channel];
+                       info->u.cyz.buf_ctrl = &zfw_ctrl->buf_ctrl[channel];
+
+                       if (cinfo->hw_ver == ZO_V1)
+                               info->xmit_fifo_size = CYZ_FIFO_SIZE;
+                       else
+                               info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE;
+#ifdef CONFIG_CYZ_INTR
+                       setup_timer(&cyz_rx_full_timer[port],
+                               cyz_rx_restart, (unsigned long)info);
+#endif
+               } else {
+                       unsigned short chip_number;
+                       int index = cinfo->bus_index;
+
+                       info->port.ops = &cyy_port_ops;
+                       info->type = PORT_CIRRUS;
+                       info->xmit_fifo_size = CyMAX_CHAR_FIFO;
+                       info->cor1 = CyPARITY_NONE | Cy_1_STOP | Cy_8_BITS;
+                       info->cor2 = CyETC;
+                       info->cor3 = 0x08;      /* _very_ small rcv threshold */
+
+                       chip_number = channel / CyPORTS_PER_CHIP;
+                       info->u.cyy.base_addr = cinfo->base_addr +
+                               (cy_chip_offset[chip_number] << index);
+                       info->chip_rev = cyy_readb(info, CyGFRCR);
+
+                       if (info->chip_rev >= CD1400_REV_J) {
+                               /* It is a CD1400 rev. J or later */
+                               info->tbpr = baud_bpr_60[13];   /* Tx BPR */
+                               info->tco = baud_co_60[13];     /* Tx CO */
+                               info->rbpr = baud_bpr_60[13];   /* Rx BPR */
+                               info->rco = baud_co_60[13];     /* Rx CO */
+                               info->rtsdtr_inv = 1;
+                       } else {
+                               info->tbpr = baud_bpr_25[13];   /* Tx BPR */
+                               info->tco = baud_co_25[13];     /* Tx CO */
+                               info->rbpr = baud_bpr_25[13];   /* Rx BPR */
+                               info->rco = baud_co_25[13];     /* Rx CO */
+                               info->rtsdtr_inv = 0;
+                       }
+                       info->read_status_mask = CyTIMEOUT | CySPECHAR |
+                               CyBREAK | CyPARITY | CyFRAME | CyOVERRUN;
+               }
+
+       }
+
+#ifndef CONFIG_CYZ_INTR
+       if (cy_is_Z(cinfo) && !timer_pending(&cyz_timerlist)) {
+               mod_timer(&cyz_timerlist, jiffies + 1);
+#ifdef CY_PCI_DEBUG
+               printk(KERN_DEBUG "Cyclades-Z polling initialized\n");
+#endif
+       }
+#endif
+       return 0;
+}
+
+/* initialize chips on Cyclom-Y card -- return number of valid
+   chips (which is number of ports/4) */
+static unsigned short __devinit cyy_init_card(void __iomem *true_base_addr,
+               int index)
+{
+       unsigned int chip_number;
+       void __iomem *base_addr;
+
+       cy_writeb(true_base_addr + (Cy_HwReset << index), 0);
+       /* Cy_HwReset is 0x1400 */
+       cy_writeb(true_base_addr + (Cy_ClrIntr << index), 0);
+       /* Cy_ClrIntr is 0x1800 */
+       udelay(500L);
+
+       for (chip_number = 0; chip_number < CyMAX_CHIPS_PER_CARD;
+                                                       chip_number++) {
+               base_addr =
+                   true_base_addr + (cy_chip_offset[chip_number] << index);
+               mdelay(1);
+               if (readb(base_addr + (CyCCR << index)) != 0x00) {
+                       /*************
+                       printk(" chip #%d at %#6lx is never idle (CCR != 0)\n",
+                       chip_number, (unsigned long)base_addr);
+                       *************/
+                       return chip_number;
+               }
+
+               cy_writeb(base_addr + (CyGFRCR << index), 0);
+               udelay(10L);
+
+               /* The Cyclom-16Y does not decode address bit 9 and therefore
+                  cannot distinguish between references to chip 0 and a non-
+                  existent chip 4.  If the preceding clearing of the supposed
+                  chip 4 GFRCR register appears at chip 0, there is no chip 4
+                  and this must be a Cyclom-16Y, not a Cyclom-32Ye.
+                */
+               if (chip_number == 4 && readb(true_base_addr +
+                               (cy_chip_offset[0] << index) +
+                               (CyGFRCR << index)) == 0) {
+                       return chip_number;
+               }
+
+               cy_writeb(base_addr + (CyCCR << index), CyCHIP_RESET);
+               mdelay(1);
+
+               if (readb(base_addr + (CyGFRCR << index)) == 0x00) {
+                       /*
+                          printk(" chip #%d at %#6lx is not responding ",
+                          chip_number, (unsigned long)base_addr);
+                          printk("(GFRCR stayed 0)\n",
+                        */
+                       return chip_number;
+               }
+               if ((0xf0 & (readb(base_addr + (CyGFRCR << index)))) !=
+                               0x40) {
+                       /*
+                       printk(" chip #%d at %#6lx is not valid (GFRCR == "
+                                       "%#2x)\n",
+                                       chip_number, (unsigned long)base_addr,
+                                       base_addr[CyGFRCR<<index]);
+                        */
+                       return chip_number;
+               }
+               cy_writeb(base_addr + (CyGCR << index), CyCH0_SERIAL);
+               if (readb(base_addr + (CyGFRCR << index)) >= CD1400_REV_J) {
+                       /* It is a CD1400 rev. J or later */
+                       /* Impossible to reach 5ms with this chip.
+                          Changed to 2ms instead (f = 500 Hz). */
+                       cy_writeb(base_addr + (CyPPR << index), CyCLOCK_60_2MS);
+               } else {
+                       /* f = 200 Hz */
+                       cy_writeb(base_addr + (CyPPR << index), CyCLOCK_25_5MS);
+               }
+
+               /*
+                  printk(" chip #%d at %#6lx is rev 0x%2x\n",
+                  chip_number, (unsigned long)base_addr,
+                  readb(base_addr+(CyGFRCR<<index)));
+                */
+       }
+       return chip_number;
+}                              /* cyy_init_card */
+
+/*
+ * ---------------------------------------------------------------------
+ * cy_detect_isa() - Probe for Cyclom-Y/ISA boards.
+ * sets global variables and return the number of ISA boards found.
+ * ---------------------------------------------------------------------
+ */
+static int __init cy_detect_isa(void)
+{
+#ifdef CONFIG_ISA
+       unsigned short cy_isa_irq, nboard;
+       void __iomem *cy_isa_address;
+       unsigned short i, j, cy_isa_nchan;
+       int isparam = 0;
+
+       nboard = 0;
+
+       /* Check for module parameters */
+       for (i = 0; i < NR_CARDS; i++) {
+               if (maddr[i] || i) {
+                       isparam = 1;
+                       cy_isa_addresses[i] = maddr[i];
+               }
+               if (!maddr[i])
+                       break;
+       }
+
+       /* scan the address table probing for Cyclom-Y/ISA boards */
+       for (i = 0; i < NR_ISA_ADDRS; i++) {
+               unsigned int isa_address = cy_isa_addresses[i];
+               if (isa_address == 0x0000)
+                       return nboard;
+
+               /* probe for CD1400... */
+               cy_isa_address = ioremap_nocache(isa_address, CyISA_Ywin);
+               if (cy_isa_address == NULL) {
+                       printk(KERN_ERR "Cyclom-Y/ISA: can't remap base "
+                                       "address\n");
+                       continue;
+               }
+               cy_isa_nchan = CyPORTS_PER_CHIP *
+                       cyy_init_card(cy_isa_address, 0);
+               if (cy_isa_nchan == 0) {
+                       iounmap(cy_isa_address);
+                       continue;
+               }
+
+               if (isparam && i < NR_CARDS && irq[i])
+                       cy_isa_irq = irq[i];
+               else
+                       /* find out the board's irq by probing */
+                       cy_isa_irq = detect_isa_irq(cy_isa_address);
+               if (cy_isa_irq == 0) {
+                       printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but the "
+                               "IRQ could not be detected.\n",
+                               (unsigned long)cy_isa_address);
+                       iounmap(cy_isa_address);
+                       continue;
+               }
+
+               if ((cy_next_channel + cy_isa_nchan) > NR_PORTS) {
+                       printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no "
+                               "more channels are available. Change NR_PORTS "
+                               "in cyclades.c and recompile kernel.\n",
+                               (unsigned long)cy_isa_address);
+                       iounmap(cy_isa_address);
+                       return nboard;
+               }
+               /* fill the next cy_card structure available */
+               for (j = 0; j < NR_CARDS; j++) {
+                       if (cy_card[j].base_addr == NULL)
+                               break;
+               }
+               if (j == NR_CARDS) {    /* no more cy_cards available */
+                       printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but no "
+                               "more cards can be used. Change NR_CARDS in "
+                               "cyclades.c and recompile kernel.\n",
+                               (unsigned long)cy_isa_address);
+                       iounmap(cy_isa_address);
+                       return nboard;
+               }
+
+               /* allocate IRQ */
+               if (request_irq(cy_isa_irq, cyy_interrupt,
+                               IRQF_DISABLED, "Cyclom-Y", &cy_card[j])) {
+                       printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but "
+                               "could not allocate IRQ#%d.\n",
+                               (unsigned long)cy_isa_address, cy_isa_irq);
+                       iounmap(cy_isa_address);
+                       return nboard;
+               }
+
+               /* set cy_card */
+               cy_card[j].base_addr = cy_isa_address;
+               cy_card[j].ctl_addr.p9050 = NULL;
+               cy_card[j].irq = (int)cy_isa_irq;
+               cy_card[j].bus_index = 0;
+               cy_card[j].first_line = cy_next_channel;
+               cy_card[j].num_chips = cy_isa_nchan / CyPORTS_PER_CHIP;
+               cy_card[j].nports = cy_isa_nchan;
+               if (cy_init_card(&cy_card[j])) {
+                       cy_card[j].base_addr = NULL;
+                       free_irq(cy_isa_irq, &cy_card[j]);
+                       iounmap(cy_isa_address);
+                       continue;
+               }
+               nboard++;
+
+               printk(KERN_INFO "Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d found: "
+                       "%d channels starting from port %d\n",
+                       j + 1, (unsigned long)cy_isa_address,
+                       (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)),
+                       cy_isa_irq, cy_isa_nchan, cy_next_channel);
+
+               for (j = cy_next_channel;
+                               j < cy_next_channel + cy_isa_nchan; j++)
+                       tty_register_device(cy_serial_driver, j, NULL);
+               cy_next_channel += cy_isa_nchan;
+       }
+       return nboard;
+#else
+       return 0;
+#endif                         /* CONFIG_ISA */
+}                              /* cy_detect_isa */
+
+#ifdef CONFIG_PCI
+static inline int __devinit cyc_isfwstr(const char *str, unsigned int size)
+{
+       unsigned int a;
+
+       for (a = 0; a < size && *str; a++, str++)
+               if (*str & 0x80)
+                       return -EINVAL;
+
+       for (; a < size; a++, str++)
+               if (*str)
+                       return -EINVAL;
+
+       return 0;
+}
+
+static inline void __devinit cyz_fpga_copy(void __iomem *fpga, const u8 *data,
+               unsigned int size)
+{
+       for (; size > 0; size--) {
+               cy_writel(fpga, *data++);
+               udelay(10);
+       }
+}
+
+static void __devinit plx_init(struct pci_dev *pdev, int irq,
+               struct RUNTIME_9060 __iomem *addr)
+{
+       /* Reset PLX */
+       cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x40000000);
+       udelay(100L);
+       cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x40000000);
+
+       /* Reload Config. Registers from EEPROM */
+       cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x20000000);
+       udelay(100L);
+       cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x20000000);
+
+       /* For some yet unknown reason, once the PLX9060 reloads the EEPROM,
+        * the IRQ is lost and, thus, we have to re-write it to the PCI config.
+        * registers. This will remain here until we find a permanent fix.
+        */
+       pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq);
+}
+
+static int __devinit __cyz_load_fw(const struct firmware *fw,
+               const char *name, const u32 mailbox, void __iomem *base,
+               void __iomem *fpga)
+{
+       const void *ptr = fw->data;
+       const struct zfile_header *h = ptr;
+       const struct zfile_config *c, *cs;
+       const struct zfile_block *b, *bs;
+       unsigned int a, tmp, len = fw->size;
+#define BAD_FW KERN_ERR "Bad firmware: "
+       if (len < sizeof(*h)) {
+               printk(BAD_FW "too short: %u<%zu\n", len, sizeof(*h));
+               return -EINVAL;
+       }
+
+       cs = ptr + h->config_offset;
+       bs = ptr + h->block_offset;
+
+       if ((void *)(cs + h->n_config) > ptr + len ||
+                       (void *)(bs + h->n_blocks) > ptr + len) {
+               printk(BAD_FW "too short");
+               return  -EINVAL;
+       }
+
+       if (cyc_isfwstr(h->name, sizeof(h->name)) ||
+                       cyc_isfwstr(h->date, sizeof(h->date))) {
+               printk(BAD_FW "bad formatted header string\n");
+               return -EINVAL;
+       }
+
+       if (strncmp(name, h->name, sizeof(h->name))) {
+               printk(BAD_FW "bad name '%s' (expected '%s')\n", h->name, name);
+               return -EINVAL;
+       }
+
+       tmp = 0;
+       for (c = cs; c < cs + h->n_config; c++) {
+               for (a = 0; a < c->n_blocks; a++)
+                       if (c->block_list[a] > h->n_blocks) {
+                               printk(BAD_FW "bad block ref number in cfgs\n");
+                               return -EINVAL;
+                       }
+               if (c->mailbox == mailbox && c->function == 0) /* 0 is normal */
+                       tmp++;
+       }
+       if (!tmp) {
+               printk(BAD_FW "nothing appropriate\n");
+               return -EINVAL;
+       }
+
+       for (b = bs; b < bs + h->n_blocks; b++)
+               if (b->file_offset + b->size > len) {
+                       printk(BAD_FW "bad block data offset\n");
+                       return -EINVAL;
+               }
+
+       /* everything is OK, let's seek'n'load it */
+       for (c = cs; c < cs + h->n_config; c++)
+               if (c->mailbox == mailbox && c->function == 0)
+                       break;
+
+       for (a = 0; a < c->n_blocks; a++) {
+               b = &bs[c->block_list[a]];
+               if (b->type == ZBLOCK_FPGA) {
+                       if (fpga != NULL)
+                               cyz_fpga_copy(fpga, ptr + b->file_offset,
+                                               b->size);
+               } else {
+                       if (base != NULL)
+                               memcpy_toio(base + b->ram_offset,
+                                              ptr + b->file_offset, b->size);
+               }
+       }
+#undef BAD_FW
+       return 0;
+}
+
+static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
+               struct RUNTIME_9060 __iomem *ctl_addr, int irq)
+{
+       const struct firmware *fw;
+       struct FIRM_ID __iomem *fid = base_addr + ID_ADDRESS;
+       struct CUSTOM_REG __iomem *cust = base_addr;
+       struct ZFW_CTRL __iomem *pt_zfwctrl;
+       void __iomem *tmp;
+       u32 mailbox, status, nchan;
+       unsigned int i;
+       int retval;
+
+       retval = request_firmware(&fw, "cyzfirm.bin", &pdev->dev);
+       if (retval) {
+               dev_err(&pdev->dev, "can't get firmware\n");
+               goto err;
+       }
+
+       /* Check whether the firmware is already loaded and running. If
+          positive, skip this board */
+       if (__cyz_fpga_loaded(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) {
+               u32 cntval = readl(base_addr + 0x190);
+
+               udelay(100);
+               if (cntval != readl(base_addr + 0x190)) {
+                       /* FW counter is working, FW is running */
+                       dev_dbg(&pdev->dev, "Cyclades-Z FW already loaded. "
+                                       "Skipping board.\n");
+                       retval = 0;
+                       goto err_rel;
+               }
+       }
+
+       /* start boot */
+       cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) &
+                       ~0x00030800UL);
+
+       mailbox = readl(&ctl_addr->mail_box_0);
+
+       if (mailbox == 0 || __cyz_fpga_loaded(ctl_addr)) {
+               /* stops CPU and set window to beginning of RAM */
+               cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
+               cy_writel(&cust->cpu_stop, 0);
+               cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
+               udelay(100);
+       }
+
+       plx_init(pdev, irq, ctl_addr);
+
+       if (mailbox != 0) {
+               /* load FPGA */
+               retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, NULL,
+                               base_addr);
+               if (retval)
+                       goto err_rel;
+               if (!__cyz_fpga_loaded(ctl_addr)) {
+                       dev_err(&pdev->dev, "fw upload successful, but fw is "
+                                       "not loaded\n");
+                       goto err_rel;
+               }
+       }
+
+       /* stops CPU and set window to beginning of RAM */
+       cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
+       cy_writel(&cust->cpu_stop, 0);
+       cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
+       udelay(100);
+
+       /* clear memory */
+       for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++)
+               cy_writeb(tmp, 255);
+       if (mailbox != 0) {
+               /* set window to last 512K of RAM */
+               cy_writel(&ctl_addr->loc_addr_base, WIN_RAM + RAM_SIZE);
+               for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++)
+                       cy_writeb(tmp, 255);
+               /* set window to beginning of RAM */
+               cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
+       }
+
+       retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, base_addr, NULL);
+       release_firmware(fw);
+       if (retval)
+               goto err;
+
+       /* finish boot and start boards */
+       cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
+       cy_writel(&cust->cpu_start, 0);
+       cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
+       i = 0;
+       while ((status = readl(&fid->signature)) != ZFIRM_ID && i++ < 40)
+               msleep(100);
+       if (status != ZFIRM_ID) {
+               if (status == ZFIRM_HLT) {
+                       dev_err(&pdev->dev, "you need an external power supply "
+                               "for this number of ports. Firmware halted and "
+                               "board reset.\n");
+                       retval = -EIO;
+                       goto err;
+               }
+               dev_warn(&pdev->dev, "fid->signature = 0x%x... Waiting "
+                               "some more time\n", status);
+               while ((status = readl(&fid->signature)) != ZFIRM_ID &&
+                               i++ < 200)
+                       msleep(100);
+               if (status != ZFIRM_ID) {
+                       dev_err(&pdev->dev, "Board not started in 20 seconds! "
+                                       "Giving up. (fid->signature = 0x%x)\n",
+                                       status);
+                       dev_info(&pdev->dev, "*** Warning ***: if you are "
+                               "upgrading the FW, please power cycle the "
+                               "system before loading the new FW to the "
+                               "Cyclades-Z.\n");
+
+                       if (__cyz_fpga_loaded(ctl_addr))
+                               plx_init(pdev, irq, ctl_addr);
+
+                       retval = -EIO;
+                       goto err;
+               }
+               dev_dbg(&pdev->dev, "Firmware started after %d seconds.\n",
+                               i / 10);
+       }
+       pt_zfwctrl = base_addr + readl(&fid->zfwctrl_addr);
+
+       dev_dbg(&pdev->dev, "fid=> %p, zfwctrl_addr=> %x, npt_zfwctrl=> %p\n",
+                       base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr),
+                       base_addr + readl(&fid->zfwctrl_addr));
+
+       nchan = readl(&pt_zfwctrl->board_ctrl.n_channel);
+       dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u\n",
+               readl(&pt_zfwctrl->board_ctrl.fw_version), nchan);
+
+       if (nchan == 0) {
+               dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please "
+                       "check the connection between the Z host card and the "
+                       "serial expanders.\n");
+
+               if (__cyz_fpga_loaded(ctl_addr))
+                       plx_init(pdev, irq, ctl_addr);
+
+               dev_info(&pdev->dev, "Null number of ports detected. Board "
+                               "reset.\n");
+               retval = 0;
+               goto err;
+       }
+
+       cy_writel(&pt_zfwctrl->board_ctrl.op_system, C_OS_LINUX);
+       cy_writel(&pt_zfwctrl->board_ctrl.dr_version, DRIVER_VERSION);
+
+       /*
+          Early firmware failed to start looking for commands.
+          This enables firmware interrupts for those commands.
+        */
+       cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) |
+                       (1 << 17));
+       cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) |
+                       0x00030800UL);
+
+       return nchan;
+err_rel:
+       release_firmware(fw);
+err:
+       return retval;
+}
+
+static int __devinit cy_pci_probe(struct pci_dev *pdev,
+               const struct pci_device_id *ent)
+{
+       void __iomem *addr0 = NULL, *addr2 = NULL;
+       char *card_name = NULL;
+       u32 uninitialized_var(mailbox);
+       unsigned int device_id, nchan = 0, card_no, i;
+       unsigned char plx_ver;
+       int retval, irq;
+
+       retval = pci_enable_device(pdev);
+       if (retval) {
+               dev_err(&pdev->dev, "cannot enable device\n");
+               goto err;
+       }
+
+       /* read PCI configuration area */
+       irq = pdev->irq;
+       device_id = pdev->device & ~PCI_DEVICE_ID_MASK;
+
+#if defined(__alpha__)
+       if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) {   /* below 1M? */
+               dev_err(&pdev->dev, "Cyclom-Y/PCI not supported for low "
+                       "addresses on Alpha systems.\n");
+               retval = -EIO;
+               goto err_dis;
+       }
+#endif
+       if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo) {
+               dev_err(&pdev->dev, "Cyclades-Z/PCI not supported for low "
+                       "addresses\n");
+               retval = -EIO;
+               goto err_dis;
+       }
+
+       if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) {
+               dev_warn(&pdev->dev, "PCI I/O bit incorrectly set. Ignoring "
+                               "it...\n");
+               pdev->resource[2].flags &= ~IORESOURCE_IO;
+       }
+
+       retval = pci_request_regions(pdev, "cyclades");
+       if (retval) {
+               dev_err(&pdev->dev, "failed to reserve resources\n");
+               goto err_dis;
+       }
+
+       retval = -EIO;
+       if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
+                       device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
+               card_name = "Cyclom-Y";
+
+               addr0 = ioremap_nocache(pci_resource_start(pdev, 0),
+                               CyPCI_Yctl);
+               if (addr0 == NULL) {
+                       dev_err(&pdev->dev, "can't remap ctl region\n");
+                       goto err_reg;
+               }
+               addr2 = ioremap_nocache(pci_resource_start(pdev, 2),
+                               CyPCI_Ywin);
+               if (addr2 == NULL) {
+                       dev_err(&pdev->dev, "can't remap base region\n");
+                       goto err_unmap;
+               }
+
+               nchan = CyPORTS_PER_CHIP * cyy_init_card(addr2, 1);
+               if (nchan == 0) {
+                       dev_err(&pdev->dev, "Cyclom-Y PCI host card with no "
+                                       "Serial-Modules\n");
+                       goto err_unmap;
+               }
+       } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) {
+               struct RUNTIME_9060 __iomem *ctl_addr;
+
+               ctl_addr = addr0 = ioremap_nocache(pci_resource_start(pdev, 0),
+                               CyPCI_Zctl);
+               if (addr0 == NULL) {
+                       dev_err(&pdev->dev, "can't remap ctl region\n");
+                       goto err_reg;
+               }
+
+               /* Disable interrupts on the PLX before resetting it */
+               cy_writew(&ctl_addr->intr_ctrl_stat,
+                               readw(&ctl_addr->intr_ctrl_stat) & ~0x0900);
+
+               plx_init(pdev, irq, addr0);
+
+               mailbox = readl(&ctl_addr->mail_box_0);
+
+               addr2 = ioremap_nocache(pci_resource_start(pdev, 2),
+                               mailbox == ZE_V1 ? CyPCI_Ze_win : CyPCI_Zwin);
+               if (addr2 == NULL) {
+                       dev_err(&pdev->dev, "can't remap base region\n");
+                       goto err_unmap;
+               }
+
+               if (mailbox == ZE_V1) {
+                       card_name = "Cyclades-Ze";
+               } else {
+                       card_name = "Cyclades-8Zo";
+#ifdef CY_PCI_DEBUG
+                       if (mailbox == ZO_V1) {
+                               cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
+                               dev_info(&pdev->dev, "Cyclades-8Zo/PCI: FPGA "
+                                       "id %lx, ver %lx\n", (ulong)(0xff &
+                                       readl(&((struct CUSTOM_REG *)addr2)->
+                                               fpga_id)), (ulong)(0xff &
+                                       readl(&((struct CUSTOM_REG *)addr2)->
+                                               fpga_version)));
+                               cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
+                       } else {
+                               dev_info(&pdev->dev, "Cyclades-Z/PCI: New "
+                                       "Cyclades-Z board.  FPGA not loaded\n");
+                       }
+#endif
+                       /* The following clears the firmware id word.  This
+                          ensures that the driver will not attempt to talk to
+                          the board until it has been properly initialized.
+                        */
+                       if ((mailbox == ZO_V1) || (mailbox == ZO_V2))
+                               cy_writel(addr2 + ID_ADDRESS, 0L);
+               }
+
+               retval = cyz_load_fw(pdev, addr2, addr0, irq);
+               if (retval <= 0)
+                       goto err_unmap;
+               nchan = retval;
+       }
+
+       if ((cy_next_channel + nchan) > NR_PORTS) {
+               dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no "
+                       "channels are available. Change NR_PORTS in "
+                       "cyclades.c and recompile kernel.\n");
+               goto err_unmap;
+       }
+       /* fill the next cy_card structure available */
+       for (card_no = 0; card_no < NR_CARDS; card_no++) {
+               if (cy_card[card_no].base_addr == NULL)
+                       break;
+       }
+       if (card_no == NR_CARDS) {      /* no more cy_cards available */
+               dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no "
+                       "more cards can be used. Change NR_CARDS in "
+                       "cyclades.c and recompile kernel.\n");
+               goto err_unmap;
+       }
+
+       if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
+                       device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
+               /* allocate IRQ */
+               retval = request_irq(irq, cyy_interrupt,
+                               IRQF_SHARED, "Cyclom-Y", &cy_card[card_no]);
+               if (retval) {
+                       dev_err(&pdev->dev, "could not allocate IRQ\n");
+                       goto err_unmap;
+               }
+               cy_card[card_no].num_chips = nchan / CyPORTS_PER_CHIP;
+       } else {
+               struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS;
+               struct ZFW_CTRL __iomem *zfw_ctrl;
+
+               zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff);
+
+               cy_card[card_no].hw_ver = mailbox;
+               cy_card[card_no].num_chips = (unsigned int)-1;
+               cy_card[card_no].board_ctrl = &zfw_ctrl->board_ctrl;
+#ifdef CONFIG_CYZ_INTR
+               /* allocate IRQ only if board has an IRQ */
+               if (irq != 0 && irq != 255) {
+                       retval = request_irq(irq, cyz_interrupt,
+                                       IRQF_SHARED, "Cyclades-Z",
+                                       &cy_card[card_no]);
+                       if (retval) {
+                               dev_err(&pdev->dev, "could not allocate IRQ\n");
+                               goto err_unmap;
+                       }
+               }
+#endif                         /* CONFIG_CYZ_INTR */
+       }
+
+       /* set cy_card */
+       cy_card[card_no].base_addr = addr2;
+       cy_card[card_no].ctl_addr.p9050 = addr0;
+       cy_card[card_no].irq = irq;
+       cy_card[card_no].bus_index = 1;
+       cy_card[card_no].first_line = cy_next_channel;
+       cy_card[card_no].nports = nchan;
+       retval = cy_init_card(&cy_card[card_no]);
+       if (retval)
+               goto err_null;
+
+       pci_set_drvdata(pdev, &cy_card[card_no]);
+
+       if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
+                       device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
+               /* enable interrupts in the PCI interface */
+               plx_ver = readb(addr2 + CyPLX_VER) & 0x0f;
+               switch (plx_ver) {
+               case PLX_9050:
+                       cy_writeb(addr0 + 0x4c, 0x43);
+                       break;
+
+               case PLX_9060:
+               case PLX_9080:
+               default:        /* Old boards, use PLX_9060 */
+               {
+                       struct RUNTIME_9060 __iomem *ctl_addr = addr0;
+                       plx_init(pdev, irq, ctl_addr);
+                       cy_writew(&ctl_addr->intr_ctrl_stat,
+                               readw(&ctl_addr->intr_ctrl_stat) | 0x0900);
+                       break;
+               }
+               }
+       }
+
+       dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from "
+               "port %d.\n", card_name, card_no + 1, nchan, cy_next_channel);
+       for (i = cy_next_channel; i < cy_next_channel + nchan; i++)
+               tty_register_device(cy_serial_driver, i, &pdev->dev);
+       cy_next_channel += nchan;
+
+       return 0;
+err_null:
+       cy_card[card_no].base_addr = NULL;
+       free_irq(irq, &cy_card[card_no]);
+err_unmap:
+       iounmap(addr0);
+       if (addr2)
+               iounmap(addr2);
+err_reg:
+       pci_release_regions(pdev);
+err_dis:
+       pci_disable_device(pdev);
+err:
+       return retval;
+}
+
+static void __devexit cy_pci_remove(struct pci_dev *pdev)
+{
+       struct cyclades_card *cinfo = pci_get_drvdata(pdev);
+       unsigned int i;
+
+       /* non-Z with old PLX */
+       if (!cy_is_Z(cinfo) && (readb(cinfo->base_addr + CyPLX_VER) & 0x0f) ==
+                       PLX_9050)
+               cy_writeb(cinfo->ctl_addr.p9050 + 0x4c, 0);
+       else
+#ifndef CONFIG_CYZ_INTR
+               if (!cy_is_Z(cinfo))
+#endif
+               cy_writew(&cinfo->ctl_addr.p9060->intr_ctrl_stat,
+                       readw(&cinfo->ctl_addr.p9060->intr_ctrl_stat) &
+                       ~0x0900);
+
+       iounmap(cinfo->base_addr);
+       if (cinfo->ctl_addr.p9050)
+               iounmap(cinfo->ctl_addr.p9050);
+       if (cinfo->irq
+#ifndef CONFIG_CYZ_INTR
+               && !cy_is_Z(cinfo)
+#endif /* CONFIG_CYZ_INTR */
+               )
+               free_irq(cinfo->irq, cinfo);
+       pci_release_regions(pdev);
+
+       cinfo->base_addr = NULL;
+       for (i = cinfo->first_line; i < cinfo->first_line +
+                       cinfo->nports; i++)
+               tty_unregister_device(cy_serial_driver, i);
+       cinfo->nports = 0;
+       kfree(cinfo->ports);
+}
+
+static struct pci_driver cy_pci_driver = {
+       .name = "cyclades",
+       .id_table = cy_pci_dev_id,
+       .probe = cy_pci_probe,
+       .remove = __devexit_p(cy_pci_remove)
+};
+#endif
+
+static int cyclades_proc_show(struct seq_file *m, void *v)
+{
+       struct cyclades_port *info;
+       unsigned int i, j;
+       __u32 cur_jifs = jiffies;
+
+       seq_puts(m, "Dev TimeOpen   BytesOut  IdleOut    BytesIn   "
+                       "IdleIn  Overruns  Ldisc\n");
+
+       /* Output one line for each known port */
+       for (i = 0; i < NR_CARDS; i++)
+               for (j = 0; j < cy_card[i].nports; j++) {
+                       info = &cy_card[i].ports[j];
+
+                       if (info->port.count) {
+                               /* XXX is the ldisc num worth this? */
+                               struct tty_struct *tty;
+                               struct tty_ldisc *ld;
+                               int num = 0;
+                               tty = tty_port_tty_get(&info->port);
+                               if (tty) {
+                                       ld = tty_ldisc_ref(tty);
+                                       if (ld) {
+                                               num = ld->ops->num;
+                                               tty_ldisc_deref(ld);
+                                       }
+                                       tty_kref_put(tty);
+                               }
+                               seq_printf(m, "%3d %8lu %10lu %8lu "
+                                       "%10lu %8lu %9lu %6d\n", info->line,
+                                       (cur_jifs - info->idle_stats.in_use) /
+                                       HZ, info->idle_stats.xmit_bytes,
+                                       (cur_jifs - info->idle_stats.xmit_idle)/
+                                       HZ, info->idle_stats.recv_bytes,
+                                       (cur_jifs - info->idle_stats.recv_idle)/
+                                       HZ, info->idle_stats.overruns,
+                                       num);
+                       } else
+                               seq_printf(m, "%3d %8lu %10lu %8lu "
+                                       "%10lu %8lu %9lu %6ld\n",
+                                       info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L);
+               }
+       return 0;
+}
+
+static int cyclades_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, cyclades_proc_show, NULL);
+}
+
+static const struct file_operations cyclades_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = cyclades_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/* The serial driver boot-time initialization code!
+    Hardware I/O ports are mapped to character special devices on a
+    first found, first allocated manner.  That is, this code searches
+    for Cyclom cards in the system.  As each is found, it is probed
+    to discover how many chips (and thus how many ports) are present.
+    These ports are mapped to the tty ports 32 and upward in monotonic
+    fashion.  If an 8-port card is replaced with a 16-port card, the
+    port mapping on a following card will shift.
+
+    This approach is different from what is used in the other serial
+    device driver because the Cyclom is more properly a multiplexer,
+    not just an aggregation of serial ports on one card.
+
+    If there are more cards with more ports than have been
+    statically allocated above, a warning is printed and the
+    extra ports are ignored.
+ */
+
+static const struct tty_operations cy_ops = {
+       .open = cy_open,
+       .close = cy_close,
+       .write = cy_write,
+       .put_char = cy_put_char,
+       .flush_chars = cy_flush_chars,
+       .write_room = cy_write_room,
+       .chars_in_buffer = cy_chars_in_buffer,
+       .flush_buffer = cy_flush_buffer,
+       .ioctl = cy_ioctl,
+       .throttle = cy_throttle,
+       .unthrottle = cy_unthrottle,
+       .set_termios = cy_set_termios,
+       .stop = cy_stop,
+       .start = cy_start,
+       .hangup = cy_hangup,
+       .break_ctl = cy_break,
+       .wait_until_sent = cy_wait_until_sent,
+       .tiocmget = cy_tiocmget,
+       .tiocmset = cy_tiocmset,
+       .get_icount = cy_get_icount,
+       .proc_fops = &cyclades_proc_fops,
+};
+
+static int __init cy_init(void)
+{
+       unsigned int nboards;
+       int retval = -ENOMEM;
+
+       cy_serial_driver = alloc_tty_driver(NR_PORTS);
+       if (!cy_serial_driver)
+               goto err;
+
+       printk(KERN_INFO "Cyclades driver " CY_VERSION " (built %s %s)\n",
+                       __DATE__, __TIME__);
+
+       /* Initialize the tty_driver structure */
+
+       cy_serial_driver->owner = THIS_MODULE;
+       cy_serial_driver->driver_name = "cyclades";
+       cy_serial_driver->name = "ttyC";
+       cy_serial_driver->major = CYCLADES_MAJOR;
+       cy_serial_driver->minor_start = 0;
+       cy_serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
+       cy_serial_driver->subtype = SERIAL_TYPE_NORMAL;
+       cy_serial_driver->init_termios = tty_std_termios;
+       cy_serial_driver->init_termios.c_cflag =
+           B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       cy_serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+       tty_set_operations(cy_serial_driver, &cy_ops);
+
+       retval = tty_register_driver(cy_serial_driver);
+       if (retval) {
+               printk(KERN_ERR "Couldn't register Cyclades serial driver\n");
+               goto err_frtty;
+       }
+
+       /* the code below is responsible to find the boards. Each different
+          type of board has its own detection routine. If a board is found,
+          the next cy_card structure available is set by the detection
+          routine. These functions are responsible for checking the
+          availability of cy_card and cy_port data structures and updating
+          the cy_next_channel. */
+
+       /* look for isa boards */
+       nboards = cy_detect_isa();
+
+#ifdef CONFIG_PCI
+       /* look for pci boards */
+       retval = pci_register_driver(&cy_pci_driver);
+       if (retval && !nboards) {
+               tty_unregister_driver(cy_serial_driver);
+               goto err_frtty;
+       }
+#endif
+
+       return 0;
+err_frtty:
+       put_tty_driver(cy_serial_driver);
+err:
+       return retval;
+}                              /* cy_init */
+
+static void __exit cy_cleanup_module(void)
+{
+       struct cyclades_card *card;
+       unsigned int i, e1;
+
+#ifndef CONFIG_CYZ_INTR
+       del_timer_sync(&cyz_timerlist);
+#endif /* CONFIG_CYZ_INTR */
+
+       e1 = tty_unregister_driver(cy_serial_driver);
+       if (e1)
+               printk(KERN_ERR "failed to unregister Cyclades serial "
+                               "driver(%d)\n", e1);
+
+#ifdef CONFIG_PCI
+       pci_unregister_driver(&cy_pci_driver);
+#endif
+
+       for (i = 0; i < NR_CARDS; i++) {
+               card = &cy_card[i];
+               if (card->base_addr) {
+                       /* clear interrupt */
+                       cy_writeb(card->base_addr + Cy_ClrIntr, 0);
+                       iounmap(card->base_addr);
+                       if (card->ctl_addr.p9050)
+                               iounmap(card->ctl_addr.p9050);
+                       if (card->irq
+#ifndef CONFIG_CYZ_INTR
+                               && !cy_is_Z(card)
+#endif /* CONFIG_CYZ_INTR */
+                               )
+                               free_irq(card->irq, card);
+                       for (e1 = card->first_line; e1 < card->first_line +
+                                       card->nports; e1++)
+                               tty_unregister_device(cy_serial_driver, e1);
+                       kfree(card->ports);
+               }
+       }
+
+       put_tty_driver(cy_serial_driver);
+} /* cy_cleanup_module */
+
+module_init(cy_init);
+module_exit(cy_cleanup_module);
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CY_VERSION);
+MODULE_ALIAS_CHARDEV_MAJOR(CYCLADES_MAJOR);
+MODULE_FIRMWARE("cyzfirm.bin");
diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c
new file mode 100644 (file)
index 0000000..db1cf9c
--- /dev/null
@@ -0,0 +1,1736 @@
+/*
+ *     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.
+ *
+ *     Original driver code supplied by Multi-Tech
+ *
+ *     Changes
+ *     1/9/98  alan@lxorguk.ukuu.org.uk
+ *                                     Merge to 2.0.x kernel tree
+ *                                     Obtain and use official major/minors
+ *                                     Loader switched to a misc device
+ *                                     (fixed range check bug as a side effect)
+ *                                     Printk clean up
+ *     9/12/98 alan@lxorguk.ukuu.org.uk
+ *                                     Rough port to 2.1.x
+ *
+ *     10/6/99 sameer                  Merged the ISA and PCI drivers to
+ *                                     a new unified driver.
+ *
+ *     3/9/99  sameer                  Added support for ISI4616 cards.
+ *
+ *     16/9/99 sameer                  We do not force RTS low anymore.
+ *                                     This is to prevent the firmware
+ *                                     from getting confused.
+ *
+ *     26/10/99 sameer                 Cosmetic changes:The driver now
+ *                                     dumps the Port Count information
+ *                                     along with I/O address and IRQ.
+ *
+ *     13/12/99 sameer                 Fixed the problem with IRQ sharing.
+ *
+ *     10/5/00  sameer                 Fixed isicom_shutdown_board()
+ *                                     to not lower DTR on all the ports
+ *                                     when the last port on the card is
+ *                                     closed.
+ *
+ *     10/5/00  sameer                 Signal mask setup command added
+ *                                     to  isicom_setup_port and
+ *                                     isicom_shutdown_port.
+ *
+ *     24/5/00  sameer                 The driver is now SMP aware.
+ *
+ *
+ *     27/11/00 Vinayak P Risbud       Fixed the Driver Crash Problem
+ *
+ *
+ *     03/01/01  anil .s               Added support for resetting the
+ *                                     internal modems on ISI cards.
+ *
+ *     08/02/01  anil .s               Upgraded the driver for kernel
+ *                                     2.4.x
+ *
+ *     11/04/01  Kevin                 Fixed firmware load problem with
+ *                                     ISIHP-4X card
+ *
+ *     30/04/01  anil .s               Fixed the remote login through
+ *                                     ISI port problem. Now the link
+ *                                     does not go down before password
+ *                                     prompt.
+ *
+ *     03/05/01  anil .s               Fixed the problem with IRQ sharing
+ *                                     among ISI-PCI cards.
+ *
+ *     03/05/01  anil .s               Added support to display the version
+ *                                     info during insmod as well as module
+ *                                     listing by lsmod.
+ *
+ *     10/05/01  anil .s               Done the modifications to the source
+ *                                     file and Install script so that the
+ *                                     same installation can be used for
+ *                                     2.2.x and 2.4.x kernel.
+ *
+ *     06/06/01  anil .s               Now we drop both dtr and rts during
+ *                                     shutdown_port as well as raise them
+ *                                     during isicom_config_port.
+ *
+ *     09/06/01 acme@conectiva.com.br  use capable, not suser, do
+ *                                     restore_flags on failure in
+ *                                     isicom_send_break, verify put_user
+ *                                     result
+ *
+ *     11/02/03  ranjeeth              Added support for 230 Kbps and 460 Kbps
+ *                                     Baud index extended to 21
+ *
+ *     20/03/03  ranjeeth              Made to work for Linux Advanced server.
+ *                                     Taken care of license warning.
+ *
+ *     10/12/03  Ravindra              Made to work for Fedora Core 1 of
+ *                                     Red Hat Distribution
+ *
+ *     06/01/05  Alan Cox              Merged the ISI and base kernel strands
+ *                                     into a single 2.6 driver
+ *
+ *     ***********************************************************
+ *
+ *     To use this driver you also need the support package. You
+ *     can find this in RPM format on
+ *             ftp://ftp.linux.org.uk/pub/linux/alan
+ *
+ *     You can find the original tools for this direct from Multitech
+ *             ftp://ftp.multitech.com/ISI-Cards/
+ *
+ *     Having installed the cards the module options (/etc/modprobe.conf)
+ *
+ *     options isicom   io=card1,card2,card3,card4 irq=card1,card2,card3,card4
+ *
+ *     Omit those entries for boards you don't have installed.
+ *
+ *     TODO
+ *             Merge testing
+ *             64-bit verification
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/termios.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/serial.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <asm/system.h>
+
+#include <linux/pci.h>
+
+#include <linux/isicom.h>
+
+#define InterruptTheCard(base) outw(0, (base) + 0xc)
+#define ClearInterrupt(base) inw((base) + 0x0a)
+
+#ifdef DEBUG
+#define isicom_paranoia_check(a, b, c) __isicom_paranoia_check((a), (b), (c))
+#else
+#define isicom_paranoia_check(a, b, c) 0
+#endif
+
+static int isicom_probe(struct pci_dev *, const struct pci_device_id *);
+static void __devexit isicom_remove(struct pci_dev *);
+
+static struct pci_device_id isicom_pci_tbl[] = {
+       { PCI_DEVICE(VENDOR_ID, 0x2028) },
+       { PCI_DEVICE(VENDOR_ID, 0x2051) },
+       { PCI_DEVICE(VENDOR_ID, 0x2052) },
+       { PCI_DEVICE(VENDOR_ID, 0x2053) },
+       { PCI_DEVICE(VENDOR_ID, 0x2054) },
+       { PCI_DEVICE(VENDOR_ID, 0x2055) },
+       { PCI_DEVICE(VENDOR_ID, 0x2056) },
+       { PCI_DEVICE(VENDOR_ID, 0x2057) },
+       { PCI_DEVICE(VENDOR_ID, 0x2058) },
+       { 0 }
+};
+MODULE_DEVICE_TABLE(pci, isicom_pci_tbl);
+
+static struct pci_driver isicom_driver = {
+       .name           = "isicom",
+       .id_table       = isicom_pci_tbl,
+       .probe          = isicom_probe,
+       .remove         = __devexit_p(isicom_remove)
+};
+
+static int prev_card = 3;      /*      start servicing isi_card[0]     */
+static struct tty_driver *isicom_normal;
+
+static void isicom_tx(unsigned long _data);
+static void isicom_start(struct tty_struct *tty);
+
+static DEFINE_TIMER(tx, isicom_tx, 0, 0);
+
+/*   baud index mappings from linux defns to isi */
+
+static signed char linuxb_to_isib[] = {
+       -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21
+};
+
+struct isi_board {
+       unsigned long           base;
+       int                     irq;
+       unsigned char           port_count;
+       unsigned short          status;
+       unsigned short          port_status; /* each bit for each port */
+       unsigned short          shift_count;
+       struct isi_port         *ports;
+       signed char             count;
+       spinlock_t              card_lock; /* Card wide lock 11/5/00 -sameer */
+       unsigned long           flags;
+       unsigned int            index;
+};
+
+struct isi_port {
+       unsigned short          magic;
+       struct tty_port         port;
+       u16                     channel;
+       u16                     status;
+       struct isi_board        *card;
+       unsigned char           *xmit_buf;
+       int                     xmit_head;
+       int                     xmit_tail;
+       int                     xmit_cnt;
+};
+
+static struct isi_board isi_card[BOARD_COUNT];
+static struct isi_port  isi_ports[PORT_COUNT];
+
+/*
+ *     Locking functions for card level locking. We need to own both
+ *     the kernel lock for the card and have the card in a position that
+ *     it wants to talk.
+ */
+
+static inline int WaitTillCardIsFree(unsigned long base)
+{
+       unsigned int count = 0;
+       unsigned int a = in_atomic(); /* do we run under spinlock? */
+
+       while (!(inw(base + 0xe) & 0x1) && count++ < 100)
+               if (a)
+                       mdelay(1);
+               else
+                       msleep(1);
+
+       return !(inw(base + 0xe) & 0x1);
+}
+
+static int lock_card(struct isi_board *card)
+{
+       unsigned long base = card->base;
+       unsigned int retries, a;
+
+       for (retries = 0; retries < 10; retries++) {
+               spin_lock_irqsave(&card->card_lock, card->flags);
+               for (a = 0; a < 10; a++) {
+                       if (inw(base + 0xe) & 0x1)
+                               return 1;
+                       udelay(10);
+               }
+               spin_unlock_irqrestore(&card->card_lock, card->flags);
+               msleep(10);
+       }
+       pr_warning("Failed to lock Card (0x%lx)\n", card->base);
+
+       return 0;       /* Failed to acquire the card! */
+}
+
+static void unlock_card(struct isi_board *card)
+{
+       spin_unlock_irqrestore(&card->card_lock, card->flags);
+}
+
+/*
+ *  ISI Card specific ops ...
+ */
+
+/* card->lock HAS to be held */
+static void raise_dtr(struct isi_port *port)
+{
+       struct isi_board *card = port->card;
+       unsigned long base = card->base;
+       u16 channel = port->channel;
+
+       if (WaitTillCardIsFree(base))
+               return;
+
+       outw(0x8000 | (channel << card->shift_count) | 0x02, base);
+       outw(0x0504, base);
+       InterruptTheCard(base);
+       port->status |= ISI_DTR;
+}
+
+/* card->lock HAS to be held */
+static inline void drop_dtr(struct isi_port *port)
+{
+       struct isi_board *card = port->card;
+       unsigned long base = card->base;
+       u16 channel = port->channel;
+
+       if (WaitTillCardIsFree(base))
+               return;
+
+       outw(0x8000 | (channel << card->shift_count) | 0x02, base);
+       outw(0x0404, base);
+       InterruptTheCard(base);
+       port->status &= ~ISI_DTR;
+}
+
+/* card->lock HAS to be held */
+static inline void raise_rts(struct isi_port *port)
+{
+       struct isi_board *card = port->card;
+       unsigned long base = card->base;
+       u16 channel = port->channel;
+
+       if (WaitTillCardIsFree(base))
+               return;
+
+       outw(0x8000 | (channel << card->shift_count) | 0x02, base);
+       outw(0x0a04, base);
+       InterruptTheCard(base);
+       port->status |= ISI_RTS;
+}
+
+/* card->lock HAS to be held */
+static inline void drop_rts(struct isi_port *port)
+{
+       struct isi_board *card = port->card;
+       unsigned long base = card->base;
+       u16 channel = port->channel;
+
+       if (WaitTillCardIsFree(base))
+               return;
+
+       outw(0x8000 | (channel << card->shift_count) | 0x02, base);
+       outw(0x0804, base);
+       InterruptTheCard(base);
+       port->status &= ~ISI_RTS;
+}
+
+/* card->lock MUST NOT be held */
+
+static void isicom_dtr_rts(struct tty_port *port, int on)
+{
+       struct isi_port *ip = container_of(port, struct isi_port, port);
+       struct isi_board *card = ip->card;
+       unsigned long base = card->base;
+       u16 channel = ip->channel;
+
+       if (!lock_card(card))
+               return;
+
+       if (on) {
+               outw(0x8000 | (channel << card->shift_count) | 0x02, base);
+               outw(0x0f04, base);
+               InterruptTheCard(base);
+               ip->status |= (ISI_DTR | ISI_RTS);
+       } else {
+               outw(0x8000 | (channel << card->shift_count) | 0x02, base);
+               outw(0x0C04, base);
+               InterruptTheCard(base);
+               ip->status &= ~(ISI_DTR | ISI_RTS);
+       }
+       unlock_card(card);
+}
+
+/* card->lock HAS to be held */
+static void drop_dtr_rts(struct isi_port *port)
+{
+       struct isi_board *card = port->card;
+       unsigned long base = card->base;
+       u16 channel = port->channel;
+
+       if (WaitTillCardIsFree(base))
+               return;
+
+       outw(0x8000 | (channel << card->shift_count) | 0x02, base);
+       outw(0x0c04, base);
+       InterruptTheCard(base);
+       port->status &= ~(ISI_RTS | ISI_DTR);
+}
+
+/*
+ *     ISICOM Driver specific routines ...
+ *
+ */
+
+static inline int __isicom_paranoia_check(struct isi_port const *port,
+       char *name, const char *routine)
+{
+       if (!port) {
+               pr_warning("Warning: bad isicom magic for dev %s in %s.\n",
+                          name, routine);
+               return 1;
+       }
+       if (port->magic != ISICOM_MAGIC) {
+               pr_warning("Warning: NULL isicom port for dev %s in %s.\n",
+                          name, routine);
+               return 1;
+       }
+
+       return 0;
+}
+
+/*
+ *     Transmitter.
+ *
+ *     We shovel data into the card buffers on a regular basis. The card
+ *     will do the rest of the work for us.
+ */
+
+static void isicom_tx(unsigned long _data)
+{
+       unsigned long flags, base;
+       unsigned int retries;
+       short count = (BOARD_COUNT-1), card;
+       short txcount, wrd, residue, word_count, cnt;
+       struct isi_port *port;
+       struct tty_struct *tty;
+
+       /*      find next active board  */
+       card = (prev_card + 1) & 0x0003;
+       while (count-- > 0) {
+               if (isi_card[card].status & BOARD_ACTIVE)
+                       break;
+               card = (card + 1) & 0x0003;
+       }
+       if (!(isi_card[card].status & BOARD_ACTIVE))
+               goto sched_again;
+
+       prev_card = card;
+
+       count = isi_card[card].port_count;
+       port = isi_card[card].ports;
+       base = isi_card[card].base;
+
+       spin_lock_irqsave(&isi_card[card].card_lock, flags);
+       for (retries = 0; retries < 100; retries++) {
+               if (inw(base + 0xe) & 0x1)
+                       break;
+               udelay(2);
+       }
+       if (retries >= 100)
+               goto unlock;
+
+       tty = tty_port_tty_get(&port->port);
+       if (tty == NULL)
+               goto put_unlock;
+
+       for (; count > 0; count--, port++) {
+               /* port not active or tx disabled to force flow control */
+               if (!(port->port.flags & ASYNC_INITIALIZED) ||
+                               !(port->status & ISI_TXOK))
+                       continue;
+
+               txcount = min_t(short, TX_SIZE, port->xmit_cnt);
+               if (txcount <= 0 || tty->stopped || tty->hw_stopped)
+                       continue;
+
+               if (!(inw(base + 0x02) & (1 << port->channel)))
+                       continue;
+
+               pr_debug("txing %d bytes, port%d.\n",
+                        txcount, port->channel + 1);
+               outw((port->channel << isi_card[card].shift_count) | txcount,
+                       base);
+               residue = NO;
+               wrd = 0;
+               while (1) {
+                       cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE
+                                       - port->xmit_tail));
+                       if (residue == YES) {
+                               residue = NO;
+                               if (cnt > 0) {
+                                       wrd |= (port->port.xmit_buf[port->xmit_tail]
+                                                                       << 8);
+                                       port->xmit_tail = (port->xmit_tail + 1)
+                                               & (SERIAL_XMIT_SIZE - 1);
+                                       port->xmit_cnt--;
+                                       txcount--;
+                                       cnt--;
+                                       outw(wrd, base);
+                               } else {
+                                       outw(wrd, base);
+                                       break;
+                               }
+                       }
+                       if (cnt <= 0)
+                               break;
+                       word_count = cnt >> 1;
+                       outsw(base, port->port.xmit_buf+port->xmit_tail, word_count);
+                       port->xmit_tail = (port->xmit_tail
+                               + (word_count << 1)) & (SERIAL_XMIT_SIZE - 1);
+                       txcount -= (word_count << 1);
+                       port->xmit_cnt -= (word_count << 1);
+                       if (cnt & 0x0001) {
+                               residue = YES;
+                               wrd = port->port.xmit_buf[port->xmit_tail];
+                               port->xmit_tail = (port->xmit_tail + 1)
+                                       & (SERIAL_XMIT_SIZE - 1);
+                               port->xmit_cnt--;
+                               txcount--;
+                       }
+               }
+
+               InterruptTheCard(base);
+               if (port->xmit_cnt <= 0)
+                       port->status &= ~ISI_TXOK;
+               if (port->xmit_cnt <= WAKEUP_CHARS)
+                       tty_wakeup(tty);
+       }
+
+put_unlock:
+       tty_kref_put(tty);
+unlock:
+       spin_unlock_irqrestore(&isi_card[card].card_lock, flags);
+       /*      schedule another tx for hopefully in about 10ms */
+sched_again:
+       mod_timer(&tx, jiffies + msecs_to_jiffies(10));
+}
+
+/*
+ *     Main interrupt handler routine
+ */
+
+static irqreturn_t isicom_interrupt(int irq, void *dev_id)
+{
+       struct isi_board *card = dev_id;
+       struct isi_port *port;
+       struct tty_struct *tty;
+       unsigned long base;
+       u16 header, word_count, count, channel;
+       short byte_count;
+       unsigned char *rp;
+
+       if (!card || !(card->status & FIRMWARE_LOADED))
+               return IRQ_NONE;
+
+       base = card->base;
+
+       /* did the card interrupt us? */
+       if (!(inw(base + 0x0e) & 0x02))
+               return IRQ_NONE;
+
+       spin_lock(&card->card_lock);
+
+       /*
+        * disable any interrupts from the PCI card and lower the
+        * interrupt line
+        */
+       outw(0x8000, base+0x04);
+       ClearInterrupt(base);
+
+       inw(base);              /* get the dummy word out */
+       header = inw(base);
+       channel = (header & 0x7800) >> card->shift_count;
+       byte_count = header & 0xff;
+
+       if (channel + 1 > card->port_count) {
+               pr_warning("%s(0x%lx): %d(channel) > port_count.\n",
+                          __func__, base, channel+1);
+               outw(0x0000, base+0x04); /* enable interrupts */
+               spin_unlock(&card->card_lock);
+               return IRQ_HANDLED;
+       }
+       port = card->ports + channel;
+       if (!(port->port.flags & ASYNC_INITIALIZED)) {
+               outw(0x0000, base+0x04); /* enable interrupts */
+               spin_unlock(&card->card_lock);
+               return IRQ_HANDLED;
+       }
+
+       tty = tty_port_tty_get(&port->port);
+       if (tty == NULL) {
+               word_count = byte_count >> 1;
+               while (byte_count > 1) {
+                       inw(base);
+                       byte_count -= 2;
+               }
+               if (byte_count & 0x01)
+                       inw(base);
+               outw(0x0000, base+0x04); /* enable interrupts */
+               spin_unlock(&card->card_lock);
+               return IRQ_HANDLED;
+       }
+
+       if (header & 0x8000) {          /* Status Packet */
+               header = inw(base);
+               switch (header & 0xff) {
+               case 0: /* Change in EIA signals */
+                       if (port->port.flags & ASYNC_CHECK_CD) {
+                               if (port->status & ISI_DCD) {
+                                       if (!(header & ISI_DCD)) {
+                                       /* Carrier has been lost  */
+                                               pr_debug("%s: DCD->low.\n",
+                                                        __func__);
+                                               port->status &= ~ISI_DCD;
+                                               tty_hangup(tty);
+                                       }
+                               } else if (header & ISI_DCD) {
+                               /* Carrier has been detected */
+                                       pr_debug("%s: DCD->high.\n",
+                                               __func__);
+                                       port->status |= ISI_DCD;
+                                       wake_up_interruptible(&port->port.open_wait);
+                               }
+                       } else {
+                               if (header & ISI_DCD)
+                                       port->status |= ISI_DCD;
+                               else
+                                       port->status &= ~ISI_DCD;
+                       }
+
+                       if (port->port.flags & ASYNC_CTS_FLOW) {
+                               if (tty->hw_stopped) {
+                                       if (header & ISI_CTS) {
+                                               port->port.tty->hw_stopped = 0;
+                                               /* start tx ing */
+                                               port->status |= (ISI_TXOK
+                                                       | ISI_CTS);
+                                               tty_wakeup(tty);
+                                       }
+                               } else if (!(header & ISI_CTS)) {
+                                       tty->hw_stopped = 1;
+                                       /* stop tx ing */
+                                       port->status &= ~(ISI_TXOK | ISI_CTS);
+                               }
+                       } else {
+                               if (header & ISI_CTS)
+                                       port->status |= ISI_CTS;
+                               else
+                                       port->status &= ~ISI_CTS;
+                       }
+
+                       if (header & ISI_DSR)
+                               port->status |= ISI_DSR;
+                       else
+                               port->status &= ~ISI_DSR;
+
+                       if (header & ISI_RI)
+                               port->status |= ISI_RI;
+                       else
+                               port->status &= ~ISI_RI;
+
+                       break;
+
+               case 1: /* Received Break !!! */
+                       tty_insert_flip_char(tty, 0, TTY_BREAK);
+                       if (port->port.flags & ASYNC_SAK)
+                               do_SAK(tty);
+                       tty_flip_buffer_push(tty);
+                       break;
+
+               case 2: /* Statistics            */
+                       pr_debug("%s: stats!!!\n", __func__);
+                       break;
+
+               default:
+                       pr_debug("%s: Unknown code in status packet.\n",
+                                __func__);
+                       break;
+               }
+       } else {                                /* Data   Packet */
+
+               count = tty_prepare_flip_string(tty, &rp, byte_count & ~1);
+               pr_debug("%s: Can rx %d of %d bytes.\n",
+                        __func__, count, byte_count);
+               word_count = count >> 1;
+               insw(base, rp, word_count);
+               byte_count -= (word_count << 1);
+               if (count & 0x0001) {
+                       tty_insert_flip_char(tty,  inw(base) & 0xff,
+                               TTY_NORMAL);
+                       byte_count -= 2;
+               }
+               if (byte_count > 0) {
+                       pr_debug("%s(0x%lx:%d): Flip buffer overflow! dropping bytes...\n",
+                                __func__, base, channel + 1);
+               /* drain out unread xtra data */
+               while (byte_count > 0) {
+                               inw(base);
+                               byte_count -= 2;
+                       }
+               }
+               tty_flip_buffer_push(tty);
+       }
+       outw(0x0000, base+0x04); /* enable interrupts */
+       spin_unlock(&card->card_lock);
+       tty_kref_put(tty);
+
+       return IRQ_HANDLED;
+}
+
+static void isicom_config_port(struct tty_struct *tty)
+{
+       struct isi_port *port = tty->driver_data;
+       struct isi_board *card = port->card;
+       unsigned long baud;
+       unsigned long base = card->base;
+       u16 channel_setup, channel = port->channel,
+               shift_count = card->shift_count;
+       unsigned char flow_ctrl;
+
+       /* FIXME: Switch to new tty baud API */
+       baud = C_BAUD(tty);
+       if (baud & CBAUDEX) {
+               baud &= ~CBAUDEX;
+
+               /*  if CBAUDEX bit is on and the baud is set to either 50 or 75
+                *  then the card is programmed for 57.6Kbps or 115Kbps
+                *  respectively.
+                */
+
+               /* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */
+               if (baud < 1 || baud > 4)
+                       tty->termios->c_cflag &= ~CBAUDEX;
+               else
+                       baud += 15;
+       }
+       if (baud == 15) {
+
+               /*  the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set
+                *  by the set_serial_info ioctl ... this is done by
+                *  the 'setserial' utility.
+                */
+
+               if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                       baud++; /*  57.6 Kbps */
+               if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                       baud += 2; /*  115  Kbps */
+               if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                       baud += 3; /* 230 kbps*/
+               if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                       baud += 4; /* 460 kbps*/
+       }
+       if (linuxb_to_isib[baud] == -1) {
+               /* hang up */
+               drop_dtr(port);
+               return;
+       } else
+               raise_dtr(port);
+
+       if (WaitTillCardIsFree(base) == 0) {
+               outw(0x8000 | (channel << shift_count) | 0x03, base);
+               outw(linuxb_to_isib[baud] << 8 | 0x03, base);
+               channel_setup = 0;
+               switch (C_CSIZE(tty)) {
+               case CS5:
+                       channel_setup |= ISICOM_CS5;
+                       break;
+               case CS6:
+                       channel_setup |= ISICOM_CS6;
+                       break;
+               case CS7:
+                       channel_setup |= ISICOM_CS7;
+                       break;
+               case CS8:
+                       channel_setup |= ISICOM_CS8;
+                       break;
+               }
+
+               if (C_CSTOPB(tty))
+                       channel_setup |= ISICOM_2SB;
+               if (C_PARENB(tty)) {
+                       channel_setup |= ISICOM_EVPAR;
+                       if (C_PARODD(tty))
+                               channel_setup |= ISICOM_ODPAR;
+               }
+               outw(channel_setup, base);
+               InterruptTheCard(base);
+       }
+       if (C_CLOCAL(tty))
+               port->port.flags &= ~ASYNC_CHECK_CD;
+       else
+               port->port.flags |= ASYNC_CHECK_CD;
+
+       /* flow control settings ...*/
+       flow_ctrl = 0;
+       port->port.flags &= ~ASYNC_CTS_FLOW;
+       if (C_CRTSCTS(tty)) {
+               port->port.flags |= ASYNC_CTS_FLOW;
+               flow_ctrl |= ISICOM_CTSRTS;
+       }
+       if (I_IXON(tty))
+               flow_ctrl |= ISICOM_RESPOND_XONXOFF;
+       if (I_IXOFF(tty))
+               flow_ctrl |= ISICOM_INITIATE_XONXOFF;
+
+       if (WaitTillCardIsFree(base) == 0) {
+               outw(0x8000 | (channel << shift_count) | 0x04, base);
+               outw(flow_ctrl << 8 | 0x05, base);
+               outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base);
+               InterruptTheCard(base);
+       }
+
+       /*      rx enabled -> enable port for rx on the card    */
+       if (C_CREAD(tty)) {
+               card->port_status |= (1 << channel);
+               outw(card->port_status, base + 0x02);
+       }
+}
+
+/* open et all */
+
+static inline void isicom_setup_board(struct isi_board *bp)
+{
+       int channel;
+       struct isi_port *port;
+
+       bp->count++;
+       if (!(bp->status & BOARD_INIT)) {
+               port = bp->ports;
+               for (channel = 0; channel < bp->port_count; channel++, port++)
+                       drop_dtr_rts(port);
+       }
+       bp->status |= BOARD_ACTIVE | BOARD_INIT;
+}
+
+/* Activate and thus setup board are protected from races against shutdown
+   by the tty_port mutex */
+
+static int isicom_activate(struct tty_port *tport, struct tty_struct *tty)
+{
+       struct isi_port *port = container_of(tport, struct isi_port, port);
+       struct isi_board *card = port->card;
+       unsigned long flags;
+
+       if (tty_port_alloc_xmit_buf(tport) < 0)
+               return -ENOMEM;
+
+       spin_lock_irqsave(&card->card_lock, flags);
+       isicom_setup_board(card);
+
+       port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+
+       /*      discard any residual data       */
+       if (WaitTillCardIsFree(card->base) == 0) {
+               outw(0x8000 | (port->channel << card->shift_count) | 0x02,
+                               card->base);
+               outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base);
+               InterruptTheCard(card->base);
+       }
+       isicom_config_port(tty);
+       spin_unlock_irqrestore(&card->card_lock, flags);
+
+       return 0;
+}
+
+static int isicom_carrier_raised(struct tty_port *port)
+{
+       struct isi_port *ip = container_of(port, struct isi_port, port);
+       return (ip->status & ISI_DCD)?1 : 0;
+}
+
+static struct tty_port *isicom_find_port(struct tty_struct *tty)
+{
+       struct isi_port *port;
+       struct isi_board *card;
+       unsigned int board;
+       int line = tty->index;
+
+       if (line < 0 || line > PORT_COUNT-1)
+               return NULL;
+       board = BOARD(line);
+       card = &isi_card[board];
+
+       if (!(card->status & FIRMWARE_LOADED))
+               return NULL;
+
+       /*  open on a port greater than the port count for the card !!! */
+       if (line > ((board * 16) + card->port_count - 1))
+               return NULL;
+
+       port = &isi_ports[line];
+       if (isicom_paranoia_check(port, tty->name, "isicom_open"))
+               return NULL;
+
+       return &port->port;
+}
+
+static int isicom_open(struct tty_struct *tty, struct file *filp)
+{
+       struct isi_port *port;
+       struct tty_port *tport;
+
+       tport = isicom_find_port(tty);
+       if (tport == NULL)
+               return -ENODEV;
+       port = container_of(tport, struct isi_port, port);
+
+       tty->driver_data = port;
+       return tty_port_open(tport, tty, filp);
+}
+
+/* close et all */
+
+/* card->lock HAS to be held */
+static void isicom_shutdown_port(struct isi_port *port)
+{
+       struct isi_board *card = port->card;
+
+       if (--card->count < 0) {
+               pr_debug("%s: bad board(0x%lx) count %d.\n",
+                        __func__, card->base, card->count);
+               card->count = 0;
+       }
+       /* last port was closed, shutdown that board too */
+       if (!card->count)
+               card->status &= BOARD_ACTIVE;
+}
+
+static void isicom_flush_buffer(struct tty_struct *tty)
+{
+       struct isi_port *port = tty->driver_data;
+       struct isi_board *card = port->card;
+       unsigned long flags;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_flush_buffer"))
+               return;
+
+       spin_lock_irqsave(&card->card_lock, flags);
+       port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+       spin_unlock_irqrestore(&card->card_lock, flags);
+
+       tty_wakeup(tty);
+}
+
+static void isicom_shutdown(struct tty_port *port)
+{
+       struct isi_port *ip = container_of(port, struct isi_port, port);
+       struct isi_board *card = ip->card;
+       unsigned long flags;
+
+       /* indicate to the card that no more data can be received
+          on this port */
+       spin_lock_irqsave(&card->card_lock, flags);
+       card->port_status &= ~(1 << ip->channel);
+       outw(card->port_status, card->base + 0x02);
+       isicom_shutdown_port(ip);
+       spin_unlock_irqrestore(&card->card_lock, flags);
+       tty_port_free_xmit_buf(port);
+}
+
+static void isicom_close(struct tty_struct *tty, struct file *filp)
+{
+       struct isi_port *ip = tty->driver_data;
+       struct tty_port *port;
+
+       if (ip == NULL)
+               return;
+
+       port = &ip->port;
+       if (isicom_paranoia_check(ip, tty->name, "isicom_close"))
+               return;
+       tty_port_close(port, tty, filp);
+}
+
+/* write et all */
+static int isicom_write(struct tty_struct *tty,        const unsigned char *buf,
+       int count)
+{
+       struct isi_port *port = tty->driver_data;
+       struct isi_board *card = port->card;
+       unsigned long flags;
+       int cnt, total = 0;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_write"))
+               return 0;
+
+       spin_lock_irqsave(&card->card_lock, flags);
+
+       while (1) {
+               cnt = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt
+                               - 1, SERIAL_XMIT_SIZE - port->xmit_head));
+               if (cnt <= 0)
+                       break;
+
+               memcpy(port->port.xmit_buf + port->xmit_head, buf, cnt);
+               port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE
+                       - 1);
+               port->xmit_cnt += cnt;
+               buf += cnt;
+               count -= cnt;
+               total += cnt;
+       }
+       if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped)
+               port->status |= ISI_TXOK;
+       spin_unlock_irqrestore(&card->card_lock, flags);
+       return total;
+}
+
+/* put_char et all */
+static int isicom_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct isi_port *port = tty->driver_data;
+       struct isi_board *card = port->card;
+       unsigned long flags;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_put_char"))
+               return 0;
+
+       spin_lock_irqsave(&card->card_lock, flags);
+       if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+               spin_unlock_irqrestore(&card->card_lock, flags);
+               return 0;
+       }
+
+       port->port.xmit_buf[port->xmit_head++] = ch;
+       port->xmit_head &= (SERIAL_XMIT_SIZE - 1);
+       port->xmit_cnt++;
+       spin_unlock_irqrestore(&card->card_lock, flags);
+       return 1;
+}
+
+/* flush_chars et all */
+static void isicom_flush_chars(struct tty_struct *tty)
+{
+       struct isi_port *port = tty->driver_data;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_flush_chars"))
+               return;
+
+       if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+                       !port->port.xmit_buf)
+               return;
+
+       /* this tells the transmitter to consider this port for
+          data output to the card ... that's the best we can do. */
+       port->status |= ISI_TXOK;
+}
+
+/* write_room et all */
+static int isicom_write_room(struct tty_struct *tty)
+{
+       struct isi_port *port = tty->driver_data;
+       int free;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_write_room"))
+               return 0;
+
+       free = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
+       if (free < 0)
+               free = 0;
+       return free;
+}
+
+/* chars_in_buffer et all */
+static int isicom_chars_in_buffer(struct tty_struct *tty)
+{
+       struct isi_port *port = tty->driver_data;
+       if (isicom_paranoia_check(port, tty->name, "isicom_chars_in_buffer"))
+               return 0;
+       return port->xmit_cnt;
+}
+
+/* ioctl et all */
+static int isicom_send_break(struct tty_struct *tty, int length)
+{
+       struct isi_port *port = tty->driver_data;
+       struct isi_board *card = port->card;
+       unsigned long base = card->base;
+
+       if (length == -1)
+               return -EOPNOTSUPP;
+
+       if (!lock_card(card))
+               return -EINVAL;
+
+       outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base);
+       outw((length & 0xff) << 8 | 0x00, base);
+       outw((length & 0xff00), base);
+       InterruptTheCard(base);
+
+       unlock_card(card);
+       return 0;
+}
+
+static int isicom_tiocmget(struct tty_struct *tty)
+{
+       struct isi_port *port = tty->driver_data;
+       /* just send the port status */
+       u16 status = port->status;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
+               return -ENODEV;
+
+       return  ((status & ISI_RTS) ? TIOCM_RTS : 0) |
+               ((status & ISI_DTR) ? TIOCM_DTR : 0) |
+               ((status & ISI_DCD) ? TIOCM_CAR : 0) |
+               ((status & ISI_DSR) ? TIOCM_DSR : 0) |
+               ((status & ISI_CTS) ? TIOCM_CTS : 0) |
+               ((status & ISI_RI ) ? TIOCM_RI  : 0);
+}
+
+static int isicom_tiocmset(struct tty_struct *tty,
+                                       unsigned int set, unsigned int clear)
+{
+       struct isi_port *port = tty->driver_data;
+       unsigned long flags;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
+               return -ENODEV;
+
+       spin_lock_irqsave(&port->card->card_lock, flags);
+       if (set & TIOCM_RTS)
+               raise_rts(port);
+       if (set & TIOCM_DTR)
+               raise_dtr(port);
+
+       if (clear & TIOCM_RTS)
+               drop_rts(port);
+       if (clear & TIOCM_DTR)
+               drop_dtr(port);
+       spin_unlock_irqrestore(&port->card->card_lock, flags);
+
+       return 0;
+}
+
+static int isicom_set_serial_info(struct tty_struct *tty,
+                                       struct serial_struct __user *info)
+{
+       struct isi_port *port = tty->driver_data;
+       struct serial_struct newinfo;
+       int reconfig_port;
+
+       if (copy_from_user(&newinfo, info, sizeof(newinfo)))
+               return -EFAULT;
+
+       mutex_lock(&port->port.mutex);
+       reconfig_port = ((port->port.flags & ASYNC_SPD_MASK) !=
+               (newinfo.flags & ASYNC_SPD_MASK));
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               if ((newinfo.close_delay != port->port.close_delay) ||
+                               (newinfo.closing_wait != port->port.closing_wait) ||
+                               ((newinfo.flags & ~ASYNC_USR_MASK) !=
+                               (port->port.flags & ~ASYNC_USR_MASK))) {
+                       mutex_unlock(&port->port.mutex);
+                       return -EPERM;
+               }
+               port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
+                               (newinfo.flags & ASYNC_USR_MASK));
+       } else {
+               port->port.close_delay = newinfo.close_delay;
+               port->port.closing_wait = newinfo.closing_wait;
+               port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
+                               (newinfo.flags & ASYNC_FLAGS));
+       }
+       if (reconfig_port) {
+               unsigned long flags;
+               spin_lock_irqsave(&port->card->card_lock, flags);
+               isicom_config_port(tty);
+               spin_unlock_irqrestore(&port->card->card_lock, flags);
+       }
+       mutex_unlock(&port->port.mutex);
+       return 0;
+}
+
+static int isicom_get_serial_info(struct isi_port *port,
+       struct serial_struct __user *info)
+{
+       struct serial_struct out_info;
+
+       mutex_lock(&port->port.mutex);
+       memset(&out_info, 0, sizeof(out_info));
+/*     out_info.type = ? */
+       out_info.line = port - isi_ports;
+       out_info.port = port->card->base;
+       out_info.irq = port->card->irq;
+       out_info.flags = port->port.flags;
+/*     out_info.baud_base = ? */
+       out_info.close_delay = port->port.close_delay;
+       out_info.closing_wait = port->port.closing_wait;
+       mutex_unlock(&port->port.mutex);
+       if (copy_to_user(info, &out_info, sizeof(out_info)))
+               return -EFAULT;
+       return 0;
+}
+
+static int isicom_ioctl(struct tty_struct *tty,
+       unsigned int cmd, unsigned long arg)
+{
+       struct isi_port *port = tty->driver_data;
+       void __user *argp = (void __user *)arg;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
+               return -ENODEV;
+
+       switch (cmd) {
+       case TIOCGSERIAL:
+               return isicom_get_serial_info(port, argp);
+
+       case TIOCSSERIAL:
+               return isicom_set_serial_info(tty, argp);
+
+       default:
+               return -ENOIOCTLCMD;
+       }
+       return 0;
+}
+
+/* set_termios et all */
+static void isicom_set_termios(struct tty_struct *tty,
+       struct ktermios *old_termios)
+{
+       struct isi_port *port = tty->driver_data;
+       unsigned long flags;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_set_termios"))
+               return;
+
+       if (tty->termios->c_cflag == old_termios->c_cflag &&
+                       tty->termios->c_iflag == old_termios->c_iflag)
+               return;
+
+       spin_lock_irqsave(&port->card->card_lock, flags);
+       isicom_config_port(tty);
+       spin_unlock_irqrestore(&port->card->card_lock, flags);
+
+       if ((old_termios->c_cflag & CRTSCTS) &&
+                       !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               isicom_start(tty);
+       }
+}
+
+/* throttle et all */
+static void isicom_throttle(struct tty_struct *tty)
+{
+       struct isi_port *port = tty->driver_data;
+       struct isi_board *card = port->card;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_throttle"))
+               return;
+
+       /* tell the card that this port cannot handle any more data for now */
+       card->port_status &= ~(1 << port->channel);
+       outw(card->port_status, card->base + 0x02);
+}
+
+/* unthrottle et all */
+static void isicom_unthrottle(struct tty_struct *tty)
+{
+       struct isi_port *port = tty->driver_data;
+       struct isi_board *card = port->card;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_unthrottle"))
+               return;
+
+       /* tell the card that this port is ready to accept more data */
+       card->port_status |= (1 << port->channel);
+       outw(card->port_status, card->base + 0x02);
+}
+
+/* stop et all */
+static void isicom_stop(struct tty_struct *tty)
+{
+       struct isi_port *port = tty->driver_data;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_stop"))
+               return;
+
+       /* this tells the transmitter not to consider this port for
+          data output to the card. */
+       port->status &= ~ISI_TXOK;
+}
+
+/* start et all */
+static void isicom_start(struct tty_struct *tty)
+{
+       struct isi_port *port = tty->driver_data;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_start"))
+               return;
+
+       /* this tells the transmitter to consider this port for
+          data output to the card. */
+       port->status |= ISI_TXOK;
+}
+
+static void isicom_hangup(struct tty_struct *tty)
+{
+       struct isi_port *port = tty->driver_data;
+
+       if (isicom_paranoia_check(port, tty->name, "isicom_hangup"))
+               return;
+       tty_port_hangup(&port->port);
+}
+
+
+/*
+ * Driver init and deinit functions
+ */
+
+static const struct tty_operations isicom_ops = {
+       .open                   = isicom_open,
+       .close                  = isicom_close,
+       .write                  = isicom_write,
+       .put_char               = isicom_put_char,
+       .flush_chars            = isicom_flush_chars,
+       .write_room             = isicom_write_room,
+       .chars_in_buffer        = isicom_chars_in_buffer,
+       .ioctl                  = isicom_ioctl,
+       .set_termios            = isicom_set_termios,
+       .throttle               = isicom_throttle,
+       .unthrottle             = isicom_unthrottle,
+       .stop                   = isicom_stop,
+       .start                  = isicom_start,
+       .hangup                 = isicom_hangup,
+       .flush_buffer           = isicom_flush_buffer,
+       .tiocmget               = isicom_tiocmget,
+       .tiocmset               = isicom_tiocmset,
+       .break_ctl              = isicom_send_break,
+};
+
+static const struct tty_port_operations isicom_port_ops = {
+       .carrier_raised         = isicom_carrier_raised,
+       .dtr_rts                = isicom_dtr_rts,
+       .activate               = isicom_activate,
+       .shutdown               = isicom_shutdown,
+};
+
+static int __devinit reset_card(struct pci_dev *pdev,
+       const unsigned int card, unsigned int *signature)
+{
+       struct isi_board *board = pci_get_drvdata(pdev);
+       unsigned long base = board->base;
+       unsigned int sig, portcount = 0;
+       int retval = 0;
+
+       dev_dbg(&pdev->dev, "ISILoad:Resetting Card%d at 0x%lx\n", card + 1,
+               base);
+
+       inw(base + 0x8);
+
+       msleep(10);
+
+       outw(0, base + 0x8); /* Reset */
+
+       msleep(1000);
+
+       sig = inw(base + 0x4) & 0xff;
+
+       if (sig != 0xa5 && sig != 0xbb && sig != 0xcc && sig != 0xdd &&
+                       sig != 0xee) {
+               dev_warn(&pdev->dev, "ISILoad:Card%u reset failure (Possible "
+                       "bad I/O Port Address 0x%lx).\n", card + 1, base);
+               dev_dbg(&pdev->dev, "Sig=0x%x\n", sig);
+               retval = -EIO;
+               goto end;
+       }
+
+       msleep(10);
+
+       portcount = inw(base + 0x2);
+       if (!(inw(base + 0xe) & 0x1) || (portcount != 0 && portcount != 4 &&
+                               portcount != 8 && portcount != 16)) {
+               dev_err(&pdev->dev, "ISILoad:PCI Card%d reset failure.\n",
+                       card + 1);
+               retval = -EIO;
+               goto end;
+       }
+
+       switch (sig) {
+       case 0xa5:
+       case 0xbb:
+       case 0xdd:
+               board->port_count = (portcount == 4) ? 4 : 8;
+               board->shift_count = 12;
+               break;
+       case 0xcc:
+       case 0xee:
+               board->port_count = 16;
+               board->shift_count = 11;
+               break;
+       }
+       dev_info(&pdev->dev, "-Done\n");
+       *signature = sig;
+
+end:
+       return retval;
+}
+
+static int __devinit load_firmware(struct pci_dev *pdev,
+       const unsigned int index, const unsigned int signature)
+{
+       struct isi_board *board = pci_get_drvdata(pdev);
+       const struct firmware *fw;
+       unsigned long base = board->base;
+       unsigned int a;
+       u16 word_count, status;
+       int retval = -EIO;
+       char *name;
+       u8 *data;
+
+       struct stframe {
+               u16     addr;
+               u16     count;
+               u8      data[0];
+       } *frame;
+
+       switch (signature) {
+       case 0xa5:
+               name = "isi608.bin";
+               break;
+       case 0xbb:
+               name = "isi608em.bin";
+               break;
+       case 0xcc:
+               name = "isi616em.bin";
+               break;
+       case 0xdd:
+               name = "isi4608.bin";
+               break;
+       case 0xee:
+               name = "isi4616.bin";
+               break;
+       default:
+               dev_err(&pdev->dev, "Unknown signature.\n");
+               goto end;
+       }
+
+       retval = request_firmware(&fw, name, &pdev->dev);
+       if (retval)
+               goto end;
+
+       retval = -EIO;
+
+       for (frame = (struct stframe *)fw->data;
+                       frame < (struct stframe *)(fw->data + fw->size);
+                       frame = (struct stframe *)((u8 *)(frame + 1) +
+                               frame->count)) {
+               if (WaitTillCardIsFree(base))
+                       goto errrelfw;
+
+               outw(0xf0, base);       /* start upload sequence */
+               outw(0x00, base);
+               outw(frame->addr, base); /* lsb of address */
+
+               word_count = frame->count / 2 + frame->count % 2;
+               outw(word_count, base);
+               InterruptTheCard(base);
+
+               udelay(100); /* 0x2f */
+
+               if (WaitTillCardIsFree(base))
+                       goto errrelfw;
+
+               status = inw(base + 0x4);
+               if (status != 0) {
+                       dev_warn(&pdev->dev, "Card%d rejected load header:\n"
+                                "Address:0x%x\n"
+                                "Count:0x%x\n"
+                                "Status:0x%x\n",
+                                index + 1, frame->addr, frame->count, status);
+                       goto errrelfw;
+               }
+               outsw(base, frame->data, word_count);
+
+               InterruptTheCard(base);
+
+               udelay(50); /* 0x0f */
+
+               if (WaitTillCardIsFree(base))
+                       goto errrelfw;
+
+               status = inw(base + 0x4);
+               if (status != 0) {
+                       dev_err(&pdev->dev, "Card%d got out of sync.Card "
+                               "Status:0x%x\n", index + 1, status);
+                       goto errrelfw;
+               }
+       }
+
+/* XXX: should we test it by reading it back and comparing with original like
+ * in load firmware package? */
+       for (frame = (struct stframe *)fw->data;
+                       frame < (struct stframe *)(fw->data + fw->size);
+                       frame = (struct stframe *)((u8 *)(frame + 1) +
+                               frame->count)) {
+               if (WaitTillCardIsFree(base))
+                       goto errrelfw;
+
+               outw(0xf1, base); /* start download sequence */
+               outw(0x00, base);
+               outw(frame->addr, base); /* lsb of address */
+
+               word_count = (frame->count >> 1) + frame->count % 2;
+               outw(word_count + 1, base);
+               InterruptTheCard(base);
+
+               udelay(50); /* 0xf */
+
+               if (WaitTillCardIsFree(base))
+                       goto errrelfw;
+
+               status = inw(base + 0x4);
+               if (status != 0) {
+                       dev_warn(&pdev->dev, "Card%d rejected verify header:\n"
+                                "Address:0x%x\n"
+                                "Count:0x%x\n"
+                                "Status: 0x%x\n",
+                                index + 1, frame->addr, frame->count, status);
+                       goto errrelfw;
+               }
+
+               data = kmalloc(word_count * 2, GFP_KERNEL);
+               if (data == NULL) {
+                       dev_err(&pdev->dev, "Card%d, firmware upload "
+                               "failed, not enough memory\n", index + 1);
+                       goto errrelfw;
+               }
+               inw(base);
+               insw(base, data, word_count);
+               InterruptTheCard(base);
+
+               for (a = 0; a < frame->count; a++)
+                       if (data[a] != frame->data[a]) {
+                               kfree(data);
+                               dev_err(&pdev->dev, "Card%d, firmware upload "
+                                       "failed\n", index + 1);
+                               goto errrelfw;
+                       }
+               kfree(data);
+
+               udelay(50); /* 0xf */
+
+               if (WaitTillCardIsFree(base))
+                       goto errrelfw;
+
+               status = inw(base + 0x4);
+               if (status != 0) {
+                       dev_err(&pdev->dev, "Card%d verify got out of sync. "
+                               "Card Status:0x%x\n", index + 1, status);
+                       goto errrelfw;
+               }
+       }
+
+       /* xfer ctrl */
+       if (WaitTillCardIsFree(base))
+               goto errrelfw;
+
+       outw(0xf2, base);
+       outw(0x800, base);
+       outw(0x0, base);
+       outw(0x0, base);
+       InterruptTheCard(base);
+       outw(0x0, base + 0x4); /* for ISI4608 cards */
+
+       board->status |= FIRMWARE_LOADED;
+       retval = 0;
+
+errrelfw:
+       release_firmware(fw);
+end:
+       return retval;
+}
+
+/*
+ *     Insmod can set static symbols so keep these static
+ */
+static unsigned int card_count;
+
+static int __devinit isicom_probe(struct pci_dev *pdev,
+       const struct pci_device_id *ent)
+{
+       unsigned int uninitialized_var(signature), index;
+       int retval = -EPERM;
+       struct isi_board *board = NULL;
+
+       if (card_count >= BOARD_COUNT)
+               goto err;
+
+       retval = pci_enable_device(pdev);
+       if (retval) {
+               dev_err(&pdev->dev, "failed to enable\n");
+               goto err;
+       }
+
+       dev_info(&pdev->dev, "ISI PCI Card(Device ID 0x%x)\n", ent->device);
+
+       /* allot the first empty slot in the array */
+       for (index = 0; index < BOARD_COUNT; index++) {
+               if (isi_card[index].base == 0) {
+                       board = &isi_card[index];
+                       break;
+               }
+       }
+       if (index == BOARD_COUNT) {
+               retval = -ENODEV;
+               goto err_disable;
+       }
+
+       board->index = index;
+       board->base = pci_resource_start(pdev, 3);
+       board->irq = pdev->irq;
+       card_count++;
+
+       pci_set_drvdata(pdev, board);
+
+       retval = pci_request_region(pdev, 3, ISICOM_NAME);
+       if (retval) {
+               dev_err(&pdev->dev, "I/O Region 0x%lx-0x%lx is busy. Card%d "
+                       "will be disabled.\n", board->base, board->base + 15,
+                       index + 1);
+               retval = -EBUSY;
+               goto errdec;
+       }
+
+       retval = request_irq(board->irq, isicom_interrupt,
+                       IRQF_SHARED | IRQF_DISABLED, ISICOM_NAME, board);
+       if (retval < 0) {
+               dev_err(&pdev->dev, "Could not install handler at Irq %d. "
+                       "Card%d will be disabled.\n", board->irq, index + 1);
+               goto errunrr;
+       }
+
+       retval = reset_card(pdev, index, &signature);
+       if (retval < 0)
+               goto errunri;
+
+       retval = load_firmware(pdev, index, signature);
+       if (retval < 0)
+               goto errunri;
+
+       for (index = 0; index < board->port_count; index++)
+               tty_register_device(isicom_normal, board->index * 16 + index,
+                               &pdev->dev);
+
+       return 0;
+
+errunri:
+       free_irq(board->irq, board);
+errunrr:
+       pci_release_region(pdev, 3);
+errdec:
+       board->base = 0;
+       card_count--;
+err_disable:
+       pci_disable_device(pdev);
+err:
+       return retval;
+}
+
+static void __devexit isicom_remove(struct pci_dev *pdev)
+{
+       struct isi_board *board = pci_get_drvdata(pdev);
+       unsigned int i;
+
+       for (i = 0; i < board->port_count; i++)
+               tty_unregister_device(isicom_normal, board->index * 16 + i);
+
+       free_irq(board->irq, board);
+       pci_release_region(pdev, 3);
+       board->base = 0;
+       card_count--;
+       pci_disable_device(pdev);
+}
+
+static int __init isicom_init(void)
+{
+       int retval, idx, channel;
+       struct isi_port *port;
+
+       for (idx = 0; idx < BOARD_COUNT; idx++) {
+               port = &isi_ports[idx * 16];
+               isi_card[idx].ports = port;
+               spin_lock_init(&isi_card[idx].card_lock);
+               for (channel = 0; channel < 16; channel++, port++) {
+                       tty_port_init(&port->port);
+                       port->port.ops = &isicom_port_ops;
+                       port->magic = ISICOM_MAGIC;
+                       port->card = &isi_card[idx];
+                       port->channel = channel;
+                       port->port.close_delay = 50 * HZ/100;
+                       port->port.closing_wait = 3000 * HZ/100;
+                       port->status = 0;
+                       /*  . . .  */
+               }
+               isi_card[idx].base = 0;
+               isi_card[idx].irq = 0;
+       }
+
+       /* tty driver structure initialization */
+       isicom_normal = alloc_tty_driver(PORT_COUNT);
+       if (!isicom_normal) {
+               retval = -ENOMEM;
+               goto error;
+       }
+
+       isicom_normal->owner                    = THIS_MODULE;
+       isicom_normal->name                     = "ttyM";
+       isicom_normal->major                    = ISICOM_NMAJOR;
+       isicom_normal->minor_start              = 0;
+       isicom_normal->type                     = TTY_DRIVER_TYPE_SERIAL;
+       isicom_normal->subtype                  = SERIAL_TYPE_NORMAL;
+       isicom_normal->init_termios             = tty_std_termios;
+       isicom_normal->init_termios.c_cflag     = B9600 | CS8 | CREAD | HUPCL |
+               CLOCAL;
+       isicom_normal->flags                    = TTY_DRIVER_REAL_RAW |
+               TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK;
+       tty_set_operations(isicom_normal, &isicom_ops);
+
+       retval = tty_register_driver(isicom_normal);
+       if (retval) {
+               pr_debug("Couldn't register the dialin driver\n");
+               goto err_puttty;
+       }
+
+       retval = pci_register_driver(&isicom_driver);
+       if (retval < 0) {
+               pr_err("Unable to register pci driver.\n");
+               goto err_unrtty;
+       }
+
+       mod_timer(&tx, jiffies + 1);
+
+       return 0;
+err_unrtty:
+       tty_unregister_driver(isicom_normal);
+err_puttty:
+       put_tty_driver(isicom_normal);
+error:
+       return retval;
+}
+
+static void __exit isicom_exit(void)
+{
+       del_timer_sync(&tx);
+
+       pci_unregister_driver(&isicom_driver);
+       tty_unregister_driver(isicom_normal);
+       put_tty_driver(isicom_normal);
+}
+
+module_init(isicom_init);
+module_exit(isicom_exit);
+
+MODULE_AUTHOR("MultiTech");
+MODULE_DESCRIPTION("Driver for the ISI series of cards by MultiTech");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("isi608.bin");
+MODULE_FIRMWARE("isi608em.bin");
+MODULE_FIRMWARE("isi616em.bin");
+MODULE_FIRMWARE("isi4608.bin");
+MODULE_FIRMWARE("isi4616.bin");
diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c
new file mode 100644 (file)
index 0000000..35b0c38
--- /dev/null
@@ -0,0 +1,2092 @@
+/*****************************************************************************/
+/*
+ *           moxa.c  -- MOXA Intellio family multiport serial driver.
+ *
+ *      Copyright (C) 1999-2000  Moxa Technologies (support@moxa.com).
+ *      Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
+ *
+ *      This code is loosely based on the Linux serial driver, written by
+ *      Linus Torvalds, Theodore T'so and others.
+ *
+ *      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.
+ */
+
+/*
+ *    MOXA Intellio Series Driver
+ *      for             : LINUX
+ *      date            : 1999/1/7
+ *      version         : 5.1
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/serial.h>
+#include <linux/tty_driver.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include "moxa.h"
+
+#define MOXA_VERSION           "6.0k"
+
+#define MOXA_FW_HDRLEN         32
+
+#define MOXAMAJOR              172
+
+#define MAX_BOARDS             4       /* Don't change this value */
+#define MAX_PORTS_PER_BOARD    32      /* Don't change this value */
+#define MAX_PORTS              (MAX_BOARDS * MAX_PORTS_PER_BOARD)
+
+#define MOXA_IS_320(brd) ((brd)->boardType == MOXA_BOARD_C320_ISA || \
+               (brd)->boardType == MOXA_BOARD_C320_PCI)
+
+/*
+ *    Define the Moxa PCI vendor and device IDs.
+ */
+#define MOXA_BUS_TYPE_ISA      0
+#define MOXA_BUS_TYPE_PCI      1
+
+enum {
+       MOXA_BOARD_C218_PCI = 1,
+       MOXA_BOARD_C218_ISA,
+       MOXA_BOARD_C320_PCI,
+       MOXA_BOARD_C320_ISA,
+       MOXA_BOARD_CP204J,
+};
+
+static char *moxa_brdname[] =
+{
+       "C218 Turbo PCI series",
+       "C218 Turbo ISA series",
+       "C320 Turbo PCI series",
+       "C320 Turbo ISA series",
+       "CP-204J series",
+};
+
+#ifdef CONFIG_PCI
+static struct pci_device_id moxa_pcibrds[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C218),
+               .driver_data = MOXA_BOARD_C218_PCI },
+       { PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C320),
+               .driver_data = MOXA_BOARD_C320_PCI },
+       { PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP204J),
+               .driver_data = MOXA_BOARD_CP204J },
+       { 0 }
+};
+MODULE_DEVICE_TABLE(pci, moxa_pcibrds);
+#endif /* CONFIG_PCI */
+
+struct moxa_port;
+
+static struct moxa_board_conf {
+       int boardType;
+       int numPorts;
+       int busType;
+
+       unsigned int ready;
+
+       struct moxa_port *ports;
+
+       void __iomem *basemem;
+       void __iomem *intNdx;
+       void __iomem *intPend;
+       void __iomem *intTable;
+} moxa_boards[MAX_BOARDS];
+
+struct mxser_mstatus {
+       tcflag_t cflag;
+       int cts;
+       int dsr;
+       int ri;
+       int dcd;
+};
+
+struct moxaq_str {
+       int inq;
+       int outq;
+};
+
+struct moxa_port {
+       struct tty_port port;
+       struct moxa_board_conf *board;
+       void __iomem *tableAddr;
+
+       int type;
+       int cflag;
+       unsigned long statusflags;
+
+       u8 DCDState;            /* Protected by the port lock */
+       u8 lineCtrl;
+       u8 lowChkFlag;
+};
+
+struct mon_str {
+       int tick;
+       int rxcnt[MAX_PORTS];
+       int txcnt[MAX_PORTS];
+};
+
+/* statusflags */
+#define TXSTOPPED      1
+#define LOWWAIT        2
+#define EMPTYWAIT      3
+
+#define SERIAL_DO_RESTART
+
+#define WAKEUP_CHARS           256
+
+static int ttymajor = MOXAMAJOR;
+static struct mon_str moxaLog;
+static unsigned int moxaFuncTout = HZ / 2;
+static unsigned int moxaLowWaterChk;
+static DEFINE_MUTEX(moxa_openlock);
+static DEFINE_SPINLOCK(moxa_lock);
+
+static unsigned long baseaddr[MAX_BOARDS];
+static unsigned int type[MAX_BOARDS];
+static unsigned int numports[MAX_BOARDS];
+
+MODULE_AUTHOR("William Chen");
+MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("c218tunx.cod");
+MODULE_FIRMWARE("cp204unx.cod");
+MODULE_FIRMWARE("c320tunx.cod");
+
+module_param_array(type, uint, NULL, 0);
+MODULE_PARM_DESC(type, "card type: C218=2, C320=4");
+module_param_array(baseaddr, ulong, NULL, 0);
+MODULE_PARM_DESC(baseaddr, "base address");
+module_param_array(numports, uint, NULL, 0);
+MODULE_PARM_DESC(numports, "numports (ignored for C218)");
+
+module_param(ttymajor, int, 0);
+
+/*
+ * static functions:
+ */
+static int moxa_open(struct tty_struct *, struct file *);
+static void moxa_close(struct tty_struct *, struct file *);
+static int moxa_write(struct tty_struct *, const unsigned char *, int);
+static int moxa_write_room(struct tty_struct *);
+static void moxa_flush_buffer(struct tty_struct *);
+static int moxa_chars_in_buffer(struct tty_struct *);
+static void moxa_set_termios(struct tty_struct *, struct ktermios *);
+static void moxa_stop(struct tty_struct *);
+static void moxa_start(struct tty_struct *);
+static void moxa_hangup(struct tty_struct *);
+static int moxa_tiocmget(struct tty_struct *tty);
+static int moxa_tiocmset(struct tty_struct *tty,
+                        unsigned int set, unsigned int clear);
+static void moxa_poll(unsigned long);
+static void moxa_set_tty_param(struct tty_struct *, struct ktermios *);
+static void moxa_shutdown(struct tty_port *);
+static int moxa_carrier_raised(struct tty_port *);
+static void moxa_dtr_rts(struct tty_port *, int);
+/*
+ * moxa board interface functions:
+ */
+static void MoxaPortEnable(struct moxa_port *);
+static void MoxaPortDisable(struct moxa_port *);
+static int MoxaPortSetTermio(struct moxa_port *, struct ktermios *, speed_t);
+static int MoxaPortGetLineOut(struct moxa_port *, int *, int *);
+static void MoxaPortLineCtrl(struct moxa_port *, int, int);
+static void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int);
+static int MoxaPortLineStatus(struct moxa_port *);
+static void MoxaPortFlushData(struct moxa_port *, int);
+static int MoxaPortWriteData(struct tty_struct *, const unsigned char *, int);
+static int MoxaPortReadData(struct moxa_port *);
+static int MoxaPortTxQueue(struct moxa_port *);
+static int MoxaPortRxQueue(struct moxa_port *);
+static int MoxaPortTxFree(struct moxa_port *);
+static void MoxaPortTxDisable(struct moxa_port *);
+static void MoxaPortTxEnable(struct moxa_port *);
+static int moxa_get_serial_info(struct moxa_port *, struct serial_struct __user *);
+static int moxa_set_serial_info(struct moxa_port *, struct serial_struct __user *);
+static void MoxaSetFifo(struct moxa_port *port, int enable);
+
+/*
+ * I/O functions
+ */
+
+static DEFINE_SPINLOCK(moxafunc_lock);
+
+static void moxa_wait_finish(void __iomem *ofsAddr)
+{
+       unsigned long end = jiffies + moxaFuncTout;
+
+       while (readw(ofsAddr + FuncCode) != 0)
+               if (time_after(jiffies, end))
+                       return;
+       if (readw(ofsAddr + FuncCode) != 0 && printk_ratelimit())
+               printk(KERN_WARNING "moxa function expired\n");
+}
+
+static void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg)
+{
+        unsigned long flags;
+        spin_lock_irqsave(&moxafunc_lock, flags);
+       writew(arg, ofsAddr + FuncArg);
+       writew(cmd, ofsAddr + FuncCode);
+       moxa_wait_finish(ofsAddr);
+       spin_unlock_irqrestore(&moxafunc_lock, flags);
+}
+
+static int moxafuncret(void __iomem *ofsAddr, u16 cmd, u16 arg)
+{
+        unsigned long flags;
+        u16 ret;
+        spin_lock_irqsave(&moxafunc_lock, flags);
+       writew(arg, ofsAddr + FuncArg);
+       writew(cmd, ofsAddr + FuncCode);
+       moxa_wait_finish(ofsAddr);
+       ret = readw(ofsAddr + FuncArg);
+       spin_unlock_irqrestore(&moxafunc_lock, flags);
+       return ret;
+}
+
+static void moxa_low_water_check(void __iomem *ofsAddr)
+{
+       u16 rptr, wptr, mask, len;
+
+       if (readb(ofsAddr + FlagStat) & Xoff_state) {
+               rptr = readw(ofsAddr + RXrptr);
+               wptr = readw(ofsAddr + RXwptr);
+               mask = readw(ofsAddr + RX_mask);
+               len = (wptr - rptr) & mask;
+               if (len <= Low_water)
+                       moxafunc(ofsAddr, FC_SendXon, 0);
+       }
+}
+
+/*
+ * TTY operations
+ */
+
+static int moxa_ioctl(struct tty_struct *tty,
+                     unsigned int cmd, unsigned long arg)
+{
+       struct moxa_port *ch = tty->driver_data;
+       void __user *argp = (void __user *)arg;
+       int status, ret = 0;
+
+       if (tty->index == MAX_PORTS) {
+               if (cmd != MOXA_GETDATACOUNT && cmd != MOXA_GET_IOQUEUE &&
+                               cmd != MOXA_GETMSTATUS)
+                       return -EINVAL;
+       } else if (!ch)
+               return -ENODEV;
+
+       switch (cmd) {
+       case MOXA_GETDATACOUNT:
+               moxaLog.tick = jiffies;
+               if (copy_to_user(argp, &moxaLog, sizeof(moxaLog)))
+                       ret = -EFAULT;
+               break;
+       case MOXA_FLUSH_QUEUE:
+               MoxaPortFlushData(ch, arg);
+               break;
+       case MOXA_GET_IOQUEUE: {
+               struct moxaq_str __user *argm = argp;
+               struct moxaq_str tmp;
+               struct moxa_port *p;
+               unsigned int i, j;
+
+               for (i = 0; i < MAX_BOARDS; i++) {
+                       p = moxa_boards[i].ports;
+                       for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
+                               memset(&tmp, 0, sizeof(tmp));
+                               spin_lock_bh(&moxa_lock);
+                               if (moxa_boards[i].ready) {
+                                       tmp.inq = MoxaPortRxQueue(p);
+                                       tmp.outq = MoxaPortTxQueue(p);
+                               }
+                               spin_unlock_bh(&moxa_lock);
+                               if (copy_to_user(argm, &tmp, sizeof(tmp)))
+                                       return -EFAULT;
+                       }
+               }
+               break;
+       } case MOXA_GET_OQUEUE:
+               status = MoxaPortTxQueue(ch);
+               ret = put_user(status, (unsigned long __user *)argp);
+               break;
+       case MOXA_GET_IQUEUE:
+               status = MoxaPortRxQueue(ch);
+               ret = put_user(status, (unsigned long __user *)argp);
+               break;
+       case MOXA_GETMSTATUS: {
+               struct mxser_mstatus __user *argm = argp;
+               struct mxser_mstatus tmp;
+               struct moxa_port *p;
+               unsigned int i, j;
+
+               for (i = 0; i < MAX_BOARDS; i++) {
+                       p = moxa_boards[i].ports;
+                       for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
+                               struct tty_struct *ttyp;
+                               memset(&tmp, 0, sizeof(tmp));
+                               spin_lock_bh(&moxa_lock);
+                               if (!moxa_boards[i].ready) {
+                                       spin_unlock_bh(&moxa_lock);
+                                       goto copy;
+                                }
+
+                               status = MoxaPortLineStatus(p);
+                               spin_unlock_bh(&moxa_lock);
+
+                               if (status & 1)
+                                       tmp.cts = 1;
+                               if (status & 2)
+                                       tmp.dsr = 1;
+                               if (status & 4)
+                                       tmp.dcd = 1;
+
+                               ttyp = tty_port_tty_get(&p->port);
+                               if (!ttyp || !ttyp->termios)
+                                       tmp.cflag = p->cflag;
+                               else
+                                       tmp.cflag = ttyp->termios->c_cflag;
+                               tty_kref_put(tty);
+copy:
+                               if (copy_to_user(argm, &tmp, sizeof(tmp)))
+                                       return -EFAULT;
+                       }
+               }
+               break;
+       }
+       case TIOCGSERIAL:
+               mutex_lock(&ch->port.mutex);
+               ret = moxa_get_serial_info(ch, argp);
+               mutex_unlock(&ch->port.mutex);
+               break;
+       case TIOCSSERIAL:
+               mutex_lock(&ch->port.mutex);
+               ret = moxa_set_serial_info(ch, argp);
+               mutex_unlock(&ch->port.mutex);
+               break;
+       default:
+               ret = -ENOIOCTLCMD;
+       }
+       return ret;
+}
+
+static int moxa_break_ctl(struct tty_struct *tty, int state)
+{
+       struct moxa_port *port = tty->driver_data;
+
+       moxafunc(port->tableAddr, state ? FC_SendBreak : FC_StopBreak,
+                       Magic_code);
+       return 0;
+}
+
+static const struct tty_operations moxa_ops = {
+       .open = moxa_open,
+       .close = moxa_close,
+       .write = moxa_write,
+       .write_room = moxa_write_room,
+       .flush_buffer = moxa_flush_buffer,
+       .chars_in_buffer = moxa_chars_in_buffer,
+       .ioctl = moxa_ioctl,
+       .set_termios = moxa_set_termios,
+       .stop = moxa_stop,
+       .start = moxa_start,
+       .hangup = moxa_hangup,
+       .break_ctl = moxa_break_ctl,
+       .tiocmget = moxa_tiocmget,
+       .tiocmset = moxa_tiocmset,
+};
+
+static const struct tty_port_operations moxa_port_ops = {
+       .carrier_raised = moxa_carrier_raised,
+       .dtr_rts = moxa_dtr_rts,
+       .shutdown = moxa_shutdown,
+};
+
+static struct tty_driver *moxaDriver;
+static DEFINE_TIMER(moxaTimer, moxa_poll, 0, 0);
+
+/*
+ * HW init
+ */
+
+static int moxa_check_fw_model(struct moxa_board_conf *brd, u8 model)
+{
+       switch (brd->boardType) {
+       case MOXA_BOARD_C218_ISA:
+       case MOXA_BOARD_C218_PCI:
+               if (model != 1)
+                       goto err;
+               break;
+       case MOXA_BOARD_CP204J:
+               if (model != 3)
+                       goto err;
+               break;
+       default:
+               if (model != 2)
+                       goto err;
+               break;
+       }
+       return 0;
+err:
+       return -EINVAL;
+}
+
+static int moxa_check_fw(const void *ptr)
+{
+       const __le16 *lptr = ptr;
+
+       if (*lptr != cpu_to_le16(0x7980))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int moxa_load_bios(struct moxa_board_conf *brd, const u8 *buf,
+               size_t len)
+{
+       void __iomem *baseAddr = brd->basemem;
+       u16 tmp;
+
+       writeb(HW_reset, baseAddr + Control_reg);       /* reset */
+       msleep(10);
+       memset_io(baseAddr, 0, 4096);
+       memcpy_toio(baseAddr, buf, len);        /* download BIOS */
+       writeb(0, baseAddr + Control_reg);      /* restart */
+
+       msleep(2000);
+
+       switch (brd->boardType) {
+       case MOXA_BOARD_C218_ISA:
+       case MOXA_BOARD_C218_PCI:
+               tmp = readw(baseAddr + C218_key);
+               if (tmp != C218_KeyCode)
+                       goto err;
+               break;
+       case MOXA_BOARD_CP204J:
+               tmp = readw(baseAddr + C218_key);
+               if (tmp != CP204J_KeyCode)
+                       goto err;
+               break;
+       default:
+               tmp = readw(baseAddr + C320_key);
+               if (tmp != C320_KeyCode)
+                       goto err;
+               tmp = readw(baseAddr + C320_status);
+               if (tmp != STS_init) {
+                       printk(KERN_ERR "MOXA: bios upload failed -- CPU/Basic "
+                                       "module not found\n");
+                       return -EIO;
+               }
+               break;
+       }
+
+       return 0;
+err:
+       printk(KERN_ERR "MOXA: bios upload failed -- board not found\n");
+       return -EIO;
+}
+
+static int moxa_load_320b(struct moxa_board_conf *brd, const u8 *ptr,
+               size_t len)
+{
+       void __iomem *baseAddr = brd->basemem;
+
+       if (len < 7168) {
+               printk(KERN_ERR "MOXA: invalid 320 bios -- too short\n");
+               return -EINVAL;
+       }
+
+       writew(len - 7168 - 2, baseAddr + C320bapi_len);
+       writeb(1, baseAddr + Control_reg);      /* Select Page 1 */
+       memcpy_toio(baseAddr + DynPage_addr, ptr, 7168);
+       writeb(2, baseAddr + Control_reg);      /* Select Page 2 */
+       memcpy_toio(baseAddr + DynPage_addr, ptr + 7168, len - 7168);
+
+       return 0;
+}
+
+static int moxa_real_load_code(struct moxa_board_conf *brd, const void *ptr,
+               size_t len)
+{
+       void __iomem *baseAddr = brd->basemem;
+       const __le16 *uptr = ptr;
+       size_t wlen, len2, j;
+       unsigned long key, loadbuf, loadlen, checksum, checksum_ok;
+       unsigned int i, retry;
+       u16 usum, keycode;
+
+       keycode = (brd->boardType == MOXA_BOARD_CP204J) ? CP204J_KeyCode :
+                               C218_KeyCode;
+
+       switch (brd->boardType) {
+       case MOXA_BOARD_CP204J:
+       case MOXA_BOARD_C218_ISA:
+       case MOXA_BOARD_C218_PCI:
+               key = C218_key;
+               loadbuf = C218_LoadBuf;
+               loadlen = C218DLoad_len;
+               checksum = C218check_sum;
+               checksum_ok = C218chksum_ok;
+               break;
+       default:
+               key = C320_key;
+               keycode = C320_KeyCode;
+               loadbuf = C320_LoadBuf;
+               loadlen = C320DLoad_len;
+               checksum = C320check_sum;
+               checksum_ok = C320chksum_ok;
+               break;
+       }
+
+       usum = 0;
+       wlen = len >> 1;
+       for (i = 0; i < wlen; i++)
+               usum += le16_to_cpu(uptr[i]);
+       retry = 0;
+       do {
+               wlen = len >> 1;
+               j = 0;
+               while (wlen) {
+                       len2 = (wlen > 2048) ? 2048 : wlen;
+                       wlen -= len2;
+                       memcpy_toio(baseAddr + loadbuf, ptr + j, len2 << 1);
+                       j += len2 << 1;
+
+                       writew(len2, baseAddr + loadlen);
+                       writew(0, baseAddr + key);
+                       for (i = 0; i < 100; i++) {
+                               if (readw(baseAddr + key) == keycode)
+                                       break;
+                               msleep(10);
+                       }
+                       if (readw(baseAddr + key) != keycode)
+                               return -EIO;
+               }
+               writew(0, baseAddr + loadlen);
+               writew(usum, baseAddr + checksum);
+               writew(0, baseAddr + key);
+               for (i = 0; i < 100; i++) {
+                       if (readw(baseAddr + key) == keycode)
+                               break;
+                       msleep(10);
+               }
+               retry++;
+       } while ((readb(baseAddr + checksum_ok) != 1) && (retry < 3));
+       if (readb(baseAddr + checksum_ok) != 1)
+               return -EIO;
+
+       writew(0, baseAddr + key);
+       for (i = 0; i < 600; i++) {
+               if (readw(baseAddr + Magic_no) == Magic_code)
+                       break;
+               msleep(10);
+       }
+       if (readw(baseAddr + Magic_no) != Magic_code)
+               return -EIO;
+
+       if (MOXA_IS_320(brd)) {
+               if (brd->busType == MOXA_BUS_TYPE_PCI) {        /* ASIC board */
+                       writew(0x3800, baseAddr + TMS320_PORT1);
+                       writew(0x3900, baseAddr + TMS320_PORT2);
+                       writew(28499, baseAddr + TMS320_CLOCK);
+               } else {
+                       writew(0x3200, baseAddr + TMS320_PORT1);
+                       writew(0x3400, baseAddr + TMS320_PORT2);
+                       writew(19999, baseAddr + TMS320_CLOCK);
+               }
+       }
+       writew(1, baseAddr + Disable_IRQ);
+       writew(0, baseAddr + Magic_no);
+       for (i = 0; i < 500; i++) {
+               if (readw(baseAddr + Magic_no) == Magic_code)
+                       break;
+               msleep(10);
+       }
+       if (readw(baseAddr + Magic_no) != Magic_code)
+               return -EIO;
+
+       if (MOXA_IS_320(brd)) {
+               j = readw(baseAddr + Module_cnt);
+               if (j <= 0)
+                       return -EIO;
+               brd->numPorts = j * 8;
+               writew(j, baseAddr + Module_no);
+               writew(0, baseAddr + Magic_no);
+               for (i = 0; i < 600; i++) {
+                       if (readw(baseAddr + Magic_no) == Magic_code)
+                               break;
+                       msleep(10);
+               }
+               if (readw(baseAddr + Magic_no) != Magic_code)
+                       return -EIO;
+       }
+       brd->intNdx = baseAddr + IRQindex;
+       brd->intPend = baseAddr + IRQpending;
+       brd->intTable = baseAddr + IRQtable;
+
+       return 0;
+}
+
+static int moxa_load_code(struct moxa_board_conf *brd, const void *ptr,
+               size_t len)
+{
+       void __iomem *ofsAddr, *baseAddr = brd->basemem;
+       struct moxa_port *port;
+       int retval, i;
+
+       if (len % 2) {
+               printk(KERN_ERR "MOXA: bios length is not even\n");
+               return -EINVAL;
+       }
+
+       retval = moxa_real_load_code(brd, ptr, len); /* may change numPorts */
+       if (retval)
+               return retval;
+
+       switch (brd->boardType) {
+       case MOXA_BOARD_C218_ISA:
+       case MOXA_BOARD_C218_PCI:
+       case MOXA_BOARD_CP204J:
+               port = brd->ports;
+               for (i = 0; i < brd->numPorts; i++, port++) {
+                       port->board = brd;
+                       port->DCDState = 0;
+                       port->tableAddr = baseAddr + Extern_table +
+                                       Extern_size * i;
+                       ofsAddr = port->tableAddr;
+                       writew(C218rx_mask, ofsAddr + RX_mask);
+                       writew(C218tx_mask, ofsAddr + TX_mask);
+                       writew(C218rx_spage + i * C218buf_pageno, ofsAddr + Page_rxb);
+                       writew(readw(ofsAddr + Page_rxb) + C218rx_pageno, ofsAddr + EndPage_rxb);
+
+                       writew(C218tx_spage + i * C218buf_pageno, ofsAddr + Page_txb);
+                       writew(readw(ofsAddr + Page_txb) + C218tx_pageno, ofsAddr + EndPage_txb);
+
+               }
+               break;
+       default:
+               port = brd->ports;
+               for (i = 0; i < brd->numPorts; i++, port++) {
+                       port->board = brd;
+                       port->DCDState = 0;
+                       port->tableAddr = baseAddr + Extern_table +
+                                       Extern_size * i;
+                       ofsAddr = port->tableAddr;
+                       switch (brd->numPorts) {
+                       case 8:
+                               writew(C320p8rx_mask, ofsAddr + RX_mask);
+                               writew(C320p8tx_mask, ofsAddr + TX_mask);
+                               writew(C320p8rx_spage + i * C320p8buf_pgno, ofsAddr + Page_rxb);
+                               writew(readw(ofsAddr + Page_rxb) + C320p8rx_pgno, ofsAddr + EndPage_rxb);
+                               writew(C320p8tx_spage + i * C320p8buf_pgno, ofsAddr + Page_txb);
+                               writew(readw(ofsAddr + Page_txb) + C320p8tx_pgno, ofsAddr + EndPage_txb);
+
+                               break;
+                       case 16:
+                               writew(C320p16rx_mask, ofsAddr + RX_mask);
+                               writew(C320p16tx_mask, ofsAddr + TX_mask);
+                               writew(C320p16rx_spage + i * C320p16buf_pgno, ofsAddr + Page_rxb);
+                               writew(readw(ofsAddr + Page_rxb) + C320p16rx_pgno, ofsAddr + EndPage_rxb);
+                               writew(C320p16tx_spage + i * C320p16buf_pgno, ofsAddr + Page_txb);
+                               writew(readw(ofsAddr + Page_txb) + C320p16tx_pgno, ofsAddr + EndPage_txb);
+                               break;
+
+                       case 24:
+                               writew(C320p24rx_mask, ofsAddr + RX_mask);
+                               writew(C320p24tx_mask, ofsAddr + TX_mask);
+                               writew(C320p24rx_spage + i * C320p24buf_pgno, ofsAddr + Page_rxb);
+                               writew(readw(ofsAddr + Page_rxb) + C320p24rx_pgno, ofsAddr + EndPage_rxb);
+                               writew(C320p24tx_spage + i * C320p24buf_pgno, ofsAddr + Page_txb);
+                               writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb);
+                               break;
+                       case 32:
+                               writew(C320p32rx_mask, ofsAddr + RX_mask);
+                               writew(C320p32tx_mask, ofsAddr + TX_mask);
+                               writew(C320p32tx_ofs, ofsAddr + Ofs_txb);
+                               writew(C320p32rx_spage + i * C320p32buf_pgno, ofsAddr + Page_rxb);
+                               writew(readb(ofsAddr + Page_rxb), ofsAddr + EndPage_rxb);
+                               writew(C320p32tx_spage + i * C320p32buf_pgno, ofsAddr + Page_txb);
+                               writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb);
+                               break;
+                       }
+               }
+               break;
+       }
+       return 0;
+}
+
+static int moxa_load_fw(struct moxa_board_conf *brd, const struct firmware *fw)
+{
+       const void *ptr = fw->data;
+       char rsn[64];
+       u16 lens[5];
+       size_t len;
+       unsigned int a, lenp, lencnt;
+       int ret = -EINVAL;
+       struct {
+               __le32 magic;   /* 0x34303430 */
+               u8 reserved1[2];
+               u8 type;        /* UNIX = 3 */
+               u8 model;       /* C218T=1, C320T=2, CP204=3 */
+               u8 reserved2[8];
+               __le16 len[5];
+       } const *hdr = ptr;
+
+       BUILD_BUG_ON(ARRAY_SIZE(hdr->len) != ARRAY_SIZE(lens));
+
+       if (fw->size < MOXA_FW_HDRLEN) {
+               strcpy(rsn, "too short (even header won't fit)");
+               goto err;
+       }
+       if (hdr->magic != cpu_to_le32(0x30343034)) {
+               sprintf(rsn, "bad magic: %.8x", le32_to_cpu(hdr->magic));
+               goto err;
+       }
+       if (hdr->type != 3) {
+               sprintf(rsn, "not for linux, type is %u", hdr->type);
+               goto err;
+       }
+       if (moxa_check_fw_model(brd, hdr->model)) {
+               sprintf(rsn, "not for this card, model is %u", hdr->model);
+               goto err;
+       }
+
+       len = MOXA_FW_HDRLEN;
+       lencnt = hdr->model == 2 ? 5 : 3;
+       for (a = 0; a < ARRAY_SIZE(lens); a++) {
+               lens[a] = le16_to_cpu(hdr->len[a]);
+               if (lens[a] && len + lens[a] <= fw->size &&
+                               moxa_check_fw(&fw->data[len]))
+                       printk(KERN_WARNING "MOXA firmware: unexpected input "
+                               "at offset %u, but going on\n", (u32)len);
+               if (!lens[a] && a < lencnt) {
+                       sprintf(rsn, "too few entries in fw file");
+                       goto err;
+               }
+               len += lens[a];
+       }
+
+       if (len != fw->size) {
+               sprintf(rsn, "bad length: %u (should be %u)", (u32)fw->size,
+                               (u32)len);
+               goto err;
+       }
+
+       ptr += MOXA_FW_HDRLEN;
+       lenp = 0; /* bios */
+
+       strcpy(rsn, "read above");
+
+       ret = moxa_load_bios(brd, ptr, lens[lenp]);
+       if (ret)
+               goto err;
+
+       /* we skip the tty section (lens[1]), since we don't need it */
+       ptr += lens[lenp] + lens[lenp + 1];
+       lenp += 2; /* comm */
+
+       if (hdr->model == 2) {
+               ret = moxa_load_320b(brd, ptr, lens[lenp]);
+               if (ret)
+                       goto err;
+               /* skip another tty */
+               ptr += lens[lenp] + lens[lenp + 1];
+               lenp += 2;
+       }
+
+       ret = moxa_load_code(brd, ptr, lens[lenp]);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       printk(KERN_ERR "firmware failed to load, reason: %s\n", rsn);
+       return ret;
+}
+
+static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev)
+{
+       const struct firmware *fw;
+       const char *file;
+       struct moxa_port *p;
+       unsigned int i;
+       int ret;
+
+       brd->ports = kcalloc(MAX_PORTS_PER_BOARD, sizeof(*brd->ports),
+                       GFP_KERNEL);
+       if (brd->ports == NULL) {
+               printk(KERN_ERR "cannot allocate memory for ports\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       for (i = 0, p = brd->ports; i < MAX_PORTS_PER_BOARD; i++, p++) {
+               tty_port_init(&p->port);
+               p->port.ops = &moxa_port_ops;
+               p->type = PORT_16550A;
+               p->cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
+       }
+
+       switch (brd->boardType) {
+       case MOXA_BOARD_C218_ISA:
+       case MOXA_BOARD_C218_PCI:
+               file = "c218tunx.cod";
+               break;
+       case MOXA_BOARD_CP204J:
+               file = "cp204unx.cod";
+               break;
+       default:
+               file = "c320tunx.cod";
+               break;
+       }
+
+       ret = request_firmware(&fw, file, dev);
+       if (ret) {
+               printk(KERN_ERR "MOXA: request_firmware failed. Make sure "
+                               "you've placed '%s' file into your firmware "
+                               "loader directory (e.g. /lib/firmware)\n",
+                               file);
+               goto err_free;
+       }
+
+       ret = moxa_load_fw(brd, fw);
+
+       release_firmware(fw);
+
+       if (ret)
+               goto err_free;
+
+       spin_lock_bh(&moxa_lock);
+       brd->ready = 1;
+       if (!timer_pending(&moxaTimer))
+               mod_timer(&moxaTimer, jiffies + HZ / 50);
+       spin_unlock_bh(&moxa_lock);
+
+       return 0;
+err_free:
+       kfree(brd->ports);
+err:
+       return ret;
+}
+
+static void moxa_board_deinit(struct moxa_board_conf *brd)
+{
+       unsigned int a, opened;
+
+       mutex_lock(&moxa_openlock);
+       spin_lock_bh(&moxa_lock);
+       brd->ready = 0;
+       spin_unlock_bh(&moxa_lock);
+
+       /* pci hot-un-plug support */
+       for (a = 0; a < brd->numPorts; a++)
+               if (brd->ports[a].port.flags & ASYNC_INITIALIZED) {
+                       struct tty_struct *tty = tty_port_tty_get(
+                                               &brd->ports[a].port);
+                       if (tty) {
+                               tty_hangup(tty);
+                               tty_kref_put(tty);
+                       }
+               }
+       while (1) {
+               opened = 0;
+               for (a = 0; a < brd->numPorts; a++)
+                       if (brd->ports[a].port.flags & ASYNC_INITIALIZED)
+                               opened++;
+               mutex_unlock(&moxa_openlock);
+               if (!opened)
+                       break;
+               msleep(50);
+               mutex_lock(&moxa_openlock);
+       }
+
+       iounmap(brd->basemem);
+       brd->basemem = NULL;
+       kfree(brd->ports);
+}
+
+#ifdef CONFIG_PCI
+static int __devinit moxa_pci_probe(struct pci_dev *pdev,
+               const struct pci_device_id *ent)
+{
+       struct moxa_board_conf *board;
+       unsigned int i;
+       int board_type = ent->driver_data;
+       int retval;
+
+       retval = pci_enable_device(pdev);
+       if (retval) {
+               dev_err(&pdev->dev, "can't enable pci device\n");
+               goto err;
+       }
+
+       for (i = 0; i < MAX_BOARDS; i++)
+               if (moxa_boards[i].basemem == NULL)
+                       break;
+
+       retval = -ENODEV;
+       if (i >= MAX_BOARDS) {
+               dev_warn(&pdev->dev, "more than %u MOXA Intellio family boards "
+                               "found. Board is ignored.\n", MAX_BOARDS);
+               goto err;
+       }
+
+       board = &moxa_boards[i];
+
+       retval = pci_request_region(pdev, 2, "moxa-base");
+       if (retval) {
+               dev_err(&pdev->dev, "can't request pci region 2\n");
+               goto err;
+       }
+
+       board->basemem = ioremap_nocache(pci_resource_start(pdev, 2), 0x4000);
+       if (board->basemem == NULL) {
+               dev_err(&pdev->dev, "can't remap io space 2\n");
+               goto err_reg;
+       }
+
+       board->boardType = board_type;
+       switch (board_type) {
+       case MOXA_BOARD_C218_ISA:
+       case MOXA_BOARD_C218_PCI:
+               board->numPorts = 8;
+               break;
+
+       case MOXA_BOARD_CP204J:
+               board->numPorts = 4;
+               break;
+       default:
+               board->numPorts = 0;
+               break;
+       }
+       board->busType = MOXA_BUS_TYPE_PCI;
+
+       retval = moxa_init_board(board, &pdev->dev);
+       if (retval)
+               goto err_base;
+
+       pci_set_drvdata(pdev, board);
+
+       dev_info(&pdev->dev, "board '%s' ready (%u ports, firmware loaded)\n",
+                       moxa_brdname[board_type - 1], board->numPorts);
+
+       return 0;
+err_base:
+       iounmap(board->basemem);
+       board->basemem = NULL;
+err_reg:
+       pci_release_region(pdev, 2);
+err:
+       return retval;
+}
+
+static void __devexit moxa_pci_remove(struct pci_dev *pdev)
+{
+       struct moxa_board_conf *brd = pci_get_drvdata(pdev);
+
+       moxa_board_deinit(brd);
+
+       pci_release_region(pdev, 2);
+}
+
+static struct pci_driver moxa_pci_driver = {
+       .name = "moxa",
+       .id_table = moxa_pcibrds,
+       .probe = moxa_pci_probe,
+       .remove = __devexit_p(moxa_pci_remove)
+};
+#endif /* CONFIG_PCI */
+
+static int __init moxa_init(void)
+{
+       unsigned int isabrds = 0;
+       int retval = 0;
+       struct moxa_board_conf *brd = moxa_boards;
+       unsigned int i;
+
+       printk(KERN_INFO "MOXA Intellio family driver version %s\n",
+                       MOXA_VERSION);
+       moxaDriver = alloc_tty_driver(MAX_PORTS + 1);
+       if (!moxaDriver)
+               return -ENOMEM;
+
+       moxaDriver->owner = THIS_MODULE;
+       moxaDriver->name = "ttyMX";
+       moxaDriver->major = ttymajor;
+       moxaDriver->minor_start = 0;
+       moxaDriver->type = TTY_DRIVER_TYPE_SERIAL;
+       moxaDriver->subtype = SERIAL_TYPE_NORMAL;
+       moxaDriver->init_termios = tty_std_termios;
+       moxaDriver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
+       moxaDriver->init_termios.c_ispeed = 9600;
+       moxaDriver->init_termios.c_ospeed = 9600;
+       moxaDriver->flags = TTY_DRIVER_REAL_RAW;
+       tty_set_operations(moxaDriver, &moxa_ops);
+
+       if (tty_register_driver(moxaDriver)) {
+               printk(KERN_ERR "can't register MOXA Smartio tty driver!\n");
+               put_tty_driver(moxaDriver);
+               return -1;
+       }
+
+       /* Find the boards defined from module args. */
+
+       for (i = 0; i < MAX_BOARDS; i++) {
+               if (!baseaddr[i])
+                       break;
+               if (type[i] == MOXA_BOARD_C218_ISA ||
+                               type[i] == MOXA_BOARD_C320_ISA) {
+                       pr_debug("Moxa board %2d: %s board(baseAddr=%lx)\n",
+                                       isabrds + 1, moxa_brdname[type[i] - 1],
+                                       baseaddr[i]);
+                       brd->boardType = type[i];
+                       brd->numPorts = type[i] == MOXA_BOARD_C218_ISA ? 8 :
+                                       numports[i];
+                       brd->busType = MOXA_BUS_TYPE_ISA;
+                       brd->basemem = ioremap_nocache(baseaddr[i], 0x4000);
+                       if (!brd->basemem) {
+                               printk(KERN_ERR "MOXA: can't remap %lx\n",
+                                               baseaddr[i]);
+                               continue;
+                       }
+                       if (moxa_init_board(brd, NULL)) {
+                               iounmap(brd->basemem);
+                               brd->basemem = NULL;
+                               continue;
+                       }
+
+                       printk(KERN_INFO "MOXA isa board found at 0x%.8lu and "
+                                       "ready (%u ports, firmware loaded)\n",
+                                       baseaddr[i], brd->numPorts);
+
+                       brd++;
+                       isabrds++;
+               }
+       }
+
+#ifdef CONFIG_PCI
+       retval = pci_register_driver(&moxa_pci_driver);
+       if (retval) {
+               printk(KERN_ERR "Can't register MOXA pci driver!\n");
+               if (isabrds)
+                       retval = 0;
+       }
+#endif
+
+       return retval;
+}
+
+static void __exit moxa_exit(void)
+{
+       unsigned int i;
+
+#ifdef CONFIG_PCI
+       pci_unregister_driver(&moxa_pci_driver);
+#endif
+
+       for (i = 0; i < MAX_BOARDS; i++) /* ISA boards */
+               if (moxa_boards[i].ready)
+                       moxa_board_deinit(&moxa_boards[i]);
+
+       del_timer_sync(&moxaTimer);
+
+       if (tty_unregister_driver(moxaDriver))
+               printk(KERN_ERR "Couldn't unregister MOXA Intellio family "
+                               "serial driver\n");
+       put_tty_driver(moxaDriver);
+}
+
+module_init(moxa_init);
+module_exit(moxa_exit);
+
+static void moxa_shutdown(struct tty_port *port)
+{
+       struct moxa_port *ch = container_of(port, struct moxa_port, port);
+        MoxaPortDisable(ch);
+       MoxaPortFlushData(ch, 2);
+       clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
+}
+
+static int moxa_carrier_raised(struct tty_port *port)
+{
+       struct moxa_port *ch = container_of(port, struct moxa_port, port);
+       int dcd;
+
+       spin_lock_irq(&port->lock);
+       dcd = ch->DCDState;
+       spin_unlock_irq(&port->lock);
+       return dcd;
+}
+
+static void moxa_dtr_rts(struct tty_port *port, int onoff)
+{
+       struct moxa_port *ch = container_of(port, struct moxa_port, port);
+       MoxaPortLineCtrl(ch, onoff, onoff);
+}
+
+
+static int moxa_open(struct tty_struct *tty, struct file *filp)
+{
+       struct moxa_board_conf *brd;
+       struct moxa_port *ch;
+       int port;
+       int retval;
+
+       port = tty->index;
+       if (port == MAX_PORTS) {
+               return capable(CAP_SYS_ADMIN) ? 0 : -EPERM;
+       }
+       if (mutex_lock_interruptible(&moxa_openlock))
+               return -ERESTARTSYS;
+       brd = &moxa_boards[port / MAX_PORTS_PER_BOARD];
+       if (!brd->ready) {
+               mutex_unlock(&moxa_openlock);
+               return -ENODEV;
+       }
+
+       if (port % MAX_PORTS_PER_BOARD >= brd->numPorts) {
+               mutex_unlock(&moxa_openlock);
+               return -ENODEV;
+       }
+
+       ch = &brd->ports[port % MAX_PORTS_PER_BOARD];
+       ch->port.count++;
+       tty->driver_data = ch;
+       tty_port_tty_set(&ch->port, tty);
+       mutex_lock(&ch->port.mutex);
+       if (!(ch->port.flags & ASYNC_INITIALIZED)) {
+               ch->statusflags = 0;
+               moxa_set_tty_param(tty, tty->termios);
+               MoxaPortLineCtrl(ch, 1, 1);
+               MoxaPortEnable(ch);
+               MoxaSetFifo(ch, ch->type == PORT_16550A);
+               ch->port.flags |= ASYNC_INITIALIZED;
+       }
+       mutex_unlock(&ch->port.mutex);
+       mutex_unlock(&moxa_openlock);
+
+       retval = tty_port_block_til_ready(&ch->port, tty, filp);
+       if (retval == 0)
+               set_bit(ASYNCB_NORMAL_ACTIVE, &ch->port.flags);
+       return retval;
+}
+
+static void moxa_close(struct tty_struct *tty, struct file *filp)
+{
+       struct moxa_port *ch = tty->driver_data;
+       ch->cflag = tty->termios->c_cflag;
+       tty_port_close(&ch->port, tty, filp);
+}
+
+static int moxa_write(struct tty_struct *tty,
+                     const unsigned char *buf, int count)
+{
+       struct moxa_port *ch = tty->driver_data;
+       int len;
+
+       if (ch == NULL)
+               return 0;
+
+       spin_lock_bh(&moxa_lock);
+       len = MoxaPortWriteData(tty, buf, count);
+       spin_unlock_bh(&moxa_lock);
+
+       set_bit(LOWWAIT, &ch->statusflags);
+       return len;
+}
+
+static int moxa_write_room(struct tty_struct *tty)
+{
+       struct moxa_port *ch;
+
+       if (tty->stopped)
+               return 0;
+       ch = tty->driver_data;
+       if (ch == NULL)
+               return 0;
+       return MoxaPortTxFree(ch);
+}
+
+static void moxa_flush_buffer(struct tty_struct *tty)
+{
+       struct moxa_port *ch = tty->driver_data;
+
+       if (ch == NULL)
+               return;
+       MoxaPortFlushData(ch, 1);
+       tty_wakeup(tty);
+}
+
+static int moxa_chars_in_buffer(struct tty_struct *tty)
+{
+       struct moxa_port *ch = tty->driver_data;
+       int chars;
+
+       chars = MoxaPortTxQueue(ch);
+       if (chars)
+               /*
+                * Make it possible to wakeup anything waiting for output
+                * in tty_ioctl.c, etc.
+                */
+               set_bit(EMPTYWAIT, &ch->statusflags);
+       return chars;
+}
+
+static int moxa_tiocmget(struct tty_struct *tty)
+{
+       struct moxa_port *ch = tty->driver_data;
+       int flag = 0, dtr, rts;
+
+       MoxaPortGetLineOut(ch, &dtr, &rts);
+       if (dtr)
+               flag |= TIOCM_DTR;
+       if (rts)
+               flag |= TIOCM_RTS;
+       dtr = MoxaPortLineStatus(ch);
+       if (dtr & 1)
+               flag |= TIOCM_CTS;
+       if (dtr & 2)
+               flag |= TIOCM_DSR;
+       if (dtr & 4)
+               flag |= TIOCM_CD;
+       return flag;
+}
+
+static int moxa_tiocmset(struct tty_struct *tty,
+                        unsigned int set, unsigned int clear)
+{
+       struct moxa_port *ch;
+       int port;
+       int dtr, rts;
+
+       port = tty->index;
+       mutex_lock(&moxa_openlock);
+       ch = tty->driver_data;
+       if (!ch) {
+               mutex_unlock(&moxa_openlock);
+               return -EINVAL;
+       }
+
+       MoxaPortGetLineOut(ch, &dtr, &rts);
+       if (set & TIOCM_RTS)
+               rts = 1;
+       if (set & TIOCM_DTR)
+               dtr = 1;
+       if (clear & TIOCM_RTS)
+               rts = 0;
+       if (clear & TIOCM_DTR)
+               dtr = 0;
+       MoxaPortLineCtrl(ch, dtr, rts);
+       mutex_unlock(&moxa_openlock);
+       return 0;
+}
+
+static void moxa_set_termios(struct tty_struct *tty,
+               struct ktermios *old_termios)
+{
+       struct moxa_port *ch = tty->driver_data;
+
+       if (ch == NULL)
+               return;
+       moxa_set_tty_param(tty, old_termios);
+       if (!(old_termios->c_cflag & CLOCAL) && C_CLOCAL(tty))
+               wake_up_interruptible(&ch->port.open_wait);
+}
+
+static void moxa_stop(struct tty_struct *tty)
+{
+       struct moxa_port *ch = tty->driver_data;
+
+       if (ch == NULL)
+               return;
+       MoxaPortTxDisable(ch);
+       set_bit(TXSTOPPED, &ch->statusflags);
+}
+
+
+static void moxa_start(struct tty_struct *tty)
+{
+       struct moxa_port *ch = tty->driver_data;
+
+       if (ch == NULL)
+               return;
+
+       if (!(ch->statusflags & TXSTOPPED))
+               return;
+
+       MoxaPortTxEnable(ch);
+       clear_bit(TXSTOPPED, &ch->statusflags);
+}
+
+static void moxa_hangup(struct tty_struct *tty)
+{
+       struct moxa_port *ch = tty->driver_data;
+       tty_port_hangup(&ch->port);
+}
+
+static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd)
+{
+       struct tty_struct *tty;
+       unsigned long flags;
+       dcd = !!dcd;
+
+       spin_lock_irqsave(&p->port.lock, flags);
+       if (dcd != p->DCDState) {
+               p->DCDState = dcd;
+               spin_unlock_irqrestore(&p->port.lock, flags);
+               tty = tty_port_tty_get(&p->port);
+               if (tty && C_CLOCAL(tty) && !dcd)
+                       tty_hangup(tty);
+               tty_kref_put(tty);
+       }
+       else
+               spin_unlock_irqrestore(&p->port.lock, flags);
+}
+
+static int moxa_poll_port(struct moxa_port *p, unsigned int handle,
+               u16 __iomem *ip)
+{
+       struct tty_struct *tty = tty_port_tty_get(&p->port);
+       void __iomem *ofsAddr;
+       unsigned int inited = p->port.flags & ASYNC_INITIALIZED;
+       u16 intr;
+
+       if (tty) {
+               if (test_bit(EMPTYWAIT, &p->statusflags) &&
+                               MoxaPortTxQueue(p) == 0) {
+                       clear_bit(EMPTYWAIT, &p->statusflags);
+                       tty_wakeup(tty);
+               }
+               if (test_bit(LOWWAIT, &p->statusflags) && !tty->stopped &&
+                               MoxaPortTxQueue(p) <= WAKEUP_CHARS) {
+                       clear_bit(LOWWAIT, &p->statusflags);
+                       tty_wakeup(tty);
+               }
+
+               if (inited && !test_bit(TTY_THROTTLED, &tty->flags) &&
+                               MoxaPortRxQueue(p) > 0) { /* RX */
+                       MoxaPortReadData(p);
+                       tty_schedule_flip(tty);
+               }
+       } else {
+               clear_bit(EMPTYWAIT, &p->statusflags);
+               MoxaPortFlushData(p, 0); /* flush RX */
+       }
+
+       if (!handle) /* nothing else to do */
+               goto put;
+
+       intr = readw(ip); /* port irq status */
+       if (intr == 0)
+               goto put;
+
+       writew(0, ip); /* ACK port */
+       ofsAddr = p->tableAddr;
+       if (intr & IntrTx) /* disable tx intr */
+               writew(readw(ofsAddr + HostStat) & ~WakeupTx,
+                               ofsAddr + HostStat);
+
+       if (!inited)
+               goto put;
+
+       if (tty && (intr & IntrBreak) && !I_IGNBRK(tty)) { /* BREAK */
+               tty_insert_flip_char(tty, 0, TTY_BREAK);
+               tty_schedule_flip(tty);
+       }
+
+       if (intr & IntrLine)
+               moxa_new_dcdstate(p, readb(ofsAddr + FlagStat) & DCD_state);
+put:
+       tty_kref_put(tty);
+
+       return 0;
+}
+
+static void moxa_poll(unsigned long ignored)
+{
+       struct moxa_board_conf *brd;
+       u16 __iomem *ip;
+       unsigned int card, port, served = 0;
+
+       spin_lock(&moxa_lock);
+       for (card = 0; card < MAX_BOARDS; card++) {
+               brd = &moxa_boards[card];
+               if (!brd->ready)
+                       continue;
+
+               served++;
+
+               ip = NULL;
+               if (readb(brd->intPend) == 0xff)
+                       ip = brd->intTable + readb(brd->intNdx);
+
+               for (port = 0; port < brd->numPorts; port++)
+                       moxa_poll_port(&brd->ports[port], !!ip, ip + port);
+
+               if (ip)
+                       writeb(0, brd->intPend); /* ACK */
+
+               if (moxaLowWaterChk) {
+                       struct moxa_port *p = brd->ports;
+                       for (port = 0; port < brd->numPorts; port++, p++)
+                               if (p->lowChkFlag) {
+                                       p->lowChkFlag = 0;
+                                       moxa_low_water_check(p->tableAddr);
+                               }
+               }
+       }
+       moxaLowWaterChk = 0;
+
+       if (served)
+               mod_timer(&moxaTimer, jiffies + HZ / 50);
+       spin_unlock(&moxa_lock);
+}
+
+/******************************************************************************/
+
+static void moxa_set_tty_param(struct tty_struct *tty, struct ktermios *old_termios)
+{
+       register struct ktermios *ts = tty->termios;
+       struct moxa_port *ch = tty->driver_data;
+       int rts, cts, txflow, rxflow, xany, baud;
+
+       rts = cts = txflow = rxflow = xany = 0;
+       if (ts->c_cflag & CRTSCTS)
+               rts = cts = 1;
+       if (ts->c_iflag & IXON)
+               txflow = 1;
+       if (ts->c_iflag & IXOFF)
+               rxflow = 1;
+       if (ts->c_iflag & IXANY)
+               xany = 1;
+
+       /* Clear the features we don't support */
+       ts->c_cflag &= ~CMSPAR;
+       MoxaPortFlowCtrl(ch, rts, cts, txflow, rxflow, xany);
+       baud = MoxaPortSetTermio(ch, ts, tty_get_baud_rate(tty));
+       if (baud == -1)
+               baud = tty_termios_baud_rate(old_termios);
+       /* Not put the baud rate into the termios data */
+       tty_encode_baud_rate(tty, baud, baud);
+}
+
+/*****************************************************************************
+ *     Driver level functions:                                              *
+ *****************************************************************************/
+
+static void MoxaPortFlushData(struct moxa_port *port, int mode)
+{
+       void __iomem *ofsAddr;
+       if (mode < 0 || mode > 2)
+               return;
+       ofsAddr = port->tableAddr;
+       moxafunc(ofsAddr, FC_FlushQueue, mode);
+       if (mode != 1) {
+               port->lowChkFlag = 0;
+               moxa_low_water_check(ofsAddr);
+       }
+}
+
+/*
+ *    Moxa Port Number Description:
+ *
+ *      MOXA serial driver supports up to 4 MOXA-C218/C320 boards. And,
+ *      the port number using in MOXA driver functions will be 0 to 31 for
+ *      first MOXA board, 32 to 63 for second, 64 to 95 for third and 96
+ *      to 127 for fourth. For example, if you setup three MOXA boards,
+ *      first board is C218, second board is C320-16 and third board is
+ *      C320-32. The port number of first board (C218 - 8 ports) is from
+ *      0 to 7. The port number of second board (C320 - 16 ports) is form
+ *      32 to 47. The port number of third board (C320 - 32 ports) is from
+ *      64 to 95. And those port numbers form 8 to 31, 48 to 63 and 96 to
+ *      127 will be invalid.
+ *
+ *
+ *      Moxa Functions Description:
+ *
+ *      Function 1:     Driver initialization routine, this routine must be
+ *                      called when initialized driver.
+ *      Syntax:
+ *      void MoxaDriverInit();
+ *
+ *
+ *      Function 2:     Moxa driver private IOCTL command processing.
+ *      Syntax:
+ *      int  MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port);
+ *
+ *           unsigned int cmd   : IOCTL command
+ *           unsigned long arg  : IOCTL argument
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    0  (OK)
+ *                      -EINVAL
+ *                      -ENOIOCTLCMD
+ *
+ *
+ *      Function 6:     Enable this port to start Tx/Rx data.
+ *      Syntax:
+ *      void MoxaPortEnable(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *
+ *      Function 7:     Disable this port
+ *      Syntax:
+ *      void MoxaPortDisable(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *
+ *      Function 10:    Setting baud rate of this port.
+ *      Syntax:
+ *      speed_t MoxaPortSetBaud(int port, speed_t baud);
+ *           int port           : port number (0 - 127)
+ *           long baud          : baud rate (50 - 115200)
+ *
+ *           return:    0       : this port is invalid or baud < 50
+ *                      50 - 115200 : the real baud rate set to the port, if
+ *                                    the argument baud is large than maximun
+ *                                    available baud rate, the real setting
+ *                                    baud rate will be the maximun baud rate.
+ *
+ *
+ *      Function 12:    Configure the port.
+ *      Syntax:
+ *      int  MoxaPortSetTermio(int port, struct ktermios *termio, speed_t baud);
+ *           int port           : port number (0 - 127)
+ *           struct ktermios * termio : termio structure pointer
+ *          speed_t baud       : baud rate
+ *
+ *           return:    -1      : this port is invalid or termio == NULL
+ *                      0       : setting O.K.
+ *
+ *
+ *      Function 13:    Get the DTR/RTS state of this port.
+ *      Syntax:
+ *      int  MoxaPortGetLineOut(int port, int *dtrState, int *rtsState);
+ *           int port           : port number (0 - 127)
+ *           int * dtrState     : pointer to INT to receive the current DTR
+ *                                state. (if NULL, this function will not
+ *                                write to this address)
+ *           int * rtsState     : pointer to INT to receive the current RTS
+ *                                state. (if NULL, this function will not
+ *                                write to this address)
+ *
+ *           return:    -1      : this port is invalid
+ *                      0       : O.K.
+ *
+ *
+ *      Function 14:    Setting the DTR/RTS output state of this port.
+ *      Syntax:
+ *      void MoxaPortLineCtrl(int port, int dtrState, int rtsState);
+ *           int port           : port number (0 - 127)
+ *           int dtrState       : DTR output state (0: off, 1: on)
+ *           int rtsState       : RTS output state (0: off, 1: on)
+ *
+ *
+ *      Function 15:    Setting the flow control of this port.
+ *      Syntax:
+ *      void MoxaPortFlowCtrl(int port, int rtsFlow, int ctsFlow, int rxFlow,
+ *                            int txFlow,int xany);
+ *           int port           : port number (0 - 127)
+ *           int rtsFlow        : H/W RTS flow control (0: no, 1: yes)
+ *           int ctsFlow        : H/W CTS flow control (0: no, 1: yes)
+ *           int rxFlow         : S/W Rx XON/XOFF flow control (0: no, 1: yes)
+ *           int txFlow         : S/W Tx XON/XOFF flow control (0: no, 1: yes)
+ *           int xany           : S/W XANY flow control (0: no, 1: yes)
+ *
+ *
+ *      Function 16:    Get ths line status of this port
+ *      Syntax:
+ *      int  MoxaPortLineStatus(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    Bit 0 - CTS state (0: off, 1: on)
+ *                      Bit 1 - DSR state (0: off, 1: on)
+ *                      Bit 2 - DCD state (0: off, 1: on)
+ *
+ *
+ *      Function 19:    Flush the Rx/Tx buffer data of this port.
+ *      Syntax:
+ *      void MoxaPortFlushData(int port, int mode);
+ *           int port           : port number (0 - 127)
+ *           int mode    
+ *                      0       : flush the Rx buffer 
+ *                      1       : flush the Tx buffer 
+ *                      2       : flush the Rx and Tx buffer 
+ *
+ *
+ *      Function 20:    Write data.
+ *      Syntax:
+ *      int  MoxaPortWriteData(int port, unsigned char * buffer, int length);
+ *           int port           : port number (0 - 127)
+ *           unsigned char * buffer     : pointer to write data buffer.
+ *           int length         : write data length
+ *
+ *           return:    0 - length      : real write data length
+ *
+ *
+ *      Function 21:    Read data.
+ *      Syntax:
+ *      int  MoxaPortReadData(int port, struct tty_struct *tty);
+ *           int port           : port number (0 - 127)
+ *          struct tty_struct *tty : tty for data
+ *
+ *           return:    0 - length      : real read data length
+ *
+ *
+ *      Function 24:    Get the Tx buffer current queued data bytes
+ *      Syntax:
+ *      int  MoxaPortTxQueue(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    ..      : Tx buffer current queued data bytes
+ *
+ *
+ *      Function 25:    Get the Tx buffer current free space
+ *      Syntax:
+ *      int  MoxaPortTxFree(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    ..      : Tx buffer current free space
+ *
+ *
+ *      Function 26:    Get the Rx buffer current queued data bytes
+ *      Syntax:
+ *      int  MoxaPortRxQueue(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    ..      : Rx buffer current queued data bytes
+ *
+ *
+ *      Function 28:    Disable port data transmission.
+ *      Syntax:
+ *      void MoxaPortTxDisable(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *
+ *      Function 29:    Enable port data transmission.
+ *      Syntax:
+ *      void MoxaPortTxEnable(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *
+ *      Function 31:    Get the received BREAK signal count and reset it.
+ *      Syntax:
+ *      int  MoxaPortResetBrkCnt(int port);
+ *           int port           : port number (0 - 127)
+ *
+ *           return:    0 - ..  : BREAK signal count
+ *
+ *
+ */
+
+static void MoxaPortEnable(struct moxa_port *port)
+{
+       void __iomem *ofsAddr;
+       u16 lowwater = 512;
+
+       ofsAddr = port->tableAddr;
+       writew(lowwater, ofsAddr + Low_water);
+       if (MOXA_IS_320(port->board))
+               moxafunc(ofsAddr, FC_SetBreakIrq, 0);
+       else
+               writew(readw(ofsAddr + HostStat) | WakeupBreak,
+                               ofsAddr + HostStat);
+
+       moxafunc(ofsAddr, FC_SetLineIrq, Magic_code);
+       moxafunc(ofsAddr, FC_FlushQueue, 2);
+
+       moxafunc(ofsAddr, FC_EnableCH, Magic_code);
+       MoxaPortLineStatus(port);
+}
+
+static void MoxaPortDisable(struct moxa_port *port)
+{
+       void __iomem *ofsAddr = port->tableAddr;
+
+       moxafunc(ofsAddr, FC_SetFlowCtl, 0);    /* disable flow control */
+       moxafunc(ofsAddr, FC_ClrLineIrq, Magic_code);
+       writew(0, ofsAddr + HostStat);
+       moxafunc(ofsAddr, FC_DisableCH, Magic_code);
+}
+
+static speed_t MoxaPortSetBaud(struct moxa_port *port, speed_t baud)
+{
+       void __iomem *ofsAddr = port->tableAddr;
+       unsigned int clock, val;
+       speed_t max;
+
+       max = MOXA_IS_320(port->board) ? 460800 : 921600;
+       if (baud < 50)
+               return 0;
+       if (baud > max)
+               baud = max;
+       clock = 921600;
+       val = clock / baud;
+       moxafunc(ofsAddr, FC_SetBaud, val);
+       baud = clock / val;
+       return baud;
+}
+
+static int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio,
+               speed_t baud)
+{
+       void __iomem *ofsAddr;
+       tcflag_t cflag;
+       tcflag_t mode = 0;
+
+       ofsAddr = port->tableAddr;
+       cflag = termio->c_cflag;        /* termio->c_cflag */
+
+       mode = termio->c_cflag & CSIZE;
+       if (mode == CS5)
+               mode = MX_CS5;
+       else if (mode == CS6)
+               mode = MX_CS6;
+       else if (mode == CS7)
+               mode = MX_CS7;
+       else if (mode == CS8)
+               mode = MX_CS8;
+
+       if (termio->c_cflag & CSTOPB) {
+               if (mode == MX_CS5)
+                       mode |= MX_STOP15;
+               else
+                       mode |= MX_STOP2;
+       } else
+               mode |= MX_STOP1;
+
+       if (termio->c_cflag & PARENB) {
+               if (termio->c_cflag & PARODD)
+                       mode |= MX_PARODD;
+               else
+                       mode |= MX_PAREVEN;
+       } else
+               mode |= MX_PARNONE;
+
+       moxafunc(ofsAddr, FC_SetDataMode, (u16)mode);
+
+       if (MOXA_IS_320(port->board) && baud >= 921600)
+               return -1;
+
+       baud = MoxaPortSetBaud(port, baud);
+
+       if (termio->c_iflag & (IXON | IXOFF | IXANY)) {
+               spin_lock_irq(&moxafunc_lock);
+               writeb(termio->c_cc[VSTART], ofsAddr + FuncArg);
+               writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1);
+               writeb(FC_SetXonXoff, ofsAddr + FuncCode);
+               moxa_wait_finish(ofsAddr);
+               spin_unlock_irq(&moxafunc_lock);
+
+       }
+       return baud;
+}
+
+static int MoxaPortGetLineOut(struct moxa_port *port, int *dtrState,
+               int *rtsState)
+{
+       if (dtrState)
+               *dtrState = !!(port->lineCtrl & DTR_ON);
+       if (rtsState)
+               *rtsState = !!(port->lineCtrl & RTS_ON);
+
+       return 0;
+}
+
+static void MoxaPortLineCtrl(struct moxa_port *port, int dtr, int rts)
+{
+       u8 mode = 0;
+
+       if (dtr)
+               mode |= DTR_ON;
+       if (rts)
+               mode |= RTS_ON;
+       port->lineCtrl = mode;
+       moxafunc(port->tableAddr, FC_LineControl, mode);
+}
+
+static void MoxaPortFlowCtrl(struct moxa_port *port, int rts, int cts,
+               int txflow, int rxflow, int txany)
+{
+       int mode = 0;
+
+       if (rts)
+               mode |= RTS_FlowCtl;
+       if (cts)
+               mode |= CTS_FlowCtl;
+       if (txflow)
+               mode |= Tx_FlowCtl;
+       if (rxflow)
+               mode |= Rx_FlowCtl;
+       if (txany)
+               mode |= IXM_IXANY;
+       moxafunc(port->tableAddr, FC_SetFlowCtl, mode);
+}
+
+static int MoxaPortLineStatus(struct moxa_port *port)
+{
+       void __iomem *ofsAddr;
+       int val;
+
+       ofsAddr = port->tableAddr;
+       if (MOXA_IS_320(port->board))
+               val = moxafuncret(ofsAddr, FC_LineStatus, 0);
+       else
+               val = readw(ofsAddr + FlagStat) >> 4;
+       val &= 0x0B;
+       if (val & 8)
+               val |= 4;
+       moxa_new_dcdstate(port, val & 8);
+       val &= 7;
+       return val;
+}
+
+static int MoxaPortWriteData(struct tty_struct *tty,
+               const unsigned char *buffer, int len)
+{
+       struct moxa_port *port = tty->driver_data;
+       void __iomem *baseAddr, *ofsAddr, *ofs;
+       unsigned int c, total;
+       u16 head, tail, tx_mask, spage, epage;
+       u16 pageno, pageofs, bufhead;
+
+       ofsAddr = port->tableAddr;
+       baseAddr = port->board->basemem;
+       tx_mask = readw(ofsAddr + TX_mask);
+       spage = readw(ofsAddr + Page_txb);
+       epage = readw(ofsAddr + EndPage_txb);
+       tail = readw(ofsAddr + TXwptr);
+       head = readw(ofsAddr + TXrptr);
+       c = (head > tail) ? (head - tail - 1) : (head - tail + tx_mask);
+       if (c > len)
+               c = len;
+       moxaLog.txcnt[port->port.tty->index] += c;
+       total = c;
+       if (spage == epage) {
+               bufhead = readw(ofsAddr + Ofs_txb);
+               writew(spage, baseAddr + Control_reg);
+               while (c > 0) {
+                       if (head > tail)
+                               len = head - tail - 1;
+                       else
+                               len = tx_mask + 1 - tail;
+                       len = (c > len) ? len : c;
+                       ofs = baseAddr + DynPage_addr + bufhead + tail;
+                       memcpy_toio(ofs, buffer, len);
+                       buffer += len;
+                       tail = (tail + len) & tx_mask;
+                       c -= len;
+               }
+       } else {
+               pageno = spage + (tail >> 13);
+               pageofs = tail & Page_mask;
+               while (c > 0) {
+                       len = Page_size - pageofs;
+                       if (len > c)
+                               len = c;
+                       writeb(pageno, baseAddr + Control_reg);
+                       ofs = baseAddr + DynPage_addr + pageofs;
+                       memcpy_toio(ofs, buffer, len);
+                       buffer += len;
+                       if (++pageno == epage)
+                               pageno = spage;
+                       pageofs = 0;
+                       c -= len;
+               }
+               tail = (tail + total) & tx_mask;
+       }
+       writew(tail, ofsAddr + TXwptr);
+       writeb(1, ofsAddr + CD180TXirq);        /* start to send */
+       return total;
+}
+
+static int MoxaPortReadData(struct moxa_port *port)
+{
+       struct tty_struct *tty = port->port.tty;
+       unsigned char *dst;
+       void __iomem *baseAddr, *ofsAddr, *ofs;
+       unsigned int count, len, total;
+       u16 tail, rx_mask, spage, epage;
+       u16 pageno, pageofs, bufhead, head;
+
+       ofsAddr = port->tableAddr;
+       baseAddr = port->board->basemem;
+       head = readw(ofsAddr + RXrptr);
+       tail = readw(ofsAddr + RXwptr);
+       rx_mask = readw(ofsAddr + RX_mask);
+       spage = readw(ofsAddr + Page_rxb);
+       epage = readw(ofsAddr + EndPage_rxb);
+       count = (tail >= head) ? (tail - head) : (tail - head + rx_mask + 1);
+       if (count == 0)
+               return 0;
+
+       total = count;
+       moxaLog.rxcnt[tty->index] += total;
+       if (spage == epage) {
+               bufhead = readw(ofsAddr + Ofs_rxb);
+               writew(spage, baseAddr + Control_reg);
+               while (count > 0) {
+                       ofs = baseAddr + DynPage_addr + bufhead + head;
+                       len = (tail >= head) ? (tail - head) :
+                                       (rx_mask + 1 - head);
+                       len = tty_prepare_flip_string(tty, &dst,
+                                       min(len, count));
+                       memcpy_fromio(dst, ofs, len);
+                       head = (head + len) & rx_mask;
+                       count -= len;
+               }
+       } else {
+               pageno = spage + (head >> 13);
+               pageofs = head & Page_mask;
+               while (count > 0) {
+                       writew(pageno, baseAddr + Control_reg);
+                       ofs = baseAddr + DynPage_addr + pageofs;
+                       len = tty_prepare_flip_string(tty, &dst,
+                                       min(Page_size - pageofs, count));
+                       memcpy_fromio(dst, ofs, len);
+
+                       count -= len;
+                       pageofs = (pageofs + len) & Page_mask;
+                       if (pageofs == 0 && ++pageno == epage)
+                               pageno = spage;
+               }
+               head = (head + total) & rx_mask;
+       }
+       writew(head, ofsAddr + RXrptr);
+       if (readb(ofsAddr + FlagStat) & Xoff_state) {
+               moxaLowWaterChk = 1;
+               port->lowChkFlag = 1;
+       }
+       return total;
+}
+
+
+static int MoxaPortTxQueue(struct moxa_port *port)
+{
+       void __iomem *ofsAddr = port->tableAddr;
+       u16 rptr, wptr, mask;
+
+       rptr = readw(ofsAddr + TXrptr);
+       wptr = readw(ofsAddr + TXwptr);
+       mask = readw(ofsAddr + TX_mask);
+       return (wptr - rptr) & mask;
+}
+
+static int MoxaPortTxFree(struct moxa_port *port)
+{
+       void __iomem *ofsAddr = port->tableAddr;
+       u16 rptr, wptr, mask;
+
+       rptr = readw(ofsAddr + TXrptr);
+       wptr = readw(ofsAddr + TXwptr);
+       mask = readw(ofsAddr + TX_mask);
+       return mask - ((wptr - rptr) & mask);
+}
+
+static int MoxaPortRxQueue(struct moxa_port *port)
+{
+       void __iomem *ofsAddr = port->tableAddr;
+       u16 rptr, wptr, mask;
+
+       rptr = readw(ofsAddr + RXrptr);
+       wptr = readw(ofsAddr + RXwptr);
+       mask = readw(ofsAddr + RX_mask);
+       return (wptr - rptr) & mask;
+}
+
+static void MoxaPortTxDisable(struct moxa_port *port)
+{
+       moxafunc(port->tableAddr, FC_SetXoffState, Magic_code);
+}
+
+static void MoxaPortTxEnable(struct moxa_port *port)
+{
+       moxafunc(port->tableAddr, FC_SetXonState, Magic_code);
+}
+
+static int moxa_get_serial_info(struct moxa_port *info,
+               struct serial_struct __user *retinfo)
+{
+       struct serial_struct tmp = {
+               .type = info->type,
+               .line = info->port.tty->index,
+               .flags = info->port.flags,
+               .baud_base = 921600,
+               .close_delay = info->port.close_delay
+       };
+       return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;
+}
+
+
+static int moxa_set_serial_info(struct moxa_port *info,
+               struct serial_struct __user *new_info)
+{
+       struct serial_struct new_serial;
+
+       if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
+               return -EFAULT;
+
+       if (new_serial.irq != 0 || new_serial.port != 0 ||
+                       new_serial.custom_divisor != 0 ||
+                       new_serial.baud_base != 921600)
+               return -EPERM;
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               if (((new_serial.flags & ~ASYNC_USR_MASK) !=
+                    (info->port.flags & ~ASYNC_USR_MASK)))
+                       return -EPERM;
+       } else
+               info->port.close_delay = new_serial.close_delay * HZ / 100;
+
+       new_serial.flags = (new_serial.flags & ~ASYNC_FLAGS);
+       new_serial.flags |= (info->port.flags & ASYNC_FLAGS);
+
+       MoxaSetFifo(info, new_serial.type == PORT_16550A);
+
+       info->type = new_serial.type;
+       return 0;
+}
+
+
+
+/*****************************************************************************
+ *     Static local functions:                                              *
+ *****************************************************************************/
+
+static void MoxaSetFifo(struct moxa_port *port, int enable)
+{
+       void __iomem *ofsAddr = port->tableAddr;
+
+       if (!enable) {
+               moxafunc(ofsAddr, FC_SetRxFIFOTrig, 0);
+               moxafunc(ofsAddr, FC_SetTxFIFOCnt, 1);
+       } else {
+               moxafunc(ofsAddr, FC_SetRxFIFOTrig, 3);
+               moxafunc(ofsAddr, FC_SetTxFIFOCnt, 16);
+       }
+}
diff --git a/drivers/tty/moxa.h b/drivers/tty/moxa.h
new file mode 100644 (file)
index 0000000..87d16ce
--- /dev/null
@@ -0,0 +1,304 @@
+#ifndef MOXA_H_FILE
+#define MOXA_H_FILE
+
+#define        MOXA            0x400
+#define MOXA_GET_IQUEUE        (MOXA + 1)      /* get input buffered count */
+#define MOXA_GET_OQUEUE        (MOXA + 2)      /* get output buffered count */
+#define MOXA_GETDATACOUNT       (MOXA + 23)
+#define MOXA_GET_IOQUEUE       (MOXA + 27)
+#define MOXA_FLUSH_QUEUE       (MOXA + 28)
+#define MOXA_GETMSTATUS         (MOXA + 65)
+
+/*
+ *    System Configuration
+ */
+
+#define Magic_code     0x404
+
+/*
+ *    for C218 BIOS initialization
+ */
+#define C218_ConfBase  0x800
+#define C218_status    (C218_ConfBase + 0)     /* BIOS running status    */
+#define C218_diag      (C218_ConfBase + 2)     /* diagnostic status      */
+#define C218_key       (C218_ConfBase + 4)     /* WORD (0x218 for C218) */
+#define C218DLoad_len  (C218_ConfBase + 6)     /* WORD           */
+#define C218check_sum  (C218_ConfBase + 8)     /* BYTE           */
+#define C218chksum_ok  (C218_ConfBase + 0x0a)  /* BYTE (1:ok)            */
+#define C218_TestRx    (C218_ConfBase + 0x10)  /* 8 bytes for 8 ports    */
+#define C218_TestTx    (C218_ConfBase + 0x18)  /* 8 bytes for 8 ports    */
+#define C218_RXerr     (C218_ConfBase + 0x20)  /* 8 bytes for 8 ports    */
+#define C218_ErrFlag   (C218_ConfBase + 0x28)  /* 8 bytes for 8 ports    */
+
+#define C218_LoadBuf   0x0F00
+#define C218_KeyCode   0x218
+#define CP204J_KeyCode 0x204
+
+/*
+ *    for C320 BIOS initialization
+ */
+#define C320_ConfBase  0x800
+#define C320_LoadBuf   0x0f00
+#define STS_init       0x05    /* for C320_status        */
+
+#define C320_status    C320_ConfBase + 0       /* BIOS running status    */
+#define C320_diag      C320_ConfBase + 2       /* diagnostic status      */
+#define C320_key       C320_ConfBase + 4       /* WORD (0320H for C320) */
+#define C320DLoad_len  C320_ConfBase + 6       /* WORD           */
+#define C320check_sum  C320_ConfBase + 8       /* WORD           */
+#define C320chksum_ok  C320_ConfBase + 0x0a    /* WORD (1:ok)            */
+#define C320bapi_len   C320_ConfBase + 0x0c    /* WORD           */
+#define C320UART_no    C320_ConfBase + 0x0e    /* WORD           */
+
+#define C320_KeyCode   0x320
+
+#define FixPage_addr   0x0000  /* starting addr of static page  */
+#define DynPage_addr   0x2000  /* starting addr of dynamic page */
+#define C218_start     0x3000  /* starting addr of C218 BIOS prg */
+#define Control_reg    0x1ff0  /* select page and reset control */
+#define HW_reset       0x80
+
+/*
+ *    Function Codes
+ */
+#define FC_CardReset   0x80
+#define FC_ChannelReset 1      /* C320 firmware not supported */
+#define FC_EnableCH    2
+#define FC_DisableCH   3
+#define FC_SetParam    4
+#define FC_SetMode     5
+#define FC_SetRate     6
+#define FC_LineControl 7
+#define FC_LineStatus  8
+#define FC_XmitControl 9
+#define FC_FlushQueue  10
+#define FC_SendBreak   11
+#define FC_StopBreak   12
+#define FC_LoopbackON  13
+#define FC_LoopbackOFF 14
+#define FC_ClrIrqTable 15
+#define FC_SendXon     16
+#define FC_SetTermIrq  17      /* C320 firmware not supported */
+#define FC_SetCntIrq   18      /* C320 firmware not supported */
+#define FC_SetBreakIrq 19
+#define FC_SetLineIrq  20
+#define FC_SetFlowCtl  21
+#define FC_GenIrq      22
+#define FC_InCD180     23
+#define FC_OutCD180    24
+#define FC_InUARTreg   23
+#define FC_OutUARTreg  24
+#define FC_SetXonXoff  25
+#define FC_OutCD180CCR 26
+#define FC_ExtIQueue   27
+#define FC_ExtOQueue   28
+#define FC_ClrLineIrq  29
+#define FC_HWFlowCtl   30
+#define FC_GetClockRate 35
+#define FC_SetBaud     36
+#define FC_SetDataMode  41
+#define FC_GetCCSR      43
+#define FC_GetDataError 45
+#define FC_RxControl   50
+#define FC_ImmSend     51
+#define FC_SetXonState 52
+#define FC_SetXoffState        53
+#define FC_SetRxFIFOTrig 54
+#define FC_SetTxFIFOCnt 55
+#define FC_UnixRate    56
+#define FC_UnixResetTimer 57
+
+#define        RxFIFOTrig1     0
+#define        RxFIFOTrig4     1
+#define        RxFIFOTrig8     2
+#define        RxFIFOTrig14    3
+
+/*
+ *    Dual-Ported RAM
+ */
+#define DRAM_global    0
+#define INT_data       (DRAM_global + 0)
+#define Config_base    (DRAM_global + 0x108)
+
+#define IRQindex       (INT_data + 0)
+#define IRQpending     (INT_data + 4)
+#define IRQtable       (INT_data + 8)
+
+/*
+ *    Interrupt Status
+ */
+#define IntrRx         0x01    /* receiver data O.K.             */
+#define IntrTx         0x02    /* transmit buffer empty  */
+#define IntrFunc       0x04    /* function complete              */
+#define IntrBreak      0x08    /* received break         */
+#define IntrLine       0x10    /* line status change
+                                  for transmitter                */
+#define IntrIntr       0x20    /* received INTR code             */
+#define IntrQuit       0x40    /* received QUIT code             */
+#define IntrEOF        0x80    /* received EOF code              */
+
+#define IntrRxTrigger  0x100   /* rx data count reach tigger value */
+#define IntrTxTrigger  0x200   /* tx data count below trigger value */
+
+#define Magic_no       (Config_base + 0)
+#define Card_model_no  (Config_base + 2)
+#define Total_ports    (Config_base + 4)
+#define Module_cnt     (Config_base + 8)
+#define Module_no      (Config_base + 10)
+#define Timer_10ms     (Config_base + 14)
+#define Disable_IRQ    (Config_base + 20)
+#define TMS320_PORT1   (Config_base + 22)
+#define TMS320_PORT2   (Config_base + 24)
+#define TMS320_CLOCK   (Config_base + 26)
+
+/*
+ *    DATA BUFFER in DRAM
+ */
+#define Extern_table   0x400   /* Base address of the external table
+                                  (24 words *    64) total 3K bytes
+                                  (24 words * 128) total 6K bytes */
+#define Extern_size    0x60    /* 96 bytes                       */
+#define RXrptr         0x00    /* read pointer for RX buffer     */
+#define RXwptr         0x02    /* write pointer for RX buffer    */
+#define TXrptr         0x04    /* read pointer for TX buffer     */
+#define TXwptr         0x06    /* write pointer for TX buffer    */
+#define HostStat       0x08    /* IRQ flag and general flag      */
+#define FlagStat       0x0A
+#define FlowControl    0x0C    /* B7 B6 B5 B4 B3 B2 B1 B0              */
+                               /*  x  x  x  x  |  |  |  |            */
+                               /*              |  |  |  + CTS flow   */
+                               /*              |  |  +--- RTS flow   */
+                               /*              |  +------ TX Xon/Xoff */
+                               /*              +--------- RX Xon/Xoff */
+#define Break_cnt      0x0E    /* received break count   */
+#define CD180TXirq     0x10    /* if non-0: enable TX irq        */
+#define RX_mask        0x12
+#define TX_mask        0x14
+#define Ofs_rxb        0x16
+#define Ofs_txb        0x18
+#define Page_rxb       0x1A
+#define Page_txb       0x1C
+#define EndPage_rxb    0x1E
+#define EndPage_txb    0x20
+#define Data_error     0x22
+#define RxTrigger      0x28
+#define TxTrigger      0x2a
+
+#define rRXwptr        0x34
+#define Low_water      0x36
+
+#define FuncCode       0x40
+#define FuncArg        0x42
+#define FuncArg1       0x44
+
+#define C218rx_size    0x2000  /* 8K bytes */
+#define C218tx_size    0x8000  /* 32K bytes */
+
+#define C218rx_mask    (C218rx_size - 1)
+#define C218tx_mask    (C218tx_size - 1)
+
+#define C320p8rx_size  0x2000
+#define C320p8tx_size  0x8000
+#define C320p8rx_mask  (C320p8rx_size - 1)
+#define C320p8tx_mask  (C320p8tx_size - 1)
+
+#define C320p16rx_size 0x2000
+#define C320p16tx_size 0x4000
+#define C320p16rx_mask (C320p16rx_size - 1)
+#define C320p16tx_mask (C320p16tx_size - 1)
+
+#define C320p24rx_size 0x2000
+#define C320p24tx_size 0x2000
+#define C320p24rx_mask (C320p24rx_size - 1)
+#define C320p24tx_mask (C320p24tx_size - 1)
+
+#define C320p32rx_size 0x1000
+#define C320p32tx_size 0x1000
+#define C320p32rx_mask (C320p32rx_size - 1)
+#define C320p32tx_mask (C320p32tx_size - 1)
+
+#define Page_size      0x2000U
+#define Page_mask      (Page_size - 1)
+#define C218rx_spage   3
+#define C218tx_spage   4
+#define C218rx_pageno  1
+#define C218tx_pageno  4
+#define C218buf_pageno 5
+
+#define C320p8rx_spage 3
+#define C320p8tx_spage 4
+#define C320p8rx_pgno  1
+#define C320p8tx_pgno  4
+#define C320p8buf_pgno 5
+
+#define C320p16rx_spage 3
+#define C320p16tx_spage 4
+#define C320p16rx_pgno 1
+#define C320p16tx_pgno 2
+#define C320p16buf_pgno 3
+
+#define C320p24rx_spage 3
+#define C320p24tx_spage 4
+#define C320p24rx_pgno 1
+#define C320p24tx_pgno 1
+#define C320p24buf_pgno 2
+
+#define C320p32rx_spage 3
+#define C320p32tx_ofs  C320p32rx_size
+#define C320p32tx_spage 3
+#define C320p32buf_pgno 1
+
+/*
+ *    Host Status
+ */
+#define WakeupRx       0x01
+#define WakeupTx       0x02
+#define WakeupBreak    0x08
+#define WakeupLine     0x10
+#define WakeupIntr     0x20
+#define WakeupQuit     0x40
+#define WakeupEOF      0x80    /* used in VTIME control */
+#define WakeupRxTrigger        0x100
+#define WakeupTxTrigger        0x200
+/*
+ *    Flag status
+ */
+#define Rx_over                0x01
+#define Xoff_state     0x02
+#define Tx_flowOff     0x04
+#define Tx_enable      0x08
+#define CTS_state      0x10
+#define DSR_state      0x20
+#define DCD_state      0x80
+/*
+ *    FlowControl
+ */
+#define CTS_FlowCtl    1
+#define RTS_FlowCtl    2
+#define Tx_FlowCtl     4
+#define Rx_FlowCtl     8
+#define IXM_IXANY      0x10
+
+#define LowWater       128
+
+#define DTR_ON         1
+#define RTS_ON         2
+#define CTS_ON         1
+#define DSR_ON         2
+#define DCD_ON         8
+
+/* mode definition */
+#define        MX_CS8          0x03
+#define        MX_CS7          0x02
+#define        MX_CS6          0x01
+#define        MX_CS5          0x00
+
+#define        MX_STOP1        0x00
+#define        MX_STOP15       0x04
+#define        MX_STOP2        0x08
+
+#define        MX_PARNONE      0x00
+#define        MX_PAREVEN      0x40
+#define        MX_PARODD       0xC0
+
+#endif
diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c
new file mode 100644 (file)
index 0000000..d188f37
--- /dev/null
@@ -0,0 +1,2757 @@
+/*
+ *          mxser.c  -- MOXA Smartio/Industio family multiport serial driver.
+ *
+ *      Copyright (C) 1999-2006  Moxa Technologies (support@moxa.com).
+ *     Copyright (C) 2006-2008  Jiri Slaby <jirislaby@gmail.com>
+ *
+ *      This code is loosely based on the 1.8 moxa driver which is based on
+ *     Linux serial driver, written by Linus Torvalds, Theodore T'so and
+ *     others.
+ *
+ *      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.
+ *
+ *     Fed through a cleanup, indent and remove of non 2.6 code by Alan Cox
+ *     <alan@lxorguk.ukuu.org.uk>. The original 1.8 code is available on
+ *     www.moxa.com.
+ *     - Fixed x86_64 cleanness
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "mxser.h"
+
+#define        MXSER_VERSION   "2.0.5"         /* 1.14 */
+#define        MXSERMAJOR       174
+
+#define MXSER_BOARDS           4       /* Max. boards */
+#define MXSER_PORTS_PER_BOARD  8       /* Max. ports per board */
+#define MXSER_PORTS            (MXSER_BOARDS * MXSER_PORTS_PER_BOARD)
+#define MXSER_ISR_PASS_LIMIT   100
+
+/*CheckIsMoxaMust return value*/
+#define MOXA_OTHER_UART                0x00
+#define MOXA_MUST_MU150_HWID   0x01
+#define MOXA_MUST_MU860_HWID   0x02
+
+#define WAKEUP_CHARS           256
+
+#define UART_MCR_AFE           0x20
+#define UART_LSR_SPECIAL       0x1E
+
+#define PCI_DEVICE_ID_POS104UL 0x1044
+#define PCI_DEVICE_ID_CB108    0x1080
+#define PCI_DEVICE_ID_CP102UF  0x1023
+#define PCI_DEVICE_ID_CP112UL  0x1120
+#define PCI_DEVICE_ID_CB114    0x1142
+#define PCI_DEVICE_ID_CP114UL  0x1143
+#define PCI_DEVICE_ID_CB134I   0x1341
+#define PCI_DEVICE_ID_CP138U   0x1380
+
+
+#define C168_ASIC_ID    1
+#define C104_ASIC_ID    2
+#define C102_ASIC_ID   0xB
+#define CI132_ASIC_ID  4
+#define CI134_ASIC_ID  3
+#define CI104J_ASIC_ID  5
+
+#define MXSER_HIGHBAUD 1
+#define MXSER_HAS2     2
+
+/* This is only for PCI */
+static const struct {
+       int type;
+       int tx_fifo;
+       int rx_fifo;
+       int xmit_fifo_size;
+       int rx_high_water;
+       int rx_trigger;
+       int rx_low_water;
+       long max_baud;
+} Gpci_uart_info[] = {
+       {MOXA_OTHER_UART, 16, 16, 16, 14, 14, 1, 921600L},
+       {MOXA_MUST_MU150_HWID, 64, 64, 64, 48, 48, 16, 230400L},
+       {MOXA_MUST_MU860_HWID, 128, 128, 128, 96, 96, 32, 921600L}
+};
+#define UART_INFO_NUM  ARRAY_SIZE(Gpci_uart_info)
+
+struct mxser_cardinfo {
+       char *name;
+       unsigned int nports;
+       unsigned int flags;
+};
+
+static const struct mxser_cardinfo mxser_cards[] = {
+/* 0*/ { "C168 series",        8, },
+       { "C104 series",        4, },
+       { "CI-104J series",     4, },
+       { "C168H/PCI series",   8, },
+       { "C104H/PCI series",   4, },
+/* 5*/ { "C102 series",        4, MXSER_HAS2 },        /* C102-ISA */
+       { "CI-132 series",      4, MXSER_HAS2 },
+       { "CI-134 series",      4, },
+       { "CP-132 series",      2, },
+       { "CP-114 series",      4, },
+/*10*/ { "CT-114 series",      4, },
+       { "CP-102 series",      2, MXSER_HIGHBAUD },
+       { "CP-104U series",     4, },
+       { "CP-168U series",     8, },
+       { "CP-132U series",     2, },
+/*15*/ { "CP-134U series",     4, },
+       { "CP-104JU series",    4, },
+       { "Moxa UC7000 Serial", 8, },           /* RC7000 */
+       { "CP-118U series",     8, },
+       { "CP-102UL series",    2, },
+/*20*/ { "CP-102U series",     2, },
+       { "CP-118EL series",    8, },
+       { "CP-168EL series",    8, },
+       { "CP-104EL series",    4, },
+       { "CB-108 series",      8, },
+/*25*/ { "CB-114 series",      4, },
+       { "CB-134I series",     4, },
+       { "CP-138U series",     8, },
+       { "POS-104UL series",   4, },
+       { "CP-114UL series",    4, },
+/*30*/ { "CP-102UF series",    2, },
+       { "CP-112UL series",    2, },
+};
+
+/* driver_data correspond to the lines in the structure above
+   see also ISA probe function before you change something */
+static struct pci_device_id mxser_pcibrds[] = {
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C168),   .driver_data = 3 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C104),   .driver_data = 4 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132),  .driver_data = 8 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114),  .driver_data = 9 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CT114),  .driver_data = 10 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102),  .driver_data = 11 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104U), .driver_data = 12 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168U), .driver_data = 13 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132U), .driver_data = 14 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134U), .driver_data = 15 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104JU),.driver_data = 16 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_RC7000), .driver_data = 17 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118U), .driver_data = 18 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102UL),.driver_data = 19 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102U), .driver_data = 20 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL),.driver_data = 21 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL),.driver_data = 22 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL),.driver_data = 23 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB108),       .driver_data = 24 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB114),       .driver_data = 25 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB134I),      .driver_data = 26 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP138U),      .driver_data = 27 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_POS104UL),    .driver_data = 28 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP114UL),     .driver_data = 29 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP102UF),     .driver_data = 30 },
+       { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP112UL),     .driver_data = 31 },
+       { }
+};
+MODULE_DEVICE_TABLE(pci, mxser_pcibrds);
+
+static unsigned long ioaddr[MXSER_BOARDS];
+static int ttymajor = MXSERMAJOR;
+
+/* Variables for insmod */
+
+MODULE_AUTHOR("Casper Yang");
+MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver");
+module_param_array(ioaddr, ulong, NULL, 0);
+MODULE_PARM_DESC(ioaddr, "ISA io addresses to look for a moxa board");
+module_param(ttymajor, int, 0);
+MODULE_LICENSE("GPL");
+
+struct mxser_log {
+       int tick;
+       unsigned long rxcnt[MXSER_PORTS];
+       unsigned long txcnt[MXSER_PORTS];
+};
+
+struct mxser_mon {
+       unsigned long rxcnt;
+       unsigned long txcnt;
+       unsigned long up_rxcnt;
+       unsigned long up_txcnt;
+       int modem_status;
+       unsigned char hold_reason;
+};
+
+struct mxser_mon_ext {
+       unsigned long rx_cnt[32];
+       unsigned long tx_cnt[32];
+       unsigned long up_rxcnt[32];
+       unsigned long up_txcnt[32];
+       int modem_status[32];
+
+       long baudrate[32];
+       int databits[32];
+       int stopbits[32];
+       int parity[32];
+       int flowctrl[32];
+       int fifo[32];
+       int iftype[32];
+};
+
+struct mxser_board;
+
+struct mxser_port {
+       struct tty_port port;
+       struct mxser_board *board;
+
+       unsigned long ioaddr;
+       unsigned long opmode_ioaddr;
+       int max_baud;
+
+       int rx_high_water;
+       int rx_trigger;         /* Rx fifo trigger level */
+       int rx_low_water;
+       int baud_base;          /* max. speed */
+       int type;               /* UART type */
+
+       int x_char;             /* xon/xoff character */
+       int IER;                /* Interrupt Enable Register */
+       int MCR;                /* Modem control register */
+
+       unsigned char stop_rx;
+       unsigned char ldisc_stop_rx;
+
+       int custom_divisor;
+       unsigned char err_shadow;
+
+       struct async_icount icount; /* kernel counters for 4 input interrupts */
+       int timeout;
+
+       int read_status_mask;
+       int ignore_status_mask;
+       int xmit_fifo_size;
+       int xmit_head;
+       int xmit_tail;
+       int xmit_cnt;
+
+       struct ktermios normal_termios;
+
+       struct mxser_mon mon_data;
+
+       spinlock_t slock;
+};
+
+struct mxser_board {
+       unsigned int idx;
+       int irq;
+       const struct mxser_cardinfo *info;
+       unsigned long vector;
+       unsigned long vector_mask;
+
+       int chip_flag;
+       int uart_type;
+
+       struct mxser_port ports[MXSER_PORTS_PER_BOARD];
+};
+
+struct mxser_mstatus {
+       tcflag_t cflag;
+       int cts;
+       int dsr;
+       int ri;
+       int dcd;
+};
+
+static struct mxser_board mxser_boards[MXSER_BOARDS];
+static struct tty_driver *mxvar_sdriver;
+static struct mxser_log mxvar_log;
+static int mxser_set_baud_method[MXSER_PORTS + 1];
+
+static void mxser_enable_must_enchance_mode(unsigned long baseio)
+{
+       u8 oldlcr;
+       u8 efr;
+
+       oldlcr = inb(baseio + UART_LCR);
+       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
+
+       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
+       efr |= MOXA_MUST_EFR_EFRB_ENABLE;
+
+       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
+       outb(oldlcr, baseio + UART_LCR);
+}
+
+#ifdef CONFIG_PCI
+static void mxser_disable_must_enchance_mode(unsigned long baseio)
+{
+       u8 oldlcr;
+       u8 efr;
+
+       oldlcr = inb(baseio + UART_LCR);
+       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
+
+       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
+       efr &= ~MOXA_MUST_EFR_EFRB_ENABLE;
+
+       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
+       outb(oldlcr, baseio + UART_LCR);
+}
+#endif
+
+static void mxser_set_must_xon1_value(unsigned long baseio, u8 value)
+{
+       u8 oldlcr;
+       u8 efr;
+
+       oldlcr = inb(baseio + UART_LCR);
+       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
+
+       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
+       efr &= ~MOXA_MUST_EFR_BANK_MASK;
+       efr |= MOXA_MUST_EFR_BANK0;
+
+       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
+       outb(value, baseio + MOXA_MUST_XON1_REGISTER);
+       outb(oldlcr, baseio + UART_LCR);
+}
+
+static void mxser_set_must_xoff1_value(unsigned long baseio, u8 value)
+{
+       u8 oldlcr;
+       u8 efr;
+
+       oldlcr = inb(baseio + UART_LCR);
+       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
+
+       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
+       efr &= ~MOXA_MUST_EFR_BANK_MASK;
+       efr |= MOXA_MUST_EFR_BANK0;
+
+       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
+       outb(value, baseio + MOXA_MUST_XOFF1_REGISTER);
+       outb(oldlcr, baseio + UART_LCR);
+}
+
+static void mxser_set_must_fifo_value(struct mxser_port *info)
+{
+       u8 oldlcr;
+       u8 efr;
+
+       oldlcr = inb(info->ioaddr + UART_LCR);
+       outb(MOXA_MUST_ENTER_ENCHANCE, info->ioaddr + UART_LCR);
+
+       efr = inb(info->ioaddr + MOXA_MUST_EFR_REGISTER);
+       efr &= ~MOXA_MUST_EFR_BANK_MASK;
+       efr |= MOXA_MUST_EFR_BANK1;
+
+       outb(efr, info->ioaddr + MOXA_MUST_EFR_REGISTER);
+       outb((u8)info->rx_high_water, info->ioaddr + MOXA_MUST_RBRTH_REGISTER);
+       outb((u8)info->rx_trigger, info->ioaddr + MOXA_MUST_RBRTI_REGISTER);
+       outb((u8)info->rx_low_water, info->ioaddr + MOXA_MUST_RBRTL_REGISTER);
+       outb(oldlcr, info->ioaddr + UART_LCR);
+}
+
+static void mxser_set_must_enum_value(unsigned long baseio, u8 value)
+{
+       u8 oldlcr;
+       u8 efr;
+
+       oldlcr = inb(baseio + UART_LCR);
+       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
+
+       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
+       efr &= ~MOXA_MUST_EFR_BANK_MASK;
+       efr |= MOXA_MUST_EFR_BANK2;
+
+       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
+       outb(value, baseio + MOXA_MUST_ENUM_REGISTER);
+       outb(oldlcr, baseio + UART_LCR);
+}
+
+#ifdef CONFIG_PCI
+static void mxser_get_must_hardware_id(unsigned long baseio, u8 *pId)
+{
+       u8 oldlcr;
+       u8 efr;
+
+       oldlcr = inb(baseio + UART_LCR);
+       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
+
+       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
+       efr &= ~MOXA_MUST_EFR_BANK_MASK;
+       efr |= MOXA_MUST_EFR_BANK2;
+
+       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
+       *pId = inb(baseio + MOXA_MUST_HWID_REGISTER);
+       outb(oldlcr, baseio + UART_LCR);
+}
+#endif
+
+static void SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(unsigned long baseio)
+{
+       u8 oldlcr;
+       u8 efr;
+
+       oldlcr = inb(baseio + UART_LCR);
+       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
+
+       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
+       efr &= ~MOXA_MUST_EFR_SF_MASK;
+
+       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
+       outb(oldlcr, baseio + UART_LCR);
+}
+
+static void mxser_enable_must_tx_software_flow_control(unsigned long baseio)
+{
+       u8 oldlcr;
+       u8 efr;
+
+       oldlcr = inb(baseio + UART_LCR);
+       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
+
+       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
+       efr &= ~MOXA_MUST_EFR_SF_TX_MASK;
+       efr |= MOXA_MUST_EFR_SF_TX1;
+
+       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
+       outb(oldlcr, baseio + UART_LCR);
+}
+
+static void mxser_disable_must_tx_software_flow_control(unsigned long baseio)
+{
+       u8 oldlcr;
+       u8 efr;
+
+       oldlcr = inb(baseio + UART_LCR);
+       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
+
+       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
+       efr &= ~MOXA_MUST_EFR_SF_TX_MASK;
+
+       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
+       outb(oldlcr, baseio + UART_LCR);
+}
+
+static void mxser_enable_must_rx_software_flow_control(unsigned long baseio)
+{
+       u8 oldlcr;
+       u8 efr;
+
+       oldlcr = inb(baseio + UART_LCR);
+       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
+
+       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
+       efr &= ~MOXA_MUST_EFR_SF_RX_MASK;
+       efr |= MOXA_MUST_EFR_SF_RX1;
+
+       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
+       outb(oldlcr, baseio + UART_LCR);
+}
+
+static void mxser_disable_must_rx_software_flow_control(unsigned long baseio)
+{
+       u8 oldlcr;
+       u8 efr;
+
+       oldlcr = inb(baseio + UART_LCR);
+       outb(MOXA_MUST_ENTER_ENCHANCE, baseio + UART_LCR);
+
+       efr = inb(baseio + MOXA_MUST_EFR_REGISTER);
+       efr &= ~MOXA_MUST_EFR_SF_RX_MASK;
+
+       outb(efr, baseio + MOXA_MUST_EFR_REGISTER);
+       outb(oldlcr, baseio + UART_LCR);
+}
+
+#ifdef CONFIG_PCI
+static int __devinit CheckIsMoxaMust(unsigned long io)
+{
+       u8 oldmcr, hwid;
+       int i;
+
+       outb(0, io + UART_LCR);
+       mxser_disable_must_enchance_mode(io);
+       oldmcr = inb(io + UART_MCR);
+       outb(0, io + UART_MCR);
+       mxser_set_must_xon1_value(io, 0x11);
+       if ((hwid = inb(io + UART_MCR)) != 0) {
+               outb(oldmcr, io + UART_MCR);
+               return MOXA_OTHER_UART;
+       }
+
+       mxser_get_must_hardware_id(io, &hwid);
+       for (i = 1; i < UART_INFO_NUM; i++) { /* 0 = OTHER_UART */
+               if (hwid == Gpci_uart_info[i].type)
+                       return (int)hwid;
+       }
+       return MOXA_OTHER_UART;
+}
+#endif
+
+static void process_txrx_fifo(struct mxser_port *info)
+{
+       int i;
+
+       if ((info->type == PORT_16450) || (info->type == PORT_8250)) {
+               info->rx_trigger = 1;
+               info->rx_high_water = 1;
+               info->rx_low_water = 1;
+               info->xmit_fifo_size = 1;
+       } else
+               for (i = 0; i < UART_INFO_NUM; i++)
+                       if (info->board->chip_flag == Gpci_uart_info[i].type) {
+                               info->rx_trigger = Gpci_uart_info[i].rx_trigger;
+                               info->rx_low_water = Gpci_uart_info[i].rx_low_water;
+                               info->rx_high_water = Gpci_uart_info[i].rx_high_water;
+                               info->xmit_fifo_size = Gpci_uart_info[i].xmit_fifo_size;
+                               break;
+                       }
+}
+
+static unsigned char mxser_get_msr(int baseaddr, int mode, int port)
+{
+       static unsigned char mxser_msr[MXSER_PORTS + 1];
+       unsigned char status = 0;
+
+       status = inb(baseaddr + UART_MSR);
+
+       mxser_msr[port] &= 0x0F;
+       mxser_msr[port] |= status;
+       status = mxser_msr[port];
+       if (mode)
+               mxser_msr[port] = 0;
+
+       return status;
+}
+
+static int mxser_carrier_raised(struct tty_port *port)
+{
+       struct mxser_port *mp = container_of(port, struct mxser_port, port);
+       return (inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD)?1:0;
+}
+
+static void mxser_dtr_rts(struct tty_port *port, int on)
+{
+       struct mxser_port *mp = container_of(port, struct mxser_port, port);
+       unsigned long flags;
+
+       spin_lock_irqsave(&mp->slock, flags);
+       if (on)
+               outb(inb(mp->ioaddr + UART_MCR) |
+                       UART_MCR_DTR | UART_MCR_RTS, mp->ioaddr + UART_MCR);
+       else
+               outb(inb(mp->ioaddr + UART_MCR)&~(UART_MCR_DTR | UART_MCR_RTS),
+                       mp->ioaddr + UART_MCR);
+       spin_unlock_irqrestore(&mp->slock, flags);
+}
+
+static int mxser_set_baud(struct tty_struct *tty, long newspd)
+{
+       struct mxser_port *info = tty->driver_data;
+       int quot = 0, baud;
+       unsigned char cval;
+
+       if (!info->ioaddr)
+               return -1;
+
+       if (newspd > info->max_baud)
+               return -1;
+
+       if (newspd == 134) {
+               quot = 2 * info->baud_base / 269;
+               tty_encode_baud_rate(tty, 134, 134);
+       } else if (newspd) {
+               quot = info->baud_base / newspd;
+               if (quot == 0)
+                       quot = 1;
+               baud = info->baud_base/quot;
+               tty_encode_baud_rate(tty, baud, baud);
+       } else {
+               quot = 0;
+       }
+
+       info->timeout = ((info->xmit_fifo_size * HZ * 10 * quot) / info->baud_base);
+       info->timeout += HZ / 50;       /* Add .02 seconds of slop */
+
+       if (quot) {
+               info->MCR |= UART_MCR_DTR;
+               outb(info->MCR, info->ioaddr + UART_MCR);
+       } else {
+               info->MCR &= ~UART_MCR_DTR;
+               outb(info->MCR, info->ioaddr + UART_MCR);
+               return 0;
+       }
+
+       cval = inb(info->ioaddr + UART_LCR);
+
+       outb(cval | UART_LCR_DLAB, info->ioaddr + UART_LCR);    /* set DLAB */
+
+       outb(quot & 0xff, info->ioaddr + UART_DLL);     /* LS of divisor */
+       outb(quot >> 8, info->ioaddr + UART_DLM);       /* MS of divisor */
+       outb(cval, info->ioaddr + UART_LCR);    /* reset DLAB */
+
+#ifdef BOTHER
+       if (C_BAUD(tty) == BOTHER) {
+               quot = info->baud_base % newspd;
+               quot *= 8;
+               if (quot % newspd > newspd / 2) {
+                       quot /= newspd;
+                       quot++;
+               } else
+                       quot /= newspd;
+
+               mxser_set_must_enum_value(info->ioaddr, quot);
+       } else
+#endif
+               mxser_set_must_enum_value(info->ioaddr, 0);
+
+       return 0;
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static int mxser_change_speed(struct tty_struct *tty,
+                                       struct ktermios *old_termios)
+{
+       struct mxser_port *info = tty->driver_data;
+       unsigned cflag, cval, fcr;
+       int ret = 0;
+       unsigned char status;
+
+       cflag = tty->termios->c_cflag;
+       if (!info->ioaddr)
+               return ret;
+
+       if (mxser_set_baud_method[tty->index] == 0)
+               mxser_set_baud(tty, tty_get_baud_rate(tty));
+
+       /* byte size and parity */
+       switch (cflag & CSIZE) {
+       case CS5:
+               cval = 0x00;
+               break;
+       case CS6:
+               cval = 0x01;
+               break;
+       case CS7:
+               cval = 0x02;
+               break;
+       case CS8:
+               cval = 0x03;
+               break;
+       default:
+               cval = 0x00;
+               break;          /* too keep GCC shut... */
+       }
+       if (cflag & CSTOPB)
+               cval |= 0x04;
+       if (cflag & PARENB)
+               cval |= UART_LCR_PARITY;
+       if (!(cflag & PARODD))
+               cval |= UART_LCR_EPAR;
+       if (cflag & CMSPAR)
+               cval |= UART_LCR_SPAR;
+
+       if ((info->type == PORT_8250) || (info->type == PORT_16450)) {
+               if (info->board->chip_flag) {
+                       fcr = UART_FCR_ENABLE_FIFO;
+                       fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE;
+                       mxser_set_must_fifo_value(info);
+               } else
+                       fcr = 0;
+       } else {
+               fcr = UART_FCR_ENABLE_FIFO;
+               if (info->board->chip_flag) {
+                       fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE;
+                       mxser_set_must_fifo_value(info);
+               } else {
+                       switch (info->rx_trigger) {
+                       case 1:
+                               fcr |= UART_FCR_TRIGGER_1;
+                               break;
+                       case 4:
+                               fcr |= UART_FCR_TRIGGER_4;
+                               break;
+                       case 8:
+                               fcr |= UART_FCR_TRIGGER_8;
+                               break;
+                       default:
+                               fcr |= UART_FCR_TRIGGER_14;
+                               break;
+                       }
+               }
+       }
+
+       /* CTS flow control flag and modem status interrupts */
+       info->IER &= ~UART_IER_MSI;
+       info->MCR &= ~UART_MCR_AFE;
+       if (cflag & CRTSCTS) {
+               info->port.flags |= ASYNC_CTS_FLOW;
+               info->IER |= UART_IER_MSI;
+               if ((info->type == PORT_16550A) || (info->board->chip_flag)) {
+                       info->MCR |= UART_MCR_AFE;
+               } else {
+                       status = inb(info->ioaddr + UART_MSR);
+                       if (tty->hw_stopped) {
+                               if (status & UART_MSR_CTS) {
+                                       tty->hw_stopped = 0;
+                                       if (info->type != PORT_16550A &&
+                                                       !info->board->chip_flag) {
+                                               outb(info->IER & ~UART_IER_THRI,
+                                                       info->ioaddr +
+                                                       UART_IER);
+                                               info->IER |= UART_IER_THRI;
+                                               outb(info->IER, info->ioaddr +
+                                                               UART_IER);
+                                       }
+                                       tty_wakeup(tty);
+                               }
+                       } else {
+                               if (!(status & UART_MSR_CTS)) {
+                                       tty->hw_stopped = 1;
+                                       if ((info->type != PORT_16550A) &&
+                                                       (!info->board->chip_flag)) {
+                                               info->IER &= ~UART_IER_THRI;
+                                               outb(info->IER, info->ioaddr +
+                                                               UART_IER);
+                                       }
+                               }
+                       }
+               }
+       } else {
+               info->port.flags &= ~ASYNC_CTS_FLOW;
+       }
+       outb(info->MCR, info->ioaddr + UART_MCR);
+       if (cflag & CLOCAL) {
+               info->port.flags &= ~ASYNC_CHECK_CD;
+       } else {
+               info->port.flags |= ASYNC_CHECK_CD;
+               info->IER |= UART_IER_MSI;
+       }
+       outb(info->IER, info->ioaddr + UART_IER);
+
+       /*
+        * Set up parity check flag
+        */
+       info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+       if (I_INPCK(tty))
+               info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+       if (I_BRKINT(tty) || I_PARMRK(tty))
+               info->read_status_mask |= UART_LSR_BI;
+
+       info->ignore_status_mask = 0;
+
+       if (I_IGNBRK(tty)) {
+               info->ignore_status_mask |= UART_LSR_BI;
+               info->read_status_mask |= UART_LSR_BI;
+               /*
+                * If we're ignore parity and break indicators, ignore
+                * overruns too.  (For real raw support).
+                */
+               if (I_IGNPAR(tty)) {
+                       info->ignore_status_mask |=
+                                               UART_LSR_OE |
+                                               UART_LSR_PE |
+                                               UART_LSR_FE;
+                       info->read_status_mask |=
+                                               UART_LSR_OE |
+                                               UART_LSR_PE |
+                                               UART_LSR_FE;
+               }
+       }
+       if (info->board->chip_flag) {
+               mxser_set_must_xon1_value(info->ioaddr, START_CHAR(tty));
+               mxser_set_must_xoff1_value(info->ioaddr, STOP_CHAR(tty));
+               if (I_IXON(tty)) {
+                       mxser_enable_must_rx_software_flow_control(
+                                       info->ioaddr);
+               } else {
+                       mxser_disable_must_rx_software_flow_control(
+                                       info->ioaddr);
+               }
+               if (I_IXOFF(tty)) {
+                       mxser_enable_must_tx_software_flow_control(
+                                       info->ioaddr);
+               } else {
+                       mxser_disable_must_tx_software_flow_control(
+                                       info->ioaddr);
+               }
+       }
+
+
+       outb(fcr, info->ioaddr + UART_FCR);     /* set fcr */
+       outb(cval, info->ioaddr + UART_LCR);
+
+       return ret;
+}
+
+static void mxser_check_modem_status(struct tty_struct *tty,
+                               struct mxser_port *port, int status)
+{
+       /* update input line counters */
+       if (status & UART_MSR_TERI)
+               port->icount.rng++;
+       if (status & UART_MSR_DDSR)
+               port->icount.dsr++;
+       if (status & UART_MSR_DDCD)
+               port->icount.dcd++;
+       if (status & UART_MSR_DCTS)
+               port->icount.cts++;
+       port->mon_data.modem_status = status;
+       wake_up_interruptible(&port->port.delta_msr_wait);
+
+       if ((port->port.flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
+               if (status & UART_MSR_DCD)
+                       wake_up_interruptible(&port->port.open_wait);
+       }
+
+       if (port->port.flags & ASYNC_CTS_FLOW) {
+               if (tty->hw_stopped) {
+                       if (status & UART_MSR_CTS) {
+                               tty->hw_stopped = 0;
+
+                               if ((port->type != PORT_16550A) &&
+                                               (!port->board->chip_flag)) {
+                                       outb(port->IER & ~UART_IER_THRI,
+                                               port->ioaddr + UART_IER);
+                                       port->IER |= UART_IER_THRI;
+                                       outb(port->IER, port->ioaddr +
+                                                       UART_IER);
+                               }
+                               tty_wakeup(tty);
+                       }
+               } else {
+                       if (!(status & UART_MSR_CTS)) {
+                               tty->hw_stopped = 1;
+                               if (port->type != PORT_16550A &&
+                                               !port->board->chip_flag) {
+                                       port->IER &= ~UART_IER_THRI;
+                                       outb(port->IER, port->ioaddr +
+                                                       UART_IER);
+                               }
+                       }
+               }
+       }
+}
+
+static int mxser_activate(struct tty_port *port, struct tty_struct *tty)
+{
+       struct mxser_port *info = container_of(port, struct mxser_port, port);
+       unsigned long page;
+       unsigned long flags;
+
+       page = __get_free_page(GFP_KERNEL);
+       if (!page)
+               return -ENOMEM;
+
+       spin_lock_irqsave(&info->slock, flags);
+
+       if (!info->ioaddr || !info->type) {
+               set_bit(TTY_IO_ERROR, &tty->flags);
+               free_page(page);
+               spin_unlock_irqrestore(&info->slock, flags);
+               return 0;
+       }
+       info->port.xmit_buf = (unsigned char *) page;
+
+       /*
+        * Clear the FIFO buffers and disable them
+        * (they will be reenabled in mxser_change_speed())
+        */
+       if (info->board->chip_flag)
+               outb((UART_FCR_CLEAR_RCVR |
+                       UART_FCR_CLEAR_XMIT |
+                       MOXA_MUST_FCR_GDA_MODE_ENABLE), info->ioaddr + UART_FCR);
+       else
+               outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT),
+                       info->ioaddr + UART_FCR);
+
+       /*
+        * At this point there's no way the LSR could still be 0xFF;
+        * if it is, then bail out, because there's likely no UART
+        * here.
+        */
+       if (inb(info->ioaddr + UART_LSR) == 0xff) {
+               spin_unlock_irqrestore(&info->slock, flags);
+               if (capable(CAP_SYS_ADMIN)) {
+                       set_bit(TTY_IO_ERROR, &tty->flags);
+                       return 0;
+               } else
+                       return -ENODEV;
+       }
+
+       /*
+        * Clear the interrupt registers.
+        */
+       (void) inb(info->ioaddr + UART_LSR);
+       (void) inb(info->ioaddr + UART_RX);
+       (void) inb(info->ioaddr + UART_IIR);
+       (void) inb(info->ioaddr + UART_MSR);
+
+       /*
+        * Now, initialize the UART
+        */
+       outb(UART_LCR_WLEN8, info->ioaddr + UART_LCR);  /* reset DLAB */
+       info->MCR = UART_MCR_DTR | UART_MCR_RTS;
+       outb(info->MCR, info->ioaddr + UART_MCR);
+
+       /*
+        * Finally, enable interrupts
+        */
+       info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+
+       if (info->board->chip_flag)
+               info->IER |= MOXA_MUST_IER_EGDAI;
+       outb(info->IER, info->ioaddr + UART_IER);       /* enable interrupts */
+
+       /*
+        * And clear the interrupt registers again for luck.
+        */
+       (void) inb(info->ioaddr + UART_LSR);
+       (void) inb(info->ioaddr + UART_RX);
+       (void) inb(info->ioaddr + UART_IIR);
+       (void) inb(info->ioaddr + UART_MSR);
+
+       clear_bit(TTY_IO_ERROR, &tty->flags);
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+       /*
+        * and set the speed of the serial port
+        */
+       mxser_change_speed(tty, NULL);
+       spin_unlock_irqrestore(&info->slock, flags);
+
+       return 0;
+}
+
+/*
+ * This routine will shutdown a serial port
+ */
+static void mxser_shutdown_port(struct tty_port *port)
+{
+       struct mxser_port *info = container_of(port, struct mxser_port, port);
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->slock, flags);
+
+       /*
+        * 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(&info->port.delta_msr_wait);
+
+       /*
+        * Free the xmit buffer, if necessary
+        */
+       if (info->port.xmit_buf) {
+               free_page((unsigned long) info->port.xmit_buf);
+               info->port.xmit_buf = NULL;
+       }
+
+       info->IER = 0;
+       outb(0x00, info->ioaddr + UART_IER);
+
+       /* clear Rx/Tx FIFO's */
+       if (info->board->chip_flag)
+               outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT |
+                               MOXA_MUST_FCR_GDA_MODE_ENABLE,
+                               info->ioaddr + UART_FCR);
+       else
+               outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
+                       info->ioaddr + UART_FCR);
+
+       /* read data port to reset things */
+       (void) inb(info->ioaddr + UART_RX);
+
+
+       if (info->board->chip_flag)
+               SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(info->ioaddr);
+
+       spin_unlock_irqrestore(&info->slock, flags);
+}
+
+/*
+ * 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 mxser_open(struct tty_struct *tty, struct file *filp)
+{
+       struct mxser_port *info;
+       int line;
+
+       line = tty->index;
+       if (line == MXSER_PORTS)
+               return 0;
+       if (line < 0 || line > MXSER_PORTS)
+               return -ENODEV;
+       info = &mxser_boards[line / MXSER_PORTS_PER_BOARD].ports[line % MXSER_PORTS_PER_BOARD];
+       if (!info->ioaddr)
+               return -ENODEV;
+
+       tty->driver_data = info;
+       return tty_port_open(&info->port, tty, filp);
+}
+
+static void mxser_flush_buffer(struct tty_struct *tty)
+{
+       struct mxser_port *info = tty->driver_data;
+       char fcr;
+       unsigned long flags;
+
+
+       spin_lock_irqsave(&info->slock, flags);
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+       fcr = inb(info->ioaddr + UART_FCR);
+       outb((fcr | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT),
+               info->ioaddr + UART_FCR);
+       outb(fcr, info->ioaddr + UART_FCR);
+
+       spin_unlock_irqrestore(&info->slock, flags);
+
+       tty_wakeup(tty);
+}
+
+
+static void mxser_close_port(struct tty_port *port)
+{
+       struct mxser_port *info = container_of(port, struct mxser_port, port);
+       unsigned long timeout;
+       /*
+        * At this point we stop accepting input.  To do this, we
+        * disable the receive line status interrupts, and tell the
+        * interrupt driver to stop checking the data ready bit in the
+        * line status register.
+        */
+       info->IER &= ~UART_IER_RLSI;
+       if (info->board->chip_flag)
+               info->IER &= ~MOXA_MUST_RECV_ISR;
+
+       outb(info->IER, info->ioaddr + UART_IER);
+       /*
+        * Before we drop DTR, make sure the UART transmitter
+        * has completely drained; this is especially
+        * important if there is a transmit FIFO!
+        */
+       timeout = jiffies + HZ;
+       while (!(inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT)) {
+               schedule_timeout_interruptible(5);
+               if (time_after(jiffies, timeout))
+                       break;
+       }
+}
+
+/*
+ * 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 mxser_close(struct tty_struct *tty, struct file *filp)
+{
+       struct mxser_port *info = tty->driver_data;
+       struct tty_port *port = &info->port;
+
+       if (tty->index == MXSER_PORTS || info == NULL)
+               return;
+       if (tty_port_close_start(port, tty, filp) == 0)
+               return;
+       mutex_lock(&port->mutex);
+       mxser_close_port(port);
+       mxser_flush_buffer(tty);
+       mxser_shutdown_port(port);
+       clear_bit(ASYNCB_INITIALIZED, &port->flags);
+       mutex_unlock(&port->mutex);
+       /* Right now the tty_port set is done outside of the close_end helper
+          as we don't yet have everyone using refcounts */     
+       tty_port_close_end(port, tty);
+       tty_port_tty_set(port, NULL);
+}
+
+static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+       int c, total = 0;
+       struct mxser_port *info = tty->driver_data;
+       unsigned long flags;
+
+       if (!info->port.xmit_buf)
+               return 0;
+
+       while (1) {
+               c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                                         SERIAL_XMIT_SIZE - info->xmit_head));
+               if (c <= 0)
+                       break;
+
+               memcpy(info->port.xmit_buf + info->xmit_head, buf, c);
+               spin_lock_irqsave(&info->slock, flags);
+               info->xmit_head = (info->xmit_head + c) &
+                                 (SERIAL_XMIT_SIZE - 1);
+               info->xmit_cnt += c;
+               spin_unlock_irqrestore(&info->slock, flags);
+
+               buf += c;
+               count -= c;
+               total += c;
+       }
+
+       if (info->xmit_cnt && !tty->stopped) {
+               if (!tty->hw_stopped ||
+                               (info->type == PORT_16550A) ||
+                               (info->board->chip_flag)) {
+                       spin_lock_irqsave(&info->slock, flags);
+                       outb(info->IER & ~UART_IER_THRI, info->ioaddr +
+                                       UART_IER);
+                       info->IER |= UART_IER_THRI;
+                       outb(info->IER, info->ioaddr + UART_IER);
+                       spin_unlock_irqrestore(&info->slock, flags);
+               }
+       }
+       return total;
+}
+
+static int mxser_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct mxser_port *info = tty->driver_data;
+       unsigned long flags;
+
+       if (!info->port.xmit_buf)
+               return 0;
+
+       if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1)
+               return 0;
+
+       spin_lock_irqsave(&info->slock, flags);
+       info->port.xmit_buf[info->xmit_head++] = ch;
+       info->xmit_head &= SERIAL_XMIT_SIZE - 1;
+       info->xmit_cnt++;
+       spin_unlock_irqrestore(&info->slock, flags);
+       if (!tty->stopped) {
+               if (!tty->hw_stopped ||
+                               (info->type == PORT_16550A) ||
+                               info->board->chip_flag) {
+                       spin_lock_irqsave(&info->slock, flags);
+                       outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER);
+                       info->IER |= UART_IER_THRI;
+                       outb(info->IER, info->ioaddr + UART_IER);
+                       spin_unlock_irqrestore(&info->slock, flags);
+               }
+       }
+       return 1;
+}
+
+
+static void mxser_flush_chars(struct tty_struct *tty)
+{
+       struct mxser_port *info = tty->driver_data;
+       unsigned long flags;
+
+       if (info->xmit_cnt <= 0 || tty->stopped || !info->port.xmit_buf ||
+                       (tty->hw_stopped && info->type != PORT_16550A &&
+                        !info->board->chip_flag))
+               return;
+
+       spin_lock_irqsave(&info->slock, flags);
+
+       outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER);
+       info->IER |= UART_IER_THRI;
+       outb(info->IER, info->ioaddr + UART_IER);
+
+       spin_unlock_irqrestore(&info->slock, flags);
+}
+
+static int mxser_write_room(struct tty_struct *tty)
+{
+       struct mxser_port *info = tty->driver_data;
+       int ret;
+
+       ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+       return ret < 0 ? 0 : ret;
+}
+
+static int mxser_chars_in_buffer(struct tty_struct *tty)
+{
+       struct mxser_port *info = tty->driver_data;
+       return info->xmit_cnt;
+}
+
+/*
+ * ------------------------------------------------------------
+ * friends of mxser_ioctl()
+ * ------------------------------------------------------------
+ */
+static int mxser_get_serial_info(struct tty_struct *tty,
+               struct serial_struct __user *retinfo)
+{
+       struct mxser_port *info = tty->driver_data;
+       struct serial_struct tmp = {
+               .type = info->type,
+               .line = tty->index,
+               .port = info->ioaddr,
+               .irq = info->board->irq,
+               .flags = info->port.flags,
+               .baud_base = info->baud_base,
+               .close_delay = info->port.close_delay,
+               .closing_wait = info->port.closing_wait,
+               .custom_divisor = info->custom_divisor,
+               .hub6 = 0
+       };
+       if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+               return -EFAULT;
+       return 0;
+}
+
+static int mxser_set_serial_info(struct tty_struct *tty,
+               struct serial_struct __user *new_info)
+{
+       struct mxser_port *info = tty->driver_data;
+       struct tty_port *port = &info->port;
+       struct serial_struct new_serial;
+       speed_t baud;
+       unsigned long sl_flags;
+       unsigned int flags;
+       int retval = 0;
+
+       if (!new_info || !info->ioaddr)
+               return -ENODEV;
+       if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
+               return -EFAULT;
+
+       if (new_serial.irq != info->board->irq ||
+                       new_serial.port != info->ioaddr)
+               return -EINVAL;
+
+       flags = port->flags & ASYNC_SPD_MASK;
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               if ((new_serial.baud_base != info->baud_base) ||
+                               (new_serial.close_delay != info->port.close_delay) ||
+                               ((new_serial.flags & ~ASYNC_USR_MASK) != (info->port.flags & ~ASYNC_USR_MASK)))
+                       return -EPERM;
+               info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) |
+                               (new_serial.flags & ASYNC_USR_MASK));
+       } else {
+               /*
+                * OK, past this point, all the error checking has been done.
+                * At this point, we start making changes.....
+                */
+               port->flags = ((port->flags & ~ASYNC_FLAGS) |
+                               (new_serial.flags & ASYNC_FLAGS));
+               port->close_delay = new_serial.close_delay * HZ / 100;
+               port->closing_wait = new_serial.closing_wait * HZ / 100;
+               tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+               if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
+                               (new_serial.baud_base != info->baud_base ||
+                               new_serial.custom_divisor !=
+                               info->custom_divisor)) {
+                       if (new_serial.custom_divisor == 0)
+                               return -EINVAL;
+                       baud = new_serial.baud_base / new_serial.custom_divisor;
+                       tty_encode_baud_rate(tty, baud, baud);
+               }
+       }
+
+       info->type = new_serial.type;
+
+       process_txrx_fifo(info);
+
+       if (test_bit(ASYNCB_INITIALIZED, &port->flags)) {
+               if (flags != (port->flags & ASYNC_SPD_MASK)) {
+                       spin_lock_irqsave(&info->slock, sl_flags);
+                       mxser_change_speed(tty, NULL);
+                       spin_unlock_irqrestore(&info->slock, sl_flags);
+               }
+       } else {
+               retval = mxser_activate(port, tty);
+               if (retval == 0)
+                       set_bit(ASYNCB_INITIALIZED, &port->flags);
+       }
+       return retval;
+}
+
+/*
+ * mxser_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 mxser_get_lsr_info(struct mxser_port *info,
+               unsigned int __user *value)
+{
+       unsigned char status;
+       unsigned int result;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->slock, flags);
+       status = inb(info->ioaddr + UART_LSR);
+       spin_unlock_irqrestore(&info->slock, flags);
+       result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
+       return put_user(result, value);
+}
+
+static int mxser_tiocmget(struct tty_struct *tty)
+{
+       struct mxser_port *info = tty->driver_data;
+       unsigned char control, status;
+       unsigned long flags;
+
+
+       if (tty->index == MXSER_PORTS)
+               return -ENOIOCTLCMD;
+       if (test_bit(TTY_IO_ERROR, &tty->flags))
+               return -EIO;
+
+       control = info->MCR;
+
+       spin_lock_irqsave(&info->slock, flags);
+       status = inb(info->ioaddr + UART_MSR);
+       if (status & UART_MSR_ANY_DELTA)
+               mxser_check_modem_status(tty, info, status);
+       spin_unlock_irqrestore(&info->slock, flags);
+       return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) |
+                   ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) |
+                   ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) |
+                   ((status & UART_MSR_RI) ? TIOCM_RNG : 0) |
+                   ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) |
+                   ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
+}
+
+static int mxser_tiocmset(struct tty_struct *tty,
+               unsigned int set, unsigned int clear)
+{
+       struct mxser_port *info = tty->driver_data;
+       unsigned long flags;
+
+
+       if (tty->index == MXSER_PORTS)
+               return -ENOIOCTLCMD;
+       if (test_bit(TTY_IO_ERROR, &tty->flags))
+               return -EIO;
+
+       spin_lock_irqsave(&info->slock, flags);
+
+       if (set & TIOCM_RTS)
+               info->MCR |= UART_MCR_RTS;
+       if (set & TIOCM_DTR)
+               info->MCR |= UART_MCR_DTR;
+
+       if (clear & TIOCM_RTS)
+               info->MCR &= ~UART_MCR_RTS;
+       if (clear & TIOCM_DTR)
+               info->MCR &= ~UART_MCR_DTR;
+
+       outb(info->MCR, info->ioaddr + UART_MCR);
+       spin_unlock_irqrestore(&info->slock, flags);
+       return 0;
+}
+
+static int __init mxser_program_mode(int port)
+{
+       int id, i, j, n;
+
+       outb(0, port);
+       outb(0, port);
+       outb(0, port);
+       (void)inb(port);
+       (void)inb(port);
+       outb(0, port);
+       (void)inb(port);
+
+       id = inb(port + 1) & 0x1F;
+       if ((id != C168_ASIC_ID) &&
+                       (id != C104_ASIC_ID) &&
+                       (id != C102_ASIC_ID) &&
+                       (id != CI132_ASIC_ID) &&
+                       (id != CI134_ASIC_ID) &&
+                       (id != CI104J_ASIC_ID))
+               return -1;
+       for (i = 0, j = 0; i < 4; i++) {
+               n = inb(port + 2);
+               if (n == 'M') {
+                       j = 1;
+               } else if ((j == 1) && (n == 1)) {
+                       j = 2;
+                       break;
+               } else
+                       j = 0;
+       }
+       if (j != 2)
+               id = -2;
+       return id;
+}
+
+static void __init mxser_normal_mode(int port)
+{
+       int i, n;
+
+       outb(0xA5, port + 1);
+       outb(0x80, port + 3);
+       outb(12, port + 0);     /* 9600 bps */
+       outb(0, port + 1);
+       outb(0x03, port + 3);   /* 8 data bits */
+       outb(0x13, port + 4);   /* loop back mode */
+       for (i = 0; i < 16; i++) {
+               n = inb(port + 5);
+               if ((n & 0x61) == 0x60)
+                       break;
+               if ((n & 1) == 1)
+                       (void)inb(port);
+       }
+       outb(0x00, port + 4);
+}
+
+#define CHIP_SK        0x01    /* Serial Data Clock  in Eprom */
+#define CHIP_DO        0x02    /* Serial Data Output in Eprom */
+#define CHIP_CS        0x04    /* Serial Chip Select in Eprom */
+#define CHIP_DI        0x08    /* Serial Data Input  in Eprom */
+#define EN_CCMD        0x000   /* Chip's command register     */
+#define EN0_RSARLO     0x008   /* Remote start address reg 0  */
+#define EN0_RSARHI     0x009   /* Remote start address reg 1  */
+#define EN0_RCNTLO     0x00A   /* Remote byte count reg WR    */
+#define EN0_RCNTHI     0x00B   /* Remote byte count reg WR    */
+#define EN0_DCFG       0x00E   /* Data configuration reg WR   */
+#define EN0_PORT       0x010   /* Rcv missed frame error counter RD */
+#define ENC_PAGE0      0x000   /* Select page 0 of chip registers   */
+#define ENC_PAGE3      0x0C0   /* Select page 3 of chip registers   */
+static int __init mxser_read_register(int port, unsigned short *regs)
+{
+       int i, k, value, id;
+       unsigned int j;
+
+       id = mxser_program_mode(port);
+       if (id < 0)
+               return id;
+       for (i = 0; i < 14; i++) {
+               k = (i & 0x3F) | 0x180;
+               for (j = 0x100; j > 0; j >>= 1) {
+                       outb(CHIP_CS, port);
+                       if (k & j) {
+                               outb(CHIP_CS | CHIP_DO, port);
+                               outb(CHIP_CS | CHIP_DO | CHIP_SK, port);        /* A? bit of read */
+                       } else {
+                               outb(CHIP_CS, port);
+                               outb(CHIP_CS | CHIP_SK, port);  /* A? bit of read */
+                       }
+               }
+               (void)inb(port);
+               value = 0;
+               for (k = 0, j = 0x8000; k < 16; k++, j >>= 1) {
+                       outb(CHIP_CS, port);
+                       outb(CHIP_CS | CHIP_SK, port);
+                       if (inb(port) & CHIP_DI)
+                               value |= j;
+               }
+               regs[i] = value;
+               outb(0, port);
+       }
+       mxser_normal_mode(port);
+       return id;
+}
+
+static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
+{
+       struct mxser_port *ip;
+       struct tty_port *port;
+       struct tty_struct *tty;
+       int result, status;
+       unsigned int i, j;
+       int ret = 0;
+
+       switch (cmd) {
+       case MOXA_GET_MAJOR:
+               if (printk_ratelimit())
+                       printk(KERN_WARNING "mxser: '%s' uses deprecated ioctl "
+                                       "%x (GET_MAJOR), fix your userspace\n",
+                                       current->comm, cmd);
+               return put_user(ttymajor, (int __user *)argp);
+
+       case MOXA_CHKPORTENABLE:
+               result = 0;
+               for (i = 0; i < MXSER_BOARDS; i++)
+                       for (j = 0; j < MXSER_PORTS_PER_BOARD; j++)
+                               if (mxser_boards[i].ports[j].ioaddr)
+                                       result |= (1 << i);
+               return put_user(result, (unsigned long __user *)argp);
+       case MOXA_GETDATACOUNT:
+               /* The receive side is locked by port->slock but it isn't
+                  clear that an exact snapshot is worth copying here */
+               if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log)))
+                       ret = -EFAULT;
+               return ret;
+       case MOXA_GETMSTATUS: {
+               struct mxser_mstatus ms, __user *msu = argp;
+               for (i = 0; i < MXSER_BOARDS; i++)
+                       for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) {
+                               ip = &mxser_boards[i].ports[j];
+                               port = &ip->port;
+                               memset(&ms, 0, sizeof(ms));
+
+                               mutex_lock(&port->mutex);
+                               if (!ip->ioaddr)
+                                       goto copy;
+                               
+                               tty = tty_port_tty_get(port);
+
+                               if (!tty || !tty->termios)
+                                       ms.cflag = ip->normal_termios.c_cflag;
+                               else
+                                       ms.cflag = tty->termios->c_cflag;
+                               tty_kref_put(tty);
+                               spin_lock_irq(&ip->slock);
+                               status = inb(ip->ioaddr + UART_MSR);
+                               spin_unlock_irq(&ip->slock);
+                               if (status & UART_MSR_DCD)
+                                       ms.dcd = 1;
+                               if (status & UART_MSR_DSR)
+                                       ms.dsr = 1;
+                               if (status & UART_MSR_CTS)
+                                       ms.cts = 1;
+                       copy:
+                               mutex_unlock(&port->mutex);
+                               if (copy_to_user(msu, &ms, sizeof(ms)))
+                                       return -EFAULT;
+                               msu++;
+                       }
+               return 0;
+       }
+       case MOXA_ASPP_MON_EXT: {
+               struct mxser_mon_ext *me; /* it's 2k, stack unfriendly */
+               unsigned int cflag, iflag, p;
+               u8 opmode;
+
+               me = kzalloc(sizeof(*me), GFP_KERNEL);
+               if (!me)
+                       return -ENOMEM;
+
+               for (i = 0, p = 0; i < MXSER_BOARDS; i++) {
+                       for (j = 0; j < MXSER_PORTS_PER_BOARD; j++, p++) {
+                               if (p >= ARRAY_SIZE(me->rx_cnt)) {
+                                       i = MXSER_BOARDS;
+                                       break;
+                               }
+                               ip = &mxser_boards[i].ports[j];
+                               port = &ip->port;
+
+                               mutex_lock(&port->mutex);
+                               if (!ip->ioaddr) {
+                                       mutex_unlock(&port->mutex);
+                                       continue;
+                               }
+
+                               spin_lock_irq(&ip->slock);
+                               status = mxser_get_msr(ip->ioaddr, 0, p);
+
+                               if (status & UART_MSR_TERI)
+                                       ip->icount.rng++;
+                               if (status & UART_MSR_DDSR)
+                                       ip->icount.dsr++;
+                               if (status & UART_MSR_DDCD)
+                                       ip->icount.dcd++;
+                               if (status & UART_MSR_DCTS)
+                                       ip->icount.cts++;
+
+                               ip->mon_data.modem_status = status;
+                               me->rx_cnt[p] = ip->mon_data.rxcnt;
+                               me->tx_cnt[p] = ip->mon_data.txcnt;
+                               me->up_rxcnt[p] = ip->mon_data.up_rxcnt;
+                               me->up_txcnt[p] = ip->mon_data.up_txcnt;
+                               me->modem_status[p] =
+                                       ip->mon_data.modem_status;
+                               spin_unlock_irq(&ip->slock);
+
+                               tty = tty_port_tty_get(&ip->port);
+
+                               if (!tty || !tty->termios) {
+                                       cflag = ip->normal_termios.c_cflag;
+                                       iflag = ip->normal_termios.c_iflag;
+                                       me->baudrate[p] = tty_termios_baud_rate(&ip->normal_termios);
+                               } else {
+                                       cflag = tty->termios->c_cflag;
+                                       iflag = tty->termios->c_iflag;
+                                       me->baudrate[p] = tty_get_baud_rate(tty);
+                               }
+                               tty_kref_put(tty);
+
+                               me->databits[p] = cflag & CSIZE;
+                               me->stopbits[p] = cflag & CSTOPB;
+                               me->parity[p] = cflag & (PARENB | PARODD |
+                                               CMSPAR);
+
+                               if (cflag & CRTSCTS)
+                                       me->flowctrl[p] |= 0x03;
+
+                               if (iflag & (IXON | IXOFF))
+                                       me->flowctrl[p] |= 0x0C;
+
+                               if (ip->type == PORT_16550A)
+                                       me->fifo[p] = 1;
+
+                               opmode = inb(ip->opmode_ioaddr)>>((p % 4) * 2);
+                               opmode &= OP_MODE_MASK;
+                               me->iftype[p] = opmode;
+                               mutex_unlock(&port->mutex);
+                       }
+               }
+               if (copy_to_user(argp, me, sizeof(*me)))
+                       ret = -EFAULT;
+               kfree(me);
+               return ret;
+       }
+       default:
+               return -ENOIOCTLCMD;
+       }
+       return 0;
+}
+
+static int mxser_cflags_changed(struct mxser_port *info, unsigned long arg,
+               struct async_icount *cprev)
+{
+       struct async_icount cnow;
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&info->slock, flags);
+       cnow = info->icount;    /* atomic copy */
+       spin_unlock_irqrestore(&info->slock, flags);
+
+       ret =   ((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));
+
+       *cprev = cnow;
+
+       return ret;
+}
+
+static int mxser_ioctl(struct tty_struct *tty,
+               unsigned int cmd, unsigned long arg)
+{
+       struct mxser_port *info = tty->driver_data;
+       struct tty_port *port = &info->port;
+       struct async_icount cnow;
+       unsigned long flags;
+       void __user *argp = (void __user *)arg;
+       int retval;
+
+       if (tty->index == MXSER_PORTS)
+               return mxser_ioctl_special(cmd, argp);
+
+       if (cmd == MOXA_SET_OP_MODE || cmd == MOXA_GET_OP_MODE) {
+               int p;
+               unsigned long opmode;
+               static unsigned char ModeMask[] = { 0xfc, 0xf3, 0xcf, 0x3f };
+               int shiftbit;
+               unsigned char val, mask;
+
+               p = tty->index % 4;
+               if (cmd == MOXA_SET_OP_MODE) {
+                       if (get_user(opmode, (int __user *) argp))
+                               return -EFAULT;
+                       if (opmode != RS232_MODE &&
+                                       opmode != RS485_2WIRE_MODE &&
+                                       opmode != RS422_MODE &&
+                                       opmode != RS485_4WIRE_MODE)
+                               return -EFAULT;
+                       mask = ModeMask[p];
+                       shiftbit = p * 2;
+                       spin_lock_irq(&info->slock);
+                       val = inb(info->opmode_ioaddr);
+                       val &= mask;
+                       val |= (opmode << shiftbit);
+                       outb(val, info->opmode_ioaddr);
+                       spin_unlock_irq(&info->slock);
+               } else {
+                       shiftbit = p * 2;
+                       spin_lock_irq(&info->slock);
+                       opmode = inb(info->opmode_ioaddr) >> shiftbit;
+                       spin_unlock_irq(&info->slock);
+                       opmode &= OP_MODE_MASK;
+                       if (put_user(opmode, (int __user *)argp))
+                               return -EFAULT;
+               }
+               return 0;
+       }
+
+       if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT &&
+                       test_bit(TTY_IO_ERROR, &tty->flags))
+               return -EIO;
+
+       switch (cmd) {
+       case TIOCGSERIAL:
+               mutex_lock(&port->mutex);
+               retval = mxser_get_serial_info(tty, argp);
+               mutex_unlock(&port->mutex);
+               return retval;
+       case TIOCSSERIAL:
+               mutex_lock(&port->mutex);
+               retval = mxser_set_serial_info(tty, argp);
+               mutex_unlock(&port->mutex);
+               return retval;
+       case TIOCSERGETLSR:     /* Get line status register */
+               return  mxser_get_lsr_info(info, argp);
+               /*
+                * 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:
+               spin_lock_irqsave(&info->slock, flags);
+               cnow = info->icount;    /* note the counters on entry */
+               spin_unlock_irqrestore(&info->slock, flags);
+
+               return wait_event_interruptible(info->port.delta_msr_wait,
+                               mxser_cflags_changed(info, arg, &cnow));
+       case MOXA_HighSpeedOn:
+               return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp);
+       case MOXA_SDS_RSTICOUNTER:
+               spin_lock_irq(&info->slock);
+               info->mon_data.rxcnt = 0;
+               info->mon_data.txcnt = 0;
+               spin_unlock_irq(&info->slock);
+               return 0;
+
+       case MOXA_ASPP_OQUEUE:{
+               int len, lsr;
+
+               len = mxser_chars_in_buffer(tty);
+               spin_lock_irq(&info->slock);
+               lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_THRE;
+               spin_unlock_irq(&info->slock);
+               len += (lsr ? 0 : 1);
+
+               return put_user(len, (int __user *)argp);
+       }
+       case MOXA_ASPP_MON: {
+               int mcr, status;
+
+               spin_lock_irq(&info->slock);
+               status = mxser_get_msr(info->ioaddr, 1, tty->index);
+               mxser_check_modem_status(tty, info, status);
+
+               mcr = inb(info->ioaddr + UART_MCR);
+               spin_unlock_irq(&info->slock);
+
+               if (mcr & MOXA_MUST_MCR_XON_FLAG)
+                       info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFHOLD;
+               else
+                       info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFHOLD;
+
+               if (mcr & MOXA_MUST_MCR_TX_XON)
+                       info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFXENT;
+               else
+                       info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFXENT;
+
+               if (tty->hw_stopped)
+                       info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD;
+               else
+                       info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD;
+
+               if (copy_to_user(argp, &info->mon_data,
+                               sizeof(struct mxser_mon)))
+                       return -EFAULT;
+
+               return 0;
+       }
+       case MOXA_ASPP_LSTATUS: {
+               if (put_user(info->err_shadow, (unsigned char __user *)argp))
+                       return -EFAULT;
+
+               info->err_shadow = 0;
+               return 0;
+       }
+       case MOXA_SET_BAUD_METHOD: {
+               int method;
+
+               if (get_user(method, (int __user *)argp))
+                       return -EFAULT;
+               mxser_set_baud_method[tty->index] = method;
+               return put_user(method, (int __user *)argp);
+       }
+       default:
+               return -ENOIOCTLCMD;
+       }
+       return 0;
+}
+
+       /*
+        * 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.
+        */
+
+static int mxser_get_icount(struct tty_struct *tty,
+               struct serial_icounter_struct *icount)
+
+{
+       struct mxser_port *info = tty->driver_data;
+       struct async_icount cnow;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->slock, flags);
+       cnow = info->icount;
+       spin_unlock_irqrestore(&info->slock, flags);
+
+       icount->frame = cnow.frame;
+       icount->brk = cnow.brk;
+       icount->overrun = cnow.overrun;
+       icount->buf_overrun = cnow.buf_overrun;
+       icount->parity = cnow.parity;
+       icount->rx = cnow.rx;
+       icount->tx = cnow.tx;
+       icount->cts = cnow.cts;
+       icount->dsr = cnow.dsr;
+       icount->rng = cnow.rng;
+       icount->dcd = cnow.dcd;
+       return 0;
+}
+
+static void mxser_stoprx(struct tty_struct *tty)
+{
+       struct mxser_port *info = tty->driver_data;
+
+       info->ldisc_stop_rx = 1;
+       if (I_IXOFF(tty)) {
+               if (info->board->chip_flag) {
+                       info->IER &= ~MOXA_MUST_RECV_ISR;
+                       outb(info->IER, info->ioaddr + UART_IER);
+               } else {
+                       info->x_char = STOP_CHAR(tty);
+                       outb(0, info->ioaddr + UART_IER);
+                       info->IER |= UART_IER_THRI;
+                       outb(info->IER, info->ioaddr + UART_IER);
+               }
+       }
+
+       if (tty->termios->c_cflag & CRTSCTS) {
+               info->MCR &= ~UART_MCR_RTS;
+               outb(info->MCR, info->ioaddr + UART_MCR);
+       }
+}
+
+/*
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ */
+static void mxser_throttle(struct tty_struct *tty)
+{
+       mxser_stoprx(tty);
+}
+
+static void mxser_unthrottle(struct tty_struct *tty)
+{
+       struct mxser_port *info = tty->driver_data;
+
+       /* startrx */
+       info->ldisc_stop_rx = 0;
+       if (I_IXOFF(tty)) {
+               if (info->x_char)
+                       info->x_char = 0;
+               else {
+                       if (info->board->chip_flag) {
+                               info->IER |= MOXA_MUST_RECV_ISR;
+                               outb(info->IER, info->ioaddr + UART_IER);
+                       } else {
+                               info->x_char = START_CHAR(tty);
+                               outb(0, info->ioaddr + UART_IER);
+                               info->IER |= UART_IER_THRI;
+                               outb(info->IER, info->ioaddr + UART_IER);
+                       }
+               }
+       }
+
+       if (tty->termios->c_cflag & CRTSCTS) {
+               info->MCR |= UART_MCR_RTS;
+               outb(info->MCR, info->ioaddr + UART_MCR);
+       }
+}
+
+/*
+ * mxser_stop() and mxser_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ */
+static void mxser_stop(struct tty_struct *tty)
+{
+       struct mxser_port *info = tty->driver_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->slock, flags);
+       if (info->IER & UART_IER_THRI) {
+               info->IER &= ~UART_IER_THRI;
+               outb(info->IER, info->ioaddr + UART_IER);
+       }
+       spin_unlock_irqrestore(&info->slock, flags);
+}
+
+static void mxser_start(struct tty_struct *tty)
+{
+       struct mxser_port *info = tty->driver_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->slock, flags);
+       if (info->xmit_cnt && info->port.xmit_buf) {
+               outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER);
+               info->IER |= UART_IER_THRI;
+               outb(info->IER, info->ioaddr + UART_IER);
+       }
+       spin_unlock_irqrestore(&info->slock, flags);
+}
+
+static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+       struct mxser_port *info = tty->driver_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->slock, flags);
+       mxser_change_speed(tty, old_termios);
+       spin_unlock_irqrestore(&info->slock, flags);
+
+       if ((old_termios->c_cflag & CRTSCTS) &&
+                       !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               mxser_start(tty);
+       }
+
+       /* Handle sw stopped */
+       if ((old_termios->c_iflag & IXON) &&
+                       !(tty->termios->c_iflag & IXON)) {
+               tty->stopped = 0;
+
+               if (info->board->chip_flag) {
+                       spin_lock_irqsave(&info->slock, flags);
+                       mxser_disable_must_rx_software_flow_control(
+                                       info->ioaddr);
+                       spin_unlock_irqrestore(&info->slock, flags);
+               }
+
+               mxser_start(tty);
+       }
+}
+
+/*
+ * mxser_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       struct mxser_port *info = tty->driver_data;
+       unsigned long orig_jiffies, char_time;
+       unsigned long flags;
+       int lsr;
+
+       if (info->type == PORT_UNKNOWN)
+               return;
+
+       if (info->xmit_fifo_size == 0)
+               return;         /* Just in case.... */
+
+       orig_jiffies = jiffies;
+       /*
+        * Set the check interval to be 1/5 of the estimated time to
+        * send a single character, and make it at least 1.  The check
+        * interval should also be less than the timeout.
+        *
+        * Note: we have to use pretty tight timings here to satisfy
+        * the NIST-PCTS.
+        */
+       char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size;
+       char_time = char_time / 5;
+       if (char_time == 0)
+               char_time = 1;
+       if (timeout && timeout < char_time)
+               char_time = timeout;
+       /*
+        * If the transmitter hasn't cleared in twice the approximate
+        * amount of time to send the entire FIFO, it probably won't
+        * ever clear.  This assumes the UART isn't doing flow
+        * control, which is currently the case.  Hence, if it ever
+        * takes longer than info->timeout, this is probably due to a
+        * UART bug of some kind.  So, we clamp the timeout parameter at
+        * 2*info->timeout.
+        */
+       if (!timeout || timeout > 2 * info->timeout)
+               timeout = 2 * info->timeout;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+       printk(KERN_DEBUG "In rs_wait_until_sent(%d) check=%lu...",
+               timeout, char_time);
+       printk("jiff=%lu...", jiffies);
+#endif
+       spin_lock_irqsave(&info->slock, flags);
+       while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) {
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+               printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
+#endif
+               spin_unlock_irqrestore(&info->slock, flags);
+               schedule_timeout_interruptible(char_time);
+               spin_lock_irqsave(&info->slock, flags);
+               if (signal_pending(current))
+                       break;
+               if (timeout && time_after(jiffies, orig_jiffies + timeout))
+                       break;
+       }
+       spin_unlock_irqrestore(&info->slock, flags);
+       set_current_state(TASK_RUNNING);
+
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+       printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+#endif
+}
+
+/*
+ * This routine is called by tty_hangup() when a hangup is signaled.
+ */
+static void mxser_hangup(struct tty_struct *tty)
+{
+       struct mxser_port *info = tty->driver_data;
+
+       mxser_flush_buffer(tty);
+       tty_port_hangup(&info->port);
+}
+
+/*
+ * mxser_rs_break() --- routine which turns the break handling on or off
+ */
+static int mxser_rs_break(struct tty_struct *tty, int break_state)
+{
+       struct mxser_port *info = tty->driver_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->slock, flags);
+       if (break_state == -1)
+               outb(inb(info->ioaddr + UART_LCR) | UART_LCR_SBC,
+                       info->ioaddr + UART_LCR);
+       else
+               outb(inb(info->ioaddr + UART_LCR) & ~UART_LCR_SBC,
+                       info->ioaddr + UART_LCR);
+       spin_unlock_irqrestore(&info->slock, flags);
+       return 0;
+}
+
+static void mxser_receive_chars(struct tty_struct *tty,
+                               struct mxser_port *port, int *status)
+{
+       unsigned char ch, gdl;
+       int ignored = 0;
+       int cnt = 0;
+       int recv_room;
+       int max = 256;
+
+       recv_room = tty->receive_room;
+       if (recv_room == 0 && !port->ldisc_stop_rx)
+               mxser_stoprx(tty);
+       if (port->board->chip_flag != MOXA_OTHER_UART) {
+
+               if (*status & UART_LSR_SPECIAL)
+                       goto intr_old;
+               if (port->board->chip_flag == MOXA_MUST_MU860_HWID &&
+                               (*status & MOXA_MUST_LSR_RERR))
+                       goto intr_old;
+               if (*status & MOXA_MUST_LSR_RERR)
+                       goto intr_old;
+
+               gdl = inb(port->ioaddr + MOXA_MUST_GDL_REGISTER);
+
+               if (port->board->chip_flag == MOXA_MUST_MU150_HWID)
+                       gdl &= MOXA_MUST_GDL_MASK;
+               if (gdl >= recv_room) {
+                       if (!port->ldisc_stop_rx)
+                               mxser_stoprx(tty);
+               }
+               while (gdl--) {
+                       ch = inb(port->ioaddr + UART_RX);
+                       tty_insert_flip_char(tty, ch, 0);
+                       cnt++;
+               }
+               goto end_intr;
+       }
+intr_old:
+
+       do {
+               if (max-- < 0)
+                       break;
+
+               ch = inb(port->ioaddr + UART_RX);
+               if (port->board->chip_flag && (*status & UART_LSR_OE))
+                       outb(0x23, port->ioaddr + UART_FCR);
+               *status &= port->read_status_mask;
+               if (*status & port->ignore_status_mask) {
+                       if (++ignored > 100)
+                               break;
+               } else {
+                       char flag = 0;
+                       if (*status & UART_LSR_SPECIAL) {
+                               if (*status & UART_LSR_BI) {
+                                       flag = TTY_BREAK;
+                                       port->icount.brk++;
+
+                                       if (port->port.flags & ASYNC_SAK)
+                                               do_SAK(tty);
+                               } else if (*status & UART_LSR_PE) {
+                                       flag = TTY_PARITY;
+                                       port->icount.parity++;
+                               } else if (*status & UART_LSR_FE) {
+                                       flag = TTY_FRAME;
+                                       port->icount.frame++;
+                               } else if (*status & UART_LSR_OE) {
+                                       flag = TTY_OVERRUN;
+                                       port->icount.overrun++;
+                               } else
+                                       flag = TTY_BREAK;
+                       }
+                       tty_insert_flip_char(tty, ch, flag);
+                       cnt++;
+                       if (cnt >= recv_room) {
+                               if (!port->ldisc_stop_rx)
+                                       mxser_stoprx(tty);
+                               break;
+                       }
+
+               }
+
+               if (port->board->chip_flag)
+                       break;
+
+               *status = inb(port->ioaddr + UART_LSR);
+       } while (*status & UART_LSR_DR);
+
+end_intr:
+       mxvar_log.rxcnt[tty->index] += cnt;
+       port->mon_data.rxcnt += cnt;
+       port->mon_data.up_rxcnt += cnt;
+
+       /*
+        * We are called from an interrupt context with &port->slock
+        * being held. Drop it temporarily in order to prevent
+        * recursive locking.
+        */
+       spin_unlock(&port->slock);
+       tty_flip_buffer_push(tty);
+       spin_lock(&port->slock);
+}
+
+static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port)
+{
+       int count, cnt;
+
+       if (port->x_char) {
+               outb(port->x_char, port->ioaddr + UART_TX);
+               port->x_char = 0;
+               mxvar_log.txcnt[tty->index]++;
+               port->mon_data.txcnt++;
+               port->mon_data.up_txcnt++;
+               port->icount.tx++;
+               return;
+       }
+
+       if (port->port.xmit_buf == NULL)
+               return;
+
+       if (port->xmit_cnt <= 0 || tty->stopped ||
+                       (tty->hw_stopped &&
+                       (port->type != PORT_16550A) &&
+                       (!port->board->chip_flag))) {
+               port->IER &= ~UART_IER_THRI;
+               outb(port->IER, port->ioaddr + UART_IER);
+               return;
+       }
+
+       cnt = port->xmit_cnt;
+       count = port->xmit_fifo_size;
+       do {
+               outb(port->port.xmit_buf[port->xmit_tail++],
+                       port->ioaddr + UART_TX);
+               port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1);
+               if (--port->xmit_cnt <= 0)
+                       break;
+       } while (--count > 0);
+       mxvar_log.txcnt[tty->index] += (cnt - port->xmit_cnt);
+
+       port->mon_data.txcnt += (cnt - port->xmit_cnt);
+       port->mon_data.up_txcnt += (cnt - port->xmit_cnt);
+       port->icount.tx += (cnt - port->xmit_cnt);
+
+       if (port->xmit_cnt < WAKEUP_CHARS)
+               tty_wakeup(tty);
+
+       if (port->xmit_cnt <= 0) {
+               port->IER &= ~UART_IER_THRI;
+               outb(port->IER, port->ioaddr + UART_IER);
+       }
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+static irqreturn_t mxser_interrupt(int irq, void *dev_id)
+{
+       int status, iir, i;
+       struct mxser_board *brd = NULL;
+       struct mxser_port *port;
+       int max, irqbits, bits, msr;
+       unsigned int int_cnt, pass_counter = 0;
+       int handled = IRQ_NONE;
+       struct tty_struct *tty;
+
+       for (i = 0; i < MXSER_BOARDS; i++)
+               if (dev_id == &mxser_boards[i]) {
+                       brd = dev_id;
+                       break;
+               }
+
+       if (i == MXSER_BOARDS)
+               goto irq_stop;
+       if (brd == NULL)
+               goto irq_stop;
+       max = brd->info->nports;
+       while (pass_counter++ < MXSER_ISR_PASS_LIMIT) {
+               irqbits = inb(brd->vector) & brd->vector_mask;
+               if (irqbits == brd->vector_mask)
+                       break;
+
+               handled = IRQ_HANDLED;
+               for (i = 0, bits = 1; i < max; i++, irqbits |= bits, bits <<= 1) {
+                       if (irqbits == brd->vector_mask)
+                               break;
+                       if (bits & irqbits)
+                               continue;
+                       port = &brd->ports[i];
+
+                       int_cnt = 0;
+                       spin_lock(&port->slock);
+                       do {
+                               iir = inb(port->ioaddr + UART_IIR);
+                               if (iir & UART_IIR_NO_INT)
+                                       break;
+                               iir &= MOXA_MUST_IIR_MASK;
+                               tty = tty_port_tty_get(&port->port);
+                               if (!tty ||
+                                               (port->port.flags & ASYNC_CLOSING) ||
+                                               !(port->port.flags &
+                                                       ASYNC_INITIALIZED)) {
+                                       status = inb(port->ioaddr + UART_LSR);
+                                       outb(0x27, port->ioaddr + UART_FCR);
+                                       inb(port->ioaddr + UART_MSR);
+                                       tty_kref_put(tty);
+                                       break;
+                               }
+
+                               status = inb(port->ioaddr + UART_LSR);
+
+                               if (status & UART_LSR_PE)
+                                       port->err_shadow |= NPPI_NOTIFY_PARITY;
+                               if (status & UART_LSR_FE)
+                                       port->err_shadow |= NPPI_NOTIFY_FRAMING;
+                               if (status & UART_LSR_OE)
+                                       port->err_shadow |=
+                                               NPPI_NOTIFY_HW_OVERRUN;
+                               if (status & UART_LSR_BI)
+                                       port->err_shadow |= NPPI_NOTIFY_BREAK;
+
+                               if (port->board->chip_flag) {
+                                       if (iir == MOXA_MUST_IIR_GDA ||
+                                           iir == MOXA_MUST_IIR_RDA ||
+                                           iir == MOXA_MUST_IIR_RTO ||
+                                           iir == MOXA_MUST_IIR_LSR)
+                                               mxser_receive_chars(tty, port,
+                                                               &status);
+
+                               } else {
+                                       status &= port->read_status_mask;
+                                       if (status & UART_LSR_DR)
+                                               mxser_receive_chars(tty, port,
+                                                               &status);
+                               }
+                               msr = inb(port->ioaddr + UART_MSR);
+                               if (msr & UART_MSR_ANY_DELTA)
+                                       mxser_check_modem_status(tty, port, msr);
+
+                               if (port->board->chip_flag) {
+                                       if (iir == 0x02 && (status &
+                                                               UART_LSR_THRE))
+                                               mxser_transmit_chars(tty, port);
+                               } else {
+                                       if (status & UART_LSR_THRE)
+                                               mxser_transmit_chars(tty, port);
+                               }
+                               tty_kref_put(tty);
+                       } while (int_cnt++ < MXSER_ISR_PASS_LIMIT);
+                       spin_unlock(&port->slock);
+               }
+       }
+
+irq_stop:
+       return handled;
+}
+
+static const struct tty_operations mxser_ops = {
+       .open = mxser_open,
+       .close = mxser_close,
+       .write = mxser_write,
+       .put_char = mxser_put_char,
+       .flush_chars = mxser_flush_chars,
+       .write_room = mxser_write_room,
+       .chars_in_buffer = mxser_chars_in_buffer,
+       .flush_buffer = mxser_flush_buffer,
+       .ioctl = mxser_ioctl,
+       .throttle = mxser_throttle,
+       .unthrottle = mxser_unthrottle,
+       .set_termios = mxser_set_termios,
+       .stop = mxser_stop,
+       .start = mxser_start,
+       .hangup = mxser_hangup,
+       .break_ctl = mxser_rs_break,
+       .wait_until_sent = mxser_wait_until_sent,
+       .tiocmget = mxser_tiocmget,
+       .tiocmset = mxser_tiocmset,
+       .get_icount = mxser_get_icount,
+};
+
+struct tty_port_operations mxser_port_ops = {
+       .carrier_raised = mxser_carrier_raised,
+       .dtr_rts = mxser_dtr_rts,
+       .activate = mxser_activate,
+       .shutdown = mxser_shutdown_port,
+};
+
+/*
+ * The MOXA Smartio/Industio serial driver boot-time initialization code!
+ */
+
+static void mxser_release_ISA_res(struct mxser_board *brd)
+{
+       free_irq(brd->irq, brd);
+       release_region(brd->ports[0].ioaddr, 8 * brd->info->nports);
+       release_region(brd->vector, 1);
+}
+
+static int __devinit mxser_initbrd(struct mxser_board *brd,
+               struct pci_dev *pdev)
+{
+       struct mxser_port *info;
+       unsigned int i;
+       int retval;
+
+       printk(KERN_INFO "mxser: max. baud rate = %d bps\n",
+                       brd->ports[0].max_baud);
+
+       for (i = 0; i < brd->info->nports; i++) {
+               info = &brd->ports[i];
+               tty_port_init(&info->port);
+               info->port.ops = &mxser_port_ops;
+               info->board = brd;
+               info->stop_rx = 0;
+               info->ldisc_stop_rx = 0;
+
+               /* Enhance mode enabled here */
+               if (brd->chip_flag != MOXA_OTHER_UART)
+                       mxser_enable_must_enchance_mode(info->ioaddr);
+
+               info->port.flags = ASYNC_SHARE_IRQ;
+               info->type = brd->uart_type;
+
+               process_txrx_fifo(info);
+
+               info->custom_divisor = info->baud_base * 16;
+               info->port.close_delay = 5 * HZ / 10;
+               info->port.closing_wait = 30 * HZ;
+               info->normal_termios = mxvar_sdriver->init_termios;
+               memset(&info->mon_data, 0, sizeof(struct mxser_mon));
+               info->err_shadow = 0;
+               spin_lock_init(&info->slock);
+
+               /* before set INT ISR, disable all int */
+               outb(inb(info->ioaddr + UART_IER) & 0xf0,
+                       info->ioaddr + UART_IER);
+       }
+
+       retval = request_irq(brd->irq, mxser_interrupt, IRQF_SHARED, "mxser",
+                       brd);
+       if (retval)
+               printk(KERN_ERR "Board %s: Request irq failed, IRQ (%d) may "
+                       "conflict with another device.\n",
+                       brd->info->name, brd->irq);
+
+       return retval;
+}
+
+static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd)
+{
+       int id, i, bits;
+       unsigned short regs[16], irq;
+       unsigned char scratch, scratch2;
+
+       brd->chip_flag = MOXA_OTHER_UART;
+
+       id = mxser_read_register(cap, regs);
+       switch (id) {
+       case C168_ASIC_ID:
+               brd->info = &mxser_cards[0];
+               break;
+       case C104_ASIC_ID:
+               brd->info = &mxser_cards[1];
+               break;
+       case CI104J_ASIC_ID:
+               brd->info = &mxser_cards[2];
+               break;
+       case C102_ASIC_ID:
+               brd->info = &mxser_cards[5];
+               break;
+       case CI132_ASIC_ID:
+               brd->info = &mxser_cards[6];
+               break;
+       case CI134_ASIC_ID:
+               brd->info = &mxser_cards[7];
+               break;
+       default:
+               return 0;
+       }
+
+       irq = 0;
+       /* some ISA cards have 2 ports, but we want to see them as 4-port (why?)
+          Flag-hack checks if configuration should be read as 2-port here. */
+       if (brd->info->nports == 2 || (brd->info->flags & MXSER_HAS2)) {
+               irq = regs[9] & 0xF000;
+               irq = irq | (irq >> 4);
+               if (irq != (regs[9] & 0xFF00))
+                       goto err_irqconflict;
+       } else if (brd->info->nports == 4) {
+               irq = regs[9] & 0xF000;
+               irq = irq | (irq >> 4);
+               irq = irq | (irq >> 8);
+               if (irq != regs[9])
+                       goto err_irqconflict;
+       } else if (brd->info->nports == 8) {
+               irq = regs[9] & 0xF000;
+               irq = irq | (irq >> 4);
+               irq = irq | (irq >> 8);
+               if ((irq != regs[9]) || (irq != regs[10]))
+                       goto err_irqconflict;
+       }
+
+       if (!irq) {
+               printk(KERN_ERR "mxser: interrupt number unset\n");
+               return -EIO;
+       }
+       brd->irq = ((int)(irq & 0xF000) >> 12);
+       for (i = 0; i < 8; i++)
+               brd->ports[i].ioaddr = (int) regs[i + 1] & 0xFFF8;
+       if ((regs[12] & 0x80) == 0) {
+               printk(KERN_ERR "mxser: invalid interrupt vector\n");
+               return -EIO;
+       }
+       brd->vector = (int)regs[11];    /* interrupt vector */
+       if (id == 1)
+               brd->vector_mask = 0x00FF;
+       else
+               brd->vector_mask = 0x000F;
+       for (i = 7, bits = 0x0100; i >= 0; i--, bits <<= 1) {
+               if (regs[12] & bits) {
+                       brd->ports[i].baud_base = 921600;
+                       brd->ports[i].max_baud = 921600;
+               } else {
+                       brd->ports[i].baud_base = 115200;
+                       brd->ports[i].max_baud = 115200;
+               }
+       }
+       scratch2 = inb(cap + UART_LCR) & (~UART_LCR_DLAB);
+       outb(scratch2 | UART_LCR_DLAB, cap + UART_LCR);
+       outb(0, cap + UART_EFR);        /* EFR is the same as FCR */
+       outb(scratch2, cap + UART_LCR);
+       outb(UART_FCR_ENABLE_FIFO, cap + UART_FCR);
+       scratch = inb(cap + UART_IIR);
+
+       if (scratch & 0xC0)
+               brd->uart_type = PORT_16550A;
+       else
+               brd->uart_type = PORT_16450;
+       if (!request_region(brd->ports[0].ioaddr, 8 * brd->info->nports,
+                       "mxser(IO)")) {
+               printk(KERN_ERR "mxser: can't request ports I/O region: "
+                               "0x%.8lx-0x%.8lx\n",
+                               brd->ports[0].ioaddr, brd->ports[0].ioaddr +
+                               8 * brd->info->nports - 1);
+               return -EIO;
+       }
+       if (!request_region(brd->vector, 1, "mxser(vector)")) {
+               release_region(brd->ports[0].ioaddr, 8 * brd->info->nports);
+               printk(KERN_ERR "mxser: can't request interrupt vector region: "
+                               "0x%.8lx-0x%.8lx\n",
+                               brd->ports[0].ioaddr, brd->ports[0].ioaddr +
+                               8 * brd->info->nports - 1);
+               return -EIO;
+       }
+       return brd->info->nports;
+
+err_irqconflict:
+       printk(KERN_ERR "mxser: invalid interrupt number\n");
+       return -EIO;
+}
+
+static int __devinit mxser_probe(struct pci_dev *pdev,
+               const struct pci_device_id *ent)
+{
+#ifdef CONFIG_PCI
+       struct mxser_board *brd;
+       unsigned int i, j;
+       unsigned long ioaddress;
+       int retval = -EINVAL;
+
+       for (i = 0; i < MXSER_BOARDS; i++)
+               if (mxser_boards[i].info == NULL)
+                       break;
+
+       if (i >= MXSER_BOARDS) {
+               dev_err(&pdev->dev, "too many boards found (maximum %d), board "
+                               "not configured\n", MXSER_BOARDS);
+               goto err;
+       }
+
+       brd = &mxser_boards[i];
+       brd->idx = i * MXSER_PORTS_PER_BOARD;
+       dev_info(&pdev->dev, "found MOXA %s board (BusNo=%d, DevNo=%d)\n",
+               mxser_cards[ent->driver_data].name,
+               pdev->bus->number, PCI_SLOT(pdev->devfn));
+
+       retval = pci_enable_device(pdev);
+       if (retval) {
+               dev_err(&pdev->dev, "PCI enable failed\n");
+               goto err;
+       }
+
+       /* io address */
+       ioaddress = pci_resource_start(pdev, 2);
+       retval = pci_request_region(pdev, 2, "mxser(IO)");
+       if (retval)
+               goto err_dis;
+
+       brd->info = &mxser_cards[ent->driver_data];
+       for (i = 0; i < brd->info->nports; i++)
+               brd->ports[i].ioaddr = ioaddress + 8 * i;
+
+       /* vector */
+       ioaddress = pci_resource_start(pdev, 3);
+       retval = pci_request_region(pdev, 3, "mxser(vector)");
+       if (retval)
+               goto err_zero;
+       brd->vector = ioaddress;
+
+       /* irq */
+       brd->irq = pdev->irq;
+
+       brd->chip_flag = CheckIsMoxaMust(brd->ports[0].ioaddr);
+       brd->uart_type = PORT_16550A;
+       brd->vector_mask = 0;
+
+       for (i = 0; i < brd->info->nports; i++) {
+               for (j = 0; j < UART_INFO_NUM; j++) {
+                       if (Gpci_uart_info[j].type == brd->chip_flag) {
+                               brd->ports[i].max_baud =
+                                       Gpci_uart_info[j].max_baud;
+
+                               /* exception....CP-102 */
+                               if (brd->info->flags & MXSER_HIGHBAUD)
+                                       brd->ports[i].max_baud = 921600;
+                               break;
+                       }
+               }
+       }
+
+       if (brd->chip_flag == MOXA_MUST_MU860_HWID) {
+               for (i = 0; i < brd->info->nports; i++) {
+                       if (i < 4)
+                               brd->ports[i].opmode_ioaddr = ioaddress + 4;
+                       else
+                               brd->ports[i].opmode_ioaddr = ioaddress + 0x0c;
+               }
+               outb(0, ioaddress + 4); /* default set to RS232 mode */
+               outb(0, ioaddress + 0x0c);      /* default set to RS232 mode */
+       }
+
+       for (i = 0; i < brd->info->nports; i++) {
+               brd->vector_mask |= (1 << i);
+               brd->ports[i].baud_base = 921600;
+       }
+
+       /* mxser_initbrd will hook ISR. */
+       retval = mxser_initbrd(brd, pdev);
+       if (retval)
+               goto err_rel3;
+
+       for (i = 0; i < brd->info->nports; i++)
+               tty_register_device(mxvar_sdriver, brd->idx + i, &pdev->dev);
+
+       pci_set_drvdata(pdev, brd);
+
+       return 0;
+err_rel3:
+       pci_release_region(pdev, 3);
+err_zero:
+       brd->info = NULL;
+       pci_release_region(pdev, 2);
+err_dis:
+       pci_disable_device(pdev);
+err:
+       return retval;
+#else
+       return -ENODEV;
+#endif
+}
+
+static void __devexit mxser_remove(struct pci_dev *pdev)
+{
+#ifdef CONFIG_PCI
+       struct mxser_board *brd = pci_get_drvdata(pdev);
+       unsigned int i;
+
+       for (i = 0; i < brd->info->nports; i++)
+               tty_unregister_device(mxvar_sdriver, brd->idx + i);
+
+       free_irq(pdev->irq, brd);
+       pci_release_region(pdev, 2);
+       pci_release_region(pdev, 3);
+       pci_disable_device(pdev);
+       brd->info = NULL;
+#endif
+}
+
+static struct pci_driver mxser_driver = {
+       .name = "mxser",
+       .id_table = mxser_pcibrds,
+       .probe = mxser_probe,
+       .remove = __devexit_p(mxser_remove)
+};
+
+static int __init mxser_module_init(void)
+{
+       struct mxser_board *brd;
+       unsigned int b, i, m;
+       int retval;
+
+       mxvar_sdriver = alloc_tty_driver(MXSER_PORTS + 1);
+       if (!mxvar_sdriver)
+               return -ENOMEM;
+
+       printk(KERN_INFO "MOXA Smartio/Industio family driver version %s\n",
+               MXSER_VERSION);
+
+       /* Initialize the tty_driver structure */
+       mxvar_sdriver->owner = THIS_MODULE;
+       mxvar_sdriver->magic = TTY_DRIVER_MAGIC;
+       mxvar_sdriver->name = "ttyMI";
+       mxvar_sdriver->major = ttymajor;
+       mxvar_sdriver->minor_start = 0;
+       mxvar_sdriver->num = MXSER_PORTS + 1;
+       mxvar_sdriver->type = TTY_DRIVER_TYPE_SERIAL;
+       mxvar_sdriver->subtype = SERIAL_TYPE_NORMAL;
+       mxvar_sdriver->init_termios = tty_std_termios;
+       mxvar_sdriver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL;
+       mxvar_sdriver->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_DYNAMIC_DEV;
+       tty_set_operations(mxvar_sdriver, &mxser_ops);
+
+       retval = tty_register_driver(mxvar_sdriver);
+       if (retval) {
+               printk(KERN_ERR "Couldn't install MOXA Smartio/Industio family "
+                               "tty driver !\n");
+               goto err_put;
+       }
+
+       /* Start finding ISA boards here */
+       for (m = 0, b = 0; b < MXSER_BOARDS; b++) {
+               if (!ioaddr[b])
+                       continue;
+
+               brd = &mxser_boards[m];
+               retval = mxser_get_ISA_conf(ioaddr[b], brd);
+               if (retval <= 0) {
+                       brd->info = NULL;
+                       continue;
+               }
+
+               printk(KERN_INFO "mxser: found MOXA %s board (CAP=0x%lx)\n",
+                               brd->info->name, ioaddr[b]);
+
+               /* mxser_initbrd will hook ISR. */
+               if (mxser_initbrd(brd, NULL) < 0) {
+                       brd->info = NULL;
+                       continue;
+               }
+
+               brd->idx = m * MXSER_PORTS_PER_BOARD;
+               for (i = 0; i < brd->info->nports; i++)
+                       tty_register_device(mxvar_sdriver, brd->idx + i, NULL);
+
+               m++;
+       }
+
+       retval = pci_register_driver(&mxser_driver);
+       if (retval) {
+               printk(KERN_ERR "mxser: can't register pci driver\n");
+               if (!m) {
+                       retval = -ENODEV;
+                       goto err_unr;
+               } /* else: we have some ISA cards under control */
+       }
+
+       return 0;
+err_unr:
+       tty_unregister_driver(mxvar_sdriver);
+err_put:
+       put_tty_driver(mxvar_sdriver);
+       return retval;
+}
+
+static void __exit mxser_module_exit(void)
+{
+       unsigned int i, j;
+
+       pci_unregister_driver(&mxser_driver);
+
+       for (i = 0; i < MXSER_BOARDS; i++) /* ISA remains */
+               if (mxser_boards[i].info != NULL)
+                       for (j = 0; j < mxser_boards[i].info->nports; j++)
+                               tty_unregister_device(mxvar_sdriver,
+                                               mxser_boards[i].idx + j);
+       tty_unregister_driver(mxvar_sdriver);
+       put_tty_driver(mxvar_sdriver);
+
+       for (i = 0; i < MXSER_BOARDS; i++)
+               if (mxser_boards[i].info != NULL)
+                       mxser_release_ISA_res(&mxser_boards[i]);
+}
+
+module_init(mxser_module_init);
+module_exit(mxser_module_exit);
diff --git a/drivers/tty/mxser.h b/drivers/tty/mxser.h
new file mode 100644 (file)
index 0000000..41878a6
--- /dev/null
@@ -0,0 +1,150 @@
+#ifndef _MXSER_H
+#define _MXSER_H
+
+/*
+ *     Semi-public control interfaces
+ */
+
+/*
+ *     MOXA ioctls
+ */
+
+#define MOXA                   0x400
+#define MOXA_GETDATACOUNT      (MOXA + 23)
+#define MOXA_DIAGNOSE          (MOXA + 50)
+#define MOXA_CHKPORTENABLE     (MOXA + 60)
+#define MOXA_HighSpeedOn       (MOXA + 61)
+#define MOXA_GET_MAJOR         (MOXA + 63)
+#define MOXA_GETMSTATUS                (MOXA + 65)
+#define MOXA_SET_OP_MODE       (MOXA + 66)
+#define MOXA_GET_OP_MODE       (MOXA + 67)
+
+#define RS232_MODE             0
+#define RS485_2WIRE_MODE       1
+#define RS422_MODE             2
+#define RS485_4WIRE_MODE       3
+#define OP_MODE_MASK           3
+
+#define MOXA_SDS_RSTICOUNTER   (MOXA + 69)
+#define MOXA_ASPP_OQUEUE       (MOXA + 70)
+#define MOXA_ASPP_MON          (MOXA + 73)
+#define MOXA_ASPP_LSTATUS      (MOXA + 74)
+#define MOXA_ASPP_MON_EXT      (MOXA + 75)
+#define MOXA_SET_BAUD_METHOD   (MOXA + 76)
+
+/* --------------------------------------------------- */
+
+#define NPPI_NOTIFY_PARITY     0x01
+#define NPPI_NOTIFY_FRAMING    0x02
+#define NPPI_NOTIFY_HW_OVERRUN 0x04
+#define NPPI_NOTIFY_SW_OVERRUN 0x08
+#define NPPI_NOTIFY_BREAK      0x10
+
+#define NPPI_NOTIFY_CTSHOLD         0x01       /* Tx hold by CTS low */
+#define NPPI_NOTIFY_DSRHOLD         0x02       /* Tx hold by DSR low */
+#define NPPI_NOTIFY_XOFFHOLD        0x08       /* Tx hold by Xoff received */
+#define NPPI_NOTIFY_XOFFXENT        0x10       /* Xoff Sent */
+
+/* follow just for Moxa Must chip define. */
+/* */
+/* when LCR register (offset 0x03) write following value, */
+/* the Must chip will enter enchance mode. And write value */
+/* on EFR (offset 0x02) bit 6,7 to change bank. */
+#define MOXA_MUST_ENTER_ENCHANCE       0xBF
+
+/* when enhance mode enable, access on general bank register */
+#define MOXA_MUST_GDL_REGISTER         0x07
+#define MOXA_MUST_GDL_MASK             0x7F
+#define MOXA_MUST_GDL_HAS_BAD_DATA     0x80
+
+#define MOXA_MUST_LSR_RERR             0x80    /* error in receive FIFO */
+/* enchance register bank select and enchance mode setting register */
+/* when LCR register equal to 0xBF */
+#define MOXA_MUST_EFR_REGISTER         0x02
+/* enchance mode enable */
+#define MOXA_MUST_EFR_EFRB_ENABLE      0x10
+/* enchance reister bank set 0, 1, 2 */
+#define MOXA_MUST_EFR_BANK0            0x00
+#define MOXA_MUST_EFR_BANK1            0x40
+#define MOXA_MUST_EFR_BANK2            0x80
+#define MOXA_MUST_EFR_BANK3            0xC0
+#define MOXA_MUST_EFR_BANK_MASK                0xC0
+
+/* set XON1 value register, when LCR=0xBF and change to bank0 */
+#define MOXA_MUST_XON1_REGISTER                0x04
+
+/* set XON2 value register, when LCR=0xBF and change to bank0 */
+#define MOXA_MUST_XON2_REGISTER                0x05
+
+/* set XOFF1 value register, when LCR=0xBF and change to bank0 */
+#define MOXA_MUST_XOFF1_REGISTER       0x06
+
+/* set XOFF2 value register, when LCR=0xBF and change to bank0 */
+#define MOXA_MUST_XOFF2_REGISTER       0x07
+
+#define MOXA_MUST_RBRTL_REGISTER       0x04
+#define MOXA_MUST_RBRTH_REGISTER       0x05
+#define MOXA_MUST_RBRTI_REGISTER       0x06
+#define MOXA_MUST_THRTL_REGISTER       0x07
+#define MOXA_MUST_ENUM_REGISTER                0x04
+#define MOXA_MUST_HWID_REGISTER                0x05
+#define MOXA_MUST_ECR_REGISTER         0x06
+#define MOXA_MUST_CSR_REGISTER         0x07
+
+/* good data mode enable */
+#define MOXA_MUST_FCR_GDA_MODE_ENABLE  0x20
+/* only good data put into RxFIFO */
+#define MOXA_MUST_FCR_GDA_ONLY_ENABLE  0x10
+
+/* enable CTS interrupt */
+#define MOXA_MUST_IER_ECTSI            0x80
+/* enable RTS interrupt */
+#define MOXA_MUST_IER_ERTSI            0x40
+/* enable Xon/Xoff interrupt */
+#define MOXA_MUST_IER_XINT             0x20
+/* enable GDA interrupt */
+#define MOXA_MUST_IER_EGDAI            0x10
+
+#define MOXA_MUST_RECV_ISR             (UART_IER_RDI | MOXA_MUST_IER_EGDAI)
+
+/* GDA interrupt pending */
+#define MOXA_MUST_IIR_GDA              0x1C
+#define MOXA_MUST_IIR_RDA              0x04
+#define MOXA_MUST_IIR_RTO              0x0C
+#define MOXA_MUST_IIR_LSR              0x06
+
+/* recieved Xon/Xoff or specical interrupt pending */
+#define MOXA_MUST_IIR_XSC              0x10
+
+/* RTS/CTS change state interrupt pending */
+#define MOXA_MUST_IIR_RTSCTS           0x20
+#define MOXA_MUST_IIR_MASK             0x3E
+
+#define MOXA_MUST_MCR_XON_FLAG         0x40
+#define MOXA_MUST_MCR_XON_ANY          0x80
+#define MOXA_MUST_MCR_TX_XON           0x08
+
+/* software flow control on chip mask value */
+#define MOXA_MUST_EFR_SF_MASK          0x0F
+/* send Xon1/Xoff1 */
+#define MOXA_MUST_EFR_SF_TX1           0x08
+/* send Xon2/Xoff2 */
+#define MOXA_MUST_EFR_SF_TX2           0x04
+/* send Xon1,Xon2/Xoff1,Xoff2 */
+#define MOXA_MUST_EFR_SF_TX12          0x0C
+/* don't send Xon/Xoff */
+#define MOXA_MUST_EFR_SF_TX_NO         0x00
+/* Tx software flow control mask */
+#define MOXA_MUST_EFR_SF_TX_MASK       0x0C
+/* don't receive Xon/Xoff */
+#define MOXA_MUST_EFR_SF_RX_NO         0x00
+/* receive Xon1/Xoff1 */
+#define MOXA_MUST_EFR_SF_RX1           0x02
+/* receive Xon2/Xoff2 */
+#define MOXA_MUST_EFR_SF_RX2           0x01
+/* receive Xon1,Xon2/Xoff1,Xoff2 */
+#define MOXA_MUST_EFR_SF_RX12          0x03
+/* Rx software flow control mask */
+#define MOXA_MUST_EFR_SF_RX_MASK       0x03
+
+#endif
diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c
new file mode 100644 (file)
index 0000000..513ba12
--- /dev/null
@@ -0,0 +1,1993 @@
+/*
+ * nozomi.c  -- HSDPA driver Broadband Wireless Data Card - Globe Trotter
+ *
+ * Written by: Ulf Jakobsson,
+ *             Jan Ã…kerfeldt,
+ *             Stefan Thomasson,
+ *
+ * Maintained by: Paul Hardwick (p.hardwick@option.com)
+ *
+ * Patches:
+ *          Locking code changes for Vodafone by Sphere Systems Ltd,
+ *                              Andrew Bird (ajb@spheresystems.co.uk )
+ *                              & Phil Sanderson
+ *
+ * Source has been ported from an implementation made by Filip Aben @ Option
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Copyright (c) 2005,2006 Option Wireless Sweden AB
+ * Copyright (c) 2006 Sphere Systems Ltd
+ * Copyright (c) 2006 Option Wireless n/v
+ * All rights Reserved.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * --------------------------------------------------------------------------
+ */
+
+/* Enable this to have a lot of debug printouts */
+#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/sched.h>
+#include <linux/serial.h>
+#include <linux/interrupt.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/kfifo.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+
+#include <linux/delay.h>
+
+
+#define VERSION_STRING DRIVER_DESC " 2.1d (build date: " \
+                                       __DATE__ " " __TIME__ ")"
+
+/*    Macros definitions */
+
+/* Default debug printout level */
+#define NOZOMI_DEBUG_LEVEL 0x00
+
+#define P_BUF_SIZE 128
+#define NFO(_err_flag_, args...)                               \
+do {                                                           \
+       char tmp[P_BUF_SIZE];                                   \
+       snprintf(tmp, sizeof(tmp), ##args);                     \
+       printk(_err_flag_ "[%d] %s(): %s\n", __LINE__,          \
+               __func__, tmp);                         \
+} while (0)
+
+#define DBG1(args...) D_(0x01, ##args)
+#define DBG2(args...) D_(0x02, ##args)
+#define DBG3(args...) D_(0x04, ##args)
+#define DBG4(args...) D_(0x08, ##args)
+#define DBG5(args...) D_(0x10, ##args)
+#define DBG6(args...) D_(0x20, ##args)
+#define DBG7(args...) D_(0x40, ##args)
+#define DBG8(args...) D_(0x80, ##args)
+
+#ifdef DEBUG
+/* Do we need this settable at runtime? */
+static int debug = NOZOMI_DEBUG_LEVEL;
+
+#define D(lvl, args...)  do \
+                       {if (lvl & debug) NFO(KERN_DEBUG, ##args); } \
+                       while (0)
+#define D_(lvl, args...) D(lvl, ##args)
+
+/* These printouts are always printed */
+
+#else
+static int debug;
+#define D_(lvl, args...)
+#endif
+
+/* TODO: rewrite to optimize macros... */
+
+#define TMP_BUF_MAX 256
+
+#define DUMP(buf__,len__) \
+  do {  \
+    char tbuf[TMP_BUF_MAX] = {0};\
+    if (len__ > 1) {\
+       snprintf(tbuf, len__ > TMP_BUF_MAX ? TMP_BUF_MAX : len__, "%s", buf__);\
+       if (tbuf[len__-2] == '\r') {\
+               tbuf[len__-2] = 'r';\
+       } \
+       DBG1("SENDING: '%s' (%d+n)", tbuf, len__);\
+    } else {\
+       DBG1("SENDING: '%s' (%d)", tbuf, len__);\
+    } \
+} while (0)
+
+/*    Defines */
+#define NOZOMI_NAME            "nozomi"
+#define NOZOMI_NAME_TTY                "nozomi_tty"
+#define DRIVER_DESC            "Nozomi driver"
+
+#define NTTY_TTY_MAXMINORS     256
+#define NTTY_FIFO_BUFFER_SIZE  8192
+
+/* Must be power of 2 */
+#define FIFO_BUFFER_SIZE_UL    8192
+
+/* Size of tmp send buffer to card */
+#define SEND_BUF_MAX           1024
+#define RECEIVE_BUF_MAX                4
+
+
+#define R_IIR          0x0000  /* Interrupt Identity Register */
+#define R_FCR          0x0000  /* Flow Control Register */
+#define R_IER          0x0004  /* Interrupt Enable Register */
+
+#define CONFIG_MAGIC   0xEFEFFEFE
+#define TOGGLE_VALID   0x0000
+
+/* Definition of interrupt tokens */
+#define MDM_DL1                0x0001
+#define MDM_UL1                0x0002
+#define MDM_DL2                0x0004
+#define MDM_UL2                0x0008
+#define DIAG_DL1       0x0010
+#define DIAG_DL2       0x0020
+#define DIAG_UL                0x0040
+#define APP1_DL                0x0080
+#define APP1_UL                0x0100
+#define APP2_DL                0x0200
+#define APP2_UL                0x0400
+#define CTRL_DL                0x0800
+#define CTRL_UL                0x1000
+#define RESET          0x8000
+
+#define MDM_DL         (MDM_DL1  | MDM_DL2)
+#define MDM_UL         (MDM_UL1  | MDM_UL2)
+#define DIAG_DL                (DIAG_DL1 | DIAG_DL2)
+
+/* modem signal definition */
+#define CTRL_DSR       0x0001
+#define CTRL_DCD       0x0002
+#define CTRL_RI                0x0004
+#define CTRL_CTS       0x0008
+
+#define CTRL_DTR       0x0001
+#define CTRL_RTS       0x0002
+
+#define MAX_PORT               4
+#define NOZOMI_MAX_PORTS       5
+#define NOZOMI_MAX_CARDS       (NTTY_TTY_MAXMINORS / MAX_PORT)
+
+/*    Type definitions */
+
+/*
+ * There are two types of nozomi cards,
+ * one with 2048 memory and with 8192 memory
+ */
+enum card_type {
+       F32_2 = 2048,   /* 512 bytes downlink + uplink * 2 -> 2048 */
+       F32_8 = 8192,   /* 3072 bytes downl. + 1024 bytes uplink * 2 -> 8192 */
+};
+
+/* Initialization states a card can be in */
+enum card_state {
+       NOZOMI_STATE_UKNOWN     = 0,
+       NOZOMI_STATE_ENABLED    = 1,    /* pci device enabled */
+       NOZOMI_STATE_ALLOCATED  = 2,    /* config setup done */
+       NOZOMI_STATE_READY      = 3,    /* flowcontrols received */
+};
+
+/* Two different toggle channels exist */
+enum channel_type {
+       CH_A = 0,
+       CH_B = 1,
+};
+
+/* Port definition for the card regarding flow control */
+enum ctrl_port_type {
+       CTRL_CMD        = 0,
+       CTRL_MDM        = 1,
+       CTRL_DIAG       = 2,
+       CTRL_APP1       = 3,
+       CTRL_APP2       = 4,
+       CTRL_ERROR      = -1,
+};
+
+/* Ports that the nozomi has */
+enum port_type {
+       PORT_MDM        = 0,
+       PORT_DIAG       = 1,
+       PORT_APP1       = 2,
+       PORT_APP2       = 3,
+       PORT_CTRL       = 4,
+       PORT_ERROR      = -1,
+};
+
+#ifdef __BIG_ENDIAN
+/* Big endian */
+
+struct toggles {
+       unsigned int enabled:5; /*
+                                * Toggle fields are valid if enabled is 0,
+                                * else A-channels must always be used.
+                                */
+       unsigned int diag_dl:1;
+       unsigned int mdm_dl:1;
+       unsigned int mdm_ul:1;
+} __attribute__ ((packed));
+
+/* Configuration table to read at startup of card */
+/* Is for now only needed during initialization phase */
+struct config_table {
+       u32 signature;
+       u16 product_information;
+       u16 version;
+       u8 pad3[3];
+       struct toggles toggle;
+       u8 pad1[4];
+       u16 dl_mdm_len1;        /*
+                                * If this is 64, it can hold
+                                * 60 bytes + 4 that is length field
+                                */
+       u16 dl_start;
+
+       u16 dl_diag_len1;
+       u16 dl_mdm_len2;        /*
+                                * If this is 64, it can hold
+                                * 60 bytes + 4 that is length field
+                                */
+       u16 dl_app1_len;
+
+       u16 dl_diag_len2;
+       u16 dl_ctrl_len;
+       u16 dl_app2_len;
+       u8 pad2[16];
+       u16 ul_mdm_len1;
+       u16 ul_start;
+       u16 ul_diag_len;
+       u16 ul_mdm_len2;
+       u16 ul_app1_len;
+       u16 ul_app2_len;
+       u16 ul_ctrl_len;
+} __attribute__ ((packed));
+
+/* This stores all control downlink flags */
+struct ctrl_dl {
+       u8 port;
+       unsigned int reserved:4;
+       unsigned int CTS:1;
+       unsigned int RI:1;
+       unsigned int DCD:1;
+       unsigned int DSR:1;
+} __attribute__ ((packed));
+
+/* This stores all control uplink flags */
+struct ctrl_ul {
+       u8 port;
+       unsigned int reserved:6;
+       unsigned int RTS:1;
+       unsigned int DTR:1;
+} __attribute__ ((packed));
+
+#else
+/* Little endian */
+
+/* This represents the toggle information */
+struct toggles {
+       unsigned int mdm_ul:1;
+       unsigned int mdm_dl:1;
+       unsigned int diag_dl:1;
+       unsigned int enabled:5; /*
+                                * Toggle fields are valid if enabled is 0,
+                                * else A-channels must always be used.
+                                */
+} __attribute__ ((packed));
+
+/* Configuration table to read at startup of card */
+struct config_table {
+       u32 signature;
+       u16 version;
+       u16 product_information;
+       struct toggles toggle;
+       u8 pad1[7];
+       u16 dl_start;
+       u16 dl_mdm_len1;        /*
+                                * If this is 64, it can hold
+                                * 60 bytes + 4 that is length field
+                                */
+       u16 dl_mdm_len2;
+       u16 dl_diag_len1;
+       u16 dl_diag_len2;
+       u16 dl_app1_len;
+       u16 dl_app2_len;
+       u16 dl_ctrl_len;
+       u8 pad2[16];
+       u16 ul_start;
+       u16 ul_mdm_len2;
+       u16 ul_mdm_len1;
+       u16 ul_diag_len;
+       u16 ul_app1_len;
+       u16 ul_app2_len;
+       u16 ul_ctrl_len;
+} __attribute__ ((packed));
+
+/* This stores all control downlink flags */
+struct ctrl_dl {
+       unsigned int DSR:1;
+       unsigned int DCD:1;
+       unsigned int RI:1;
+       unsigned int CTS:1;
+       unsigned int reserverd:4;
+       u8 port;
+} __attribute__ ((packed));
+
+/* This stores all control uplink flags */
+struct ctrl_ul {
+       unsigned int DTR:1;
+       unsigned int RTS:1;
+       unsigned int reserved:6;
+       u8 port;
+} __attribute__ ((packed));
+#endif
+
+/* This holds all information that is needed regarding a port */
+struct port {
+       struct tty_port port;
+       u8 update_flow_control;
+       struct ctrl_ul ctrl_ul;
+       struct ctrl_dl ctrl_dl;
+       struct kfifo fifo_ul;
+       void __iomem *dl_addr[2];
+       u32 dl_size[2];
+       u8 toggle_dl;
+       void __iomem *ul_addr[2];
+       u32 ul_size[2];
+       u8 toggle_ul;
+       u16 token_dl;
+
+       /* mutex to ensure one access patch to this port */
+       struct mutex tty_sem;
+       wait_queue_head_t tty_wait;
+       struct async_icount tty_icount;
+
+       struct nozomi *dc;
+};
+
+/* Private data one for each card in the system */
+struct nozomi {
+       void __iomem *base_addr;
+       unsigned long flip;
+
+       /* Pointers to registers */
+       void __iomem *reg_iir;
+       void __iomem *reg_fcr;
+       void __iomem *reg_ier;
+
+       u16 last_ier;
+       enum card_type card_type;
+       struct config_table config_table;       /* Configuration table */
+       struct pci_dev *pdev;
+       struct port port[NOZOMI_MAX_PORTS];
+       u8 *send_buf;
+
+       spinlock_t spin_mutex;  /* secures access to registers and tty */
+
+       unsigned int index_start;
+       enum card_state state;
+       u32 open_ttys;
+};
+
+/* This is a data packet that is read or written to/from card */
+struct buffer {
+       u32 size;               /* size is the length of the data buffer */
+       u8 *data;
+} __attribute__ ((packed));
+
+/*    Global variables */
+static const struct pci_device_id nozomi_pci_tbl[] __devinitconst = {
+       {PCI_DEVICE(0x1931, 0x000c)},   /* Nozomi HSDPA */
+       {},
+};
+
+MODULE_DEVICE_TABLE(pci, nozomi_pci_tbl);
+
+static struct nozomi *ndevs[NOZOMI_MAX_CARDS];
+static struct tty_driver *ntty_driver;
+
+static const struct tty_port_operations noz_tty_port_ops;
+
+/*
+ * find card by tty_index
+ */
+static inline struct nozomi *get_dc_by_tty(const struct tty_struct *tty)
+{
+       return tty ? ndevs[tty->index / MAX_PORT] : NULL;
+}
+
+static inline struct port *get_port_by_tty(const struct tty_struct *tty)
+{
+       struct nozomi *ndev = get_dc_by_tty(tty);
+       return ndev ? &ndev->port[tty->index % MAX_PORT] : NULL;
+}
+
+/*
+ * TODO:
+ * -Optimize
+ * -Rewrite cleaner
+ */
+
+static void read_mem32(u32 *buf, const void __iomem *mem_addr_start,
+                       u32 size_bytes)
+{
+       u32 i = 0;
+       const u32 __iomem *ptr = mem_addr_start;
+       u16 *buf16;
+
+       if (unlikely(!ptr || !buf))
+               goto out;
+
+       /* shortcut for extremely often used cases */
+       switch (size_bytes) {
+       case 2: /* 2 bytes */
+               buf16 = (u16 *) buf;
+               *buf16 = __le16_to_cpu(readw(ptr));
+               goto out;
+               break;
+       case 4: /* 4 bytes */
+               *(buf) = __le32_to_cpu(readl(ptr));
+               goto out;
+               break;
+       }
+
+       while (i < size_bytes) {
+               if (size_bytes - i == 2) {
+                       /* Handle 2 bytes in the end */
+                       buf16 = (u16 *) buf;
+                       *(buf16) = __le16_to_cpu(readw(ptr));
+                       i += 2;
+               } else {
+                       /* Read 4 bytes */
+                       *(buf) = __le32_to_cpu(readl(ptr));
+                       i += 4;
+               }
+               buf++;
+               ptr++;
+       }
+out:
+       return;
+}
+
+/*
+ * TODO:
+ * -Optimize
+ * -Rewrite cleaner
+ */
+static u32 write_mem32(void __iomem *mem_addr_start, const u32 *buf,
+                       u32 size_bytes)
+{
+       u32 i = 0;
+       u32 __iomem *ptr = mem_addr_start;
+       const u16 *buf16;
+
+       if (unlikely(!ptr || !buf))
+               return 0;
+
+       /* shortcut for extremely often used cases */
+       switch (size_bytes) {
+       case 2: /* 2 bytes */
+               buf16 = (const u16 *)buf;
+               writew(__cpu_to_le16(*buf16), ptr);
+               return 2;
+               break;
+       case 1: /*
+                * also needs to write 4 bytes in this case
+                * so falling through..
+                */
+       case 4: /* 4 bytes */
+               writel(__cpu_to_le32(*buf), ptr);
+               return 4;
+               break;
+       }
+
+       while (i < size_bytes) {
+               if (size_bytes - i == 2) {
+                       /* 2 bytes */
+                       buf16 = (const u16 *)buf;
+                       writew(__cpu_to_le16(*buf16), ptr);
+                       i += 2;
+               } else {
+                       /* 4 bytes */
+                       writel(__cpu_to_le32(*buf), ptr);
+                       i += 4;
+               }
+               buf++;
+               ptr++;
+       }
+       return i;
+}
+
+/* Setup pointers to different channels and also setup buffer sizes. */
+static void setup_memory(struct nozomi *dc)
+{
+       void __iomem *offset = dc->base_addr + dc->config_table.dl_start;
+       /* The length reported is including the length field of 4 bytes,
+        * hence subtract with 4.
+        */
+       const u16 buff_offset = 4;
+
+       /* Modem port dl configuration */
+       dc->port[PORT_MDM].dl_addr[CH_A] = offset;
+       dc->port[PORT_MDM].dl_addr[CH_B] =
+                               (offset += dc->config_table.dl_mdm_len1);
+       dc->port[PORT_MDM].dl_size[CH_A] =
+                               dc->config_table.dl_mdm_len1 - buff_offset;
+       dc->port[PORT_MDM].dl_size[CH_B] =
+                               dc->config_table.dl_mdm_len2 - buff_offset;
+
+       /* Diag port dl configuration */
+       dc->port[PORT_DIAG].dl_addr[CH_A] =
+                               (offset += dc->config_table.dl_mdm_len2);
+       dc->port[PORT_DIAG].dl_size[CH_A] =
+                               dc->config_table.dl_diag_len1 - buff_offset;
+       dc->port[PORT_DIAG].dl_addr[CH_B] =
+                               (offset += dc->config_table.dl_diag_len1);
+       dc->port[PORT_DIAG].dl_size[CH_B] =
+                               dc->config_table.dl_diag_len2 - buff_offset;
+
+       /* App1 port dl configuration */
+       dc->port[PORT_APP1].dl_addr[CH_A] =
+                               (offset += dc->config_table.dl_diag_len2);
+       dc->port[PORT_APP1].dl_size[CH_A] =
+                               dc->config_table.dl_app1_len - buff_offset;
+
+       /* App2 port dl configuration */
+       dc->port[PORT_APP2].dl_addr[CH_A] =
+                               (offset += dc->config_table.dl_app1_len);
+       dc->port[PORT_APP2].dl_size[CH_A] =
+                               dc->config_table.dl_app2_len - buff_offset;
+
+       /* Ctrl dl configuration */
+       dc->port[PORT_CTRL].dl_addr[CH_A] =
+                               (offset += dc->config_table.dl_app2_len);
+       dc->port[PORT_CTRL].dl_size[CH_A] =
+                               dc->config_table.dl_ctrl_len - buff_offset;
+
+       offset = dc->base_addr + dc->config_table.ul_start;
+
+       /* Modem Port ul configuration */
+       dc->port[PORT_MDM].ul_addr[CH_A] = offset;
+       dc->port[PORT_MDM].ul_size[CH_A] =
+                               dc->config_table.ul_mdm_len1 - buff_offset;
+       dc->port[PORT_MDM].ul_addr[CH_B] =
+                               (offset += dc->config_table.ul_mdm_len1);
+       dc->port[PORT_MDM].ul_size[CH_B] =
+                               dc->config_table.ul_mdm_len2 - buff_offset;
+
+       /* Diag port ul configuration */
+       dc->port[PORT_DIAG].ul_addr[CH_A] =
+                               (offset += dc->config_table.ul_mdm_len2);
+       dc->port[PORT_DIAG].ul_size[CH_A] =
+                               dc->config_table.ul_diag_len - buff_offset;
+
+       /* App1 port ul configuration */
+       dc->port[PORT_APP1].ul_addr[CH_A] =
+                               (offset += dc->config_table.ul_diag_len);
+       dc->port[PORT_APP1].ul_size[CH_A] =
+                               dc->config_table.ul_app1_len - buff_offset;
+
+       /* App2 port ul configuration */
+       dc->port[PORT_APP2].ul_addr[CH_A] =
+                               (offset += dc->config_table.ul_app1_len);
+       dc->port[PORT_APP2].ul_size[CH_A] =
+                               dc->config_table.ul_app2_len - buff_offset;
+
+       /* Ctrl ul configuration */
+       dc->port[PORT_CTRL].ul_addr[CH_A] =
+                               (offset += dc->config_table.ul_app2_len);
+       dc->port[PORT_CTRL].ul_size[CH_A] =
+                               dc->config_table.ul_ctrl_len - buff_offset;
+}
+
+/* Dump config table under initalization phase */
+#ifdef DEBUG
+static void dump_table(const struct nozomi *dc)
+{
+       DBG3("signature: 0x%08X", dc->config_table.signature);
+       DBG3("version: 0x%04X", dc->config_table.version);
+       DBG3("product_information: 0x%04X", \
+                               dc->config_table.product_information);
+       DBG3("toggle enabled: %d", dc->config_table.toggle.enabled);
+       DBG3("toggle up_mdm: %d", dc->config_table.toggle.mdm_ul);
+       DBG3("toggle dl_mdm: %d", dc->config_table.toggle.mdm_dl);
+       DBG3("toggle dl_dbg: %d", dc->config_table.toggle.diag_dl);
+
+       DBG3("dl_start: 0x%04X", dc->config_table.dl_start);
+       DBG3("dl_mdm_len0: 0x%04X, %d", dc->config_table.dl_mdm_len1,
+          dc->config_table.dl_mdm_len1);
+       DBG3("dl_mdm_len1: 0x%04X, %d", dc->config_table.dl_mdm_len2,
+          dc->config_table.dl_mdm_len2);
+       DBG3("dl_diag_len0: 0x%04X, %d", dc->config_table.dl_diag_len1,
+          dc->config_table.dl_diag_len1);
+       DBG3("dl_diag_len1: 0x%04X, %d", dc->config_table.dl_diag_len2,
+          dc->config_table.dl_diag_len2);
+       DBG3("dl_app1_len: 0x%04X, %d", dc->config_table.dl_app1_len,
+          dc->config_table.dl_app1_len);
+       DBG3("dl_app2_len: 0x%04X, %d", dc->config_table.dl_app2_len,
+          dc->config_table.dl_app2_len);
+       DBG3("dl_ctrl_len: 0x%04X, %d", dc->config_table.dl_ctrl_len,
+          dc->config_table.dl_ctrl_len);
+       DBG3("ul_start: 0x%04X, %d", dc->config_table.ul_start,
+          dc->config_table.ul_start);
+       DBG3("ul_mdm_len[0]: 0x%04X, %d", dc->config_table.ul_mdm_len1,
+          dc->config_table.ul_mdm_len1);
+       DBG3("ul_mdm_len[1]: 0x%04X, %d", dc->config_table.ul_mdm_len2,
+          dc->config_table.ul_mdm_len2);
+       DBG3("ul_diag_len: 0x%04X, %d", dc->config_table.ul_diag_len,
+          dc->config_table.ul_diag_len);
+       DBG3("ul_app1_len: 0x%04X, %d", dc->config_table.ul_app1_len,
+          dc->config_table.ul_app1_len);
+       DBG3("ul_app2_len: 0x%04X, %d", dc->config_table.ul_app2_len,
+          dc->config_table.ul_app2_len);
+       DBG3("ul_ctrl_len: 0x%04X, %d", dc->config_table.ul_ctrl_len,
+          dc->config_table.ul_ctrl_len);
+}
+#else
+static inline void dump_table(const struct nozomi *dc) { }
+#endif
+
+/*
+ * Read configuration table from card under intalization phase
+ * Returns 1 if ok, else 0
+ */
+static int nozomi_read_config_table(struct nozomi *dc)
+{
+       read_mem32((u32 *) &dc->config_table, dc->base_addr + 0,
+                                               sizeof(struct config_table));
+
+       if (dc->config_table.signature != CONFIG_MAGIC) {
+               dev_err(&dc->pdev->dev, "ConfigTable Bad! 0x%08X != 0x%08X\n",
+                       dc->config_table.signature, CONFIG_MAGIC);
+               return 0;
+       }
+
+       if ((dc->config_table.version == 0)
+           || (dc->config_table.toggle.enabled == TOGGLE_VALID)) {
+               int i;
+               DBG1("Second phase, configuring card");
+
+               setup_memory(dc);
+
+               dc->port[PORT_MDM].toggle_ul = dc->config_table.toggle.mdm_ul;
+               dc->port[PORT_MDM].toggle_dl = dc->config_table.toggle.mdm_dl;
+               dc->port[PORT_DIAG].toggle_dl = dc->config_table.toggle.diag_dl;
+               DBG1("toggle ports: MDM UL:%d MDM DL:%d, DIAG DL:%d",
+                  dc->port[PORT_MDM].toggle_ul,
+                  dc->port[PORT_MDM].toggle_dl, dc->port[PORT_DIAG].toggle_dl);
+
+               dump_table(dc);
+
+               for (i = PORT_MDM; i < MAX_PORT; i++) {
+                       memset(&dc->port[i].ctrl_dl, 0, sizeof(struct ctrl_dl));
+                       memset(&dc->port[i].ctrl_ul, 0, sizeof(struct ctrl_ul));
+               }
+
+               /* Enable control channel */
+               dc->last_ier = dc->last_ier | CTRL_DL;
+               writew(dc->last_ier, dc->reg_ier);
+
+               dc->state = NOZOMI_STATE_ALLOCATED;
+               dev_info(&dc->pdev->dev, "Initialization OK!\n");
+               return 1;
+       }
+
+       if ((dc->config_table.version > 0)
+           && (dc->config_table.toggle.enabled != TOGGLE_VALID)) {
+               u32 offset = 0;
+               DBG1("First phase: pushing upload buffers, clearing download");
+
+               dev_info(&dc->pdev->dev, "Version of card: %d\n",
+                        dc->config_table.version);
+
+               /* Here we should disable all I/O over F32. */
+               setup_memory(dc);
+
+               /*
+                * We should send ALL channel pair tokens back along
+                * with reset token
+                */
+
+               /* push upload modem buffers */
+               write_mem32(dc->port[PORT_MDM].ul_addr[CH_A],
+                       (u32 *) &offset, 4);
+               write_mem32(dc->port[PORT_MDM].ul_addr[CH_B],
+                       (u32 *) &offset, 4);
+
+               writew(MDM_UL | DIAG_DL | MDM_DL, dc->reg_fcr);
+
+               DBG1("First phase done");
+       }
+
+       return 1;
+}
+
+/* Enable uplink interrupts  */
+static void enable_transmit_ul(enum port_type port, struct nozomi *dc)
+{
+       static const u16 mask[] = {MDM_UL, DIAG_UL, APP1_UL, APP2_UL, CTRL_UL};
+
+       if (port < NOZOMI_MAX_PORTS) {
+               dc->last_ier |= mask[port];
+               writew(dc->last_ier, dc->reg_ier);
+       } else {
+               dev_err(&dc->pdev->dev, "Called with wrong port?\n");
+       }
+}
+
+/* Disable uplink interrupts  */
+static void disable_transmit_ul(enum port_type port, struct nozomi *dc)
+{
+       static const u16 mask[] =
+               {~MDM_UL, ~DIAG_UL, ~APP1_UL, ~APP2_UL, ~CTRL_UL};
+
+       if (port < NOZOMI_MAX_PORTS) {
+               dc->last_ier &= mask[port];
+               writew(dc->last_ier, dc->reg_ier);
+       } else {
+               dev_err(&dc->pdev->dev, "Called with wrong port?\n");
+       }
+}
+
+/* Enable downlink interrupts */
+static void enable_transmit_dl(enum port_type port, struct nozomi *dc)
+{
+       static const u16 mask[] = {MDM_DL, DIAG_DL, APP1_DL, APP2_DL, CTRL_DL};
+
+       if (port < NOZOMI_MAX_PORTS) {
+               dc->last_ier |= mask[port];
+               writew(dc->last_ier, dc->reg_ier);
+       } else {
+               dev_err(&dc->pdev->dev, "Called with wrong port?\n");
+       }
+}
+
+/* Disable downlink interrupts */
+static void disable_transmit_dl(enum port_type port, struct nozomi *dc)
+{
+       static const u16 mask[] =
+               {~MDM_DL, ~DIAG_DL, ~APP1_DL, ~APP2_DL, ~CTRL_DL};
+
+       if (port < NOZOMI_MAX_PORTS) {
+               dc->last_ier &= mask[port];
+               writew(dc->last_ier, dc->reg_ier);
+       } else {
+               dev_err(&dc->pdev->dev, "Called with wrong port?\n");
+       }
+}
+
+/*
+ * Return 1 - send buffer to card and ack.
+ * Return 0 - don't ack, don't send buffer to card.
+ */
+static int send_data(enum port_type index, struct nozomi *dc)
+{
+       u32 size = 0;
+       struct port *port = &dc->port[index];
+       const u8 toggle = port->toggle_ul;
+       void __iomem *addr = port->ul_addr[toggle];
+       const u32 ul_size = port->ul_size[toggle];
+       struct tty_struct *tty = tty_port_tty_get(&port->port);
+
+       /* Get data from tty and place in buf for now */
+       size = kfifo_out(&port->fifo_ul, dc->send_buf,
+                          ul_size < SEND_BUF_MAX ? ul_size : SEND_BUF_MAX);
+
+       if (size == 0) {
+               DBG4("No more data to send, disable link:");
+               tty_kref_put(tty);
+               return 0;
+       }
+
+       /* DUMP(buf, size); */
+
+       /* Write length + data */
+       write_mem32(addr, (u32 *) &size, 4);
+       write_mem32(addr + 4, (u32 *) dc->send_buf, size);
+
+       if (tty)
+               tty_wakeup(tty);
+
+       tty_kref_put(tty);
+       return 1;
+}
+
+/* If all data has been read, return 1, else 0 */
+static int receive_data(enum port_type index, struct nozomi *dc)
+{
+       u8 buf[RECEIVE_BUF_MAX] = { 0 };
+       int size;
+       u32 offset = 4;
+       struct port *port = &dc->port[index];
+       void __iomem *addr = port->dl_addr[port->toggle_dl];
+       struct tty_struct *tty = tty_port_tty_get(&port->port);
+       int i, ret;
+
+       if (unlikely(!tty)) {
+               DBG1("tty not open for port: %d?", index);
+               return 1;
+       }
+
+       read_mem32((u32 *) &size, addr, 4);
+       /*  DBG1( "%d bytes port: %d", size, index); */
+
+       if (test_bit(TTY_THROTTLED, &tty->flags)) {
+               DBG1("No room in tty, don't read data, don't ack interrupt, "
+                       "disable interrupt");
+
+               /* disable interrupt in downlink... */
+               disable_transmit_dl(index, dc);
+               ret = 0;
+               goto put;
+       }
+
+       if (unlikely(size == 0)) {
+               dev_err(&dc->pdev->dev, "size == 0?\n");
+               ret = 1;
+               goto put;
+       }
+
+       while (size > 0) {
+               read_mem32((u32 *) buf, addr + offset, RECEIVE_BUF_MAX);
+
+               if (size == 1) {
+                       tty_insert_flip_char(tty, buf[0], TTY_NORMAL);
+                       size = 0;
+               } else if (size < RECEIVE_BUF_MAX) {
+                       size -= tty_insert_flip_string(tty, (char *) buf, size);
+               } else {
+                       i = tty_insert_flip_string(tty, \
+                                               (char *) buf, RECEIVE_BUF_MAX);
+                       size -= i;
+                       offset += i;
+               }
+       }
+
+       set_bit(index, &dc->flip);
+       ret = 1;
+put:
+       tty_kref_put(tty);
+       return ret;
+}
+
+/* Debug for interrupts */
+#ifdef DEBUG
+static char *interrupt2str(u16 interrupt)
+{
+       static char buf[TMP_BUF_MAX];
+       char *p = buf;
+
+       interrupt & MDM_DL1 ? p += snprintf(p, TMP_BUF_MAX, "MDM_DL1 ") : NULL;
+       interrupt & MDM_DL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+                                       "MDM_DL2 ") : NULL;
+
+       interrupt & MDM_UL1 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+                                       "MDM_UL1 ") : NULL;
+       interrupt & MDM_UL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+                                       "MDM_UL2 ") : NULL;
+
+       interrupt & DIAG_DL1 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+                                       "DIAG_DL1 ") : NULL;
+       interrupt & DIAG_DL2 ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+                                       "DIAG_DL2 ") : NULL;
+
+       interrupt & DIAG_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+                                       "DIAG_UL ") : NULL;
+
+       interrupt & APP1_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+                                       "APP1_DL ") : NULL;
+       interrupt & APP2_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+                                       "APP2_DL ") : NULL;
+
+       interrupt & APP1_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+                                       "APP1_UL ") : NULL;
+       interrupt & APP2_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+                                       "APP2_UL ") : NULL;
+
+       interrupt & CTRL_DL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+                                       "CTRL_DL ") : NULL;
+       interrupt & CTRL_UL ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+                                       "CTRL_UL ") : NULL;
+
+       interrupt & RESET ? p += snprintf(p, TMP_BUF_MAX - (p - buf),
+                                       "RESET ") : NULL;
+
+       return buf;
+}
+#endif
+
+/*
+ * Receive flow control
+ * Return 1 - If ok, else 0
+ */
+static int receive_flow_control(struct nozomi *dc)
+{
+       enum port_type port = PORT_MDM;
+       struct ctrl_dl ctrl_dl;
+       struct ctrl_dl old_ctrl;
+       u16 enable_ier = 0;
+
+       read_mem32((u32 *) &ctrl_dl, dc->port[PORT_CTRL].dl_addr[CH_A], 2);
+
+       switch (ctrl_dl.port) {
+       case CTRL_CMD:
+               DBG1("The Base Band sends this value as a response to a "
+                       "request for IMSI detach sent over the control "
+                       "channel uplink (see section 7.6.1).");
+               break;
+       case CTRL_MDM:
+               port = PORT_MDM;
+               enable_ier = MDM_DL;
+               break;
+       case CTRL_DIAG:
+               port = PORT_DIAG;
+               enable_ier = DIAG_DL;
+               break;
+       case CTRL_APP1:
+               port = PORT_APP1;
+               enable_ier = APP1_DL;
+               break;
+       case CTRL_APP2:
+               port = PORT_APP2;
+               enable_ier = APP2_DL;
+               if (dc->state == NOZOMI_STATE_ALLOCATED) {
+                       /*
+                        * After card initialization the flow control
+                        * received for APP2 is always the last
+                        */
+                       dc->state = NOZOMI_STATE_READY;
+                       dev_info(&dc->pdev->dev, "Device READY!\n");
+               }
+               break;
+       default:
+               dev_err(&dc->pdev->dev,
+                       "ERROR: flow control received for non-existing port\n");
+               return 0;
+       };
+
+       DBG1("0x%04X->0x%04X", *((u16 *)&dc->port[port].ctrl_dl),
+          *((u16 *)&ctrl_dl));
+
+       old_ctrl = dc->port[port].ctrl_dl;
+       dc->port[port].ctrl_dl = ctrl_dl;
+
+       if (old_ctrl.CTS == 1 && ctrl_dl.CTS == 0) {
+               DBG1("Disable interrupt (0x%04X) on port: %d",
+                       enable_ier, port);
+               disable_transmit_ul(port, dc);
+
+       } else if (old_ctrl.CTS == 0 && ctrl_dl.CTS == 1) {
+
+               if (kfifo_len(&dc->port[port].fifo_ul)) {
+                       DBG1("Enable interrupt (0x%04X) on port: %d",
+                               enable_ier, port);
+                       DBG1("Data in buffer [%d], enable transmit! ",
+                               kfifo_len(&dc->port[port].fifo_ul));
+                       enable_transmit_ul(port, dc);
+               } else {
+                       DBG1("No data in buffer...");
+               }
+       }
+
+       if (*(u16 *)&old_ctrl == *(u16 *)&ctrl_dl) {
+               DBG1(" No change in mctrl");
+               return 1;
+       }
+       /* Update statistics */
+       if (old_ctrl.CTS != ctrl_dl.CTS)
+               dc->port[port].tty_icount.cts++;
+       if (old_ctrl.DSR != ctrl_dl.DSR)
+               dc->port[port].tty_icount.dsr++;
+       if (old_ctrl.RI != ctrl_dl.RI)
+               dc->port[port].tty_icount.rng++;
+       if (old_ctrl.DCD != ctrl_dl.DCD)
+               dc->port[port].tty_icount.dcd++;
+
+       wake_up_interruptible(&dc->port[port].tty_wait);
+
+       DBG1("port: %d DCD(%d), CTS(%d), RI(%d), DSR(%d)",
+          port,
+          dc->port[port].tty_icount.dcd, dc->port[port].tty_icount.cts,
+          dc->port[port].tty_icount.rng, dc->port[port].tty_icount.dsr);
+
+       return 1;
+}
+
+static enum ctrl_port_type port2ctrl(enum port_type port,
+                                       const struct nozomi *dc)
+{
+       switch (port) {
+       case PORT_MDM:
+               return CTRL_MDM;
+       case PORT_DIAG:
+               return CTRL_DIAG;
+       case PORT_APP1:
+               return CTRL_APP1;
+       case PORT_APP2:
+               return CTRL_APP2;
+       default:
+               dev_err(&dc->pdev->dev,
+                       "ERROR: send flow control " \
+                       "received for non-existing port\n");
+       };
+       return CTRL_ERROR;
+}
+
+/*
+ * Send flow control, can only update one channel at a time
+ * Return 0 - If we have updated all flow control
+ * Return 1 - If we need to update more flow control, ack current enable more
+ */
+static int send_flow_control(struct nozomi *dc)
+{
+       u32 i, more_flow_control_to_be_updated = 0;
+       u16 *ctrl;
+
+       for (i = PORT_MDM; i < MAX_PORT; i++) {
+               if (dc->port[i].update_flow_control) {
+                       if (more_flow_control_to_be_updated) {
+                               /* We have more flow control to be updated */
+                               return 1;
+                       }
+                       dc->port[i].ctrl_ul.port = port2ctrl(i, dc);
+                       ctrl = (u16 *)&dc->port[i].ctrl_ul;
+                       write_mem32(dc->port[PORT_CTRL].ul_addr[0], \
+                               (u32 *) ctrl, 2);
+                       dc->port[i].update_flow_control = 0;
+                       more_flow_control_to_be_updated = 1;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Handle downlink data, ports that are handled are modem and diagnostics
+ * Return 1 - ok
+ * Return 0 - toggle fields are out of sync
+ */
+static int handle_data_dl(struct nozomi *dc, enum port_type port, u8 *toggle,
+                       u16 read_iir, u16 mask1, u16 mask2)
+{
+       if (*toggle == 0 && read_iir & mask1) {
+               if (receive_data(port, dc)) {
+                       writew(mask1, dc->reg_fcr);
+                       *toggle = !(*toggle);
+               }
+
+               if (read_iir & mask2) {
+                       if (receive_data(port, dc)) {
+                               writew(mask2, dc->reg_fcr);
+                               *toggle = !(*toggle);
+                       }
+               }
+       } else if (*toggle == 1 && read_iir & mask2) {
+               if (receive_data(port, dc)) {
+                       writew(mask2, dc->reg_fcr);
+                       *toggle = !(*toggle);
+               }
+
+               if (read_iir & mask1) {
+                       if (receive_data(port, dc)) {
+                               writew(mask1, dc->reg_fcr);
+                               *toggle = !(*toggle);
+                       }
+               }
+       } else {
+               dev_err(&dc->pdev->dev, "port out of sync!, toggle:%d\n",
+                       *toggle);
+               return 0;
+       }
+       return 1;
+}
+
+/*
+ * Handle uplink data, this is currently for the modem port
+ * Return 1 - ok
+ * Return 0 - toggle field are out of sync
+ */
+static int handle_data_ul(struct nozomi *dc, enum port_type port, u16 read_iir)
+{
+       u8 *toggle = &(dc->port[port].toggle_ul);
+
+       if (*toggle == 0 && read_iir & MDM_UL1) {
+               dc->last_ier &= ~MDM_UL;
+               writew(dc->last_ier, dc->reg_ier);
+               if (send_data(port, dc)) {
+                       writew(MDM_UL1, dc->reg_fcr);
+                       dc->last_ier = dc->last_ier | MDM_UL;
+                       writew(dc->last_ier, dc->reg_ier);
+                       *toggle = !*toggle;
+               }
+
+               if (read_iir & MDM_UL2) {
+                       dc->last_ier &= ~MDM_UL;
+                       writew(dc->last_ier, dc->reg_ier);
+                       if (send_data(port, dc)) {
+                               writew(MDM_UL2, dc->reg_fcr);
+                               dc->last_ier = dc->last_ier | MDM_UL;
+                               writew(dc->last_ier, dc->reg_ier);
+                               *toggle = !*toggle;
+                       }
+               }
+
+       } else if (*toggle == 1 && read_iir & MDM_UL2) {
+               dc->last_ier &= ~MDM_UL;
+               writew(dc->last_ier, dc->reg_ier);
+               if (send_data(port, dc)) {
+                       writew(MDM_UL2, dc->reg_fcr);
+                       dc->last_ier = dc->last_ier | MDM_UL;
+                       writew(dc->last_ier, dc->reg_ier);
+                       *toggle = !*toggle;
+               }
+
+               if (read_iir & MDM_UL1) {
+                       dc->last_ier &= ~MDM_UL;
+                       writew(dc->last_ier, dc->reg_ier);
+                       if (send_data(port, dc)) {
+                               writew(MDM_UL1, dc->reg_fcr);
+                               dc->last_ier = dc->last_ier | MDM_UL;
+                               writew(dc->last_ier, dc->reg_ier);
+                               *toggle = !*toggle;
+                       }
+               }
+       } else {
+               writew(read_iir & MDM_UL, dc->reg_fcr);
+               dev_err(&dc->pdev->dev, "port out of sync!\n");
+               return 0;
+       }
+       return 1;
+}
+
+static irqreturn_t interrupt_handler(int irq, void *dev_id)
+{
+       struct nozomi *dc = dev_id;
+       unsigned int a;
+       u16 read_iir;
+
+       if (!dc)
+               return IRQ_NONE;
+
+       spin_lock(&dc->spin_mutex);
+       read_iir = readw(dc->reg_iir);
+
+       /* Card removed */
+       if (read_iir == (u16)-1)
+               goto none;
+       /*
+        * Just handle interrupt enabled in IER
+        * (by masking with dc->last_ier)
+        */
+       read_iir &= dc->last_ier;
+
+       if (read_iir == 0)
+               goto none;
+
+
+       DBG4("%s irq:0x%04X, prev:0x%04X", interrupt2str(read_iir), read_iir,
+               dc->last_ier);
+
+       if (read_iir & RESET) {
+               if (unlikely(!nozomi_read_config_table(dc))) {
+                       dc->last_ier = 0x0;
+                       writew(dc->last_ier, dc->reg_ier);
+                       dev_err(&dc->pdev->dev, "Could not read status from "
+                               "card, we should disable interface\n");
+               } else {
+                       writew(RESET, dc->reg_fcr);
+               }
+               /* No more useful info if this was the reset interrupt. */
+               goto exit_handler;
+       }
+       if (read_iir & CTRL_UL) {
+               DBG1("CTRL_UL");
+               dc->last_ier &= ~CTRL_UL;
+               writew(dc->last_ier, dc->reg_ier);
+               if (send_flow_control(dc)) {
+                       writew(CTRL_UL, dc->reg_fcr);
+                       dc->last_ier = dc->last_ier | CTRL_UL;
+                       writew(dc->last_ier, dc->reg_ier);
+               }
+       }
+       if (read_iir & CTRL_DL) {
+               receive_flow_control(dc);
+               writew(CTRL_DL, dc->reg_fcr);
+       }
+       if (read_iir & MDM_DL) {
+               if (!handle_data_dl(dc, PORT_MDM,
+                               &(dc->port[PORT_MDM].toggle_dl), read_iir,
+                               MDM_DL1, MDM_DL2)) {
+                       dev_err(&dc->pdev->dev, "MDM_DL out of sync!\n");
+                       goto exit_handler;
+               }
+       }
+       if (read_iir & MDM_UL) {
+               if (!handle_data_ul(dc, PORT_MDM, read_iir)) {
+                       dev_err(&dc->pdev->dev, "MDM_UL out of sync!\n");
+                       goto exit_handler;
+               }
+       }
+       if (read_iir & DIAG_DL) {
+               if (!handle_data_dl(dc, PORT_DIAG,
+                               &(dc->port[PORT_DIAG].toggle_dl), read_iir,
+                               DIAG_DL1, DIAG_DL2)) {
+                       dev_err(&dc->pdev->dev, "DIAG_DL out of sync!\n");
+                       goto exit_handler;
+               }
+       }
+       if (read_iir & DIAG_UL) {
+               dc->last_ier &= ~DIAG_UL;
+               writew(dc->last_ier, dc->reg_ier);
+               if (send_data(PORT_DIAG, dc)) {
+                       writew(DIAG_UL, dc->reg_fcr);
+                       dc->last_ier = dc->last_ier | DIAG_UL;
+                       writew(dc->last_ier, dc->reg_ier);
+               }
+       }
+       if (read_iir & APP1_DL) {
+               if (receive_data(PORT_APP1, dc))
+                       writew(APP1_DL, dc->reg_fcr);
+       }
+       if (read_iir & APP1_UL) {
+               dc->last_ier &= ~APP1_UL;
+               writew(dc->last_ier, dc->reg_ier);
+               if (send_data(PORT_APP1, dc)) {
+                       writew(APP1_UL, dc->reg_fcr);
+                       dc->last_ier = dc->last_ier | APP1_UL;
+                       writew(dc->last_ier, dc->reg_ier);
+               }
+       }
+       if (read_iir & APP2_DL) {
+               if (receive_data(PORT_APP2, dc))
+                       writew(APP2_DL, dc->reg_fcr);
+       }
+       if (read_iir & APP2_UL) {
+               dc->last_ier &= ~APP2_UL;
+               writew(dc->last_ier, dc->reg_ier);
+               if (send_data(PORT_APP2, dc)) {
+                       writew(APP2_UL, dc->reg_fcr);
+                       dc->last_ier = dc->last_ier | APP2_UL;
+                       writew(dc->last_ier, dc->reg_ier);
+               }
+       }
+
+exit_handler:
+       spin_unlock(&dc->spin_mutex);
+       for (a = 0; a < NOZOMI_MAX_PORTS; a++) {
+               struct tty_struct *tty;
+               if (test_and_clear_bit(a, &dc->flip)) {
+                       tty = tty_port_tty_get(&dc->port[a].port);
+                       if (tty)
+                               tty_flip_buffer_push(tty);
+                       tty_kref_put(tty);
+               }
+       }
+       return IRQ_HANDLED;
+none:
+       spin_unlock(&dc->spin_mutex);
+       return IRQ_NONE;
+}
+
+static void nozomi_get_card_type(struct nozomi *dc)
+{
+       int i;
+       u32 size = 0;
+
+       for (i = 0; i < 6; i++)
+               size += pci_resource_len(dc->pdev, i);
+
+       /* Assume card type F32_8 if no match */
+       dc->card_type = size == 2048 ? F32_2 : F32_8;
+
+       dev_info(&dc->pdev->dev, "Card type is: %d\n", dc->card_type);
+}
+
+static void nozomi_setup_private_data(struct nozomi *dc)
+{
+       void __iomem *offset = dc->base_addr + dc->card_type / 2;
+       unsigned int i;
+
+       dc->reg_fcr = (void __iomem *)(offset + R_FCR);
+       dc->reg_iir = (void __iomem *)(offset + R_IIR);
+       dc->reg_ier = (void __iomem *)(offset + R_IER);
+       dc->last_ier = 0;
+       dc->flip = 0;
+
+       dc->port[PORT_MDM].token_dl = MDM_DL;
+       dc->port[PORT_DIAG].token_dl = DIAG_DL;
+       dc->port[PORT_APP1].token_dl = APP1_DL;
+       dc->port[PORT_APP2].token_dl = APP2_DL;
+
+       for (i = 0; i < MAX_PORT; i++)
+               init_waitqueue_head(&dc->port[i].tty_wait);
+}
+
+static ssize_t card_type_show(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
+
+       return sprintf(buf, "%d\n", dc->card_type);
+}
+static DEVICE_ATTR(card_type, S_IRUGO, card_type_show, NULL);
+
+static ssize_t open_ttys_show(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
+
+       return sprintf(buf, "%u\n", dc->open_ttys);
+}
+static DEVICE_ATTR(open_ttys, S_IRUGO, open_ttys_show, NULL);
+
+static void make_sysfs_files(struct nozomi *dc)
+{
+       if (device_create_file(&dc->pdev->dev, &dev_attr_card_type))
+               dev_err(&dc->pdev->dev,
+                       "Could not create sysfs file for card_type\n");
+       if (device_create_file(&dc->pdev->dev, &dev_attr_open_ttys))
+               dev_err(&dc->pdev->dev,
+                       "Could not create sysfs file for open_ttys\n");
+}
+
+static void remove_sysfs_files(struct nozomi *dc)
+{
+       device_remove_file(&dc->pdev->dev, &dev_attr_card_type);
+       device_remove_file(&dc->pdev->dev, &dev_attr_open_ttys);
+}
+
+/* Allocate memory for one device */
+static int __devinit nozomi_card_init(struct pci_dev *pdev,
+                                     const struct pci_device_id *ent)
+{
+       resource_size_t start;
+       int ret;
+       struct nozomi *dc = NULL;
+       int ndev_idx;
+       int i;
+
+       dev_dbg(&pdev->dev, "Init, new card found\n");
+
+       for (ndev_idx = 0; ndev_idx < ARRAY_SIZE(ndevs); ndev_idx++)
+               if (!ndevs[ndev_idx])
+                       break;
+
+       if (ndev_idx >= ARRAY_SIZE(ndevs)) {
+               dev_err(&pdev->dev, "no free tty range for this card left\n");
+               ret = -EIO;
+               goto err;
+       }
+
+       dc = kzalloc(sizeof(struct nozomi), GFP_KERNEL);
+       if (unlikely(!dc)) {
+               dev_err(&pdev->dev, "Could not allocate memory\n");
+               ret = -ENOMEM;
+               goto err_free;
+       }
+
+       dc->pdev = pdev;
+
+       ret = pci_enable_device(dc->pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to enable PCI Device\n");
+               goto err_free;
+       }
+
+       ret = pci_request_regions(dc->pdev, NOZOMI_NAME);
+       if (ret) {
+               dev_err(&pdev->dev, "I/O address 0x%04x already in use\n",
+                       (int) /* nozomi_private.io_addr */ 0);
+               goto err_disable_device;
+       }
+
+       start = pci_resource_start(dc->pdev, 0);
+       if (start == 0) {
+               dev_err(&pdev->dev, "No I/O address for card detected\n");
+               ret = -ENODEV;
+               goto err_rel_regs;
+       }
+
+       /* Find out what card type it is */
+       nozomi_get_card_type(dc);
+
+       dc->base_addr = ioremap_nocache(start, dc->card_type);
+       if (!dc->base_addr) {
+               dev_err(&pdev->dev, "Unable to map card MMIO\n");
+               ret = -ENODEV;
+               goto err_rel_regs;
+       }
+
+       dc->send_buf = kmalloc(SEND_BUF_MAX, GFP_KERNEL);
+       if (!dc->send_buf) {
+               dev_err(&pdev->dev, "Could not allocate send buffer?\n");
+               ret = -ENOMEM;
+               goto err_free_sbuf;
+       }
+
+       for (i = PORT_MDM; i < MAX_PORT; i++) {
+               if (kfifo_alloc(&dc->port[i].fifo_ul,
+                     FIFO_BUFFER_SIZE_UL, GFP_ATOMIC)) {
+                       dev_err(&pdev->dev,
+                                       "Could not allocate kfifo buffer\n");
+                       ret = -ENOMEM;
+                       goto err_free_kfifo;
+               }
+       }
+
+       spin_lock_init(&dc->spin_mutex);
+
+       nozomi_setup_private_data(dc);
+
+       /* Disable all interrupts */
+       dc->last_ier = 0;
+       writew(dc->last_ier, dc->reg_ier);
+
+       ret = request_irq(pdev->irq, &interrupt_handler, IRQF_SHARED,
+                       NOZOMI_NAME, dc);
+       if (unlikely(ret)) {
+               dev_err(&pdev->dev, "can't request irq %d\n", pdev->irq);
+               goto err_free_kfifo;
+       }
+
+       DBG1("base_addr: %p", dc->base_addr);
+
+       make_sysfs_files(dc);
+
+       dc->index_start = ndev_idx * MAX_PORT;
+       ndevs[ndev_idx] = dc;
+
+       pci_set_drvdata(pdev, dc);
+
+       /* Enable RESET interrupt */
+       dc->last_ier = RESET;
+       iowrite16(dc->last_ier, dc->reg_ier);
+
+       dc->state = NOZOMI_STATE_ENABLED;
+
+       for (i = 0; i < MAX_PORT; i++) {
+               struct device *tty_dev;
+               struct port *port = &dc->port[i];
+               port->dc = dc;
+               mutex_init(&port->tty_sem);
+               tty_port_init(&port->port);
+               port->port.ops = &noz_tty_port_ops;
+               tty_dev = tty_register_device(ntty_driver, dc->index_start + i,
+                                                       &pdev->dev);
+
+               if (IS_ERR(tty_dev)) {
+                       ret = PTR_ERR(tty_dev);
+                       dev_err(&pdev->dev, "Could not allocate tty?\n");
+                       goto err_free_tty;
+               }
+       }
+
+       return 0;
+
+err_free_tty:
+       for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i)
+               tty_unregister_device(ntty_driver, i);
+err_free_kfifo:
+       for (i = 0; i < MAX_PORT; i++)
+               kfifo_free(&dc->port[i].fifo_ul);
+err_free_sbuf:
+       kfree(dc->send_buf);
+       iounmap(dc->base_addr);
+err_rel_regs:
+       pci_release_regions(pdev);
+err_disable_device:
+       pci_disable_device(pdev);
+err_free:
+       kfree(dc);
+err:
+       return ret;
+}
+
+static void __devexit tty_exit(struct nozomi *dc)
+{
+       unsigned int i;
+
+       DBG1(" ");
+
+       flush_scheduled_work();
+
+       for (i = 0; i < MAX_PORT; ++i) {
+               struct tty_struct *tty = tty_port_tty_get(&dc->port[i].port);
+               if (tty && list_empty(&tty->hangup_work.entry))
+                       tty_hangup(tty);
+               tty_kref_put(tty);
+       }
+       /* Racy below - surely should wait for scheduled work to be done or
+          complete off a hangup method ? */
+       while (dc->open_ttys)
+               msleep(1);
+       for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i)
+               tty_unregister_device(ntty_driver, i);
+}
+
+/* Deallocate memory for one device */
+static void __devexit nozomi_card_exit(struct pci_dev *pdev)
+{
+       int i;
+       struct ctrl_ul ctrl;
+       struct nozomi *dc = pci_get_drvdata(pdev);
+
+       /* Disable all interrupts */
+       dc->last_ier = 0;
+       writew(dc->last_ier, dc->reg_ier);
+
+       tty_exit(dc);
+
+       /* Send 0x0001, command card to resend the reset token.  */
+       /* This is to get the reset when the module is reloaded. */
+       ctrl.port = 0x00;
+       ctrl.reserved = 0;
+       ctrl.RTS = 0;
+       ctrl.DTR = 1;
+       DBG1("sending flow control 0x%04X", *((u16 *)&ctrl));
+
+       /* Setup dc->reg addresses to we can use defines here */
+       write_mem32(dc->port[PORT_CTRL].ul_addr[0], (u32 *)&ctrl, 2);
+       writew(CTRL_UL, dc->reg_fcr);   /* push the token to the card. */
+
+       remove_sysfs_files(dc);
+
+       free_irq(pdev->irq, dc);
+
+       for (i = 0; i < MAX_PORT; i++)
+               kfifo_free(&dc->port[i].fifo_ul);
+
+       kfree(dc->send_buf);
+
+       iounmap(dc->base_addr);
+
+       pci_release_regions(pdev);
+
+       pci_disable_device(pdev);
+
+       ndevs[dc->index_start / MAX_PORT] = NULL;
+
+       kfree(dc);
+}
+
+static void set_rts(const struct tty_struct *tty, int rts)
+{
+       struct port *port = get_port_by_tty(tty);
+
+       port->ctrl_ul.RTS = rts;
+       port->update_flow_control = 1;
+       enable_transmit_ul(PORT_CTRL, get_dc_by_tty(tty));
+}
+
+static void set_dtr(const struct tty_struct *tty, int dtr)
+{
+       struct port *port = get_port_by_tty(tty);
+
+       DBG1("SETTING DTR index: %d, dtr: %d", tty->index, dtr);
+
+       port->ctrl_ul.DTR = dtr;
+       port->update_flow_control = 1;
+       enable_transmit_ul(PORT_CTRL, get_dc_by_tty(tty));
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * TTY code
+ * ----------------------------------------------------------------------------
+ */
+
+static int ntty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+       struct port *port = get_port_by_tty(tty);
+       struct nozomi *dc = get_dc_by_tty(tty);
+       int ret;
+       if (!port || !dc || dc->state != NOZOMI_STATE_READY)
+               return -ENODEV;
+       ret = tty_init_termios(tty);
+       if (ret == 0) {
+               tty_driver_kref_get(driver);
+               tty->count++;
+               tty->driver_data = port;
+               driver->ttys[tty->index] = tty;
+       }
+       return ret;
+}
+
+static void ntty_cleanup(struct tty_struct *tty)
+{
+       tty->driver_data = NULL;
+}
+
+static int ntty_activate(struct tty_port *tport, struct tty_struct *tty)
+{
+       struct port *port = container_of(tport, struct port, port);
+       struct nozomi *dc = port->dc;
+       unsigned long flags;
+
+       DBG1("open: %d", port->token_dl);
+       spin_lock_irqsave(&dc->spin_mutex, flags);
+       dc->last_ier = dc->last_ier | port->token_dl;
+       writew(dc->last_ier, dc->reg_ier);
+       dc->open_ttys++;
+       spin_unlock_irqrestore(&dc->spin_mutex, flags);
+       printk("noz: activated %d: %p\n", tty->index, tport);
+       return 0;
+}
+
+static int ntty_open(struct tty_struct *tty, struct file *filp)
+{
+       struct port *port = tty->driver_data;
+       return tty_port_open(&port->port, tty, filp);
+}
+
+static void ntty_shutdown(struct tty_port *tport)
+{
+       struct port *port = container_of(tport, struct port, port);
+       struct nozomi *dc = port->dc;
+       unsigned long flags;
+
+       DBG1("close: %d", port->token_dl);
+       spin_lock_irqsave(&dc->spin_mutex, flags);
+       dc->last_ier &= ~(port->token_dl);
+       writew(dc->last_ier, dc->reg_ier);
+       dc->open_ttys--;
+       spin_unlock_irqrestore(&dc->spin_mutex, flags);
+       printk("noz: shutdown %p\n", tport);
+}
+
+static void ntty_close(struct tty_struct *tty, struct file *filp)
+{
+       struct port *port = tty->driver_data;
+       if (port)
+               tty_port_close(&port->port, tty, filp);
+}
+
+static void ntty_hangup(struct tty_struct *tty)
+{
+       struct port *port = tty->driver_data;
+       tty_port_hangup(&port->port);
+}
+
+/*
+ * called when the userspace process writes to the tty (/dev/noz*).
+ * Data is inserted into a fifo, which is then read and transfered to the modem.
+ */
+static int ntty_write(struct tty_struct *tty, const unsigned char *buffer,
+                     int count)
+{
+       int rval = -EINVAL;
+       struct nozomi *dc = get_dc_by_tty(tty);
+       struct port *port = tty->driver_data;
+       unsigned long flags;
+
+       /* DBG1( "WRITEx: %d, index = %d", count, index); */
+
+       if (!dc || !port)
+               return -ENODEV;
+
+       mutex_lock(&port->tty_sem);
+
+       if (unlikely(!port->port.count)) {
+               DBG1(" ");
+               goto exit;
+       }
+
+       rval = kfifo_in(&port->fifo_ul, (unsigned char *)buffer, count);
+
+       /* notify card */
+       if (unlikely(dc == NULL)) {
+               DBG1("No device context?");
+               goto exit;
+       }
+
+       spin_lock_irqsave(&dc->spin_mutex, flags);
+       /* CTS is only valid on the modem channel */
+       if (port == &(dc->port[PORT_MDM])) {
+               if (port->ctrl_dl.CTS) {
+                       DBG4("Enable interrupt");
+                       enable_transmit_ul(tty->index % MAX_PORT, dc);
+               } else {
+                       dev_err(&dc->pdev->dev,
+                               "CTS not active on modem port?\n");
+               }
+       } else {
+               enable_transmit_ul(tty->index % MAX_PORT, dc);
+       }
+       spin_unlock_irqrestore(&dc->spin_mutex, flags);
+
+exit:
+       mutex_unlock(&port->tty_sem);
+       return rval;
+}
+
+/*
+ * Calculate how much is left in device
+ * This method is called by the upper tty layer.
+ *   #according to sources N_TTY.c it expects a value >= 0 and
+ *    does not check for negative values.
+ *
+ * If the port is unplugged report lots of room and let the bits
+ * dribble away so we don't block anything.
+ */
+static int ntty_write_room(struct tty_struct *tty)
+{
+       struct port *port = tty->driver_data;
+       int room = 4096;
+       const struct nozomi *dc = get_dc_by_tty(tty);
+
+       if (dc) {
+               mutex_lock(&port->tty_sem);
+               if (port->port.count)
+                       room = kfifo_avail(&port->fifo_ul);
+               mutex_unlock(&port->tty_sem);
+       }
+       return room;
+}
+
+/* Gets io control parameters */
+static int ntty_tiocmget(struct tty_struct *tty)
+{
+       const struct port *port = tty->driver_data;
+       const struct ctrl_dl *ctrl_dl = &port->ctrl_dl;
+       const struct ctrl_ul *ctrl_ul = &port->ctrl_ul;
+
+       /* Note: these could change under us but it is not clear this
+          matters if so */
+       return  (ctrl_ul->RTS ? TIOCM_RTS : 0) |
+               (ctrl_ul->DTR ? TIOCM_DTR : 0) |
+               (ctrl_dl->DCD ? TIOCM_CAR : 0) |
+               (ctrl_dl->RI  ? TIOCM_RNG : 0) |
+               (ctrl_dl->DSR ? TIOCM_DSR : 0) |
+               (ctrl_dl->CTS ? TIOCM_CTS : 0);
+}
+
+/* Sets io controls parameters */
+static int ntty_tiocmset(struct tty_struct *tty,
+                                       unsigned int set, unsigned int clear)
+{
+       struct nozomi *dc = get_dc_by_tty(tty);
+       unsigned long flags;
+
+       spin_lock_irqsave(&dc->spin_mutex, flags);
+       if (set & TIOCM_RTS)
+               set_rts(tty, 1);
+       else if (clear & TIOCM_RTS)
+               set_rts(tty, 0);
+
+       if (set & TIOCM_DTR)
+               set_dtr(tty, 1);
+       else if (clear & TIOCM_DTR)
+               set_dtr(tty, 0);
+       spin_unlock_irqrestore(&dc->spin_mutex, flags);
+
+       return 0;
+}
+
+static int ntty_cflags_changed(struct port *port, unsigned long flags,
+               struct async_icount *cprev)
+{
+       const struct async_icount cnow = port->tty_icount;
+       int ret;
+
+       ret =   ((flags & TIOCM_RNG) && (cnow.rng != cprev->rng)) ||
+               ((flags & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) ||
+               ((flags & TIOCM_CD)  && (cnow.dcd != cprev->dcd)) ||
+               ((flags & TIOCM_CTS) && (cnow.cts != cprev->cts));
+
+       *cprev = cnow;
+
+       return ret;
+}
+
+static int ntty_tiocgicount(struct tty_struct *tty,
+                               struct serial_icounter_struct *icount)
+{
+       struct port *port = tty->driver_data;
+       const struct async_icount cnow = port->tty_icount;
+
+       icount->cts = cnow.cts;
+       icount->dsr = cnow.dsr;
+       icount->rng = cnow.rng;
+       icount->dcd = cnow.dcd;
+       icount->rx = cnow.rx;
+       icount->tx = cnow.tx;
+       icount->frame = cnow.frame;
+       icount->overrun = cnow.overrun;
+       icount->parity = cnow.parity;
+       icount->brk = cnow.brk;
+       icount->buf_overrun = cnow.buf_overrun;
+       return 0;
+}
+
+static int ntty_ioctl(struct tty_struct *tty,
+                     unsigned int cmd, unsigned long arg)
+{
+       struct port *port = tty->driver_data;
+       int rval = -ENOIOCTLCMD;
+
+       DBG1("******** IOCTL, cmd: %d", cmd);
+
+       switch (cmd) {
+       case TIOCMIWAIT: {
+               struct async_icount cprev = port->tty_icount;
+
+               rval = wait_event_interruptible(port->tty_wait,
+                               ntty_cflags_changed(port, arg, &cprev));
+               break;
+       }
+       default:
+               DBG1("ERR: 0x%08X, %d", cmd, cmd);
+               break;
+       };
+
+       return rval;
+}
+
+/*
+ * Called by the upper tty layer when tty buffers are ready
+ * to receive data again after a call to throttle.
+ */
+static void ntty_unthrottle(struct tty_struct *tty)
+{
+       struct nozomi *dc = get_dc_by_tty(tty);
+       unsigned long flags;
+
+       DBG1("UNTHROTTLE");
+       spin_lock_irqsave(&dc->spin_mutex, flags);
+       enable_transmit_dl(tty->index % MAX_PORT, dc);
+       set_rts(tty, 1);
+
+       spin_unlock_irqrestore(&dc->spin_mutex, flags);
+}
+
+/*
+ * Called by the upper tty layer when the tty buffers are almost full.
+ * The driver should stop send more data.
+ */
+static void ntty_throttle(struct tty_struct *tty)
+{
+       struct nozomi *dc = get_dc_by_tty(tty);
+       unsigned long flags;
+
+       DBG1("THROTTLE");
+       spin_lock_irqsave(&dc->spin_mutex, flags);
+       set_rts(tty, 0);
+       spin_unlock_irqrestore(&dc->spin_mutex, flags);
+}
+
+/* Returns number of chars in buffer, called by tty layer */
+static s32 ntty_chars_in_buffer(struct tty_struct *tty)
+{
+       struct port *port = tty->driver_data;
+       struct nozomi *dc = get_dc_by_tty(tty);
+       s32 rval = 0;
+
+       if (unlikely(!dc || !port)) {
+               goto exit_in_buffer;
+       }
+
+       if (unlikely(!port->port.count)) {
+               dev_err(&dc->pdev->dev, "No tty open?\n");
+               goto exit_in_buffer;
+       }
+
+       rval = kfifo_len(&port->fifo_ul);
+
+exit_in_buffer:
+       return rval;
+}
+
+static const struct tty_port_operations noz_tty_port_ops = {
+       .activate = ntty_activate,
+       .shutdown = ntty_shutdown,
+};
+
+static const struct tty_operations tty_ops = {
+       .ioctl = ntty_ioctl,
+       .open = ntty_open,
+       .close = ntty_close,
+       .hangup = ntty_hangup,
+       .write = ntty_write,
+       .write_room = ntty_write_room,
+       .unthrottle = ntty_unthrottle,
+       .throttle = ntty_throttle,
+       .chars_in_buffer = ntty_chars_in_buffer,
+       .tiocmget = ntty_tiocmget,
+       .tiocmset = ntty_tiocmset,
+       .get_icount = ntty_tiocgicount,
+       .install = ntty_install,
+       .cleanup = ntty_cleanup,
+};
+
+/* Module initialization */
+static struct pci_driver nozomi_driver = {
+       .name = NOZOMI_NAME,
+       .id_table = nozomi_pci_tbl,
+       .probe = nozomi_card_init,
+       .remove = __devexit_p(nozomi_card_exit),
+};
+
+static __init int nozomi_init(void)
+{
+       int ret;
+
+       printk(KERN_INFO "Initializing %s\n", VERSION_STRING);
+
+       ntty_driver = alloc_tty_driver(NTTY_TTY_MAXMINORS);
+       if (!ntty_driver)
+               return -ENOMEM;
+
+       ntty_driver->owner = THIS_MODULE;
+       ntty_driver->driver_name = NOZOMI_NAME_TTY;
+       ntty_driver->name = "noz";
+       ntty_driver->major = 0;
+       ntty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+       ntty_driver->subtype = SERIAL_TYPE_NORMAL;
+       ntty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+       ntty_driver->init_termios = tty_std_termios;
+       ntty_driver->init_termios.c_cflag = B115200 | CS8 | CREAD | \
+                                               HUPCL | CLOCAL;
+       ntty_driver->init_termios.c_ispeed = 115200;
+       ntty_driver->init_termios.c_ospeed = 115200;
+       tty_set_operations(ntty_driver, &tty_ops);
+
+       ret = tty_register_driver(ntty_driver);
+       if (ret) {
+               printk(KERN_ERR "Nozomi: failed to register ntty driver\n");
+               goto free_tty;
+       }
+
+       ret = pci_register_driver(&nozomi_driver);
+       if (ret) {
+               printk(KERN_ERR "Nozomi: can't register pci driver\n");
+               goto unr_tty;
+       }
+
+       return 0;
+unr_tty:
+       tty_unregister_driver(ntty_driver);
+free_tty:
+       put_tty_driver(ntty_driver);
+       return ret;
+}
+
+static __exit void nozomi_exit(void)
+{
+       printk(KERN_INFO "Unloading %s\n", DRIVER_DESC);
+       pci_unregister_driver(&nozomi_driver);
+       tty_unregister_driver(ntty_driver);
+       put_tty_driver(ntty_driver);
+}
+
+module_init(nozomi_init);
+module_exit(nozomi_exit);
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c
new file mode 100644 (file)
index 0000000..3780da8
--- /dev/null
@@ -0,0 +1,3199 @@
+/*
+ * RocketPort device driver for Linux
+ *
+ * Written by Theodore Ts'o, 1995, 1996, 1997, 1998, 1999, 2000.
+ * 
+ * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2003 by Comtrol, 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.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Kernel Synchronization:
+ *
+ * This driver has 2 kernel control paths - exception handlers (calls into the driver
+ * from user mode) and the timer bottom half (tasklet).  This is a polled driver, interrupts
+ * are not used.
+ *
+ * Critical data: 
+ * -  rp_table[], accessed through passed "info" pointers, is a global (static) array of 
+ *    serial port state information and the xmit_buf circular buffer.  Protected by 
+ *    a per port spinlock.
+ * -  xmit_flags[], an array of ints indexed by line (port) number, indicating that there
+ *    is data to be transmitted.  Protected by atomic bit operations.
+ * -  rp_num_ports, int indicating number of open ports, protected by atomic operations.
+ * 
+ * rp_write() and rp_write_char() functions use a per port semaphore to protect against
+ * simultaneous access to the same port by more than one process.
+ */
+
+/****** Defines ******/
+#define ROCKET_PARANOIA_CHECK
+#define ROCKET_DISABLE_SIMUSAGE
+
+#undef ROCKET_SOFT_FLOW
+#undef ROCKET_DEBUG_OPEN
+#undef ROCKET_DEBUG_INTR
+#undef ROCKET_DEBUG_WRITE
+#undef ROCKET_DEBUG_FLOW
+#undef ROCKET_DEBUG_THROTTLE
+#undef ROCKET_DEBUG_WAIT_UNTIL_SENT
+#undef ROCKET_DEBUG_RECEIVE
+#undef ROCKET_DEBUG_HANGUP
+#undef REV_PCI_ORDER
+#undef ROCKET_DEBUG_IO
+
+#define POLL_PERIOD HZ/100     /*  Polling period .01 seconds (10ms) */
+
+/****** Kernel includes ******/
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/major.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/mutex.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/wait.h>
+#include <linux/pci.h>
+#include <linux/uaccess.h>
+#include <asm/atomic.h>
+#include <asm/unaligned.h>
+#include <linux/bitops.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+
+/****** RocketPort includes ******/
+
+#include "rocket_int.h"
+#include "rocket.h"
+
+#define ROCKET_VERSION "2.09"
+#define ROCKET_DATE "12-June-2003"
+
+/****** RocketPort Local Variables ******/
+
+static void rp_do_poll(unsigned long dummy);
+
+static struct tty_driver *rocket_driver;
+
+static struct rocket_version driver_version = {        
+       ROCKET_VERSION, ROCKET_DATE
+};
+
+static struct r_port *rp_table[MAX_RP_PORTS];         /*  The main repository of serial port state information. */
+static unsigned int xmit_flags[NUM_BOARDS];           /*  Bit significant, indicates port had data to transmit. */
+                                                      /*  eg.  Bit 0 indicates port 0 has xmit data, ...        */
+static atomic_t rp_num_ports_open;                    /*  Number of serial ports open                           */
+static DEFINE_TIMER(rocket_timer, rp_do_poll, 0, 0);
+
+static unsigned long board1;                          /* ISA addresses, retrieved from rocketport.conf          */
+static unsigned long board2;
+static unsigned long board3;
+static unsigned long board4;
+static unsigned long controller;
+static int support_low_speed;
+static unsigned long modem1;
+static unsigned long modem2;
+static unsigned long modem3;
+static unsigned long modem4;
+static unsigned long pc104_1[8];
+static unsigned long pc104_2[8];
+static unsigned long pc104_3[8];
+static unsigned long pc104_4[8];
+static unsigned long *pc104[4] = { pc104_1, pc104_2, pc104_3, pc104_4 };
+
+static int rp_baud_base[NUM_BOARDS];                  /*  Board config info (Someday make a per-board structure)  */
+static unsigned long rcktpt_io_addr[NUM_BOARDS];
+static int rcktpt_type[NUM_BOARDS];
+static int is_PCI[NUM_BOARDS];
+static rocketModel_t rocketModel[NUM_BOARDS];
+static int max_board;
+static const struct tty_port_operations rocket_port_ops;
+
+/*
+ * The following arrays define the interrupt bits corresponding to each AIOP.
+ * These bits are different between the ISA and regular PCI boards and the
+ * Universal PCI boards.
+ */
+
+static Word_t aiop_intr_bits[AIOP_CTL_SIZE] = {
+       AIOP_INTR_BIT_0,
+       AIOP_INTR_BIT_1,
+       AIOP_INTR_BIT_2,
+       AIOP_INTR_BIT_3
+};
+
+static Word_t upci_aiop_intr_bits[AIOP_CTL_SIZE] = {
+       UPCI_AIOP_INTR_BIT_0,
+       UPCI_AIOP_INTR_BIT_1,
+       UPCI_AIOP_INTR_BIT_2,
+       UPCI_AIOP_INTR_BIT_3
+};
+
+static Byte_t RData[RDATASIZE] = {
+       0x00, 0x09, 0xf6, 0x82,
+       0x02, 0x09, 0x86, 0xfb,
+       0x04, 0x09, 0x00, 0x0a,
+       0x06, 0x09, 0x01, 0x0a,
+       0x08, 0x09, 0x8a, 0x13,
+       0x0a, 0x09, 0xc5, 0x11,
+       0x0c, 0x09, 0x86, 0x85,
+       0x0e, 0x09, 0x20, 0x0a,
+       0x10, 0x09, 0x21, 0x0a,
+       0x12, 0x09, 0x41, 0xff,
+       0x14, 0x09, 0x82, 0x00,
+       0x16, 0x09, 0x82, 0x7b,
+       0x18, 0x09, 0x8a, 0x7d,
+       0x1a, 0x09, 0x88, 0x81,
+       0x1c, 0x09, 0x86, 0x7a,
+       0x1e, 0x09, 0x84, 0x81,
+       0x20, 0x09, 0x82, 0x7c,
+       0x22, 0x09, 0x0a, 0x0a
+};
+
+static Byte_t RRegData[RREGDATASIZE] = {
+       0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */
+       0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */
+       0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */
+       0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */
+       0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */
+       0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */
+       0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */
+       0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */
+       0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */
+       0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */
+       0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */
+       0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */
+       0x22, 0x09, 0x0a, 0x0a  /* 30: Rx FIFO Enable */
+};
+
+static CONTROLLER_T sController[CTL_SIZE] = {
+       {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0},
+        {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}},
+       {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0},
+        {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}},
+       {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0},
+        {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}},
+       {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0},
+        {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}
+};
+
+static Byte_t sBitMapClrTbl[8] = {
+       0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f
+};
+
+static Byte_t sBitMapSetTbl[8] = {
+       0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
+};
+
+static int sClockPrescale = 0x14;
+
+/*
+ *  Line number is the ttySIx number (x), the Minor number.  We 
+ *  assign them sequentially, starting at zero.  The following 
+ *  array keeps track of the line number assigned to a given board/aiop/channel.
+ */
+static unsigned char lineNumbers[MAX_RP_PORTS];
+static unsigned long nextLineNumber;
+
+/*****  RocketPort Static Prototypes   *********/
+static int __init init_ISA(int i);
+static void rp_wait_until_sent(struct tty_struct *tty, int timeout);
+static void rp_flush_buffer(struct tty_struct *tty);
+static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model);
+static unsigned char GetLineNumber(int ctrl, int aiop, int ch);
+static unsigned char SetLineNumber(int ctrl, int aiop, int ch);
+static void rp_start(struct tty_struct *tty);
+static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum,
+                    int ChanNum);
+static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode);
+static void sFlushRxFIFO(CHANNEL_T * ChP);
+static void sFlushTxFIFO(CHANNEL_T * ChP);
+static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags);
+static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags);
+static void sModemReset(CONTROLLER_T * CtlP, int chan, int on);
+static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on);
+static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data);
+static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum,
+                             ByteIO_t * AiopIOList, int AiopIOListSize,
+                             WordIO_t ConfigIO, int IRQNum, Byte_t Frequency,
+                             int PeriodicOnly, int altChanRingIndicator,
+                             int UPCIRingInd);
+static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO,
+                          ByteIO_t * AiopIOList, int AiopIOListSize,
+                          int IRQNum, Byte_t Frequency, int PeriodicOnly);
+static int sReadAiopID(ByteIO_t io);
+static int sReadAiopNumChan(WordIO_t io);
+
+MODULE_AUTHOR("Theodore Ts'o");
+MODULE_DESCRIPTION("Comtrol RocketPort driver");
+module_param(board1, ulong, 0);
+MODULE_PARM_DESC(board1, "I/O port for (ISA) board #1");
+module_param(board2, ulong, 0);
+MODULE_PARM_DESC(board2, "I/O port for (ISA) board #2");
+module_param(board3, ulong, 0);
+MODULE_PARM_DESC(board3, "I/O port for (ISA) board #3");
+module_param(board4, ulong, 0);
+MODULE_PARM_DESC(board4, "I/O port for (ISA) board #4");
+module_param(controller, ulong, 0);
+MODULE_PARM_DESC(controller, "I/O port for (ISA) rocketport controller");
+module_param(support_low_speed, bool, 0);
+MODULE_PARM_DESC(support_low_speed, "1 means support 50 baud, 0 means support 460400 baud");
+module_param(modem1, ulong, 0);
+MODULE_PARM_DESC(modem1, "1 means (ISA) board #1 is a RocketModem");
+module_param(modem2, ulong, 0);
+MODULE_PARM_DESC(modem2, "1 means (ISA) board #2 is a RocketModem");
+module_param(modem3, ulong, 0);
+MODULE_PARM_DESC(modem3, "1 means (ISA) board #3 is a RocketModem");
+module_param(modem4, ulong, 0);
+MODULE_PARM_DESC(modem4, "1 means (ISA) board #4 is a RocketModem");
+module_param_array(pc104_1, ulong, NULL, 0);
+MODULE_PARM_DESC(pc104_1, "set interface types for ISA(PC104) board #1 (e.g. pc104_1=232,232,485,485,...");
+module_param_array(pc104_2, ulong, NULL, 0);
+MODULE_PARM_DESC(pc104_2, "set interface types for ISA(PC104) board #2 (e.g. pc104_2=232,232,485,485,...");
+module_param_array(pc104_3, ulong, NULL, 0);
+MODULE_PARM_DESC(pc104_3, "set interface types for ISA(PC104) board #3 (e.g. pc104_3=232,232,485,485,...");
+module_param_array(pc104_4, ulong, NULL, 0);
+MODULE_PARM_DESC(pc104_4, "set interface types for ISA(PC104) board #4 (e.g. pc104_4=232,232,485,485,...");
+
+static int rp_init(void);
+static void rp_cleanup_module(void);
+
+module_init(rp_init);
+module_exit(rp_cleanup_module);
+
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*************************************************************************/
+/*                     Module code starts here                           */
+
+static inline int rocket_paranoia_check(struct r_port *info,
+                                       const char *routine)
+{
+#ifdef ROCKET_PARANOIA_CHECK
+       if (!info)
+               return 1;
+       if (info->magic != RPORT_MAGIC) {
+               printk(KERN_WARNING "Warning: bad magic number for rocketport "
+                               "struct in %s\n", routine);
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+
+/*  Serial port receive data function.  Called (from timer poll) when an AIOPIC signals 
+ *  that receive data is present on a serial port.  Pulls data from FIFO, moves it into the 
+ *  tty layer.  
+ */
+static void rp_do_receive(struct r_port *info,
+                         struct tty_struct *tty,
+                         CHANNEL_t * cp, unsigned int ChanStatus)
+{
+       unsigned int CharNStat;
+       int ToRecv, wRecv, space;
+       unsigned char *cbuf;
+
+       ToRecv = sGetRxCnt(cp);
+#ifdef ROCKET_DEBUG_INTR
+       printk(KERN_INFO "rp_do_receive(%d)...\n", ToRecv);
+#endif
+       if (ToRecv == 0)
+               return;
+
+       /*
+        * if status indicates there are errored characters in the
+        * FIFO, then enter status mode (a word in FIFO holds
+        * character and status).
+        */
+       if (ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) {
+               if (!(ChanStatus & STATMODE)) {
+#ifdef ROCKET_DEBUG_RECEIVE
+                       printk(KERN_INFO "Entering STATMODE...\n");
+#endif
+                       ChanStatus |= STATMODE;
+                       sEnRxStatusMode(cp);
+               }
+       }
+
+       /* 
+        * if we previously entered status mode, then read down the
+        * FIFO one word at a time, pulling apart the character and
+        * the status.  Update error counters depending on status
+        */
+       if (ChanStatus & STATMODE) {
+#ifdef ROCKET_DEBUG_RECEIVE
+               printk(KERN_INFO "Ignore %x, read %x...\n",
+                       info->ignore_status_mask, info->read_status_mask);
+#endif
+               while (ToRecv) {
+                       char flag;
+
+                       CharNStat = sInW(sGetTxRxDataIO(cp));
+#ifdef ROCKET_DEBUG_RECEIVE
+                       printk(KERN_INFO "%x...\n", CharNStat);
+#endif
+                       if (CharNStat & STMBREAKH)
+                               CharNStat &= ~(STMFRAMEH | STMPARITYH);
+                       if (CharNStat & info->ignore_status_mask) {
+                               ToRecv--;
+                               continue;
+                       }
+                       CharNStat &= info->read_status_mask;
+                       if (CharNStat & STMBREAKH)
+                               flag = TTY_BREAK;
+                       else if (CharNStat & STMPARITYH)
+                               flag = TTY_PARITY;
+                       else if (CharNStat & STMFRAMEH)
+                               flag = TTY_FRAME;
+                       else if (CharNStat & STMRCVROVRH)
+                               flag = TTY_OVERRUN;
+                       else
+                               flag = TTY_NORMAL;
+                       tty_insert_flip_char(tty, CharNStat & 0xff, flag);
+                       ToRecv--;
+               }
+
+               /*
+                * after we've emptied the FIFO in status mode, turn
+                * status mode back off
+                */
+               if (sGetRxCnt(cp) == 0) {
+#ifdef ROCKET_DEBUG_RECEIVE
+                       printk(KERN_INFO "Status mode off.\n");
+#endif
+                       sDisRxStatusMode(cp);
+               }
+       } else {
+               /*
+                * we aren't in status mode, so read down the FIFO two
+                * characters at time by doing repeated word IO
+                * transfer.
+                */
+               space = tty_prepare_flip_string(tty, &cbuf, ToRecv);
+               if (space < ToRecv) {
+#ifdef ROCKET_DEBUG_RECEIVE
+                       printk(KERN_INFO "rp_do_receive:insufficient space ToRecv=%d space=%d\n", ToRecv, space);
+#endif
+                       if (space <= 0)
+                               return;
+                       ToRecv = space;
+               }
+               wRecv = ToRecv >> 1;
+               if (wRecv)
+                       sInStrW(sGetTxRxDataIO(cp), (unsigned short *) cbuf, wRecv);
+               if (ToRecv & 1)
+                       cbuf[ToRecv - 1] = sInB(sGetTxRxDataIO(cp));
+       }
+       /*  Push the data up to the tty layer */
+       tty_flip_buffer_push(tty);
+}
+
+/*
+ *  Serial port transmit data function.  Called from the timer polling loop as a 
+ *  result of a bit set in xmit_flags[], indicating data (from the tty layer) is ready
+ *  to be sent out the serial port.  Data is buffered in rp_table[line].xmit_buf, it is 
+ *  moved to the port's xmit FIFO.  *info is critical data, protected by spinlocks.
+ */
+static void rp_do_transmit(struct r_port *info)
+{
+       int c;
+       CHANNEL_t *cp = &info->channel;
+       struct tty_struct *tty;
+       unsigned long flags;
+
+#ifdef ROCKET_DEBUG_INTR
+       printk(KERN_DEBUG "%s\n", __func__);
+#endif
+       if (!info)
+               return;
+       tty = tty_port_tty_get(&info->port);
+
+       if (tty == NULL) {
+               printk(KERN_WARNING "rp: WARNING %s called with tty==NULL\n", __func__);
+               clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+               return;
+       }
+
+       spin_lock_irqsave(&info->slock, flags);
+       info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp);
+
+       /*  Loop sending data to FIFO until done or FIFO full */
+       while (1) {
+               if (tty->stopped || tty->hw_stopped)
+                       break;
+               c = min(info->xmit_fifo_room, info->xmit_cnt);
+               c = min(c, XMIT_BUF_SIZE - info->xmit_tail);
+               if (c <= 0 || info->xmit_fifo_room <= 0)
+                       break;
+               sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) (info->xmit_buf + info->xmit_tail), c / 2);
+               if (c & 1)
+                       sOutB(sGetTxRxDataIO(cp), info->xmit_buf[info->xmit_tail + c - 1]);
+               info->xmit_tail += c;
+               info->xmit_tail &= XMIT_BUF_SIZE - 1;
+               info->xmit_cnt -= c;
+               info->xmit_fifo_room -= c;
+#ifdef ROCKET_DEBUG_INTR
+               printk(KERN_INFO "tx %d chars...\n", c);
+#endif
+       }
+
+       if (info->xmit_cnt == 0)
+               clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+
+       if (info->xmit_cnt < WAKEUP_CHARS) {
+               tty_wakeup(tty);
+#ifdef ROCKETPORT_HAVE_POLL_WAIT
+               wake_up_interruptible(&tty->poll_wait);
+#endif
+       }
+
+       spin_unlock_irqrestore(&info->slock, flags);
+       tty_kref_put(tty);
+
+#ifdef ROCKET_DEBUG_INTR
+       printk(KERN_DEBUG "(%d,%d,%d,%d)...\n", info->xmit_cnt, info->xmit_head,
+              info->xmit_tail, info->xmit_fifo_room);
+#endif
+}
+
+/*
+ *  Called when a serial port signals it has read data in it's RX FIFO.
+ *  It checks what interrupts are pending and services them, including
+ *  receiving serial data.  
+ */
+static void rp_handle_port(struct r_port *info)
+{
+       CHANNEL_t *cp;
+       struct tty_struct *tty;
+       unsigned int IntMask, ChanStatus;
+
+       if (!info)
+               return;
+
+       if ((info->port.flags & ASYNC_INITIALIZED) == 0) {
+               printk(KERN_WARNING "rp: WARNING: rp_handle_port called with "
+                               "info->flags & NOT_INIT\n");
+               return;
+       }
+       tty = tty_port_tty_get(&info->port);
+       if (!tty) {
+               printk(KERN_WARNING "rp: WARNING: rp_handle_port called with "
+                               "tty==NULL\n");
+               return;
+       }
+       cp = &info->channel;
+
+       IntMask = sGetChanIntID(cp) & info->intmask;
+#ifdef ROCKET_DEBUG_INTR
+       printk(KERN_INFO "rp_interrupt %02x...\n", IntMask);
+#endif
+       ChanStatus = sGetChanStatus(cp);
+       if (IntMask & RXF_TRIG) {       /* Rx FIFO trigger level */
+               rp_do_receive(info, tty, cp, ChanStatus);
+       }
+       if (IntMask & DELTA_CD) {       /* CD change  */
+#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_INTR) || defined(ROCKET_DEBUG_HANGUP))
+               printk(KERN_INFO "ttyR%d CD now %s...\n", info->line,
+                      (ChanStatus & CD_ACT) ? "on" : "off");
+#endif
+               if (!(ChanStatus & CD_ACT) && info->cd_status) {
+#ifdef ROCKET_DEBUG_HANGUP
+                       printk(KERN_INFO "CD drop, calling hangup.\n");
+#endif
+                       tty_hangup(tty);
+               }
+               info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0;
+               wake_up_interruptible(&info->port.open_wait);
+       }
+#ifdef ROCKET_DEBUG_INTR
+       if (IntMask & DELTA_CTS) {      /* CTS change */
+               printk(KERN_INFO "CTS change...\n");
+       }
+       if (IntMask & DELTA_DSR) {      /* DSR change */
+               printk(KERN_INFO "DSR change...\n");
+       }
+#endif
+       tty_kref_put(tty);
+}
+
+/*
+ *  The top level polling routine.  Repeats every 1/100 HZ (10ms).
+ */
+static void rp_do_poll(unsigned long dummy)
+{
+       CONTROLLER_t *ctlp;
+       int ctrl, aiop, ch, line;
+       unsigned int xmitmask, i;
+       unsigned int CtlMask;
+       unsigned char AiopMask;
+       Word_t bit;
+
+       /*  Walk through all the boards (ctrl's) */
+       for (ctrl = 0; ctrl < max_board; ctrl++) {
+               if (rcktpt_io_addr[ctrl] <= 0)
+                       continue;
+
+               /*  Get a ptr to the board's control struct */
+               ctlp = sCtlNumToCtlPtr(ctrl);
+
+               /*  Get the interrupt status from the board */
+#ifdef CONFIG_PCI
+               if (ctlp->BusType == isPCI)
+                       CtlMask = sPCIGetControllerIntStatus(ctlp);
+               else
+#endif
+                       CtlMask = sGetControllerIntStatus(ctlp);
+
+               /*  Check if any AIOP read bits are set */
+               for (aiop = 0; CtlMask; aiop++) {
+                       bit = ctlp->AiopIntrBits[aiop];
+                       if (CtlMask & bit) {
+                               CtlMask &= ~bit;
+                               AiopMask = sGetAiopIntStatus(ctlp, aiop);
+
+                               /*  Check if any port read bits are set */
+                               for (ch = 0; AiopMask;  AiopMask >>= 1, ch++) {
+                                       if (AiopMask & 1) {
+
+                                               /*  Get the line number (/dev/ttyRx number). */
+                                               /*  Read the data from the port. */
+                                               line = GetLineNumber(ctrl, aiop, ch);
+                                               rp_handle_port(rp_table[line]);
+                                       }
+                               }
+                       }
+               }
+
+               xmitmask = xmit_flags[ctrl];
+
+               /*
+                *  xmit_flags contains bit-significant flags, indicating there is data
+                *  to xmit on the port. Bit 0 is port 0 on this board, bit 1 is port 
+                *  1, ... (32 total possible).  The variable i has the aiop and ch 
+                *  numbers encoded in it (port 0-7 are aiop0, 8-15 are aiop1, etc).
+                */
+               if (xmitmask) {
+                       for (i = 0; i < rocketModel[ctrl].numPorts; i++) {
+                               if (xmitmask & (1 << i)) {
+                                       aiop = (i & 0x18) >> 3;
+                                       ch = i & 0x07;
+                                       line = GetLineNumber(ctrl, aiop, ch);
+                                       rp_do_transmit(rp_table[line]);
+                               }
+                       }
+               }
+       }
+
+       /*
+        * Reset the timer so we get called at the next clock tick (10ms).
+        */
+       if (atomic_read(&rp_num_ports_open))
+               mod_timer(&rocket_timer, jiffies + POLL_PERIOD);
+}
+
+/*
+ *  Initializes the r_port structure for a port, as well as enabling the port on 
+ *  the board.  
+ *  Inputs:  board, aiop, chan numbers
+ */
+static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev)
+{
+       unsigned rocketMode;
+       struct r_port *info;
+       int line;
+       CONTROLLER_T *ctlp;
+
+       /*  Get the next available line number */
+       line = SetLineNumber(board, aiop, chan);
+
+       ctlp = sCtlNumToCtlPtr(board);
+
+       /*  Get a r_port struct for the port, fill it in and save it globally, indexed by line number */
+       info = kzalloc(sizeof (struct r_port), GFP_KERNEL);
+       if (!info) {
+               printk(KERN_ERR "Couldn't allocate info struct for line #%d\n",
+                               line);
+               return;
+       }
+
+       info->magic = RPORT_MAGIC;
+       info->line = line;
+       info->ctlp = ctlp;
+       info->board = board;
+       info->aiop = aiop;
+       info->chan = chan;
+       tty_port_init(&info->port);
+       info->port.ops = &rocket_port_ops;
+       init_completion(&info->close_wait);
+       info->flags &= ~ROCKET_MODE_MASK;
+       switch (pc104[board][line]) {
+       case 422:
+               info->flags |= ROCKET_MODE_RS422;
+               break;
+       case 485:
+               info->flags |= ROCKET_MODE_RS485;
+               break;
+       case 232:
+       default:
+               info->flags |= ROCKET_MODE_RS232;
+               break;
+       }
+
+       info->intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR;
+       if (sInitChan(ctlp, &info->channel, aiop, chan) == 0) {
+               printk(KERN_ERR "RocketPort sInitChan(%d, %d, %d) failed!\n",
+                               board, aiop, chan);
+               kfree(info);
+               return;
+       }
+
+       rocketMode = info->flags & ROCKET_MODE_MASK;
+
+       if ((info->flags & ROCKET_RTS_TOGGLE) || (rocketMode == ROCKET_MODE_RS485))
+               sEnRTSToggle(&info->channel);
+       else
+               sDisRTSToggle(&info->channel);
+
+       if (ctlp->boardType == ROCKET_TYPE_PC104) {
+               switch (rocketMode) {
+               case ROCKET_MODE_RS485:
+                       sSetInterfaceMode(&info->channel, InterfaceModeRS485);
+                       break;
+               case ROCKET_MODE_RS422:
+                       sSetInterfaceMode(&info->channel, InterfaceModeRS422);
+                       break;
+               case ROCKET_MODE_RS232:
+               default:
+                       if (info->flags & ROCKET_RTS_TOGGLE)
+                               sSetInterfaceMode(&info->channel, InterfaceModeRS232T);
+                       else
+                               sSetInterfaceMode(&info->channel, InterfaceModeRS232);
+                       break;
+               }
+       }
+       spin_lock_init(&info->slock);
+       mutex_init(&info->write_mtx);
+       rp_table[line] = info;
+       tty_register_device(rocket_driver, line, pci_dev ? &pci_dev->dev :
+                       NULL);
+}
+
+/*
+ *  Configures a rocketport port according to its termio settings.  Called from 
+ *  user mode into the driver (exception handler).  *info CD manipulation is spinlock protected.
+ */
+static void configure_r_port(struct tty_struct *tty, struct r_port *info,
+                            struct ktermios *old_termios)
+{
+       unsigned cflag;
+       unsigned long flags;
+       unsigned rocketMode;
+       int bits, baud, divisor;
+       CHANNEL_t *cp;
+       struct ktermios *t = tty->termios;
+
+       cp = &info->channel;
+       cflag = t->c_cflag;
+
+       /* Byte size and parity */
+       if ((cflag & CSIZE) == CS8) {
+               sSetData8(cp);
+               bits = 10;
+       } else {
+               sSetData7(cp);
+               bits = 9;
+       }
+       if (cflag & CSTOPB) {
+               sSetStop2(cp);
+               bits++;
+       } else {
+               sSetStop1(cp);
+       }
+
+       if (cflag & PARENB) {
+               sEnParity(cp);
+               bits++;
+               if (cflag & PARODD) {
+                       sSetOddParity(cp);
+               } else {
+                       sSetEvenParity(cp);
+               }
+       } else {
+               sDisParity(cp);
+       }
+
+       /* baud rate */
+       baud = tty_get_baud_rate(tty);
+       if (!baud)
+               baud = 9600;
+       divisor = ((rp_baud_base[info->board] + (baud >> 1)) / baud) - 1;
+       if ((divisor >= 8192 || divisor < 0) && old_termios) {
+               baud = tty_termios_baud_rate(old_termios);
+               if (!baud)
+                       baud = 9600;
+               divisor = (rp_baud_base[info->board] / baud) - 1;
+       }
+       if (divisor >= 8192 || divisor < 0) {
+               baud = 9600;
+               divisor = (rp_baud_base[info->board] / baud) - 1;
+       }
+       info->cps = baud / bits;
+       sSetBaud(cp, divisor);
+
+       /* FIXME: Should really back compute a baud rate from the divisor */
+       tty_encode_baud_rate(tty, baud, baud);
+
+       if (cflag & CRTSCTS) {
+               info->intmask |= DELTA_CTS;
+               sEnCTSFlowCtl(cp);
+       } else {
+               info->intmask &= ~DELTA_CTS;
+               sDisCTSFlowCtl(cp);
+       }
+       if (cflag & CLOCAL) {
+               info->intmask &= ~DELTA_CD;
+       } else {
+               spin_lock_irqsave(&info->slock, flags);
+               if (sGetChanStatus(cp) & CD_ACT)
+                       info->cd_status = 1;
+               else
+                       info->cd_status = 0;
+               info->intmask |= DELTA_CD;
+               spin_unlock_irqrestore(&info->slock, flags);
+       }
+
+       /*
+        * Handle software flow control in the board
+        */
+#ifdef ROCKET_SOFT_FLOW
+       if (I_IXON(tty)) {
+               sEnTxSoftFlowCtl(cp);
+               if (I_IXANY(tty)) {
+                       sEnIXANY(cp);
+               } else {
+                       sDisIXANY(cp);
+               }
+               sSetTxXONChar(cp, START_CHAR(tty));
+               sSetTxXOFFChar(cp, STOP_CHAR(tty));
+       } else {
+               sDisTxSoftFlowCtl(cp);
+               sDisIXANY(cp);
+               sClrTxXOFF(cp);
+       }
+#endif
+
+       /*
+        * Set up ignore/read mask words
+        */
+       info->read_status_mask = STMRCVROVRH | 0xFF;
+       if (I_INPCK(tty))
+               info->read_status_mask |= STMFRAMEH | STMPARITYH;
+       if (I_BRKINT(tty) || I_PARMRK(tty))
+               info->read_status_mask |= STMBREAKH;
+
+       /*
+        * Characters to ignore
+        */
+       info->ignore_status_mask = 0;
+       if (I_IGNPAR(tty))
+               info->ignore_status_mask |= STMFRAMEH | STMPARITYH;
+       if (I_IGNBRK(tty)) {
+               info->ignore_status_mask |= STMBREAKH;
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns too.  (For real raw support).
+                */
+               if (I_IGNPAR(tty))
+                       info->ignore_status_mask |= STMRCVROVRH;
+       }
+
+       rocketMode = info->flags & ROCKET_MODE_MASK;
+
+       if ((info->flags & ROCKET_RTS_TOGGLE)
+           || (rocketMode == ROCKET_MODE_RS485))
+               sEnRTSToggle(cp);
+       else
+               sDisRTSToggle(cp);
+
+       sSetRTS(&info->channel);
+
+       if (cp->CtlP->boardType == ROCKET_TYPE_PC104) {
+               switch (rocketMode) {
+               case ROCKET_MODE_RS485:
+                       sSetInterfaceMode(cp, InterfaceModeRS485);
+                       break;
+               case ROCKET_MODE_RS422:
+                       sSetInterfaceMode(cp, InterfaceModeRS422);
+                       break;
+               case ROCKET_MODE_RS232:
+               default:
+                       if (info->flags & ROCKET_RTS_TOGGLE)
+                               sSetInterfaceMode(cp, InterfaceModeRS232T);
+                       else
+                               sSetInterfaceMode(cp, InterfaceModeRS232);
+                       break;
+               }
+       }
+}
+
+static int carrier_raised(struct tty_port *port)
+{
+       struct r_port *info = container_of(port, struct r_port, port);
+       return (sGetChanStatusLo(&info->channel) & CD_ACT) ? 1 : 0;
+}
+
+static void dtr_rts(struct tty_port *port, int on)
+{
+       struct r_port *info = container_of(port, struct r_port, port);
+       if (on) {
+               sSetDTR(&info->channel);
+               sSetRTS(&info->channel);
+       } else {
+               sClrDTR(&info->channel);
+               sClrRTS(&info->channel);
+       }
+}
+
+/*
+ *  Exception handler that opens a serial port.  Creates xmit_buf storage, fills in 
+ *  port's r_port struct.  Initializes the port hardware.  
+ */
+static int rp_open(struct tty_struct *tty, struct file *filp)
+{
+       struct r_port *info;
+       struct tty_port *port;
+       int line = 0, retval;
+       CHANNEL_t *cp;
+       unsigned long page;
+
+       line = tty->index;
+       if (line < 0 || line >= MAX_RP_PORTS || ((info = rp_table[line]) == NULL))
+               return -ENXIO;
+       port = &info->port;
+       
+       page = __get_free_page(GFP_KERNEL);
+       if (!page)
+               return -ENOMEM;
+
+       if (port->flags & ASYNC_CLOSING) {
+               retval = wait_for_completion_interruptible(&info->close_wait);
+               free_page(page);
+               if (retval)
+                       return retval;
+               return ((port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS);
+       }
+
+       /*
+        * We must not sleep from here until the port is marked fully in use.
+        */
+       if (info->xmit_buf)
+               free_page(page);
+       else
+               info->xmit_buf = (unsigned char *) page;
+
+       tty->driver_data = info;
+       tty_port_tty_set(port, tty);
+
+       if (port->count++ == 0) {
+               atomic_inc(&rp_num_ports_open);
+
+#ifdef ROCKET_DEBUG_OPEN
+               printk(KERN_INFO "rocket mod++ = %d...\n",
+                               atomic_read(&rp_num_ports_open));
+#endif
+       }
+#ifdef ROCKET_DEBUG_OPEN
+       printk(KERN_INFO "rp_open ttyR%d, count=%d\n", info->line, info->port.count);
+#endif
+
+       /*
+        * Info->count is now 1; so it's safe to sleep now.
+        */
+       if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) {
+               cp = &info->channel;
+               sSetRxTrigger(cp, TRIG_1);
+               if (sGetChanStatus(cp) & CD_ACT)
+                       info->cd_status = 1;
+               else
+                       info->cd_status = 0;
+               sDisRxStatusMode(cp);
+               sFlushRxFIFO(cp);
+               sFlushTxFIFO(cp);
+
+               sEnInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN));
+               sSetRxTrigger(cp, TRIG_1);
+
+               sGetChanStatus(cp);
+               sDisRxStatusMode(cp);
+               sClrTxXOFF(cp);
+
+               sDisCTSFlowCtl(cp);
+               sDisTxSoftFlowCtl(cp);
+
+               sEnRxFIFO(cp);
+               sEnTransmit(cp);
+
+               set_bit(ASYNCB_INITIALIZED, &info->port.flags);
+
+               /*
+                * Set up the tty->alt_speed kludge
+                */
+               if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
+                       tty->alt_speed = 57600;
+               if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
+                       tty->alt_speed = 115200;
+               if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
+                       tty->alt_speed = 230400;
+               if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
+                       tty->alt_speed = 460800;
+
+               configure_r_port(tty, info, NULL);
+               if (tty->termios->c_cflag & CBAUD) {
+                       sSetDTR(cp);
+                       sSetRTS(cp);
+               }
+       }
+       /*  Starts (or resets) the maint polling loop */
+       mod_timer(&rocket_timer, jiffies + POLL_PERIOD);
+
+       retval = tty_port_block_til_ready(port, tty, filp);
+       if (retval) {
+#ifdef ROCKET_DEBUG_OPEN
+               printk(KERN_INFO "rp_open returning after block_til_ready with %d\n", retval);
+#endif
+               return retval;
+       }
+       return 0;
+}
+
+/*
+ *  Exception handler that closes a serial port. info->port.count is considered critical.
+ */
+static void rp_close(struct tty_struct *tty, struct file *filp)
+{
+       struct r_port *info = tty->driver_data;
+       struct tty_port *port = &info->port;
+       int timeout;
+       CHANNEL_t *cp;
+       
+       if (rocket_paranoia_check(info, "rp_close"))
+               return;
+
+#ifdef ROCKET_DEBUG_OPEN
+       printk(KERN_INFO "rp_close ttyR%d, count = %d\n", info->line, info->port.count);
+#endif
+
+       if (tty_port_close_start(port, tty, filp) == 0)
+               return;
+
+       mutex_lock(&port->mutex);
+       cp = &info->channel;
+       /*
+        * Before we drop DTR, make sure the UART transmitter
+        * has completely drained; this is especially
+        * important if there is a transmit FIFO!
+        */
+       timeout = (sGetTxCnt(cp) + 1) * HZ / info->cps;
+       if (timeout == 0)
+               timeout = 1;
+       rp_wait_until_sent(tty, timeout);
+       clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+
+       sDisTransmit(cp);
+       sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN));
+       sDisCTSFlowCtl(cp);
+       sDisTxSoftFlowCtl(cp);
+       sClrTxXOFF(cp);
+       sFlushRxFIFO(cp);
+       sFlushTxFIFO(cp);
+       sClrRTS(cp);
+       if (C_HUPCL(tty))
+               sClrDTR(cp);
+
+       rp_flush_buffer(tty);
+               
+       tty_ldisc_flush(tty);
+
+       clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+
+       /* We can't yet use tty_port_close_end as the buffer handling in this
+          driver is a bit different to the usual */
+
+       if (port->blocked_open) {
+               if (port->close_delay) {
+                       msleep_interruptible(jiffies_to_msecs(port->close_delay));
+               }
+               wake_up_interruptible(&port->open_wait);
+       } else {
+               if (info->xmit_buf) {
+                       free_page((unsigned long) info->xmit_buf);
+                       info->xmit_buf = NULL;
+               }
+       }
+       spin_lock_irq(&port->lock);
+       info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING | ASYNC_NORMAL_ACTIVE);
+       tty->closing = 0;
+       spin_unlock_irq(&port->lock);
+       mutex_unlock(&port->mutex);
+       tty_port_tty_set(port, NULL);
+
+       wake_up_interruptible(&port->close_wait);
+       complete_all(&info->close_wait);
+       atomic_dec(&rp_num_ports_open);
+
+#ifdef ROCKET_DEBUG_OPEN
+       printk(KERN_INFO "rocket mod-- = %d...\n",
+                       atomic_read(&rp_num_ports_open));
+       printk(KERN_INFO "rp_close ttyR%d complete shutdown\n", info->line);
+#endif
+
+}
+
+static void rp_set_termios(struct tty_struct *tty,
+                          struct ktermios *old_termios)
+{
+       struct r_port *info = tty->driver_data;
+       CHANNEL_t *cp;
+       unsigned cflag;
+
+       if (rocket_paranoia_check(info, "rp_set_termios"))
+               return;
+
+       cflag = tty->termios->c_cflag;
+
+       /*
+        * This driver doesn't support CS5 or CS6
+        */
+       if (((cflag & CSIZE) == CS5) || ((cflag & CSIZE) == CS6))
+               tty->termios->c_cflag =
+                   ((cflag & ~CSIZE) | (old_termios->c_cflag & CSIZE));
+       /* Or CMSPAR */
+       tty->termios->c_cflag &= ~CMSPAR;
+
+       configure_r_port(tty, info, old_termios);
+
+       cp = &info->channel;
+
+       /* Handle transition to B0 status */
+       if ((old_termios->c_cflag & CBAUD) && !(tty->termios->c_cflag & CBAUD)) {
+               sClrDTR(cp);
+               sClrRTS(cp);
+       }
+
+       /* Handle transition away from B0 status */
+       if (!(old_termios->c_cflag & CBAUD) && (tty->termios->c_cflag & CBAUD)) {
+               if (!tty->hw_stopped || !(tty->termios->c_cflag & CRTSCTS))
+                       sSetRTS(cp);
+               sSetDTR(cp);
+       }
+
+       if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               rp_start(tty);
+       }
+}
+
+static int rp_break(struct tty_struct *tty, int break_state)
+{
+       struct r_port *info = tty->driver_data;
+       unsigned long flags;
+
+       if (rocket_paranoia_check(info, "rp_break"))
+               return -EINVAL;
+
+       spin_lock_irqsave(&info->slock, flags);
+       if (break_state == -1)
+               sSendBreak(&info->channel);
+       else
+               sClrBreak(&info->channel);
+       spin_unlock_irqrestore(&info->slock, flags);
+       return 0;
+}
+
+/*
+ * sGetChanRI used to be a macro in rocket_int.h. When the functionality for
+ * the UPCI boards was added, it was decided to make this a function because
+ * the macro was getting too complicated. All cases except the first one
+ * (UPCIRingInd) are taken directly from the original macro.
+ */
+static int sGetChanRI(CHANNEL_T * ChP)
+{
+       CONTROLLER_t *CtlP = ChP->CtlP;
+       int ChanNum = ChP->ChanNum;
+       int RingInd = 0;
+
+       if (CtlP->UPCIRingInd)
+               RingInd = !(sInB(CtlP->UPCIRingInd) & sBitMapSetTbl[ChanNum]);
+       else if (CtlP->AltChanRingIndicator)
+               RingInd = sInB((ByteIO_t) (ChP->ChanStat + 8)) & DSR_ACT;
+       else if (CtlP->boardType == ROCKET_TYPE_PC104)
+               RingInd = !(sInB(CtlP->AiopIO[3]) & sBitMapSetTbl[ChanNum]);
+
+       return RingInd;
+}
+
+/********************************************************************************************/
+/*  Here are the routines used by rp_ioctl.  These are all called from exception handlers.  */
+
+/*
+ *  Returns the state of the serial modem control lines.  These next 2 functions 
+ *  are the way kernel versions > 2.5 handle modem control lines rather than IOCTLs.
+ */
+static int rp_tiocmget(struct tty_struct *tty)
+{
+       struct r_port *info = tty->driver_data;
+       unsigned int control, result, ChanStatus;
+
+       ChanStatus = sGetChanStatusLo(&info->channel);
+       control = info->channel.TxControl[3];
+       result = ((control & SET_RTS) ? TIOCM_RTS : 0) | 
+               ((control & SET_DTR) ?  TIOCM_DTR : 0) |
+               ((ChanStatus & CD_ACT) ? TIOCM_CAR : 0) |
+               (sGetChanRI(&info->channel) ? TIOCM_RNG : 0) |
+               ((ChanStatus & DSR_ACT) ? TIOCM_DSR : 0) |
+               ((ChanStatus & CTS_ACT) ? TIOCM_CTS : 0);
+
+       return result;
+}
+
+/* 
+ *  Sets the modem control lines
+ */
+static int rp_tiocmset(struct tty_struct *tty,
+                               unsigned int set, unsigned int clear)
+{
+       struct r_port *info = tty->driver_data;
+
+       if (set & TIOCM_RTS)
+               info->channel.TxControl[3] |= SET_RTS;
+       if (set & TIOCM_DTR)
+               info->channel.TxControl[3] |= SET_DTR;
+       if (clear & TIOCM_RTS)
+               info->channel.TxControl[3] &= ~SET_RTS;
+       if (clear & TIOCM_DTR)
+               info->channel.TxControl[3] &= ~SET_DTR;
+
+       out32(info->channel.IndexAddr, info->channel.TxControl);
+       return 0;
+}
+
+static int get_config(struct r_port *info, struct rocket_config __user *retinfo)
+{
+       struct rocket_config tmp;
+
+       if (!retinfo)
+               return -EFAULT;
+       memset(&tmp, 0, sizeof (tmp));
+       mutex_lock(&info->port.mutex);
+       tmp.line = info->line;
+       tmp.flags = info->flags;
+       tmp.close_delay = info->port.close_delay;
+       tmp.closing_wait = info->port.closing_wait;
+       tmp.port = rcktpt_io_addr[(info->line >> 5) & 3];
+       mutex_unlock(&info->port.mutex);
+
+       if (copy_to_user(retinfo, &tmp, sizeof (*retinfo)))
+               return -EFAULT;
+       return 0;
+}
+
+static int set_config(struct tty_struct *tty, struct r_port *info,
+                                       struct rocket_config __user *new_info)
+{
+       struct rocket_config new_serial;
+
+       if (copy_from_user(&new_serial, new_info, sizeof (new_serial)))
+               return -EFAULT;
+
+       mutex_lock(&info->port.mutex);
+       if (!capable(CAP_SYS_ADMIN))
+       {
+               if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK)) {
+                       mutex_unlock(&info->port.mutex);
+                       return -EPERM;
+               }
+               info->flags = ((info->flags & ~ROCKET_USR_MASK) | (new_serial.flags & ROCKET_USR_MASK));
+               configure_r_port(tty, info, NULL);
+               mutex_unlock(&info->port.mutex);
+               return 0;
+       }
+
+       info->flags = ((info->flags & ~ROCKET_FLAGS) | (new_serial.flags & ROCKET_FLAGS));
+       info->port.close_delay = new_serial.close_delay;
+       info->port.closing_wait = new_serial.closing_wait;
+
+       if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
+               tty->alt_speed = 57600;
+       if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
+               tty->alt_speed = 115200;
+       if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
+               tty->alt_speed = 230400;
+       if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
+               tty->alt_speed = 460800;
+       mutex_unlock(&info->port.mutex);
+
+       configure_r_port(tty, info, NULL);
+       return 0;
+}
+
+/*
+ *  This function fills in a rocket_ports struct with information
+ *  about what boards/ports are in the system.  This info is passed
+ *  to user space.  See setrocket.c where the info is used to create
+ *  the /dev/ttyRx ports.
+ */
+static int get_ports(struct r_port *info, struct rocket_ports __user *retports)
+{
+       struct rocket_ports tmp;
+       int board;
+
+       if (!retports)
+               return -EFAULT;
+       memset(&tmp, 0, sizeof (tmp));
+       tmp.tty_major = rocket_driver->major;
+
+       for (board = 0; board < 4; board++) {
+               tmp.rocketModel[board].model = rocketModel[board].model;
+               strcpy(tmp.rocketModel[board].modelString, rocketModel[board].modelString);
+               tmp.rocketModel[board].numPorts = rocketModel[board].numPorts;
+               tmp.rocketModel[board].loadrm2 = rocketModel[board].loadrm2;
+               tmp.rocketModel[board].startingPortNumber = rocketModel[board].startingPortNumber;
+       }
+       if (copy_to_user(retports, &tmp, sizeof (*retports)))
+               return -EFAULT;
+       return 0;
+}
+
+static int reset_rm2(struct r_port *info, void __user *arg)
+{
+       int reset;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (copy_from_user(&reset, arg, sizeof (int)))
+               return -EFAULT;
+       if (reset)
+               reset = 1;
+
+       if (rcktpt_type[info->board] != ROCKET_TYPE_MODEMII &&
+            rcktpt_type[info->board] != ROCKET_TYPE_MODEMIII)
+               return -EINVAL;
+
+       if (info->ctlp->BusType == isISA)
+               sModemReset(info->ctlp, info->chan, reset);
+       else
+               sPCIModemReset(info->ctlp, info->chan, reset);
+
+       return 0;
+}
+
+static int get_version(struct r_port *info, struct rocket_version __user *retvers)
+{
+       if (copy_to_user(retvers, &driver_version, sizeof (*retvers)))
+               return -EFAULT;
+       return 0;
+}
+
+/*  IOCTL call handler into the driver */
+static int rp_ioctl(struct tty_struct *tty,
+                   unsigned int cmd, unsigned long arg)
+{
+       struct r_port *info = tty->driver_data;
+       void __user *argp = (void __user *)arg;
+       int ret = 0;
+
+       if (cmd != RCKP_GET_PORTS && rocket_paranoia_check(info, "rp_ioctl"))
+               return -ENXIO;
+
+       switch (cmd) {
+       case RCKP_GET_STRUCT:
+               if (copy_to_user(argp, info, sizeof (struct r_port)))
+                       ret = -EFAULT;
+               break;
+       case RCKP_GET_CONFIG:
+               ret = get_config(info, argp);
+               break;
+       case RCKP_SET_CONFIG:
+               ret = set_config(tty, info, argp);
+               break;
+       case RCKP_GET_PORTS:
+               ret = get_ports(info, argp);
+               break;
+       case RCKP_RESET_RM2:
+               ret = reset_rm2(info, argp);
+               break;
+       case RCKP_GET_VERSION:
+               ret = get_version(info, argp);
+               break;
+       default:
+               ret = -ENOIOCTLCMD;
+       }
+       return ret;
+}
+
+static void rp_send_xchar(struct tty_struct *tty, char ch)
+{
+       struct r_port *info = tty->driver_data;
+       CHANNEL_t *cp;
+
+       if (rocket_paranoia_check(info, "rp_send_xchar"))
+               return;
+
+       cp = &info->channel;
+       if (sGetTxCnt(cp))
+               sWriteTxPrioByte(cp, ch);
+       else
+               sWriteTxByte(sGetTxRxDataIO(cp), ch);
+}
+
+static void rp_throttle(struct tty_struct *tty)
+{
+       struct r_port *info = tty->driver_data;
+       CHANNEL_t *cp;
+
+#ifdef ROCKET_DEBUG_THROTTLE
+       printk(KERN_INFO "throttle %s: %d....\n", tty->name,
+              tty->ldisc.chars_in_buffer(tty));
+#endif
+
+       if (rocket_paranoia_check(info, "rp_throttle"))
+               return;
+
+       cp = &info->channel;
+       if (I_IXOFF(tty))
+               rp_send_xchar(tty, STOP_CHAR(tty));
+
+       sClrRTS(&info->channel);
+}
+
+static void rp_unthrottle(struct tty_struct *tty)
+{
+       struct r_port *info = tty->driver_data;
+       CHANNEL_t *cp;
+#ifdef ROCKET_DEBUG_THROTTLE
+       printk(KERN_INFO "unthrottle %s: %d....\n", tty->name,
+              tty->ldisc.chars_in_buffer(tty));
+#endif
+
+       if (rocket_paranoia_check(info, "rp_throttle"))
+               return;
+
+       cp = &info->channel;
+       if (I_IXOFF(tty))
+               rp_send_xchar(tty, START_CHAR(tty));
+
+       sSetRTS(&info->channel);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rp_stop() and rp_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rp_stop(struct tty_struct *tty)
+{
+       struct r_port *info = tty->driver_data;
+
+#ifdef ROCKET_DEBUG_FLOW
+       printk(KERN_INFO "stop %s: %d %d....\n", tty->name,
+              info->xmit_cnt, info->xmit_fifo_room);
+#endif
+
+       if (rocket_paranoia_check(info, "rp_stop"))
+               return;
+
+       if (sGetTxCnt(&info->channel))
+               sDisTransmit(&info->channel);
+}
+
+static void rp_start(struct tty_struct *tty)
+{
+       struct r_port *info = tty->driver_data;
+
+#ifdef ROCKET_DEBUG_FLOW
+       printk(KERN_INFO "start %s: %d %d....\n", tty->name,
+              info->xmit_cnt, info->xmit_fifo_room);
+#endif
+
+       if (rocket_paranoia_check(info, "rp_stop"))
+               return;
+
+       sEnTransmit(&info->channel);
+       set_bit((info->aiop * 8) + info->chan,
+               (void *) &xmit_flags[info->board]);
+}
+
+/*
+ * rp_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       struct r_port *info = tty->driver_data;
+       CHANNEL_t *cp;
+       unsigned long orig_jiffies;
+       int check_time, exit_time;
+       int txcnt;
+
+       if (rocket_paranoia_check(info, "rp_wait_until_sent"))
+               return;
+
+       cp = &info->channel;
+
+       orig_jiffies = jiffies;
+#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
+       printk(KERN_INFO "In RP_wait_until_sent(%d) (jiff=%lu)...\n", timeout,
+              jiffies);
+       printk(KERN_INFO "cps=%d...\n", info->cps);
+#endif
+       while (1) {
+               txcnt = sGetTxCnt(cp);
+               if (!txcnt) {
+                       if (sGetChanStatusLo(cp) & TXSHRMT)
+                               break;
+                       check_time = (HZ / info->cps) / 5;
+               } else {
+                       check_time = HZ * txcnt / info->cps;
+               }
+               if (timeout) {
+                       exit_time = orig_jiffies + timeout - jiffies;
+                       if (exit_time <= 0)
+                               break;
+                       if (exit_time < check_time)
+                               check_time = exit_time;
+               }
+               if (check_time == 0)
+                       check_time = 1;
+#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
+               printk(KERN_INFO "txcnt = %d (jiff=%lu,check=%d)...\n", txcnt,
+                               jiffies, check_time);
+#endif
+               msleep_interruptible(jiffies_to_msecs(check_time));
+               if (signal_pending(current))
+                       break;
+       }
+       __set_current_state(TASK_RUNNING);
+#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
+       printk(KERN_INFO "txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies);
+#endif
+}
+
+/*
+ * rp_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rp_hangup(struct tty_struct *tty)
+{
+       CHANNEL_t *cp;
+       struct r_port *info = tty->driver_data;
+       unsigned long flags;
+
+       if (rocket_paranoia_check(info, "rp_hangup"))
+               return;
+
+#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_HANGUP))
+       printk(KERN_INFO "rp_hangup of ttyR%d...\n", info->line);
+#endif
+       rp_flush_buffer(tty);
+       spin_lock_irqsave(&info->port.lock, flags);
+       if (info->port.flags & ASYNC_CLOSING) {
+               spin_unlock_irqrestore(&info->port.lock, flags);
+               return;
+       }
+       if (info->port.count)
+               atomic_dec(&rp_num_ports_open);
+       clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+       spin_unlock_irqrestore(&info->port.lock, flags);
+
+       tty_port_hangup(&info->port);
+
+       cp = &info->channel;
+       sDisRxFIFO(cp);
+       sDisTransmit(cp);
+       sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN));
+       sDisCTSFlowCtl(cp);
+       sDisTxSoftFlowCtl(cp);
+       sClrTxXOFF(cp);
+       clear_bit(ASYNCB_INITIALIZED, &info->port.flags);
+
+       wake_up_interruptible(&info->port.open_wait);
+}
+
+/*
+ *  Exception handler - write char routine.  The RocketPort driver uses a
+ *  double-buffering strategy, with the twist that if the in-memory CPU
+ *  buffer is empty, and there's space in the transmit FIFO, the
+ *  writing routines will write directly to transmit FIFO.
+ *  Write buffer and counters protected by spinlocks
+ */
+static int rp_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct r_port *info = tty->driver_data;
+       CHANNEL_t *cp;
+       unsigned long flags;
+
+       if (rocket_paranoia_check(info, "rp_put_char"))
+               return 0;
+
+       /*
+        * Grab the port write mutex, locking out other processes that try to
+        * write to this port
+        */
+       mutex_lock(&info->write_mtx);
+
+#ifdef ROCKET_DEBUG_WRITE
+       printk(KERN_INFO "rp_put_char %c...\n", ch);
+#endif
+
+       spin_lock_irqsave(&info->slock, flags);
+       cp = &info->channel;
+
+       if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room == 0)
+               info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp);
+
+       if (tty->stopped || tty->hw_stopped || info->xmit_fifo_room == 0 || info->xmit_cnt != 0) {
+               info->xmit_buf[info->xmit_head++] = ch;
+               info->xmit_head &= XMIT_BUF_SIZE - 1;
+               info->xmit_cnt++;
+               set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+       } else {
+               sOutB(sGetTxRxDataIO(cp), ch);
+               info->xmit_fifo_room--;
+       }
+       spin_unlock_irqrestore(&info->slock, flags);
+       mutex_unlock(&info->write_mtx);
+       return 1;
+}
+
+/*
+ *  Exception handler - write routine, called when user app writes to the device.
+ *  A per port write mutex is used to protect from another process writing to
+ *  this port at the same time.  This other process could be running on the other CPU
+ *  or get control of the CPU if the copy_from_user() blocks due to a page fault (swapped out). 
+ *  Spinlocks protect the info xmit members.
+ */
+static int rp_write(struct tty_struct *tty,
+                   const unsigned char *buf, int count)
+{
+       struct r_port *info = tty->driver_data;
+       CHANNEL_t *cp;
+       const unsigned char *b;
+       int c, retval = 0;
+       unsigned long flags;
+
+       if (count <= 0 || rocket_paranoia_check(info, "rp_write"))
+               return 0;
+
+       if (mutex_lock_interruptible(&info->write_mtx))
+               return -ERESTARTSYS;
+
+#ifdef ROCKET_DEBUG_WRITE
+       printk(KERN_INFO "rp_write %d chars...\n", count);
+#endif
+       cp = &info->channel;
+
+       if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room < count)
+               info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp);
+
+        /*
+        *  If the write queue for the port is empty, and there is FIFO space, stuff bytes 
+        *  into FIFO.  Use the write queue for temp storage.
+         */
+       if (!tty->stopped && !tty->hw_stopped && info->xmit_cnt == 0 && info->xmit_fifo_room > 0) {
+               c = min(count, info->xmit_fifo_room);
+               b = buf;
+
+               /*  Push data into FIFO, 2 bytes at a time */
+               sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) b, c / 2);
+
+               /*  If there is a byte remaining, write it */
+               if (c & 1)
+                       sOutB(sGetTxRxDataIO(cp), b[c - 1]);
+
+               retval += c;
+               buf += c;
+               count -= c;
+
+               spin_lock_irqsave(&info->slock, flags);
+               info->xmit_fifo_room -= c;
+               spin_unlock_irqrestore(&info->slock, flags);
+       }
+
+       /* If count is zero, we wrote it all and are done */
+       if (!count)
+               goto end;
+
+       /*  Write remaining data into the port's xmit_buf */
+       while (1) {
+               /* Hung up ? */
+               if (!test_bit(ASYNCB_NORMAL_ACTIVE, &info->port.flags))
+                       goto end;
+               c = min(count, XMIT_BUF_SIZE - info->xmit_cnt - 1);
+               c = min(c, XMIT_BUF_SIZE - info->xmit_head);
+               if (c <= 0)
+                       break;
+
+               b = buf;
+               memcpy(info->xmit_buf + info->xmit_head, b, c);
+
+               spin_lock_irqsave(&info->slock, flags);
+               info->xmit_head =
+                   (info->xmit_head + c) & (XMIT_BUF_SIZE - 1);
+               info->xmit_cnt += c;
+               spin_unlock_irqrestore(&info->slock, flags);
+
+               buf += c;
+               count -= c;
+               retval += c;
+       }
+
+       if ((retval > 0) && !tty->stopped && !tty->hw_stopped)
+               set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+       
+end:
+       if (info->xmit_cnt < WAKEUP_CHARS) {
+               tty_wakeup(tty);
+#ifdef ROCKETPORT_HAVE_POLL_WAIT
+               wake_up_interruptible(&tty->poll_wait);
+#endif
+       }
+       mutex_unlock(&info->write_mtx);
+       return retval;
+}
+
+/*
+ * Return the number of characters that can be sent.  We estimate
+ * only using the in-memory transmit buffer only, and ignore the
+ * potential space in the transmit FIFO.
+ */
+static int rp_write_room(struct tty_struct *tty)
+{
+       struct r_port *info = tty->driver_data;
+       int ret;
+
+       if (rocket_paranoia_check(info, "rp_write_room"))
+               return 0;
+
+       ret = XMIT_BUF_SIZE - info->xmit_cnt - 1;
+       if (ret < 0)
+               ret = 0;
+#ifdef ROCKET_DEBUG_WRITE
+       printk(KERN_INFO "rp_write_room returns %d...\n", ret);
+#endif
+       return ret;
+}
+
+/*
+ * Return the number of characters in the buffer.  Again, this only
+ * counts those characters in the in-memory transmit buffer.
+ */
+static int rp_chars_in_buffer(struct tty_struct *tty)
+{
+       struct r_port *info = tty->driver_data;
+       CHANNEL_t *cp;
+
+       if (rocket_paranoia_check(info, "rp_chars_in_buffer"))
+               return 0;
+
+       cp = &info->channel;
+
+#ifdef ROCKET_DEBUG_WRITE
+       printk(KERN_INFO "rp_chars_in_buffer returns %d...\n", info->xmit_cnt);
+#endif
+       return info->xmit_cnt;
+}
+
+/*
+ *  Flushes the TX fifo for a port, deletes data in the xmit_buf stored in the
+ *  r_port struct for the port.  Note that spinlock are used to protect info members,
+ *  do not call this function if the spinlock is already held.
+ */
+static void rp_flush_buffer(struct tty_struct *tty)
+{
+       struct r_port *info = tty->driver_data;
+       CHANNEL_t *cp;
+       unsigned long flags;
+
+       if (rocket_paranoia_check(info, "rp_flush_buffer"))
+               return;
+
+       spin_lock_irqsave(&info->slock, flags);
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       spin_unlock_irqrestore(&info->slock, flags);
+
+#ifdef ROCKETPORT_HAVE_POLL_WAIT
+       wake_up_interruptible(&tty->poll_wait);
+#endif
+       tty_wakeup(tty);
+
+       cp = &info->channel;
+       sFlushTxFIFO(cp);
+}
+
+#ifdef CONFIG_PCI
+
+static struct pci_device_id __devinitdata __used rocket_pci_ids[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_ANY_ID) },
+       { }
+};
+MODULE_DEVICE_TABLE(pci, rocket_pci_ids);
+
+/*
+ *  Called when a PCI card is found.  Retrieves and stores model information,
+ *  init's aiopic and serial port hardware.
+ *  Inputs:  i is the board number (0-n)
+ */
+static __init int register_PCI(int i, struct pci_dev *dev)
+{
+       int num_aiops, aiop, max_num_aiops, num_chan, chan;
+       unsigned int aiopio[MAX_AIOPS_PER_BOARD];
+       char *str, *board_type;
+       CONTROLLER_t *ctlp;
+
+       int fast_clock = 0;
+       int altChanRingIndicator = 0;
+       int ports_per_aiop = 8;
+       WordIO_t ConfigIO = 0;
+       ByteIO_t UPCIRingInd = 0;
+
+       if (!dev || pci_enable_device(dev))
+               return 0;
+
+       rcktpt_io_addr[i] = pci_resource_start(dev, 0);
+
+       rcktpt_type[i] = ROCKET_TYPE_NORMAL;
+       rocketModel[i].loadrm2 = 0;
+       rocketModel[i].startingPortNumber = nextLineNumber;
+
+       /*  Depending on the model, set up some config variables */
+       switch (dev->device) {
+       case PCI_DEVICE_ID_RP4QUAD:
+               str = "Quadcable";
+               max_num_aiops = 1;
+               ports_per_aiop = 4;
+               rocketModel[i].model = MODEL_RP4QUAD;
+               strcpy(rocketModel[i].modelString, "RocketPort 4 port w/quad cable");
+               rocketModel[i].numPorts = 4;
+               break;
+       case PCI_DEVICE_ID_RP8OCTA:
+               str = "Octacable";
+               max_num_aiops = 1;
+               rocketModel[i].model = MODEL_RP8OCTA;
+               strcpy(rocketModel[i].modelString, "RocketPort 8 port w/octa cable");
+               rocketModel[i].numPorts = 8;
+               break;
+       case PCI_DEVICE_ID_URP8OCTA:
+               str = "Octacable";
+               max_num_aiops = 1;
+               rocketModel[i].model = MODEL_UPCI_RP8OCTA;
+               strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/octa cable");
+               rocketModel[i].numPorts = 8;
+               break;
+       case PCI_DEVICE_ID_RP8INTF:
+               str = "8";
+               max_num_aiops = 1;
+               rocketModel[i].model = MODEL_RP8INTF;
+               strcpy(rocketModel[i].modelString, "RocketPort 8 port w/external I/F");
+               rocketModel[i].numPorts = 8;
+               break;
+       case PCI_DEVICE_ID_URP8INTF:
+               str = "8";
+               max_num_aiops = 1;
+               rocketModel[i].model = MODEL_UPCI_RP8INTF;
+               strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/external I/F");
+               rocketModel[i].numPorts = 8;
+               break;
+       case PCI_DEVICE_ID_RP8J:
+               str = "8J";
+               max_num_aiops = 1;
+               rocketModel[i].model = MODEL_RP8J;
+               strcpy(rocketModel[i].modelString, "RocketPort 8 port w/RJ11 connectors");
+               rocketModel[i].numPorts = 8;
+               break;
+       case PCI_DEVICE_ID_RP4J:
+               str = "4J";
+               max_num_aiops = 1;
+               ports_per_aiop = 4;
+               rocketModel[i].model = MODEL_RP4J;
+               strcpy(rocketModel[i].modelString, "RocketPort 4 port w/RJ45 connectors");
+               rocketModel[i].numPorts = 4;
+               break;
+       case PCI_DEVICE_ID_RP8SNI:
+               str = "8 (DB78 Custom)";
+               max_num_aiops = 1;
+               rocketModel[i].model = MODEL_RP8SNI;
+               strcpy(rocketModel[i].modelString, "RocketPort 8 port w/ custom DB78");
+               rocketModel[i].numPorts = 8;
+               break;
+       case PCI_DEVICE_ID_RP16SNI:
+               str = "16 (DB78 Custom)";
+               max_num_aiops = 2;
+               rocketModel[i].model = MODEL_RP16SNI;
+               strcpy(rocketModel[i].modelString, "RocketPort 16 port w/ custom DB78");
+               rocketModel[i].numPorts = 16;
+               break;
+       case PCI_DEVICE_ID_RP16INTF:
+               str = "16";
+               max_num_aiops = 2;
+               rocketModel[i].model = MODEL_RP16INTF;
+               strcpy(rocketModel[i].modelString, "RocketPort 16 port w/external I/F");
+               rocketModel[i].numPorts = 16;
+               break;
+       case PCI_DEVICE_ID_URP16INTF:
+               str = "16";
+               max_num_aiops = 2;
+               rocketModel[i].model = MODEL_UPCI_RP16INTF;
+               strcpy(rocketModel[i].modelString, "RocketPort UPCI 16 port w/external I/F");
+               rocketModel[i].numPorts = 16;
+               break;
+       case PCI_DEVICE_ID_CRP16INTF:
+               str = "16";
+               max_num_aiops = 2;
+               rocketModel[i].model = MODEL_CPCI_RP16INTF;
+               strcpy(rocketModel[i].modelString, "RocketPort Compact PCI 16 port w/external I/F");
+               rocketModel[i].numPorts = 16;
+               break;
+       case PCI_DEVICE_ID_RP32INTF:
+               str = "32";
+               max_num_aiops = 4;
+               rocketModel[i].model = MODEL_RP32INTF;
+               strcpy(rocketModel[i].modelString, "RocketPort 32 port w/external I/F");
+               rocketModel[i].numPorts = 32;
+               break;
+       case PCI_DEVICE_ID_URP32INTF:
+               str = "32";
+               max_num_aiops = 4;
+               rocketModel[i].model = MODEL_UPCI_RP32INTF;
+               strcpy(rocketModel[i].modelString, "RocketPort UPCI 32 port w/external I/F");
+               rocketModel[i].numPorts = 32;
+               break;
+       case PCI_DEVICE_ID_RPP4:
+               str = "Plus Quadcable";
+               max_num_aiops = 1;
+               ports_per_aiop = 4;
+               altChanRingIndicator++;
+               fast_clock++;
+               rocketModel[i].model = MODEL_RPP4;
+               strcpy(rocketModel[i].modelString, "RocketPort Plus 4 port");
+               rocketModel[i].numPorts = 4;
+               break;
+       case PCI_DEVICE_ID_RPP8:
+               str = "Plus Octacable";
+               max_num_aiops = 2;
+               ports_per_aiop = 4;
+               altChanRingIndicator++;
+               fast_clock++;
+               rocketModel[i].model = MODEL_RPP8;
+               strcpy(rocketModel[i].modelString, "RocketPort Plus 8 port");
+               rocketModel[i].numPorts = 8;
+               break;
+       case PCI_DEVICE_ID_RP2_232:
+               str = "Plus 2 (RS-232)";
+               max_num_aiops = 1;
+               ports_per_aiop = 2;
+               altChanRingIndicator++;
+               fast_clock++;
+               rocketModel[i].model = MODEL_RP2_232;
+               strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS232");
+               rocketModel[i].numPorts = 2;
+               break;
+       case PCI_DEVICE_ID_RP2_422:
+               str = "Plus 2 (RS-422)";
+               max_num_aiops = 1;
+               ports_per_aiop = 2;
+               altChanRingIndicator++;
+               fast_clock++;
+               rocketModel[i].model = MODEL_RP2_422;
+               strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS422");
+               rocketModel[i].numPorts = 2;
+               break;
+       case PCI_DEVICE_ID_RP6M:
+
+               max_num_aiops = 1;
+               ports_per_aiop = 6;
+               str = "6-port";
+
+               /*  If revision is 1, the rocketmodem flash must be loaded.
+                *  If it is 2 it is a "socketed" version. */
+               if (dev->revision == 1) {
+                       rcktpt_type[i] = ROCKET_TYPE_MODEMII;
+                       rocketModel[i].loadrm2 = 1;
+               } else {
+                       rcktpt_type[i] = ROCKET_TYPE_MODEM;
+               }
+
+               rocketModel[i].model = MODEL_RP6M;
+               strcpy(rocketModel[i].modelString, "RocketModem 6 port");
+               rocketModel[i].numPorts = 6;
+               break;
+       case PCI_DEVICE_ID_RP4M:
+               max_num_aiops = 1;
+               ports_per_aiop = 4;
+               str = "4-port";
+               if (dev->revision == 1) {
+                       rcktpt_type[i] = ROCKET_TYPE_MODEMII;
+                       rocketModel[i].loadrm2 = 1;
+               } else {
+                       rcktpt_type[i] = ROCKET_TYPE_MODEM;
+               }
+
+               rocketModel[i].model = MODEL_RP4M;
+               strcpy(rocketModel[i].modelString, "RocketModem 4 port");
+               rocketModel[i].numPorts = 4;
+               break;
+       default:
+               str = "(unknown/unsupported)";
+               max_num_aiops = 0;
+               break;
+       }
+
+       /*
+        * Check for UPCI boards.
+        */
+
+       switch (dev->device) {
+       case PCI_DEVICE_ID_URP32INTF:
+       case PCI_DEVICE_ID_URP8INTF:
+       case PCI_DEVICE_ID_URP16INTF:
+       case PCI_DEVICE_ID_CRP16INTF:
+       case PCI_DEVICE_ID_URP8OCTA:
+               rcktpt_io_addr[i] = pci_resource_start(dev, 2);
+               ConfigIO = pci_resource_start(dev, 1);
+               if (dev->device == PCI_DEVICE_ID_URP8OCTA) {
+                       UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND;
+
+                       /*
+                        * Check for octa or quad cable.
+                        */
+                       if (!
+                           (sInW(ConfigIO + _PCI_9030_GPIO_CTRL) &
+                            PCI_GPIO_CTRL_8PORT)) {
+                               str = "Quadcable";
+                               ports_per_aiop = 4;
+                               rocketModel[i].numPorts = 4;
+                       }
+               }
+               break;
+       case PCI_DEVICE_ID_UPCI_RM3_8PORT:
+               str = "8 ports";
+               max_num_aiops = 1;
+               rocketModel[i].model = MODEL_UPCI_RM3_8PORT;
+               strcpy(rocketModel[i].modelString, "RocketModem III 8 port");
+               rocketModel[i].numPorts = 8;
+               rcktpt_io_addr[i] = pci_resource_start(dev, 2);
+               UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND;
+               ConfigIO = pci_resource_start(dev, 1);
+               rcktpt_type[i] = ROCKET_TYPE_MODEMIII;
+               break;
+       case PCI_DEVICE_ID_UPCI_RM3_4PORT:
+               str = "4 ports";
+               max_num_aiops = 1;
+               rocketModel[i].model = MODEL_UPCI_RM3_4PORT;
+               strcpy(rocketModel[i].modelString, "RocketModem III 4 port");
+               rocketModel[i].numPorts = 4;
+               rcktpt_io_addr[i] = pci_resource_start(dev, 2);
+               UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND;
+               ConfigIO = pci_resource_start(dev, 1);
+               rcktpt_type[i] = ROCKET_TYPE_MODEMIII;
+               break;
+       default:
+               break;
+       }
+
+       switch (rcktpt_type[i]) {
+       case ROCKET_TYPE_MODEM:
+               board_type = "RocketModem";
+               break;
+       case ROCKET_TYPE_MODEMII:
+               board_type = "RocketModem II";
+               break;
+       case ROCKET_TYPE_MODEMIII:
+               board_type = "RocketModem III";
+               break;
+       default:
+               board_type = "RocketPort";
+               break;
+       }
+
+       if (fast_clock) {
+               sClockPrescale = 0x12;  /* mod 2 (divide by 3) */
+               rp_baud_base[i] = 921600;
+       } else {
+               /*
+                * If support_low_speed is set, use the slow clock
+                * prescale, which supports 50 bps
+                */
+               if (support_low_speed) {
+                       /* mod 9 (divide by 10) prescale */
+                       sClockPrescale = 0x19;
+                       rp_baud_base[i] = 230400;
+               } else {
+                       /* mod 4 (devide by 5) prescale */
+                       sClockPrescale = 0x14;
+                       rp_baud_base[i] = 460800;
+               }
+       }
+
+       for (aiop = 0; aiop < max_num_aiops; aiop++)
+               aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x40);
+       ctlp = sCtlNumToCtlPtr(i);
+       num_aiops = sPCIInitController(ctlp, i, aiopio, max_num_aiops, ConfigIO, 0, FREQ_DIS, 0, altChanRingIndicator, UPCIRingInd);
+       for (aiop = 0; aiop < max_num_aiops; aiop++)
+               ctlp->AiopNumChan[aiop] = ports_per_aiop;
+
+       dev_info(&dev->dev, "comtrol PCI controller #%d found at "
+               "address %04lx, %d AIOP(s) (%s), creating ttyR%d - %ld\n",
+               i, rcktpt_io_addr[i], num_aiops, rocketModel[i].modelString,
+               rocketModel[i].startingPortNumber,
+               rocketModel[i].startingPortNumber + rocketModel[i].numPorts-1);
+
+       if (num_aiops <= 0) {
+               rcktpt_io_addr[i] = 0;
+               return (0);
+       }
+       is_PCI[i] = 1;
+
+       /*  Reset the AIOPIC, init the serial ports */
+       for (aiop = 0; aiop < num_aiops; aiop++) {
+               sResetAiopByNum(ctlp, aiop);
+               num_chan = ports_per_aiop;
+               for (chan = 0; chan < num_chan; chan++)
+                       init_r_port(i, aiop, chan, dev);
+       }
+
+       /*  Rocket modems must be reset */
+       if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) ||
+           (rcktpt_type[i] == ROCKET_TYPE_MODEMII) ||
+           (rcktpt_type[i] == ROCKET_TYPE_MODEMIII)) {
+               num_chan = ports_per_aiop;
+               for (chan = 0; chan < num_chan; chan++)
+                       sPCIModemReset(ctlp, chan, 1);
+               msleep(500);
+               for (chan = 0; chan < num_chan; chan++)
+                       sPCIModemReset(ctlp, chan, 0);
+               msleep(500);
+               rmSpeakerReset(ctlp, rocketModel[i].model);
+       }
+       return (1);
+}
+
+/*
+ *  Probes for PCI cards, inits them if found
+ *  Input:   board_found = number of ISA boards already found, or the
+ *           starting board number
+ *  Returns: Number of PCI boards found
+ */
+static int __init init_PCI(int boards_found)
+{
+       struct pci_dev *dev = NULL;
+       int count = 0;
+
+       /*  Work through the PCI device list, pulling out ours */
+       while ((dev = pci_get_device(PCI_VENDOR_ID_RP, PCI_ANY_ID, dev))) {
+               if (register_PCI(count + boards_found, dev))
+                       count++;
+       }
+       return (count);
+}
+
+#endif                         /* CONFIG_PCI */
+
+/*
+ *  Probes for ISA cards
+ *  Input:   i = the board number to look for
+ *  Returns: 1 if board found, 0 else
+ */
+static int __init init_ISA(int i)
+{
+       int num_aiops, num_chan = 0, total_num_chan = 0;
+       int aiop, chan;
+       unsigned int aiopio[MAX_AIOPS_PER_BOARD];
+       CONTROLLER_t *ctlp;
+       char *type_string;
+
+       /*  If io_addr is zero, no board configured */
+       if (rcktpt_io_addr[i] == 0)
+               return (0);
+
+       /*  Reserve the IO region */
+       if (!request_region(rcktpt_io_addr[i], 64, "Comtrol RocketPort")) {
+               printk(KERN_ERR "Unable to reserve IO region for configured "
+                               "ISA RocketPort at address 0x%lx, board not "
+                               "installed...\n", rcktpt_io_addr[i]);
+               rcktpt_io_addr[i] = 0;
+               return (0);
+       }
+
+       ctlp = sCtlNumToCtlPtr(i);
+
+       ctlp->boardType = rcktpt_type[i];
+
+       switch (rcktpt_type[i]) {
+       case ROCKET_TYPE_PC104:
+               type_string = "(PC104)";
+               break;
+       case ROCKET_TYPE_MODEM:
+               type_string = "(RocketModem)";
+               break;
+       case ROCKET_TYPE_MODEMII:
+               type_string = "(RocketModem II)";
+               break;
+       default:
+               type_string = "";
+               break;
+       }
+
+       /*
+        * If support_low_speed is set, use the slow clock prescale,
+        * which supports 50 bps
+        */
+       if (support_low_speed) {
+               sClockPrescale = 0x19;  /* mod 9 (divide by 10) prescale */
+               rp_baud_base[i] = 230400;
+       } else {
+               sClockPrescale = 0x14;  /* mod 4 (devide by 5) prescale */
+               rp_baud_base[i] = 460800;
+       }
+
+       for (aiop = 0; aiop < MAX_AIOPS_PER_BOARD; aiop++)
+               aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x400);
+
+       num_aiops = sInitController(ctlp, i, controller + (i * 0x400), aiopio,  MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0);
+
+       if (ctlp->boardType == ROCKET_TYPE_PC104) {
+               sEnAiop(ctlp, 2);       /* only one AIOPIC, but these */
+               sEnAiop(ctlp, 3);       /* CSels used for other stuff */
+       }
+
+       /*  If something went wrong initing the AIOP's release the ISA IO memory */
+       if (num_aiops <= 0) {
+               release_region(rcktpt_io_addr[i], 64);
+               rcktpt_io_addr[i] = 0;
+               return (0);
+       }
+  
+       rocketModel[i].startingPortNumber = nextLineNumber;
+
+       for (aiop = 0; aiop < num_aiops; aiop++) {
+               sResetAiopByNum(ctlp, aiop);
+               sEnAiop(ctlp, aiop);
+               num_chan = sGetAiopNumChan(ctlp, aiop);
+               total_num_chan += num_chan;
+               for (chan = 0; chan < num_chan; chan++)
+                       init_r_port(i, aiop, chan, NULL);
+       }
+       is_PCI[i] = 0;
+       if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) || (rcktpt_type[i] == ROCKET_TYPE_MODEMII)) {
+               num_chan = sGetAiopNumChan(ctlp, 0);
+               total_num_chan = num_chan;
+               for (chan = 0; chan < num_chan; chan++)
+                       sModemReset(ctlp, chan, 1);
+               msleep(500);
+               for (chan = 0; chan < num_chan; chan++)
+                       sModemReset(ctlp, chan, 0);
+               msleep(500);
+               strcpy(rocketModel[i].modelString, "RocketModem ISA");
+       } else {
+               strcpy(rocketModel[i].modelString, "RocketPort ISA");
+       }
+       rocketModel[i].numPorts = total_num_chan;
+       rocketModel[i].model = MODEL_ISA;
+
+       printk(KERN_INFO "RocketPort ISA card #%d found at 0x%lx - %d AIOPs %s\n", 
+              i, rcktpt_io_addr[i], num_aiops, type_string);
+
+       printk(KERN_INFO "Installing %s, creating /dev/ttyR%d - %ld\n",
+              rocketModel[i].modelString,
+              rocketModel[i].startingPortNumber,
+              rocketModel[i].startingPortNumber +
+              rocketModel[i].numPorts - 1);
+
+       return (1);
+}
+
+static const struct tty_operations rocket_ops = {
+       .open = rp_open,
+       .close = rp_close,
+       .write = rp_write,
+       .put_char = rp_put_char,
+       .write_room = rp_write_room,
+       .chars_in_buffer = rp_chars_in_buffer,
+       .flush_buffer = rp_flush_buffer,
+       .ioctl = rp_ioctl,
+       .throttle = rp_throttle,
+       .unthrottle = rp_unthrottle,
+       .set_termios = rp_set_termios,
+       .stop = rp_stop,
+       .start = rp_start,
+       .hangup = rp_hangup,
+       .break_ctl = rp_break,
+       .send_xchar = rp_send_xchar,
+       .wait_until_sent = rp_wait_until_sent,
+       .tiocmget = rp_tiocmget,
+       .tiocmset = rp_tiocmset,
+};
+
+static const struct tty_port_operations rocket_port_ops = {
+       .carrier_raised = carrier_raised,
+       .dtr_rts = dtr_rts,
+};
+
+/*
+ * The module "startup" routine; it's run when the module is loaded.
+ */
+static int __init rp_init(void)
+{
+       int ret = -ENOMEM, pci_boards_found, isa_boards_found, i;
+
+       printk(KERN_INFO "RocketPort device driver module, version %s, %s\n",
+              ROCKET_VERSION, ROCKET_DATE);
+
+       rocket_driver = alloc_tty_driver(MAX_RP_PORTS);
+       if (!rocket_driver)
+               goto err;
+
+       /*
+        *  If board 1 is non-zero, there is at least one ISA configured.  If controller is 
+        *  zero, use the default controller IO address of board1 + 0x40.
+        */
+       if (board1) {
+               if (controller == 0)
+                       controller = board1 + 0x40;
+       } else {
+               controller = 0;  /*  Used as a flag, meaning no ISA boards */
+       }
+
+       /*  If an ISA card is configured, reserve the 4 byte IO space for the Mudbac controller */
+       if (controller && (!request_region(controller, 4, "Comtrol RocketPort"))) {
+               printk(KERN_ERR "Unable to reserve IO region for first "
+                       "configured ISA RocketPort controller 0x%lx.  "
+                       "Driver exiting\n", controller);
+               ret = -EBUSY;
+               goto err_tty;
+       }
+
+       /*  Store ISA variable retrieved from command line or .conf file. */
+       rcktpt_io_addr[0] = board1;
+       rcktpt_io_addr[1] = board2;
+       rcktpt_io_addr[2] = board3;
+       rcktpt_io_addr[3] = board4;
+
+       rcktpt_type[0] = modem1 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL;
+       rcktpt_type[0] = pc104_1[0] ? ROCKET_TYPE_PC104 : rcktpt_type[0];
+       rcktpt_type[1] = modem2 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL;
+       rcktpt_type[1] = pc104_2[0] ? ROCKET_TYPE_PC104 : rcktpt_type[1];
+       rcktpt_type[2] = modem3 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL;
+       rcktpt_type[2] = pc104_3[0] ? ROCKET_TYPE_PC104 : rcktpt_type[2];
+       rcktpt_type[3] = modem4 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL;
+       rcktpt_type[3] = pc104_4[0] ? ROCKET_TYPE_PC104 : rcktpt_type[3];
+
+       /*
+        * Set up the tty driver structure and then register this
+        * driver with the tty layer.
+        */
+
+       rocket_driver->owner = THIS_MODULE;
+       rocket_driver->flags = TTY_DRIVER_DYNAMIC_DEV;
+       rocket_driver->name = "ttyR";
+       rocket_driver->driver_name = "Comtrol RocketPort";
+       rocket_driver->major = TTY_ROCKET_MAJOR;
+       rocket_driver->minor_start = 0;
+       rocket_driver->type = TTY_DRIVER_TYPE_SERIAL;
+       rocket_driver->subtype = SERIAL_TYPE_NORMAL;
+       rocket_driver->init_termios = tty_std_termios;
+       rocket_driver->init_termios.c_cflag =
+           B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       rocket_driver->init_termios.c_ispeed = 9600;
+       rocket_driver->init_termios.c_ospeed = 9600;
+#ifdef ROCKET_SOFT_FLOW
+       rocket_driver->flags |= TTY_DRIVER_REAL_RAW;
+#endif
+       tty_set_operations(rocket_driver, &rocket_ops);
+
+       ret = tty_register_driver(rocket_driver);
+       if (ret < 0) {
+               printk(KERN_ERR "Couldn't install tty RocketPort driver\n");
+               goto err_controller;
+       }
+
+#ifdef ROCKET_DEBUG_OPEN
+       printk(KERN_INFO "RocketPort driver is major %d\n", rocket_driver.major);
+#endif
+
+       /*
+        *  OK, let's probe each of the controllers looking for boards.  Any boards found
+         *  will be initialized here.
+        */
+       isa_boards_found = 0;
+       pci_boards_found = 0;
+
+       for (i = 0; i < NUM_BOARDS; i++) {
+               if (init_ISA(i))
+                       isa_boards_found++;
+       }
+
+#ifdef CONFIG_PCI
+       if (isa_boards_found < NUM_BOARDS)
+               pci_boards_found = init_PCI(isa_boards_found);
+#endif
+
+       max_board = pci_boards_found + isa_boards_found;
+
+       if (max_board == 0) {
+               printk(KERN_ERR "No rocketport ports found; unloading driver\n");
+               ret = -ENXIO;
+               goto err_ttyu;
+       }
+
+       return 0;
+err_ttyu:
+       tty_unregister_driver(rocket_driver);
+err_controller:
+       if (controller)
+               release_region(controller, 4);
+err_tty:
+       put_tty_driver(rocket_driver);
+err:
+       return ret;
+}
+
+
+static void rp_cleanup_module(void)
+{
+       int retval;
+       int i;
+
+       del_timer_sync(&rocket_timer);
+
+       retval = tty_unregister_driver(rocket_driver);
+       if (retval)
+               printk(KERN_ERR "Error %d while trying to unregister "
+                      "rocketport driver\n", -retval);
+
+       for (i = 0; i < MAX_RP_PORTS; i++)
+               if (rp_table[i]) {
+                       tty_unregister_device(rocket_driver, i);
+                       kfree(rp_table[i]);
+               }
+
+       put_tty_driver(rocket_driver);
+
+       for (i = 0; i < NUM_BOARDS; i++) {
+               if (rcktpt_io_addr[i] <= 0 || is_PCI[i])
+                       continue;
+               release_region(rcktpt_io_addr[i], 64);
+       }
+       if (controller)
+               release_region(controller, 4);
+}
+
+/***************************************************************************
+Function: sInitController
+Purpose:  Initialization of controller global registers and controller
+          structure.
+Call:     sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize,
+                          IRQNum,Frequency,PeriodicOnly)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int CtlNum; Controller number
+          ByteIO_t MudbacIO; Mudbac base I/O address.
+          ByteIO_t *AiopIOList; List of I/O addresses for each AIOP.
+             This list must be in the order the AIOPs will be found on the
+             controller.  Once an AIOP in the list is not found, it is
+             assumed that there are no more AIOPs on the controller.
+          int AiopIOListSize; Number of addresses in AiopIOList
+          int IRQNum; Interrupt Request number.  Can be any of the following:
+                         0: Disable global interrupts
+                         3: IRQ 3
+                         4: IRQ 4
+                         5: IRQ 5
+                         9: IRQ 9
+                         10: IRQ 10
+                         11: IRQ 11
+                         12: IRQ 12
+                         15: IRQ 15
+          Byte_t Frequency: A flag identifying the frequency
+                   of the periodic interrupt, can be any one of the following:
+                      FREQ_DIS - periodic interrupt disabled
+                      FREQ_137HZ - 137 Hertz
+                      FREQ_69HZ - 69 Hertz
+                      FREQ_34HZ - 34 Hertz
+                      FREQ_17HZ - 17 Hertz
+                      FREQ_9HZ - 9 Hertz
+                      FREQ_4HZ - 4 Hertz
+                   If IRQNum is set to 0 the Frequency parameter is
+                   overidden, it is forced to a value of FREQ_DIS.
+          int PeriodicOnly: 1 if all interrupts except the periodic
+                               interrupt are to be blocked.
+                            0 is both the periodic interrupt and
+                               other channel interrupts are allowed.
+                            If IRQNum is set to 0 the PeriodicOnly parameter is
+                               overidden, it is forced to a value of 0.
+Return:   int: Number of AIOPs on the controller, or CTLID_NULL if controller
+               initialization failed.
+
+Comments:
+          If periodic interrupts are to be disabled but AIOP interrupts
+          are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0.
+
+          If interrupts are to be completely disabled set IRQNum to 0.
+
+          Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an
+          invalid combination.
+
+          This function performs initialization of global interrupt modes,
+          but it does not actually enable global interrupts.  To enable
+          and disable global interrupts use functions sEnGlobalInt() and
+          sDisGlobalInt().  Enabling of global interrupts is normally not
+          done until all other initializations are complete.
+
+          Even if interrupts are globally enabled, they must also be
+          individually enabled for each channel that is to generate
+          interrupts.
+
+Warnings: No range checking on any of the parameters is done.
+
+          No context switches are allowed while executing this function.
+
+          After this function all AIOPs on the controller are disabled,
+          they can be enabled with sEnAiop().
+*/
+static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO,
+                          ByteIO_t * AiopIOList, int AiopIOListSize,
+                          int IRQNum, Byte_t Frequency, int PeriodicOnly)
+{
+       int i;
+       ByteIO_t io;
+       int done;
+
+       CtlP->AiopIntrBits = aiop_intr_bits;
+       CtlP->AltChanRingIndicator = 0;
+       CtlP->CtlNum = CtlNum;
+       CtlP->CtlID = CTLID_0001;       /* controller release 1 */
+       CtlP->BusType = isISA;
+       CtlP->MBaseIO = MudbacIO;
+       CtlP->MReg1IO = MudbacIO + 1;
+       CtlP->MReg2IO = MudbacIO + 2;
+       CtlP->MReg3IO = MudbacIO + 3;
+#if 1
+       CtlP->MReg2 = 0;        /* interrupt disable */
+       CtlP->MReg3 = 0;        /* no periodic interrupts */
+#else
+       if (sIRQMap[IRQNum] == 0) {     /* interrupts globally disabled */
+               CtlP->MReg2 = 0;        /* interrupt disable */
+               CtlP->MReg3 = 0;        /* no periodic interrupts */
+       } else {
+               CtlP->MReg2 = sIRQMap[IRQNum];  /* set IRQ number */
+               CtlP->MReg3 = Frequency;        /* set frequency */
+               if (PeriodicOnly) {     /* periodic interrupt only */
+                       CtlP->MReg3 |= PERIODIC_ONLY;
+               }
+       }
+#endif
+       sOutB(CtlP->MReg2IO, CtlP->MReg2);
+       sOutB(CtlP->MReg3IO, CtlP->MReg3);
+       sControllerEOI(CtlP);   /* clear EOI if warm init */
+       /* Init AIOPs */
+       CtlP->NumAiop = 0;
+       for (i = done = 0; i < AiopIOListSize; i++) {
+               io = AiopIOList[i];
+               CtlP->AiopIO[i] = (WordIO_t) io;
+               CtlP->AiopIntChanIO[i] = io + _INT_CHAN;
+               sOutB(CtlP->MReg2IO, CtlP->MReg2 | (i & 0x03)); /* AIOP index */
+               sOutB(MudbacIO, (Byte_t) (io >> 6));    /* set up AIOP I/O in MUDBAC */
+               if (done)
+                       continue;
+               sEnAiop(CtlP, i);       /* enable the AIOP */
+               CtlP->AiopID[i] = sReadAiopID(io);      /* read AIOP ID */
+               if (CtlP->AiopID[i] == AIOPID_NULL)     /* if AIOP does not exist */
+                       done = 1;       /* done looking for AIOPs */
+               else {
+                       CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */
+                       sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE);    /* clock prescaler */
+                       sOutB(io + _INDX_DATA, sClockPrescale);
+                       CtlP->NumAiop++;        /* bump count of AIOPs */
+               }
+               sDisAiop(CtlP, i);      /* disable AIOP */
+       }
+
+       if (CtlP->NumAiop == 0)
+               return (-1);
+       else
+               return (CtlP->NumAiop);
+}
+
+/***************************************************************************
+Function: sPCIInitController
+Purpose:  Initialization of controller global registers and controller
+          structure.
+Call:     sPCIInitController(CtlP,CtlNum,AiopIOList,AiopIOListSize,
+                          IRQNum,Frequency,PeriodicOnly)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int CtlNum; Controller number
+          ByteIO_t *AiopIOList; List of I/O addresses for each AIOP.
+             This list must be in the order the AIOPs will be found on the
+             controller.  Once an AIOP in the list is not found, it is
+             assumed that there are no more AIOPs on the controller.
+          int AiopIOListSize; Number of addresses in AiopIOList
+          int IRQNum; Interrupt Request number.  Can be any of the following:
+                         0: Disable global interrupts
+                         3: IRQ 3
+                         4: IRQ 4
+                         5: IRQ 5
+                         9: IRQ 9
+                         10: IRQ 10
+                         11: IRQ 11
+                         12: IRQ 12
+                         15: IRQ 15
+          Byte_t Frequency: A flag identifying the frequency
+                   of the periodic interrupt, can be any one of the following:
+                      FREQ_DIS - periodic interrupt disabled
+                      FREQ_137HZ - 137 Hertz
+                      FREQ_69HZ - 69 Hertz
+                      FREQ_34HZ - 34 Hertz
+                      FREQ_17HZ - 17 Hertz
+                      FREQ_9HZ - 9 Hertz
+                      FREQ_4HZ - 4 Hertz
+                   If IRQNum is set to 0 the Frequency parameter is
+                   overidden, it is forced to a value of FREQ_DIS.
+          int PeriodicOnly: 1 if all interrupts except the periodic
+                               interrupt are to be blocked.
+                            0 is both the periodic interrupt and
+                               other channel interrupts are allowed.
+                            If IRQNum is set to 0 the PeriodicOnly parameter is
+                               overidden, it is forced to a value of 0.
+Return:   int: Number of AIOPs on the controller, or CTLID_NULL if controller
+               initialization failed.
+
+Comments:
+          If periodic interrupts are to be disabled but AIOP interrupts
+          are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0.
+
+          If interrupts are to be completely disabled set IRQNum to 0.
+
+          Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an
+          invalid combination.
+
+          This function performs initialization of global interrupt modes,
+          but it does not actually enable global interrupts.  To enable
+          and disable global interrupts use functions sEnGlobalInt() and
+          sDisGlobalInt().  Enabling of global interrupts is normally not
+          done until all other initializations are complete.
+
+          Even if interrupts are globally enabled, they must also be
+          individually enabled for each channel that is to generate
+          interrupts.
+
+Warnings: No range checking on any of the parameters is done.
+
+          No context switches are allowed while executing this function.
+
+          After this function all AIOPs on the controller are disabled,
+          they can be enabled with sEnAiop().
+*/
+static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum,
+                             ByteIO_t * AiopIOList, int AiopIOListSize,
+                             WordIO_t ConfigIO, int IRQNum, Byte_t Frequency,
+                             int PeriodicOnly, int altChanRingIndicator,
+                             int UPCIRingInd)
+{
+       int i;
+       ByteIO_t io;
+
+       CtlP->AltChanRingIndicator = altChanRingIndicator;
+       CtlP->UPCIRingInd = UPCIRingInd;
+       CtlP->CtlNum = CtlNum;
+       CtlP->CtlID = CTLID_0001;       /* controller release 1 */
+       CtlP->BusType = isPCI;  /* controller release 1 */
+
+       if (ConfigIO) {
+               CtlP->isUPCI = 1;
+               CtlP->PCIIO = ConfigIO + _PCI_9030_INT_CTRL;
+               CtlP->PCIIO2 = ConfigIO + _PCI_9030_GPIO_CTRL;
+               CtlP->AiopIntrBits = upci_aiop_intr_bits;
+       } else {
+               CtlP->isUPCI = 0;
+               CtlP->PCIIO =
+                   (WordIO_t) ((ByteIO_t) AiopIOList[0] + _PCI_INT_FUNC);
+               CtlP->AiopIntrBits = aiop_intr_bits;
+       }
+
+       sPCIControllerEOI(CtlP);        /* clear EOI if warm init */
+       /* Init AIOPs */
+       CtlP->NumAiop = 0;
+       for (i = 0; i < AiopIOListSize; i++) {
+               io = AiopIOList[i];
+               CtlP->AiopIO[i] = (WordIO_t) io;
+               CtlP->AiopIntChanIO[i] = io + _INT_CHAN;
+
+               CtlP->AiopID[i] = sReadAiopID(io);      /* read AIOP ID */
+               if (CtlP->AiopID[i] == AIOPID_NULL)     /* if AIOP does not exist */
+                       break;  /* done looking for AIOPs */
+
+               CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */
+               sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE);    /* clock prescaler */
+               sOutB(io + _INDX_DATA, sClockPrescale);
+               CtlP->NumAiop++;        /* bump count of AIOPs */
+       }
+
+       if (CtlP->NumAiop == 0)
+               return (-1);
+       else
+               return (CtlP->NumAiop);
+}
+
+/***************************************************************************
+Function: sReadAiopID
+Purpose:  Read the AIOP idenfication number directly from an AIOP.
+Call:     sReadAiopID(io)
+          ByteIO_t io: AIOP base I/O address
+Return:   int: Flag AIOPID_XXXX if a valid AIOP is found, where X
+                 is replace by an identifying number.
+          Flag AIOPID_NULL if no valid AIOP is found
+Warnings: No context switches are allowed while executing this function.
+
+*/
+static int sReadAiopID(ByteIO_t io)
+{
+       Byte_t AiopID;          /* ID byte from AIOP */
+
+       sOutB(io + _CMD_REG, RESET_ALL);        /* reset AIOP */
+       sOutB(io + _CMD_REG, 0x0);
+       AiopID = sInW(io + _CHN_STAT0) & 0x07;
+       if (AiopID == 0x06)
+               return (1);
+       else                    /* AIOP does not exist */
+               return (-1);
+}
+
+/***************************************************************************
+Function: sReadAiopNumChan
+Purpose:  Read the number of channels available in an AIOP directly from
+          an AIOP.
+Call:     sReadAiopNumChan(io)
+          WordIO_t io: AIOP base I/O address
+Return:   int: The number of channels available
+Comments: The number of channels is determined by write/reads from identical
+          offsets within the SRAM address spaces for channels 0 and 4.
+          If the channel 4 space is mirrored to channel 0 it is a 4 channel
+          AIOP, otherwise it is an 8 channel.
+Warnings: No context switches are allowed while executing this function.
+*/
+static int sReadAiopNumChan(WordIO_t io)
+{
+       Word_t x;
+       static Byte_t R[4] = { 0x00, 0x00, 0x34, 0x12 };
+
+       /* write to chan 0 SRAM */
+       out32((DWordIO_t) io + _INDX_ADDR, R);
+       sOutW(io + _INDX_ADDR, 0);      /* read from SRAM, chan 0 */
+       x = sInW(io + _INDX_DATA);
+       sOutW(io + _INDX_ADDR, 0x4000); /* read from SRAM, chan 4 */
+       if (x != sInW(io + _INDX_DATA)) /* if different must be 8 chan */
+               return (8);
+       else
+               return (4);
+}
+
+/***************************************************************************
+Function: sInitChan
+Purpose:  Initialization of a channel and channel structure
+Call:     sInitChan(CtlP,ChP,AiopNum,ChanNum)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          CHANNEL_T *ChP; Ptr to channel structure
+          int AiopNum; AIOP number within controller
+          int ChanNum; Channel number within AIOP
+Return:   int: 1 if initialization succeeded, 0 if it fails because channel
+               number exceeds number of channels available in AIOP.
+Comments: This function must be called before a channel can be used.
+Warnings: No range checking on any of the parameters is done.
+
+          No context switches are allowed while executing this function.
+*/
+static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum,
+                    int ChanNum)
+{
+       int i;
+       WordIO_t AiopIO;
+       WordIO_t ChIOOff;
+       Byte_t *ChR;
+       Word_t ChOff;
+       static Byte_t R[4];
+       int brd9600;
+
+       if (ChanNum >= CtlP->AiopNumChan[AiopNum])
+               return 0;       /* exceeds num chans in AIOP */
+
+       /* Channel, AIOP, and controller identifiers */
+       ChP->CtlP = CtlP;
+       ChP->ChanID = CtlP->AiopID[AiopNum];
+       ChP->AiopNum = AiopNum;
+       ChP->ChanNum = ChanNum;
+
+       /* Global direct addresses */
+       AiopIO = CtlP->AiopIO[AiopNum];
+       ChP->Cmd = (ByteIO_t) AiopIO + _CMD_REG;
+       ChP->IntChan = (ByteIO_t) AiopIO + _INT_CHAN;
+       ChP->IntMask = (ByteIO_t) AiopIO + _INT_MASK;
+       ChP->IndexAddr = (DWordIO_t) AiopIO + _INDX_ADDR;
+       ChP->IndexData = AiopIO + _INDX_DATA;
+
+       /* Channel direct addresses */
+       ChIOOff = AiopIO + ChP->ChanNum * 2;
+       ChP->TxRxData = ChIOOff + _TD0;
+       ChP->ChanStat = ChIOOff + _CHN_STAT0;
+       ChP->TxRxCount = ChIOOff + _FIFO_CNT0;
+       ChP->IntID = (ByteIO_t) AiopIO + ChP->ChanNum + _INT_ID0;
+
+       /* Initialize the channel from the RData array */
+       for (i = 0; i < RDATASIZE; i += 4) {
+               R[0] = RData[i];
+               R[1] = RData[i + 1] + 0x10 * ChanNum;
+               R[2] = RData[i + 2];
+               R[3] = RData[i + 3];
+               out32(ChP->IndexAddr, R);
+       }
+
+       ChR = ChP->R;
+       for (i = 0; i < RREGDATASIZE; i += 4) {
+               ChR[i] = RRegData[i];
+               ChR[i + 1] = RRegData[i + 1] + 0x10 * ChanNum;
+               ChR[i + 2] = RRegData[i + 2];
+               ChR[i + 3] = RRegData[i + 3];
+       }
+
+       /* Indexed registers */
+       ChOff = (Word_t) ChanNum *0x1000;
+
+       if (sClockPrescale == 0x14)
+               brd9600 = 47;
+       else
+               brd9600 = 23;
+
+       ChP->BaudDiv[0] = (Byte_t) (ChOff + _BAUD);
+       ChP->BaudDiv[1] = (Byte_t) ((ChOff + _BAUD) >> 8);
+       ChP->BaudDiv[2] = (Byte_t) brd9600;
+       ChP->BaudDiv[3] = (Byte_t) (brd9600 >> 8);
+       out32(ChP->IndexAddr, ChP->BaudDiv);
+
+       ChP->TxControl[0] = (Byte_t) (ChOff + _TX_CTRL);
+       ChP->TxControl[1] = (Byte_t) ((ChOff + _TX_CTRL) >> 8);
+       ChP->TxControl[2] = 0;
+       ChP->TxControl[3] = 0;
+       out32(ChP->IndexAddr, ChP->TxControl);
+
+       ChP->RxControl[0] = (Byte_t) (ChOff + _RX_CTRL);
+       ChP->RxControl[1] = (Byte_t) ((ChOff + _RX_CTRL) >> 8);
+       ChP->RxControl[2] = 0;
+       ChP->RxControl[3] = 0;
+       out32(ChP->IndexAddr, ChP->RxControl);
+
+       ChP->TxEnables[0] = (Byte_t) (ChOff + _TX_ENBLS);
+       ChP->TxEnables[1] = (Byte_t) ((ChOff + _TX_ENBLS) >> 8);
+       ChP->TxEnables[2] = 0;
+       ChP->TxEnables[3] = 0;
+       out32(ChP->IndexAddr, ChP->TxEnables);
+
+       ChP->TxCompare[0] = (Byte_t) (ChOff + _TXCMP1);
+       ChP->TxCompare[1] = (Byte_t) ((ChOff + _TXCMP1) >> 8);
+       ChP->TxCompare[2] = 0;
+       ChP->TxCompare[3] = 0;
+       out32(ChP->IndexAddr, ChP->TxCompare);
+
+       ChP->TxReplace1[0] = (Byte_t) (ChOff + _TXREP1B1);
+       ChP->TxReplace1[1] = (Byte_t) ((ChOff + _TXREP1B1) >> 8);
+       ChP->TxReplace1[2] = 0;
+       ChP->TxReplace1[3] = 0;
+       out32(ChP->IndexAddr, ChP->TxReplace1);
+
+       ChP->TxReplace2[0] = (Byte_t) (ChOff + _TXREP2);
+       ChP->TxReplace2[1] = (Byte_t) ((ChOff + _TXREP2) >> 8);
+       ChP->TxReplace2[2] = 0;
+       ChP->TxReplace2[3] = 0;
+       out32(ChP->IndexAddr, ChP->TxReplace2);
+
+       ChP->TxFIFOPtrs = ChOff + _TXF_OUTP;
+       ChP->TxFIFO = ChOff + _TX_FIFO;
+
+       sOutB(ChP->Cmd, (Byte_t) ChanNum | RESTXFCNT);  /* apply reset Tx FIFO count */
+       sOutB(ChP->Cmd, (Byte_t) ChanNum);      /* remove reset Tx FIFO count */
+       sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs);      /* clear Tx in/out ptrs */
+       sOutW(ChP->IndexData, 0);
+       ChP->RxFIFOPtrs = ChOff + _RXF_OUTP;
+       ChP->RxFIFO = ChOff + _RX_FIFO;
+
+       sOutB(ChP->Cmd, (Byte_t) ChanNum | RESRXFCNT);  /* apply reset Rx FIFO count */
+       sOutB(ChP->Cmd, (Byte_t) ChanNum);      /* remove reset Rx FIFO count */
+       sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs);      /* clear Rx out ptr */
+       sOutW(ChP->IndexData, 0);
+       sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2);  /* clear Rx in ptr */
+       sOutW(ChP->IndexData, 0);
+       ChP->TxPrioCnt = ChOff + _TXP_CNT;
+       sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioCnt);
+       sOutB(ChP->IndexData, 0);
+       ChP->TxPrioPtr = ChOff + _TXP_PNTR;
+       sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioPtr);
+       sOutB(ChP->IndexData, 0);
+       ChP->TxPrioBuf = ChOff + _TXP_BUF;
+       sEnRxProcessor(ChP);    /* start the Rx processor */
+
+       return 1;
+}
+
+/***************************************************************************
+Function: sStopRxProcessor
+Purpose:  Stop the receive processor from processing a channel.
+Call:     sStopRxProcessor(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+
+Comments: The receive processor can be started again with sStartRxProcessor().
+          This function causes the receive processor to skip over the
+          stopped channel.  It does not stop it from processing other channels.
+
+Warnings: No context switches are allowed while executing this function.
+
+          Do not leave the receive processor stopped for more than one
+          character time.
+
+          After calling this function a delay of 4 uS is required to ensure
+          that the receive processor is no longer processing this channel.
+*/
+static void sStopRxProcessor(CHANNEL_T * ChP)
+{
+       Byte_t R[4];
+
+       R[0] = ChP->R[0];
+       R[1] = ChP->R[1];
+       R[2] = 0x0a;
+       R[3] = ChP->R[3];
+       out32(ChP->IndexAddr, R);
+}
+
+/***************************************************************************
+Function: sFlushRxFIFO
+Purpose:  Flush the Rx FIFO
+Call:     sFlushRxFIFO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   void
+Comments: To prevent data from being enqueued or dequeued in the Tx FIFO
+          while it is being flushed the receive processor is stopped
+          and the transmitter is disabled.  After these operations a
+          4 uS delay is done before clearing the pointers to allow
+          the receive processor to stop.  These items are handled inside
+          this function.
+Warnings: No context switches are allowed while executing this function.
+*/
+static void sFlushRxFIFO(CHANNEL_T * ChP)
+{
+       int i;
+       Byte_t Ch;              /* channel number within AIOP */
+       int RxFIFOEnabled;      /* 1 if Rx FIFO enabled */
+
+       if (sGetRxCnt(ChP) == 0)        /* Rx FIFO empty */
+               return;         /* don't need to flush */
+
+       RxFIFOEnabled = 0;
+       if (ChP->R[0x32] == 0x08) {     /* Rx FIFO is enabled */
+               RxFIFOEnabled = 1;
+               sDisRxFIFO(ChP);        /* disable it */
+               for (i = 0; i < 2000 / 200; i++)        /* delay 2 uS to allow proc to disable FIFO */
+                       sInB(ChP->IntChan);     /* depends on bus i/o timing */
+       }
+       sGetChanStatus(ChP);    /* clear any pending Rx errors in chan stat */
+       Ch = (Byte_t) sGetChanNum(ChP);
+       sOutB(ChP->Cmd, Ch | RESRXFCNT);        /* apply reset Rx FIFO count */
+       sOutB(ChP->Cmd, Ch);    /* remove reset Rx FIFO count */
+       sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs);      /* clear Rx out ptr */
+       sOutW(ChP->IndexData, 0);
+       sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2);  /* clear Rx in ptr */
+       sOutW(ChP->IndexData, 0);
+       if (RxFIFOEnabled)
+               sEnRxFIFO(ChP); /* enable Rx FIFO */
+}
+
+/***************************************************************************
+Function: sFlushTxFIFO
+Purpose:  Flush the Tx FIFO
+Call:     sFlushTxFIFO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   void
+Comments: To prevent data from being enqueued or dequeued in the Tx FIFO
+          while it is being flushed the receive processor is stopped
+          and the transmitter is disabled.  After these operations a
+          4 uS delay is done before clearing the pointers to allow
+          the receive processor to stop.  These items are handled inside
+          this function.
+Warnings: No context switches are allowed while executing this function.
+*/
+static void sFlushTxFIFO(CHANNEL_T * ChP)
+{
+       int i;
+       Byte_t Ch;              /* channel number within AIOP */
+       int TxEnabled;          /* 1 if transmitter enabled */
+
+       if (sGetTxCnt(ChP) == 0)        /* Tx FIFO empty */
+               return;         /* don't need to flush */
+
+       TxEnabled = 0;
+       if (ChP->TxControl[3] & TX_ENABLE) {
+               TxEnabled = 1;
+               sDisTransmit(ChP);      /* disable transmitter */
+       }
+       sStopRxProcessor(ChP);  /* stop Rx processor */
+       for (i = 0; i < 4000 / 200; i++)        /* delay 4 uS to allow proc to stop */
+               sInB(ChP->IntChan);     /* depends on bus i/o timing */
+       Ch = (Byte_t) sGetChanNum(ChP);
+       sOutB(ChP->Cmd, Ch | RESTXFCNT);        /* apply reset Tx FIFO count */
+       sOutB(ChP->Cmd, Ch);    /* remove reset Tx FIFO count */
+       sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs);      /* clear Tx in/out ptrs */
+       sOutW(ChP->IndexData, 0);
+       if (TxEnabled)
+               sEnTransmit(ChP);       /* enable transmitter */
+       sStartRxProcessor(ChP); /* restart Rx processor */
+}
+
+/***************************************************************************
+Function: sWriteTxPrioByte
+Purpose:  Write a byte of priority transmit data to a channel
+Call:     sWriteTxPrioByte(ChP,Data)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Byte_t Data; The transmit data byte
+
+Return:   int: 1 if the bytes is successfully written, otherwise 0.
+
+Comments: The priority byte is transmitted before any data in the Tx FIFO.
+
+Warnings: No context switches are allowed while executing this function.
+*/
+static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data)
+{
+       Byte_t DWBuf[4];        /* buffer for double word writes */
+       Word_t *WordPtr;        /* must be far because Win SS != DS */
+       register DWordIO_t IndexAddr;
+
+       if (sGetTxCnt(ChP) > 1) {       /* write it to Tx priority buffer */
+               IndexAddr = ChP->IndexAddr;
+               sOutW((WordIO_t) IndexAddr, ChP->TxPrioCnt);    /* get priority buffer status */
+               if (sInB((ByteIO_t) ChP->IndexData) & PRI_PEND) /* priority buffer busy */
+                       return (0);     /* nothing sent */
+
+               WordPtr = (Word_t *) (&DWBuf[0]);
+               *WordPtr = ChP->TxPrioBuf;      /* data byte address */
+
+               DWBuf[2] = Data;        /* data byte value */
+               out32(IndexAddr, DWBuf);        /* write it out */
+
+               *WordPtr = ChP->TxPrioCnt;      /* Tx priority count address */
+
+               DWBuf[2] = PRI_PEND + 1;        /* indicate 1 byte pending */
+               DWBuf[3] = 0;   /* priority buffer pointer */
+               out32(IndexAddr, DWBuf);        /* write it out */
+       } else {                /* write it to Tx FIFO */
+
+               sWriteTxByte(sGetTxRxDataIO(ChP), Data);
+       }
+       return (1);             /* 1 byte sent */
+}
+
+/***************************************************************************
+Function: sEnInterrupts
+Purpose:  Enable one or more interrupts for a channel
+Call:     sEnInterrupts(ChP,Flags)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Word_t Flags: Interrupt enable flags, can be any combination
+             of the following flags:
+                TXINT_EN:   Interrupt on Tx FIFO empty
+                RXINT_EN:   Interrupt on Rx FIFO at trigger level (see
+                            sSetRxTrigger())
+                SRCINT_EN:  Interrupt on SRC (Special Rx Condition)
+                MCINT_EN:   Interrupt on modem input change
+                CHANINT_EN: Allow channel interrupt signal to the AIOP's
+                            Interrupt Channel Register.
+Return:   void
+Comments: If an interrupt enable flag is set in Flags, that interrupt will be
+          enabled.  If an interrupt enable flag is not set in Flags, that
+          interrupt will not be changed.  Interrupts can be disabled with
+          function sDisInterrupts().
+
+          This function sets the appropriate bit for the channel in the AIOP's
+          Interrupt Mask Register if the CHANINT_EN flag is set.  This allows
+          this channel's bit to be set in the AIOP's Interrupt Channel Register.
+
+          Interrupts must also be globally enabled before channel interrupts
+          will be passed on to the host.  This is done with function
+          sEnGlobalInt().
+
+          In some cases it may be desirable to disable interrupts globally but
+          enable channel interrupts.  This would allow the global interrupt
+          status register to be used to determine which AIOPs need service.
+*/
+static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags)
+{
+       Byte_t Mask;            /* Interrupt Mask Register */
+
+       ChP->RxControl[2] |=
+           ((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN));
+
+       out32(ChP->IndexAddr, ChP->RxControl);
+
+       ChP->TxControl[2] |= ((Byte_t) Flags & TXINT_EN);
+
+       out32(ChP->IndexAddr, ChP->TxControl);
+
+       if (Flags & CHANINT_EN) {
+               Mask = sInB(ChP->IntMask) | sBitMapSetTbl[ChP->ChanNum];
+               sOutB(ChP->IntMask, Mask);
+       }
+}
+
+/***************************************************************************
+Function: sDisInterrupts
+Purpose:  Disable one or more interrupts for a channel
+Call:     sDisInterrupts(ChP,Flags)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Word_t Flags: Interrupt flags, can be any combination
+             of the following flags:
+                TXINT_EN:   Interrupt on Tx FIFO empty
+                RXINT_EN:   Interrupt on Rx FIFO at trigger level (see
+                            sSetRxTrigger())
+                SRCINT_EN:  Interrupt on SRC (Special Rx Condition)
+                MCINT_EN:   Interrupt on modem input change
+                CHANINT_EN: Disable channel interrupt signal to the
+                            AIOP's Interrupt Channel Register.
+Return:   void
+Comments: If an interrupt flag is set in Flags, that interrupt will be
+          disabled.  If an interrupt flag is not set in Flags, that
+          interrupt will not be changed.  Interrupts can be enabled with
+          function sEnInterrupts().
+
+          This function clears the appropriate bit for the channel in the AIOP's
+          Interrupt Mask Register if the CHANINT_EN flag is set.  This blocks
+          this channel's bit from being set in the AIOP's Interrupt Channel
+          Register.
+*/
+static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags)
+{
+       Byte_t Mask;            /* Interrupt Mask Register */
+
+       ChP->RxControl[2] &=
+           ~((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN));
+       out32(ChP->IndexAddr, ChP->RxControl);
+       ChP->TxControl[2] &= ~((Byte_t) Flags & TXINT_EN);
+       out32(ChP->IndexAddr, ChP->TxControl);
+
+       if (Flags & CHANINT_EN) {
+               Mask = sInB(ChP->IntMask) & sBitMapClrTbl[ChP->ChanNum];
+               sOutB(ChP->IntMask, Mask);
+       }
+}
+
+static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode)
+{
+       sOutB(ChP->CtlP->AiopIO[2], (mode & 0x18) | ChP->ChanNum);
+}
+
+/*
+ *  Not an official SSCI function, but how to reset RocketModems.
+ *  ISA bus version
+ */
+static void sModemReset(CONTROLLER_T * CtlP, int chan, int on)
+{
+       ByteIO_t addr;
+       Byte_t val;
+
+       addr = CtlP->AiopIO[0] + 0x400;
+       val = sInB(CtlP->MReg3IO);
+       /* if AIOP[1] is not enabled, enable it */
+       if ((val & 2) == 0) {
+               val = sInB(CtlP->MReg2IO);
+               sOutB(CtlP->MReg2IO, (val & 0xfc) | (1 & 0x03));
+               sOutB(CtlP->MBaseIO, (unsigned char) (addr >> 6));
+       }
+
+       sEnAiop(CtlP, 1);
+       if (!on)
+               addr += 8;
+       sOutB(addr + chan, 0);  /* apply or remove reset */
+       sDisAiop(CtlP, 1);
+}
+
+/*
+ *  Not an official SSCI function, but how to reset RocketModems.
+ *  PCI bus version
+ */
+static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on)
+{
+       ByteIO_t addr;
+
+       addr = CtlP->AiopIO[0] + 0x40;  /* 2nd AIOP */
+       if (!on)
+               addr += 8;
+       sOutB(addr + chan, 0);  /* apply or remove reset */
+}
+
+/*  Resets the speaker controller on RocketModem II and III devices */
+static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model)
+{
+       ByteIO_t addr;
+
+       /* RocketModem II speaker control is at the 8th port location of offset 0x40 */
+       if ((model == MODEL_RP4M) || (model == MODEL_RP6M)) {
+               addr = CtlP->AiopIO[0] + 0x4F;
+               sOutB(addr, 0);
+       }
+
+       /* RocketModem III speaker control is at the 1st port location of offset 0x80 */
+       if ((model == MODEL_UPCI_RM3_8PORT)
+           || (model == MODEL_UPCI_RM3_4PORT)) {
+               addr = CtlP->AiopIO[0] + 0x88;
+               sOutB(addr, 0);
+       }
+}
+
+/*  Returns the line number given the controller (board), aiop and channel number */
+static unsigned char GetLineNumber(int ctrl, int aiop, int ch)
+{
+       return lineNumbers[(ctrl << 5) | (aiop << 3) | ch];
+}
+
+/*
+ *  Stores the line number associated with a given controller (board), aiop
+ *  and channel number.  
+ *  Returns:  The line number assigned 
+ */
+static unsigned char SetLineNumber(int ctrl, int aiop, int ch)
+{
+       lineNumbers[(ctrl << 5) | (aiop << 3) | ch] = nextLineNumber++;
+       return (nextLineNumber - 1);
+}
diff --git a/drivers/tty/rocket.h b/drivers/tty/rocket.h
new file mode 100644 (file)
index 0000000..ec863f3
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * rocket.h --- the exported interface of the rocket driver to its configuration program.
+ *
+ * Written by Theodore Ts'o, Copyright 1997.
+ * Copyright 1997 Comtrol Corporation. 
+ *
+ */
+
+/*  Model Information Struct */
+typedef struct {
+       unsigned long model;
+       char modelString[80];
+       unsigned long numPorts;
+       int loadrm2;
+       int startingPortNumber;
+} rocketModel_t;
+
+struct rocket_config {
+       int line;
+       int flags;
+       int closing_wait;
+       int close_delay;
+       int port;
+       int reserved[32];
+};
+
+struct rocket_ports {
+       int tty_major;
+       int callout_major;
+       rocketModel_t rocketModel[8];
+};
+
+struct rocket_version {
+       char rocket_version[32];
+       char rocket_date[32];
+       char reserved[64];
+};
+
+/*
+ * Rocketport flags
+ */
+/*#define ROCKET_CALLOUT_NOHUP    0x00000001 */
+#define ROCKET_FORCE_CD                0x00000002
+#define ROCKET_HUP_NOTIFY      0x00000004
+#define ROCKET_SPLIT_TERMIOS   0x00000008
+#define ROCKET_SPD_MASK                0x00000070
+#define ROCKET_SPD_HI          0x00000010      /* Use 56000 instead of 38400 bps */
+#define ROCKET_SPD_VHI         0x00000020      /* Use 115200 instead of 38400 bps */
+#define ROCKET_SPD_SHI         0x00000030      /* Use 230400 instead of 38400 bps */
+#define ROCKET_SPD_WARP                0x00000040      /* Use 460800 instead of 38400 bps */
+#define ROCKET_SAK             0x00000080
+#define ROCKET_SESSION_LOCKOUT 0x00000100
+#define ROCKET_PGRP_LOCKOUT    0x00000200
+#define ROCKET_RTS_TOGGLE      0x00000400
+#define ROCKET_MODE_MASK        0x00003000
+#define ROCKET_MODE_RS232       0x00000000
+#define ROCKET_MODE_RS485       0x00001000
+#define ROCKET_MODE_RS422       0x00002000
+#define ROCKET_FLAGS           0x00003FFF
+
+#define ROCKET_USR_MASK 0x0071 /* Legal flags that non-privileged
+                                * users can set or reset */
+
+/*
+ * For closing_wait and closing_wait2
+ */
+#define ROCKET_CLOSING_WAIT_NONE       ASYNC_CLOSING_WAIT_NONE
+#define ROCKET_CLOSING_WAIT_INF                ASYNC_CLOSING_WAIT_INF
+
+/*
+ * Rocketport ioctls -- "RP"
+ */
+#define RCKP_GET_STRUCT                0x00525001
+#define RCKP_GET_CONFIG                0x00525002
+#define RCKP_SET_CONFIG                0x00525003
+#define RCKP_GET_PORTS         0x00525004
+#define RCKP_RESET_RM2         0x00525005
+#define RCKP_GET_VERSION       0x00525006
+
+/*  Rocketport Models */
+#define MODEL_RP32INTF        0x0001   /* RP 32 port w/external I/F   */
+#define MODEL_RP8INTF         0x0002   /* RP 8 port w/external I/F    */
+#define MODEL_RP16INTF        0x0003   /* RP 16 port w/external I/F   */
+#define MODEL_RP8OCTA         0x0005   /* RP 8 port w/octa cable      */
+#define MODEL_RP4QUAD         0x0004   /* RP 4 port w/quad cable      */
+#define MODEL_RP8J            0x0006   /* RP 8 port w/RJ11 connectors */
+#define MODEL_RP4J            0x0007   /* RP 4 port w/RJ45 connectors */
+#define MODEL_RP8SNI          0x0008   /* RP 8 port w/ DB78 SNI connector */
+#define MODEL_RP16SNI         0x0009   /* RP 16 port w/ DB78 SNI connector */
+#define MODEL_RPP4            0x000A   /* RP Plus 4 port              */
+#define MODEL_RPP8            0x000B   /* RP Plus 8 port              */
+#define MODEL_RP2_232         0x000E   /* RP Plus 2 port RS232        */
+#define MODEL_RP2_422         0x000F   /* RP Plus 2 port RS232        */
+
+/*  Rocketmodem II Models */
+#define MODEL_RP6M            0x000C   /* RM 6 port                   */
+#define MODEL_RP4M            0x000D   /* RM 4 port                   */
+
+/* Universal PCI boards */
+#define MODEL_UPCI_RP32INTF   0x0801   /* RP UPCI 32 port w/external I/F     */
+#define MODEL_UPCI_RP8INTF    0x0802   /* RP UPCI 8 port w/external I/F      */
+#define MODEL_UPCI_RP16INTF   0x0803   /* RP UPCI 16 port w/external I/F     */
+#define MODEL_UPCI_RP8OCTA    0x0805   /* RP UPCI 8 port w/octa cable        */ 
+#define MODEL_UPCI_RM3_8PORT  0x080C   /* RP UPCI Rocketmodem III 8 port     */
+#define MODEL_UPCI_RM3_4PORT  0x080C   /* RP UPCI Rocketmodem III 4 port     */
+
+/*  Compact PCI 16 port  */
+#define MODEL_CPCI_RP16INTF   0x0903   /* RP Compact PCI 16 port w/external I/F */
+
+/* All ISA boards */
+#define MODEL_ISA             0x1000
diff --git a/drivers/tty/rocket_int.h b/drivers/tty/rocket_int.h
new file mode 100644 (file)
index 0000000..67e0f1e
--- /dev/null
@@ -0,0 +1,1214 @@
+/*
+ * rocket_int.h --- internal header file for rocket.c
+ *
+ * Written by Theodore Ts'o, Copyright 1997.
+ * Copyright 1997 Comtrol Corporation.  
+ * 
+ */
+
+/*
+ * Definition of the types in rcktpt_type
+ */
+#define ROCKET_TYPE_NORMAL     0
+#define ROCKET_TYPE_MODEM      1
+#define ROCKET_TYPE_MODEMII    2
+#define ROCKET_TYPE_MODEMIII   3
+#define ROCKET_TYPE_PC104       4
+
+#include <linux/mutex.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+
+typedef unsigned char Byte_t;
+typedef unsigned int ByteIO_t;
+
+typedef unsigned int Word_t;
+typedef unsigned int WordIO_t;
+
+typedef unsigned int DWordIO_t;
+
+/*
+ * Note!  Normally the Linux I/O macros already take care of
+ * byte-swapping the I/O instructions.  However, all accesses using
+ * sOutDW aren't really 32-bit accesses, but should be handled in byte
+ * order.  Hence the use of the cpu_to_le32() macro to byte-swap
+ * things to no-op the byte swapping done by the big-endian outl()
+ * instruction.
+ */
+
+static inline void sOutB(unsigned short port, unsigned char value)
+{
+#ifdef ROCKET_DEBUG_IO
+       printk(KERN_DEBUG "sOutB(%x, %x)...\n", port, value);
+#endif
+       outb_p(value, port);
+}
+
+static inline void sOutW(unsigned short port, unsigned short value)
+{
+#ifdef ROCKET_DEBUG_IO
+       printk(KERN_DEBUG "sOutW(%x, %x)...\n", port, value);
+#endif
+       outw_p(value, port);
+}
+
+static inline void out32(unsigned short port, Byte_t *p)
+{
+       u32 value = get_unaligned_le32(p);
+#ifdef ROCKET_DEBUG_IO
+       printk(KERN_DEBUG "out32(%x, %lx)...\n", port, value);
+#endif
+       outl_p(value, port);
+}
+
+static inline unsigned char sInB(unsigned short port)
+{
+       return inb_p(port);
+}
+
+static inline unsigned short sInW(unsigned short port)
+{
+       return inw_p(port);
+}
+
+/* This is used to move arrays of bytes so byte swapping isn't appropriate. */
+#define sOutStrW(port, addr, count) if (count) outsw(port, addr, count)
+#define sInStrW(port, addr, count) if (count) insw(port, addr, count)
+
+#define CTL_SIZE 8
+#define AIOP_CTL_SIZE 4
+#define CHAN_AIOP_SIZE 8
+#define MAX_PORTS_PER_AIOP 8
+#define MAX_AIOPS_PER_BOARD 4
+#define MAX_PORTS_PER_BOARD 32
+
+/* Bus type ID */
+#define        isISA   0
+#define        isPCI   1
+#define        isMC    2
+
+/* Controller ID numbers */
+#define CTLID_NULL  -1         /* no controller exists */
+#define CTLID_0001  0x0001     /* controller release 1 */
+
+/* AIOP ID numbers, identifies AIOP type implementing channel */
+#define AIOPID_NULL -1         /* no AIOP or channel exists */
+#define AIOPID_0001 0x0001     /* AIOP release 1 */
+
+/************************************************************************
+ Global Register Offsets - Direct Access - Fixed values
+************************************************************************/
+
+#define _CMD_REG   0x38                /* Command Register            8    Write */
+#define _INT_CHAN  0x39                /* Interrupt Channel Register  8    Read */
+#define _INT_MASK  0x3A                /* Interrupt Mask Register     8    Read / Write */
+#define _UNUSED    0x3B                /* Unused                      8 */
+#define _INDX_ADDR 0x3C                /* Index Register Address      16   Write */
+#define _INDX_DATA 0x3E                /* Index Register Data         8/16 Read / Write */
+
+/************************************************************************
+ Channel Register Offsets for 1st channel in AIOP - Direct Access
+************************************************************************/
+#define _TD0       0x00                /* Transmit Data               16   Write */
+#define _RD0       0x00                /* Receive Data                16   Read */
+#define _CHN_STAT0 0x20                /* Channel Status              8/16 Read / Write */
+#define _FIFO_CNT0 0x10                /* Transmit/Receive FIFO Count 16   Read */
+#define _INT_ID0   0x30                /* Interrupt Identification    8    Read */
+
+/************************************************************************
+ Tx Control Register Offsets - Indexed - External - Fixed
+************************************************************************/
+#define _TX_ENBLS  0x980       /* Tx Processor Enables Register 8 Read / Write */
+#define _TXCMP1    0x988       /* Transmit Compare Value #1     8 Read / Write */
+#define _TXCMP2    0x989       /* Transmit Compare Value #2     8 Read / Write */
+#define _TXREP1B1  0x98A       /* Tx Replace Value #1 - Byte 1  8 Read / Write */
+#define _TXREP1B2  0x98B       /* Tx Replace Value #1 - Byte 2  8 Read / Write */
+#define _TXREP2    0x98C       /* Transmit Replace Value #2     8 Read / Write */
+
+/************************************************************************
+Memory Controller Register Offsets - Indexed - External - Fixed
+************************************************************************/
+#define _RX_FIFO    0x000      /* Rx FIFO */
+#define _TX_FIFO    0x800      /* Tx FIFO */
+#define _RXF_OUTP   0x990      /* Rx FIFO OUT pointer        16 Read / Write */
+#define _RXF_INP    0x992      /* Rx FIFO IN pointer         16 Read / Write */
+#define _TXF_OUTP   0x994      /* Tx FIFO OUT pointer        8  Read / Write */
+#define _TXF_INP    0x995      /* Tx FIFO IN pointer         8  Read / Write */
+#define _TXP_CNT    0x996      /* Tx Priority Count          8  Read / Write */
+#define _TXP_PNTR   0x997      /* Tx Priority Pointer        8  Read / Write */
+
+#define PRI_PEND    0x80       /* Priority data pending (bit7, Tx pri cnt) */
+#define TXFIFO_SIZE 255                /* size of Tx FIFO */
+#define RXFIFO_SIZE 1023       /* size of Rx FIFO */
+
+/************************************************************************
+Tx Priority Buffer - Indexed - External - Fixed
+************************************************************************/
+#define _TXP_BUF    0x9C0      /* Tx Priority Buffer  32  Bytes   Read / Write */
+#define TXP_SIZE    0x20       /* 32 bytes */
+
+/************************************************************************
+Channel Register Offsets - Indexed - Internal - Fixed
+************************************************************************/
+
+#define _TX_CTRL    0xFF0      /* Transmit Control               16  Write */
+#define _RX_CTRL    0xFF2      /* Receive Control                 8  Write */
+#define _BAUD       0xFF4      /* Baud Rate                      16  Write */
+#define _CLK_PRE    0xFF6      /* Clock Prescaler                 8  Write */
+
+#define STMBREAK   0x08                /* BREAK */
+#define STMFRAME   0x04                /* framing error */
+#define STMRCVROVR 0x02                /* receiver over run error */
+#define STMPARITY  0x01                /* parity error */
+#define STMERROR   (STMBREAK | STMFRAME | STMPARITY)
+#define STMBREAKH   0x800      /* BREAK */
+#define STMFRAMEH   0x400      /* framing error */
+#define STMRCVROVRH 0x200      /* receiver over run error */
+#define STMPARITYH  0x100      /* parity error */
+#define STMERRORH   (STMBREAKH | STMFRAMEH | STMPARITYH)
+
+#define CTS_ACT   0x20         /* CTS input asserted */
+#define DSR_ACT   0x10         /* DSR input asserted */
+#define CD_ACT    0x08         /* CD input asserted */
+#define TXFIFOMT  0x04         /* Tx FIFO is empty */
+#define TXSHRMT   0x02         /* Tx shift register is empty */
+#define RDA       0x01         /* Rx data available */
+#define DRAINED (TXFIFOMT | TXSHRMT)   /* indicates Tx is drained */
+
+#define STATMODE  0x8000       /* status mode enable bit */
+#define RXFOVERFL 0x2000       /* receive FIFO overflow */
+#define RX2MATCH  0x1000       /* receive compare byte 2 match */
+#define RX1MATCH  0x0800       /* receive compare byte 1 match */
+#define RXBREAK   0x0400       /* received BREAK */
+#define RXFRAME   0x0200       /* received framing error */
+#define RXPARITY  0x0100       /* received parity error */
+#define STATERROR (RXBREAK | RXFRAME | RXPARITY)
+
+#define CTSFC_EN  0x80         /* CTS flow control enable bit */
+#define RTSTOG_EN 0x40         /* RTS toggle enable bit */
+#define TXINT_EN  0x10         /* transmit interrupt enable */
+#define STOP2     0x08         /* enable 2 stop bits (0 = 1 stop) */
+#define PARITY_EN 0x04         /* enable parity (0 = no parity) */
+#define EVEN_PAR  0x02         /* even parity (0 = odd parity) */
+#define DATA8BIT  0x01         /* 8 bit data (0 = 7 bit data) */
+
+#define SETBREAK  0x10         /* send break condition (must clear) */
+#define LOCALLOOP 0x08         /* local loopback set for test */
+#define SET_DTR   0x04         /* assert DTR */
+#define SET_RTS   0x02         /* assert RTS */
+#define TX_ENABLE 0x01         /* enable transmitter */
+
+#define RTSFC_EN  0x40         /* RTS flow control enable */
+#define RXPROC_EN 0x20         /* receive processor enable */
+#define TRIG_NO   0x00         /* Rx FIFO trigger level 0 (no trigger) */
+#define TRIG_1    0x08         /* trigger level 1 char */
+#define TRIG_1_2  0x10         /* trigger level 1/2 */
+#define TRIG_7_8  0x18         /* trigger level 7/8 */
+#define TRIG_MASK 0x18         /* trigger level mask */
+#define SRCINT_EN 0x04         /* special Rx condition interrupt enable */
+#define RXINT_EN  0x02         /* Rx interrupt enable */
+#define MCINT_EN  0x01         /* modem change interrupt enable */
+
+#define RXF_TRIG  0x20         /* Rx FIFO trigger level interrupt */
+#define TXFIFO_MT 0x10         /* Tx FIFO empty interrupt */
+#define SRC_INT   0x08         /* special receive condition interrupt */
+#define DELTA_CD  0x04         /* CD change interrupt */
+#define DELTA_CTS 0x02         /* CTS change interrupt */
+#define DELTA_DSR 0x01         /* DSR change interrupt */
+
+#define REP1W2_EN 0x10         /* replace byte 1 with 2 bytes enable */
+#define IGN2_EN   0x08         /* ignore byte 2 enable */
+#define IGN1_EN   0x04         /* ignore byte 1 enable */
+#define COMP2_EN  0x02         /* compare byte 2 enable */
+#define COMP1_EN  0x01         /* compare byte 1 enable */
+
+#define RESET_ALL 0x80         /* reset AIOP (all channels) */
+#define TXOVERIDE 0x40         /* Transmit software off override */
+#define RESETUART 0x20         /* reset channel's UART */
+#define RESTXFCNT 0x10         /* reset channel's Tx FIFO count register */
+#define RESRXFCNT 0x08         /* reset channel's Rx FIFO count register */
+
+#define INTSTAT0  0x01         /* AIOP 0 interrupt status */
+#define INTSTAT1  0x02         /* AIOP 1 interrupt status */
+#define INTSTAT2  0x04         /* AIOP 2 interrupt status */
+#define INTSTAT3  0x08         /* AIOP 3 interrupt status */
+
+#define INTR_EN   0x08         /* allow interrupts to host */
+#define INT_STROB 0x04         /* strobe and clear interrupt line (EOI) */
+
+/**************************************************************************
+ MUDBAC remapped for PCI
+**************************************************************************/
+
+#define _CFG_INT_PCI  0x40
+#define _PCI_INT_FUNC 0x3A
+
+#define PCI_STROB 0x2000       /* bit 13 of int aiop register */
+#define INTR_EN_PCI   0x0010   /* allow interrupts to host */
+
+/*
+ * Definitions for Universal PCI board registers
+ */
+#define _PCI_9030_INT_CTRL     0x4c          /* Offsets from BAR1 */
+#define _PCI_9030_GPIO_CTRL    0x54
+#define PCI_INT_CTRL_AIOP      0x0001
+#define PCI_GPIO_CTRL_8PORT    0x4000
+#define _PCI_9030_RING_IND     0xc0          /* Offsets from BAR1 */
+
+#define CHAN3_EN  0x08         /* enable AIOP 3 */
+#define CHAN2_EN  0x04         /* enable AIOP 2 */
+#define CHAN1_EN  0x02         /* enable AIOP 1 */
+#define CHAN0_EN  0x01         /* enable AIOP 0 */
+#define FREQ_DIS  0x00
+#define FREQ_274HZ 0x60
+#define FREQ_137HZ 0x50
+#define FREQ_69HZ  0x40
+#define FREQ_34HZ  0x30
+#define FREQ_17HZ  0x20
+#define FREQ_9HZ   0x10
+#define PERIODIC_ONLY 0x80     /* only PERIODIC interrupt */
+
+#define CHANINT_EN 0x0100      /* flags to enable/disable channel ints */
+
+#define RDATASIZE 72
+#define RREGDATASIZE 52
+
+/*
+ * AIOP interrupt bits for ISA/PCI boards and UPCI boards.
+ */
+#define AIOP_INTR_BIT_0                0x0001
+#define AIOP_INTR_BIT_1                0x0002
+#define AIOP_INTR_BIT_2                0x0004
+#define AIOP_INTR_BIT_3                0x0008
+
+#define AIOP_INTR_BITS ( \
+       AIOP_INTR_BIT_0 \
+       | AIOP_INTR_BIT_1 \
+       | AIOP_INTR_BIT_2 \
+       | AIOP_INTR_BIT_3)
+
+#define UPCI_AIOP_INTR_BIT_0   0x0004
+#define UPCI_AIOP_INTR_BIT_1   0x0020
+#define UPCI_AIOP_INTR_BIT_2   0x0100
+#define UPCI_AIOP_INTR_BIT_3   0x0800
+
+#define UPCI_AIOP_INTR_BITS ( \
+       UPCI_AIOP_INTR_BIT_0 \
+       | UPCI_AIOP_INTR_BIT_1 \
+       | UPCI_AIOP_INTR_BIT_2 \
+       | UPCI_AIOP_INTR_BIT_3)
+
+/* Controller level information structure */
+typedef struct {
+       int CtlID;
+       int CtlNum;
+       int BusType;
+       int boardType;
+       int isUPCI;
+       WordIO_t PCIIO;
+       WordIO_t PCIIO2;
+       ByteIO_t MBaseIO;
+       ByteIO_t MReg1IO;
+       ByteIO_t MReg2IO;
+       ByteIO_t MReg3IO;
+       Byte_t MReg2;
+       Byte_t MReg3;
+       int NumAiop;
+       int AltChanRingIndicator;
+       ByteIO_t UPCIRingInd;
+       WordIO_t AiopIO[AIOP_CTL_SIZE];
+       ByteIO_t AiopIntChanIO[AIOP_CTL_SIZE];
+       int AiopID[AIOP_CTL_SIZE];
+       int AiopNumChan[AIOP_CTL_SIZE];
+       Word_t *AiopIntrBits;
+} CONTROLLER_T;
+
+typedef CONTROLLER_T CONTROLLER_t;
+
+/* Channel level information structure */
+typedef struct {
+       CONTROLLER_T *CtlP;
+       int AiopNum;
+       int ChanID;
+       int ChanNum;
+       int rtsToggle;
+
+       ByteIO_t Cmd;
+       ByteIO_t IntChan;
+       ByteIO_t IntMask;
+       DWordIO_t IndexAddr;
+       WordIO_t IndexData;
+
+       WordIO_t TxRxData;
+       WordIO_t ChanStat;
+       WordIO_t TxRxCount;
+       ByteIO_t IntID;
+
+       Word_t TxFIFO;
+       Word_t TxFIFOPtrs;
+       Word_t RxFIFO;
+       Word_t RxFIFOPtrs;
+       Word_t TxPrioCnt;
+       Word_t TxPrioPtr;
+       Word_t TxPrioBuf;
+
+       Byte_t R[RREGDATASIZE];
+
+       Byte_t BaudDiv[4];
+       Byte_t TxControl[4];
+       Byte_t RxControl[4];
+       Byte_t TxEnables[4];
+       Byte_t TxCompare[4];
+       Byte_t TxReplace1[4];
+       Byte_t TxReplace2[4];
+} CHANNEL_T;
+
+typedef CHANNEL_T CHANNEL_t;
+typedef CHANNEL_T *CHANPTR_T;
+
+#define InterfaceModeRS232  0x00
+#define InterfaceModeRS422  0x08
+#define InterfaceModeRS485  0x10
+#define InterfaceModeRS232T 0x18
+
+/***************************************************************************
+Function: sClrBreak
+Purpose:  Stop sending a transmit BREAK signal
+Call:     sClrBreak(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sClrBreak(ChP) \
+do { \
+   (ChP)->TxControl[3] &= ~SETBREAK; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sClrDTR
+Purpose:  Clr the DTR output
+Call:     sClrDTR(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sClrDTR(ChP) \
+do { \
+   (ChP)->TxControl[3] &= ~SET_DTR; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sClrRTS
+Purpose:  Clr the RTS output
+Call:     sClrRTS(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sClrRTS(ChP) \
+do { \
+   if ((ChP)->rtsToggle) break; \
+   (ChP)->TxControl[3] &= ~SET_RTS; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sClrTxXOFF
+Purpose:  Clear any existing transmit software flow control off condition
+Call:     sClrTxXOFF(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sClrTxXOFF(ChP) \
+do { \
+   sOutB((ChP)->Cmd,TXOVERIDE | (Byte_t)(ChP)->ChanNum); \
+   sOutB((ChP)->Cmd,(Byte_t)(ChP)->ChanNum); \
+} while (0)
+
+/***************************************************************************
+Function: sCtlNumToCtlPtr
+Purpose:  Convert a controller number to controller structure pointer
+Call:     sCtlNumToCtlPtr(CtlNum)
+          int CtlNum; Controller number
+Return:   CONTROLLER_T *: Ptr to controller structure
+*/
+#define sCtlNumToCtlPtr(CTLNUM) &sController[CTLNUM]
+
+/***************************************************************************
+Function: sControllerEOI
+Purpose:  Strobe the MUDBAC's End Of Interrupt bit.
+Call:     sControllerEOI(CtlP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+*/
+#define sControllerEOI(CTLP) sOutB((CTLP)->MReg2IO,(CTLP)->MReg2 | INT_STROB)
+
+/***************************************************************************
+Function: sPCIControllerEOI
+Purpose:  Strobe the PCI End Of Interrupt bit.
+          For the UPCI boards, toggle the AIOP interrupt enable bit
+         (this was taken from the Windows driver).
+Call:     sPCIControllerEOI(CtlP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+*/
+#define sPCIControllerEOI(CTLP) \
+do { \
+    if ((CTLP)->isUPCI) { \
+       Word_t w = sInW((CTLP)->PCIIO); \
+       sOutW((CTLP)->PCIIO, (w ^ PCI_INT_CTRL_AIOP)); \
+       sOutW((CTLP)->PCIIO, w); \
+    } \
+    else { \
+       sOutW((CTLP)->PCIIO, PCI_STROB); \
+    } \
+} while (0)
+
+/***************************************************************************
+Function: sDisAiop
+Purpose:  Disable I/O access to an AIOP
+Call:     sDisAiop(CltP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int AiopNum; Number of AIOP on controller
+*/
+#define sDisAiop(CTLP,AIOPNUM) \
+do { \
+   (CTLP)->MReg3 &= sBitMapClrTbl[AIOPNUM]; \
+   sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \
+} while (0)
+
+/***************************************************************************
+Function: sDisCTSFlowCtl
+Purpose:  Disable output flow control using CTS
+Call:     sDisCTSFlowCtl(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sDisCTSFlowCtl(ChP) \
+do { \
+   (ChP)->TxControl[2] &= ~CTSFC_EN; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sDisIXANY
+Purpose:  Disable IXANY Software Flow Control
+Call:     sDisIXANY(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sDisIXANY(ChP) \
+do { \
+   (ChP)->R[0x0e] = 0x86; \
+   out32((ChP)->IndexAddr,&(ChP)->R[0x0c]); \
+} while (0)
+
+/***************************************************************************
+Function: DisParity
+Purpose:  Disable parity
+Call:     sDisParity(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: Function sSetParity() can be used in place of functions sEnParity(),
+          sDisParity(), sSetOddParity(), and sSetEvenParity().
+*/
+#define sDisParity(ChP) \
+do { \
+   (ChP)->TxControl[2] &= ~PARITY_EN; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sDisRTSToggle
+Purpose:  Disable RTS toggle
+Call:     sDisRTSToggle(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sDisRTSToggle(ChP) \
+do { \
+   (ChP)->TxControl[2] &= ~RTSTOG_EN; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+   (ChP)->rtsToggle = 0; \
+} while (0)
+
+/***************************************************************************
+Function: sDisRxFIFO
+Purpose:  Disable Rx FIFO
+Call:     sDisRxFIFO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sDisRxFIFO(ChP) \
+do { \
+   (ChP)->R[0x32] = 0x0a; \
+   out32((ChP)->IndexAddr,&(ChP)->R[0x30]); \
+} while (0)
+
+/***************************************************************************
+Function: sDisRxStatusMode
+Purpose:  Disable the Rx status mode
+Call:     sDisRxStatusMode(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: This takes the channel out of the receive status mode.  All
+          subsequent reads of receive data using sReadRxWord() will return
+          two data bytes.
+*/
+#define sDisRxStatusMode(ChP) sOutW((ChP)->ChanStat,0)
+
+/***************************************************************************
+Function: sDisTransmit
+Purpose:  Disable transmit
+Call:     sDisTransmit(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+          This disables movement of Tx data from the Tx FIFO into the 1 byte
+          Tx buffer.  Therefore there could be up to a 2 byte latency
+          between the time sDisTransmit() is called and the transmit buffer
+          and transmit shift register going completely empty.
+*/
+#define sDisTransmit(ChP) \
+do { \
+   (ChP)->TxControl[3] &= ~TX_ENABLE; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sDisTxSoftFlowCtl
+Purpose:  Disable Tx Software Flow Control
+Call:     sDisTxSoftFlowCtl(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sDisTxSoftFlowCtl(ChP) \
+do { \
+   (ChP)->R[0x06] = 0x8a; \
+   out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \
+} while (0)
+
+/***************************************************************************
+Function: sEnAiop
+Purpose:  Enable I/O access to an AIOP
+Call:     sEnAiop(CltP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int AiopNum; Number of AIOP on controller
+*/
+#define sEnAiop(CTLP,AIOPNUM) \
+do { \
+   (CTLP)->MReg3 |= sBitMapSetTbl[AIOPNUM]; \
+   sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \
+} while (0)
+
+/***************************************************************************
+Function: sEnCTSFlowCtl
+Purpose:  Enable output flow control using CTS
+Call:     sEnCTSFlowCtl(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sEnCTSFlowCtl(ChP) \
+do { \
+   (ChP)->TxControl[2] |= CTSFC_EN; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sEnIXANY
+Purpose:  Enable IXANY Software Flow Control
+Call:     sEnIXANY(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sEnIXANY(ChP) \
+do { \
+   (ChP)->R[0x0e] = 0x21; \
+   out32((ChP)->IndexAddr,&(ChP)->R[0x0c]); \
+} while (0)
+
+/***************************************************************************
+Function: EnParity
+Purpose:  Enable parity
+Call:     sEnParity(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: Function sSetParity() can be used in place of functions sEnParity(),
+          sDisParity(), sSetOddParity(), and sSetEvenParity().
+
+Warnings: Before enabling parity odd or even parity should be chosen using
+          functions sSetOddParity() or sSetEvenParity().
+*/
+#define sEnParity(ChP) \
+do { \
+   (ChP)->TxControl[2] |= PARITY_EN; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sEnRTSToggle
+Purpose:  Enable RTS toggle
+Call:     sEnRTSToggle(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: This function will disable RTS flow control and clear the RTS
+          line to allow operation of RTS toggle.
+*/
+#define sEnRTSToggle(ChP) \
+do { \
+   (ChP)->RxControl[2] &= ~RTSFC_EN; \
+   out32((ChP)->IndexAddr,(ChP)->RxControl); \
+   (ChP)->TxControl[2] |= RTSTOG_EN; \
+   (ChP)->TxControl[3] &= ~SET_RTS; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+   (ChP)->rtsToggle = 1; \
+} while (0)
+
+/***************************************************************************
+Function: sEnRxFIFO
+Purpose:  Enable Rx FIFO
+Call:     sEnRxFIFO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sEnRxFIFO(ChP) \
+do { \
+   (ChP)->R[0x32] = 0x08; \
+   out32((ChP)->IndexAddr,&(ChP)->R[0x30]); \
+} while (0)
+
+/***************************************************************************
+Function: sEnRxProcessor
+Purpose:  Enable the receive processor
+Call:     sEnRxProcessor(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: This function is used to start the receive processor.  When
+          the channel is in the reset state the receive processor is not
+          running.  This is done to prevent the receive processor from
+          executing invalid microcode instructions prior to the
+          downloading of the microcode.
+
+Warnings: This function must be called after valid microcode has been
+          downloaded to the AIOP, and it must not be called before the
+          microcode has been downloaded.
+*/
+#define sEnRxProcessor(ChP) \
+do { \
+   (ChP)->RxControl[2] |= RXPROC_EN; \
+   out32((ChP)->IndexAddr,(ChP)->RxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sEnRxStatusMode
+Purpose:  Enable the Rx status mode
+Call:     sEnRxStatusMode(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: This places the channel in the receive status mode.  All subsequent
+          reads of receive data using sReadRxWord() will return a data byte
+          in the low word and a status byte in the high word.
+
+*/
+#define sEnRxStatusMode(ChP) sOutW((ChP)->ChanStat,STATMODE)
+
+/***************************************************************************
+Function: sEnTransmit
+Purpose:  Enable transmit
+Call:     sEnTransmit(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sEnTransmit(ChP) \
+do { \
+   (ChP)->TxControl[3] |= TX_ENABLE; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sEnTxSoftFlowCtl
+Purpose:  Enable Tx Software Flow Control
+Call:     sEnTxSoftFlowCtl(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sEnTxSoftFlowCtl(ChP) \
+do { \
+   (ChP)->R[0x06] = 0xc5; \
+   out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \
+} while (0)
+
+/***************************************************************************
+Function: sGetAiopIntStatus
+Purpose:  Get the AIOP interrupt status
+Call:     sGetAiopIntStatus(CtlP,AiopNum)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int AiopNum; AIOP number
+Return:   Byte_t: The AIOP interrupt status.  Bits 0 through 7
+                         represent channels 0 through 7 respectively.  If a
+                         bit is set that channel is interrupting.
+*/
+#define sGetAiopIntStatus(CTLP,AIOPNUM) sInB((CTLP)->AiopIntChanIO[AIOPNUM])
+
+/***************************************************************************
+Function: sGetAiopNumChan
+Purpose:  Get the number of channels supported by an AIOP
+Call:     sGetAiopNumChan(CtlP,AiopNum)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int AiopNum; AIOP number
+Return:   int: The number of channels supported by the AIOP
+*/
+#define sGetAiopNumChan(CTLP,AIOPNUM) (CTLP)->AiopNumChan[AIOPNUM]
+
+/***************************************************************************
+Function: sGetChanIntID
+Purpose:  Get a channel's interrupt identification byte
+Call:     sGetChanIntID(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   Byte_t: The channel interrupt ID.  Can be any
+             combination of the following flags:
+                RXF_TRIG:     Rx FIFO trigger level interrupt
+                TXFIFO_MT:    Tx FIFO empty interrupt
+                SRC_INT:      Special receive condition interrupt
+                DELTA_CD:     CD change interrupt
+                DELTA_CTS:    CTS change interrupt
+                DELTA_DSR:    DSR change interrupt
+*/
+#define sGetChanIntID(ChP) (sInB((ChP)->IntID) & (RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR))
+
+/***************************************************************************
+Function: sGetChanNum
+Purpose:  Get the number of a channel within an AIOP
+Call:     sGetChanNum(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   int: Channel number within AIOP, or NULLCHAN if channel does
+               not exist.
+*/
+#define sGetChanNum(ChP) (ChP)->ChanNum
+
+/***************************************************************************
+Function: sGetChanStatus
+Purpose:  Get the channel status
+Call:     sGetChanStatus(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   Word_t: The channel status.  Can be any combination of
+             the following flags:
+                LOW BYTE FLAGS
+                CTS_ACT:      CTS input asserted
+                DSR_ACT:      DSR input asserted
+                CD_ACT:       CD input asserted
+                TXFIFOMT:     Tx FIFO is empty
+                TXSHRMT:      Tx shift register is empty
+                RDA:          Rx data available
+
+                HIGH BYTE FLAGS
+                STATMODE:     status mode enable bit
+                RXFOVERFL:    receive FIFO overflow
+                RX2MATCH:     receive compare byte 2 match
+                RX1MATCH:     receive compare byte 1 match
+                RXBREAK:      received BREAK
+                RXFRAME:      received framing error
+                RXPARITY:     received parity error
+Warnings: This function will clear the high byte flags in the Channel
+          Status Register.
+*/
+#define sGetChanStatus(ChP) sInW((ChP)->ChanStat)
+
+/***************************************************************************
+Function: sGetChanStatusLo
+Purpose:  Get the low byte only of the channel status
+Call:     sGetChanStatusLo(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   Byte_t: The channel status low byte.  Can be any combination
+             of the following flags:
+                CTS_ACT:      CTS input asserted
+                DSR_ACT:      DSR input asserted
+                CD_ACT:       CD input asserted
+                TXFIFOMT:     Tx FIFO is empty
+                TXSHRMT:      Tx shift register is empty
+                RDA:          Rx data available
+*/
+#define sGetChanStatusLo(ChP) sInB((ByteIO_t)(ChP)->ChanStat)
+
+/**********************************************************************
+ * Get RI status of channel
+ * Defined as a function in rocket.c   -aes
+ */
+#if 0
+#define sGetChanRI(ChP) ((ChP)->CtlP->AltChanRingIndicator ? \
+                          (sInB((ByteIO_t)((ChP)->ChanStat+8)) & DSR_ACT) : \
+                            (((ChP)->CtlP->boardType == ROCKET_TYPE_PC104) ? \
+                               (!(sInB((ChP)->CtlP->AiopIO[3]) & sBitMapSetTbl[(ChP)->ChanNum])) : \
+                             0))
+#endif
+
+/***************************************************************************
+Function: sGetControllerIntStatus
+Purpose:  Get the controller interrupt status
+Call:     sGetControllerIntStatus(CtlP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+Return:   Byte_t: The controller interrupt status in the lower 4
+                         bits.  Bits 0 through 3 represent AIOP's 0
+                         through 3 respectively.  If a bit is set that
+                         AIOP is interrupting.  Bits 4 through 7 will
+                         always be cleared.
+*/
+#define sGetControllerIntStatus(CTLP) (sInB((CTLP)->MReg1IO) & 0x0f)
+
+/***************************************************************************
+Function: sPCIGetControllerIntStatus
+Purpose:  Get the controller interrupt status
+Call:     sPCIGetControllerIntStatus(CtlP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+Return:   unsigned char: The controller interrupt status in the lower 4
+                         bits and bit 4.  Bits 0 through 3 represent AIOP's 0
+                         through 3 respectively. Bit 4 is set if the int 
+                        was generated from periodic. If a bit is set the
+                        AIOP is interrupting.
+*/
+#define sPCIGetControllerIntStatus(CTLP) \
+       ((CTLP)->isUPCI ? \
+         (sInW((CTLP)->PCIIO2) & UPCI_AIOP_INTR_BITS) : \
+         ((sInW((CTLP)->PCIIO) >> 8) & AIOP_INTR_BITS))
+
+/***************************************************************************
+
+Function: sGetRxCnt
+Purpose:  Get the number of data bytes in the Rx FIFO
+Call:     sGetRxCnt(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   int: The number of data bytes in the Rx FIFO.
+Comments: Byte read of count register is required to obtain Rx count.
+
+*/
+#define sGetRxCnt(ChP) sInW((ChP)->TxRxCount)
+
+/***************************************************************************
+Function: sGetTxCnt
+Purpose:  Get the number of data bytes in the Tx FIFO
+Call:     sGetTxCnt(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   Byte_t: The number of data bytes in the Tx FIFO.
+Comments: Byte read of count register is required to obtain Tx count.
+
+*/
+#define sGetTxCnt(ChP) sInB((ByteIO_t)(ChP)->TxRxCount)
+
+/*****************************************************************************
+Function: sGetTxRxDataIO
+Purpose:  Get the I/O address of a channel's TxRx Data register
+Call:     sGetTxRxDataIO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   WordIO_t: I/O address of a channel's TxRx Data register
+*/
+#define sGetTxRxDataIO(ChP) (ChP)->TxRxData
+
+/***************************************************************************
+Function: sInitChanDefaults
+Purpose:  Initialize a channel structure to it's default state.
+Call:     sInitChanDefaults(ChP)
+          CHANNEL_T *ChP; Ptr to the channel structure
+Comments: This function must be called once for every channel structure
+          that exists before any other SSCI calls can be made.
+
+*/
+#define sInitChanDefaults(ChP) \
+do { \
+   (ChP)->CtlP = NULLCTLPTR; \
+   (ChP)->AiopNum = NULLAIOP; \
+   (ChP)->ChanID = AIOPID_NULL; \
+   (ChP)->ChanNum = NULLCHAN; \
+} while (0)
+
+/***************************************************************************
+Function: sResetAiopByNum
+Purpose:  Reset the AIOP by number
+Call:     sResetAiopByNum(CTLP,AIOPNUM)
+       CONTROLLER_T CTLP; Ptr to controller structure
+       AIOPNUM; AIOP index 
+*/
+#define sResetAiopByNum(CTLP,AIOPNUM) \
+do { \
+   sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,RESET_ALL); \
+   sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,0x0); \
+} while (0)
+
+/***************************************************************************
+Function: sSendBreak
+Purpose:  Send a transmit BREAK signal
+Call:     sSendBreak(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSendBreak(ChP) \
+do { \
+   (ChP)->TxControl[3] |= SETBREAK; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sSetBaud
+Purpose:  Set baud rate
+Call:     sSetBaud(ChP,Divisor)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Word_t Divisor; 16 bit baud rate divisor for channel
+*/
+#define sSetBaud(ChP,DIVISOR) \
+do { \
+   (ChP)->BaudDiv[2] = (Byte_t)(DIVISOR); \
+   (ChP)->BaudDiv[3] = (Byte_t)((DIVISOR) >> 8); \
+   out32((ChP)->IndexAddr,(ChP)->BaudDiv); \
+} while (0)
+
+/***************************************************************************
+Function: sSetData7
+Purpose:  Set data bits to 7
+Call:     sSetData7(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetData7(ChP) \
+do { \
+   (ChP)->TxControl[2] &= ~DATA8BIT; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sSetData8
+Purpose:  Set data bits to 8
+Call:     sSetData8(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetData8(ChP) \
+do { \
+   (ChP)->TxControl[2] |= DATA8BIT; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sSetDTR
+Purpose:  Set the DTR output
+Call:     sSetDTR(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetDTR(ChP) \
+do { \
+   (ChP)->TxControl[3] |= SET_DTR; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sSetEvenParity
+Purpose:  Set even parity
+Call:     sSetEvenParity(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: Function sSetParity() can be used in place of functions sEnParity(),
+          sDisParity(), sSetOddParity(), and sSetEvenParity().
+
+Warnings: This function has no effect unless parity is enabled with function
+          sEnParity().
+*/
+#define sSetEvenParity(ChP) \
+do { \
+   (ChP)->TxControl[2] |= EVEN_PAR; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sSetOddParity
+Purpose:  Set odd parity
+Call:     sSetOddParity(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: Function sSetParity() can be used in place of functions sEnParity(),
+          sDisParity(), sSetOddParity(), and sSetEvenParity().
+
+Warnings: This function has no effect unless parity is enabled with function
+          sEnParity().
+*/
+#define sSetOddParity(ChP) \
+do { \
+   (ChP)->TxControl[2] &= ~EVEN_PAR; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sSetRTS
+Purpose:  Set the RTS output
+Call:     sSetRTS(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetRTS(ChP) \
+do { \
+   if ((ChP)->rtsToggle) break; \
+   (ChP)->TxControl[3] |= SET_RTS; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sSetRxTrigger
+Purpose:  Set the Rx FIFO trigger level
+Call:     sSetRxProcessor(ChP,Level)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Byte_t Level; Number of characters in Rx FIFO at which the
+             interrupt will be generated.  Can be any of the following flags:
+
+             TRIG_NO:   no trigger
+             TRIG_1:    1 character in FIFO
+             TRIG_1_2:  FIFO 1/2 full
+             TRIG_7_8:  FIFO 7/8 full
+Comments: An interrupt will be generated when the trigger level is reached
+          only if function sEnInterrupt() has been called with flag
+          RXINT_EN set.  The RXF_TRIG flag in the Interrupt Idenfification
+          register will be set whenever the trigger level is reached
+          regardless of the setting of RXINT_EN.
+
+*/
+#define sSetRxTrigger(ChP,LEVEL) \
+do { \
+   (ChP)->RxControl[2] &= ~TRIG_MASK; \
+   (ChP)->RxControl[2] |= LEVEL; \
+   out32((ChP)->IndexAddr,(ChP)->RxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sSetStop1
+Purpose:  Set stop bits to 1
+Call:     sSetStop1(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetStop1(ChP) \
+do { \
+   (ChP)->TxControl[2] &= ~STOP2; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sSetStop2
+Purpose:  Set stop bits to 2
+Call:     sSetStop2(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetStop2(ChP) \
+do { \
+   (ChP)->TxControl[2] |= STOP2; \
+   out32((ChP)->IndexAddr,(ChP)->TxControl); \
+} while (0)
+
+/***************************************************************************
+Function: sSetTxXOFFChar
+Purpose:  Set the Tx XOFF flow control character
+Call:     sSetTxXOFFChar(ChP,Ch)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Byte_t Ch; The value to set the Tx XOFF character to
+*/
+#define sSetTxXOFFChar(ChP,CH) \
+do { \
+   (ChP)->R[0x07] = (CH); \
+   out32((ChP)->IndexAddr,&(ChP)->R[0x04]); \
+} while (0)
+
+/***************************************************************************
+Function: sSetTxXONChar
+Purpose:  Set the Tx XON flow control character
+Call:     sSetTxXONChar(ChP,Ch)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Byte_t Ch; The value to set the Tx XON character to
+*/
+#define sSetTxXONChar(ChP,CH) \
+do { \
+   (ChP)->R[0x0b] = (CH); \
+   out32((ChP)->IndexAddr,&(ChP)->R[0x08]); \
+} while (0)
+
+/***************************************************************************
+Function: sStartRxProcessor
+Purpose:  Start a channel's receive processor
+Call:     sStartRxProcessor(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: This function is used to start a Rx processor after it was
+          stopped with sStopRxProcessor() or sStopSWInFlowCtl().  It
+          will restart both the Rx processor and software input flow control.
+
+*/
+#define sStartRxProcessor(ChP) out32((ChP)->IndexAddr,&(ChP)->R[0])
+
+/***************************************************************************
+Function: sWriteTxByte
+Purpose:  Write a transmit data byte to a channel.
+          ByteIO_t io: Channel transmit register I/O address.  This can
+                           be obtained with sGetTxRxDataIO().
+          Byte_t Data; The transmit data byte.
+Warnings: This function writes the data byte without checking to see if
+          sMaxTxSize is exceeded in the Tx FIFO.
+*/
+#define sWriteTxByte(IO,DATA) sOutB(IO,DATA)
+
+/*
+ * Begin Linux specific definitions for the Rocketport driver
+ *
+ * This code is Copyright Theodore Ts'o, 1995-1997
+ */
+
+struct r_port {
+       int magic;
+       struct tty_port port;
+       int line;
+       int flags;              /* Don't yet match the ASY_ flags!! */
+       unsigned int board:3;
+       unsigned int aiop:2;
+       unsigned int chan:3;
+       CONTROLLER_t *ctlp;
+       CHANNEL_t channel;
+       int intmask;
+       int xmit_fifo_room;     /* room in xmit fifo */
+       unsigned char *xmit_buf;
+       int xmit_head;
+       int xmit_tail;
+       int xmit_cnt;
+       int cd_status;
+       int ignore_status_mask;
+       int read_status_mask;
+       int cps;
+
+       struct completion close_wait;   /* Not yet matching the core */
+       spinlock_t slock;
+       struct mutex write_mtx;
+};
+
+#define RPORT_MAGIC 0x525001
+
+#define NUM_BOARDS 8
+#define MAX_RP_PORTS (32*NUM_BOARDS)
+
+/*
+ * The size of the xmit buffer is 1 page, or 4096 bytes
+ */
+#define XMIT_BUF_SIZE 4096
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/*
+ * Assigned major numbers for the Comtrol Rocketport
+ */
+#define TTY_ROCKET_MAJOR       46
+#define CUA_ROCKET_MAJOR       47
+
+#ifdef PCI_VENDOR_ID_RP
+#undef PCI_VENDOR_ID_RP
+#undef PCI_DEVICE_ID_RP8OCTA
+#undef PCI_DEVICE_ID_RP8INTF
+#undef PCI_DEVICE_ID_RP16INTF
+#undef PCI_DEVICE_ID_RP32INTF
+#undef PCI_DEVICE_ID_URP8OCTA
+#undef PCI_DEVICE_ID_URP8INTF
+#undef PCI_DEVICE_ID_URP16INTF
+#undef PCI_DEVICE_ID_CRP16INTF
+#undef PCI_DEVICE_ID_URP32INTF
+#endif
+
+/*  Comtrol PCI Vendor ID */
+#define PCI_VENDOR_ID_RP               0x11fe
+
+/*  Comtrol Device ID's */
+#define PCI_DEVICE_ID_RP32INTF         0x0001  /* Rocketport 32 port w/external I/F     */
+#define PCI_DEVICE_ID_RP8INTF          0x0002  /* Rocketport 8 port w/external I/F      */
+#define PCI_DEVICE_ID_RP16INTF         0x0003  /* Rocketport 16 port w/external I/F     */
+#define PCI_DEVICE_ID_RP4QUAD          0x0004  /* Rocketport 4 port w/quad cable        */
+#define PCI_DEVICE_ID_RP8OCTA          0x0005  /* Rocketport 8 port w/octa cable        */
+#define PCI_DEVICE_ID_RP8J             0x0006  /* Rocketport 8 port w/RJ11 connectors   */
+#define PCI_DEVICE_ID_RP4J             0x0007  /* Rocketport 4 port w/RJ11 connectors   */
+#define PCI_DEVICE_ID_RP8SNI           0x0008  /* Rocketport 8 port w/ DB78 SNI (Siemens) connector */
+#define PCI_DEVICE_ID_RP16SNI          0x0009  /* Rocketport 16 port w/ DB78 SNI (Siemens) connector   */
+#define PCI_DEVICE_ID_RPP4             0x000A  /* Rocketport Plus 4 port                */
+#define PCI_DEVICE_ID_RPP8             0x000B  /* Rocketport Plus 8 port                */
+#define PCI_DEVICE_ID_RP6M             0x000C  /* RocketModem 6 port                    */
+#define PCI_DEVICE_ID_RP4M             0x000D  /* RocketModem 4 port                    */
+#define PCI_DEVICE_ID_RP2_232           0x000E /* Rocketport Plus 2 port RS232          */
+#define PCI_DEVICE_ID_RP2_422           0x000F /* Rocketport Plus 2 port RS422          */ 
+
+/* Universal PCI boards  */
+#define PCI_DEVICE_ID_URP32INTF                0x0801  /* Rocketport UPCI 32 port w/external I/F */ 
+#define PCI_DEVICE_ID_URP8INTF         0x0802  /* Rocketport UPCI 8 port w/external I/F  */
+#define PCI_DEVICE_ID_URP16INTF                0x0803  /* Rocketport UPCI 16 port w/external I/F */
+#define PCI_DEVICE_ID_URP8OCTA         0x0805  /* Rocketport UPCI 8 port w/octa cable    */
+#define PCI_DEVICE_ID_UPCI_RM3_8PORT    0x080C /* Rocketmodem III 8 port                 */
+#define PCI_DEVICE_ID_UPCI_RM3_4PORT    0x080D /* Rocketmodem III 4 port                 */
+
+/* Compact PCI device */ 
+#define PCI_DEVICE_ID_CRP16INTF                0x0903  /* Rocketport Compact PCI 16 port w/external I/F */
+
diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c
new file mode 100644 (file)
index 0000000..18888d0
--- /dev/null
@@ -0,0 +1,8119 @@
+/*
+ * linux/drivers/char/synclink.c
+ *
+ * $Id: synclink.c,v 4.38 2005/11/07 16:30:34 paulkf Exp $
+ *
+ * Device driver for Microgate SyncLink ISA and PCI
+ * high speed multiprotocol serial adapters.
+ *
+ * written by Paul Fulghum for Microgate Corporation
+ * paulkf@microgate.com
+ *
+ * Microgate and SyncLink are trademarks of Microgate Corporation
+ *
+ * Derived from serial.c written by Theodore Ts'o and Linus Torvalds
+ *
+ * Original release 01/11/99
+ *
+ * This code is released under the GNU General Public License (GPL)
+ *
+ * This driver is primarily intended for use in synchronous
+ * HDLC mode. Asynchronous mode is also provided.
+ *
+ * When operating in synchronous mode, each call to mgsl_write()
+ * contains exactly one complete HDLC frame. Calling mgsl_put_char
+ * will start assembling an HDLC frame that will not be sent until
+ * mgsl_flush_chars or mgsl_write is called.
+ * 
+ * Synchronous receive data is reported as complete frames. To accomplish
+ * this, the TTY flip buffer is bypassed (too small to hold largest
+ * frame and may fragment frames) and the line discipline
+ * receive entry point is called directly.
+ *
+ * This driver has been tested with a slightly modified ppp.c driver
+ * for synchronous PPP.
+ *
+ * 2000/02/16
+ * Added interface for syncppp.c driver (an alternate synchronous PPP
+ * implementation that also supports Cisco HDLC). Each device instance
+ * registers as a tty device AND a network device (if dosyncppp option
+ * is set for the device). The functionality is determined by which
+ * device interface is opened.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(__i386__)
+#  define BREAKPOINT() asm("   int $3");
+#else
+#  define BREAKPOINT() { }
+#endif
+
+#define MAX_ISA_DEVICES 10
+#define MAX_PCI_DEVICES 10
+#define MAX_TOTAL_DEVICES 20
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/ioctl.h>
+#include <linux/synclink.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <linux/bitops.h>
+#include <asm/types.h>
+#include <linux/termios.h>
+#include <linux/workqueue.h>
+#include <linux/hdlc.h>
+#include <linux/dma-mapping.h>
+
+#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_MODULE))
+#define SYNCLINK_GENERIC_HDLC 1
+#else
+#define SYNCLINK_GENERIC_HDLC 0
+#endif
+
+#define GET_USER(error,value,addr) error = get_user(value,addr)
+#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0
+#define PUT_USER(error,value,addr) error = put_user(value,addr)
+#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0
+
+#include <asm/uaccess.h>
+
+#define RCLRVALUE 0xffff
+
+static MGSL_PARAMS default_params = {
+       MGSL_MODE_HDLC,                 /* unsigned long mode */
+       0,                              /* unsigned char loopback; */
+       HDLC_FLAG_UNDERRUN_ABORT15,     /* unsigned short flags; */
+       HDLC_ENCODING_NRZI_SPACE,       /* unsigned char encoding; */
+       0,                              /* unsigned long clock_speed; */
+       0xff,                           /* unsigned char addr_filter; */
+       HDLC_CRC_16_CCITT,              /* unsigned short crc_type; */
+       HDLC_PREAMBLE_LENGTH_8BITS,     /* unsigned char preamble_length; */
+       HDLC_PREAMBLE_PATTERN_NONE,     /* unsigned char preamble; */
+       9600,                           /* unsigned long data_rate; */
+       8,                              /* unsigned char data_bits; */
+       1,                              /* unsigned char stop_bits; */
+       ASYNC_PARITY_NONE               /* unsigned char parity; */
+};
+
+#define SHARED_MEM_ADDRESS_SIZE 0x40000
+#define BUFFERLISTSIZE 4096
+#define DMABUFFERSIZE 4096
+#define MAXRXFRAMES 7
+
+typedef struct _DMABUFFERENTRY
+{
+       u32 phys_addr;  /* 32-bit flat physical address of data buffer */
+       volatile u16 count;     /* buffer size/data count */
+       volatile u16 status;    /* Control/status field */
+       volatile u16 rcc;       /* character count field */
+       u16 reserved;   /* padding required by 16C32 */
+       u32 link;       /* 32-bit flat link to next buffer entry */
+       char *virt_addr;        /* virtual address of data buffer */
+       u32 phys_entry; /* physical address of this buffer entry */
+       dma_addr_t dma_addr;
+} DMABUFFERENTRY, *DMAPBUFFERENTRY;
+
+/* The queue of BH actions to be performed */
+
+#define BH_RECEIVE  1
+#define BH_TRANSMIT 2
+#define BH_STATUS   4
+
+#define IO_PIN_SHUTDOWN_LIMIT 100
+
+struct _input_signal_events {
+       int     ri_up;  
+       int     ri_down;
+       int     dsr_up;
+       int     dsr_down;
+       int     dcd_up;
+       int     dcd_down;
+       int     cts_up;
+       int     cts_down;
+};
+
+/* transmit holding buffer definitions*/
+#define MAX_TX_HOLDING_BUFFERS 5
+struct tx_holding_buffer {
+       int     buffer_size;
+       unsigned char * buffer;
+};
+
+
+/*
+ * Device instance data structure
+ */
+struct mgsl_struct {
+       int                     magic;
+       struct tty_port         port;
+       int                     line;
+       int                     hw_version;
+       
+       struct mgsl_icount      icount;
+       
+       int                     timeout;
+       int                     x_char;         /* xon/xoff character */
+       u16                     read_status_mask;
+       u16                     ignore_status_mask;     
+       unsigned char           *xmit_buf;
+       int                     xmit_head;
+       int                     xmit_tail;
+       int                     xmit_cnt;
+       
+       wait_queue_head_t       status_event_wait_q;
+       wait_queue_head_t       event_wait_q;
+       struct timer_list       tx_timer;       /* HDLC transmit timeout timer */
+       struct mgsl_struct      *next_device;   /* device list link */
+       
+       spinlock_t irq_spinlock;                /* spinlock for synchronizing with ISR */
+       struct work_struct task;                /* task structure for scheduling bh */
+
+       u32 EventMask;                  /* event trigger mask */
+       u32 RecordedEvents;             /* pending events */
+
+       u32 max_frame_size;             /* as set by device config */
+
+       u32 pending_bh;
+
+       bool bh_running;                /* Protection from multiple */
+       int isr_overflow;
+       bool bh_requested;
+       
+       int dcd_chkcount;               /* check counts to prevent */
+       int cts_chkcount;               /* too many IRQs if a signal */
+       int dsr_chkcount;               /* is floating */
+       int ri_chkcount;
+
+       char *buffer_list;              /* virtual address of Rx & Tx buffer lists */
+       u32 buffer_list_phys;
+       dma_addr_t buffer_list_dma_addr;
+
+       unsigned int rx_buffer_count;   /* count of total allocated Rx buffers */
+       DMABUFFERENTRY *rx_buffer_list; /* list of receive buffer entries */
+       unsigned int current_rx_buffer;
+
+       int num_tx_dma_buffers;         /* number of tx dma frames required */
+       int tx_dma_buffers_used;
+       unsigned int tx_buffer_count;   /* count of total allocated Tx buffers */
+       DMABUFFERENTRY *tx_buffer_list; /* list of transmit buffer entries */
+       int start_tx_dma_buffer;        /* tx dma buffer to start tx dma operation */
+       int current_tx_buffer;          /* next tx dma buffer to be loaded */
+       
+       unsigned char *intermediate_rxbuffer;
+
+       int num_tx_holding_buffers;     /* number of tx holding buffer allocated */
+       int get_tx_holding_index;       /* next tx holding buffer for adapter to load */
+       int put_tx_holding_index;       /* next tx holding buffer to store user request */
+       int tx_holding_count;           /* number of tx holding buffers waiting */
+       struct tx_holding_buffer tx_holding_buffers[MAX_TX_HOLDING_BUFFERS];
+
+       bool rx_enabled;
+       bool rx_overflow;
+       bool rx_rcc_underrun;
+
+       bool tx_enabled;
+       bool tx_active;
+       u32 idle_mode;
+
+       u16 cmr_value;
+       u16 tcsr_value;
+
+       char device_name[25];           /* device instance name */
+
+       unsigned int bus_type;  /* expansion bus type (ISA,EISA,PCI) */
+       unsigned char bus;              /* expansion bus number (zero based) */
+       unsigned char function;         /* PCI device number */
+
+       unsigned int io_base;           /* base I/O address of adapter */
+       unsigned int io_addr_size;      /* size of the I/O address range */
+       bool io_addr_requested;         /* true if I/O address requested */
+       
+       unsigned int irq_level;         /* interrupt level */
+       unsigned long irq_flags;
+       bool irq_requested;             /* true if IRQ requested */
+       
+       unsigned int dma_level;         /* DMA channel */
+       bool dma_requested;             /* true if dma channel requested */
+
+       u16 mbre_bit;
+       u16 loopback_bits;
+       u16 usc_idle_mode;
+
+       MGSL_PARAMS params;             /* communications parameters */
+
+       unsigned char serial_signals;   /* current serial signal states */
+
+       bool irq_occurred;              /* for diagnostics use */
+       unsigned int init_error;        /* Initialization startup error                 (DIAGS) */
+       int     fDiagnosticsmode;       /* Driver in Diagnostic mode?                   (DIAGS) */
+
+       u32 last_mem_alloc;
+       unsigned char* memory_base;     /* shared memory address (PCI only) */
+       u32 phys_memory_base;
+       bool shared_mem_requested;
+
+       unsigned char* lcr_base;        /* local config registers (PCI only) */
+       u32 phys_lcr_base;
+       u32 lcr_offset;
+       bool lcr_mem_requested;
+
+       u32 misc_ctrl_value;
+       char flag_buf[MAX_ASYNC_BUFFER_SIZE];
+       char char_buf[MAX_ASYNC_BUFFER_SIZE];   
+       bool drop_rts_on_tx_done;
+
+       bool loopmode_insert_requested;
+       bool loopmode_send_done_requested;
+       
+       struct  _input_signal_events    input_signal_events;
+
+       /* generic HDLC device parts */
+       int netcount;
+       spinlock_t netlock;
+
+#if SYNCLINK_GENERIC_HDLC
+       struct net_device *netdev;
+#endif
+};
+
+#define MGSL_MAGIC 0x5401
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#ifndef SERIAL_XMIT_SIZE
+#define SERIAL_XMIT_SIZE 4096
+#endif
+
+/*
+ * These macros define the offsets used in calculating the
+ * I/O address of the specified USC registers.
+ */
+
+
+#define DCPIN 2                /* Bit 1 of I/O address */
+#define SDPIN 4                /* Bit 2 of I/O address */
+
+#define DCAR 0         /* DMA command/address register */
+#define CCAR SDPIN             /* channel command/address register */
+#define DATAREG DCPIN + SDPIN  /* serial data register */
+#define MSBONLY 0x41
+#define LSBONLY 0x40
+
+/*
+ * These macros define the register address (ordinal number)
+ * used for writing address/value pairs to the USC.
+ */
+
+#define CMR    0x02    /* Channel mode Register */
+#define CCSR   0x04    /* Channel Command/status Register */
+#define CCR    0x06    /* Channel Control Register */
+#define PSR    0x08    /* Port status Register */
+#define PCR    0x0a    /* Port Control Register */
+#define TMDR   0x0c    /* Test mode Data Register */
+#define TMCR   0x0e    /* Test mode Control Register */
+#define CMCR   0x10    /* Clock mode Control Register */
+#define HCR    0x12    /* Hardware Configuration Register */
+#define IVR    0x14    /* Interrupt Vector Register */
+#define IOCR   0x16    /* Input/Output Control Register */
+#define ICR    0x18    /* Interrupt Control Register */
+#define DCCR   0x1a    /* Daisy Chain Control Register */
+#define MISR   0x1c    /* Misc Interrupt status Register */
+#define SICR   0x1e    /* status Interrupt Control Register */
+#define RDR    0x20    /* Receive Data Register */
+#define RMR    0x22    /* Receive mode Register */
+#define RCSR   0x24    /* Receive Command/status Register */
+#define RICR   0x26    /* Receive Interrupt Control Register */
+#define RSR    0x28    /* Receive Sync Register */
+#define RCLR   0x2a    /* Receive count Limit Register */
+#define RCCR   0x2c    /* Receive Character count Register */
+#define TC0R   0x2e    /* Time Constant 0 Register */
+#define TDR    0x30    /* Transmit Data Register */
+#define TMR    0x32    /* Transmit mode Register */
+#define TCSR   0x34    /* Transmit Command/status Register */
+#define TICR   0x36    /* Transmit Interrupt Control Register */
+#define TSR    0x38    /* Transmit Sync Register */
+#define TCLR   0x3a    /* Transmit count Limit Register */
+#define TCCR   0x3c    /* Transmit Character count Register */
+#define TC1R   0x3e    /* Time Constant 1 Register */
+
+
+/*
+ * MACRO DEFINITIONS FOR DMA REGISTERS
+ */
+
+#define DCR    0x06    /* DMA Control Register (shared) */
+#define DACR   0x08    /* DMA Array count Register (shared) */
+#define BDCR   0x12    /* Burst/Dwell Control Register (shared) */
+#define DIVR   0x14    /* DMA Interrupt Vector Register (shared) */    
+#define DICR   0x18    /* DMA Interrupt Control Register (shared) */
+#define CDIR   0x1a    /* Clear DMA Interrupt Register (shared) */
+#define SDIR   0x1c    /* Set DMA Interrupt Register (shared) */
+
+#define TDMR   0x02    /* Transmit DMA mode Register */
+#define TDIAR  0x1e    /* Transmit DMA Interrupt Arm Register */
+#define TBCR   0x2a    /* Transmit Byte count Register */
+#define TARL   0x2c    /* Transmit Address Register (low) */
+#define TARU   0x2e    /* Transmit Address Register (high) */
+#define NTBCR  0x3a    /* Next Transmit Byte count Register */
+#define NTARL  0x3c    /* Next Transmit Address Register (low) */
+#define NTARU  0x3e    /* Next Transmit Address Register (high) */
+
+#define RDMR   0x82    /* Receive DMA mode Register (non-shared) */
+#define RDIAR  0x9e    /* Receive DMA Interrupt Arm Register */
+#define RBCR   0xaa    /* Receive Byte count Register */
+#define RARL   0xac    /* Receive Address Register (low) */
+#define RARU   0xae    /* Receive Address Register (high) */
+#define NRBCR  0xba    /* Next Receive Byte count Register */
+#define NRARL  0xbc    /* Next Receive Address Register (low) */
+#define NRARU  0xbe    /* Next Receive Address Register (high) */
+
+
+/*
+ * MACRO DEFINITIONS FOR MODEM STATUS BITS
+ */
+
+#define MODEMSTATUS_DTR 0x80
+#define MODEMSTATUS_DSR 0x40
+#define MODEMSTATUS_RTS 0x20
+#define MODEMSTATUS_CTS 0x10
+#define MODEMSTATUS_RI  0x04
+#define MODEMSTATUS_DCD 0x01
+
+
+/*
+ * Channel Command/Address Register (CCAR) Command Codes
+ */
+
+#define RTCmd_Null                     0x0000
+#define RTCmd_ResetHighestIus          0x1000
+#define RTCmd_TriggerChannelLoadDma    0x2000
+#define RTCmd_TriggerRxDma             0x2800
+#define RTCmd_TriggerTxDma             0x3000
+#define RTCmd_TriggerRxAndTxDma                0x3800
+#define RTCmd_PurgeRxFifo              0x4800
+#define RTCmd_PurgeTxFifo              0x5000
+#define RTCmd_PurgeRxAndTxFifo         0x5800
+#define RTCmd_LoadRcc                  0x6800
+#define RTCmd_LoadTcc                  0x7000
+#define RTCmd_LoadRccAndTcc            0x7800
+#define RTCmd_LoadTC0                  0x8800
+#define RTCmd_LoadTC1                  0x9000
+#define RTCmd_LoadTC0AndTC1            0x9800
+#define RTCmd_SerialDataLSBFirst       0xa000
+#define RTCmd_SerialDataMSBFirst       0xa800
+#define RTCmd_SelectBigEndian          0xb000
+#define RTCmd_SelectLittleEndian       0xb800
+
+
+/*
+ * DMA Command/Address Register (DCAR) Command Codes
+ */
+
+#define DmaCmd_Null                    0x0000
+#define DmaCmd_ResetTxChannel          0x1000
+#define DmaCmd_ResetRxChannel          0x1200
+#define DmaCmd_StartTxChannel          0x2000
+#define DmaCmd_StartRxChannel          0x2200
+#define DmaCmd_ContinueTxChannel       0x3000
+#define DmaCmd_ContinueRxChannel       0x3200
+#define DmaCmd_PauseTxChannel          0x4000
+#define DmaCmd_PauseRxChannel          0x4200
+#define DmaCmd_AbortTxChannel          0x5000
+#define DmaCmd_AbortRxChannel          0x5200
+#define DmaCmd_InitTxChannel           0x7000
+#define DmaCmd_InitRxChannel           0x7200
+#define DmaCmd_ResetHighestDmaIus      0x8000
+#define DmaCmd_ResetAllChannels                0x9000
+#define DmaCmd_StartAllChannels                0xa000
+#define DmaCmd_ContinueAllChannels     0xb000
+#define DmaCmd_PauseAllChannels                0xc000
+#define DmaCmd_AbortAllChannels                0xd000
+#define DmaCmd_InitAllChannels         0xf000
+
+#define TCmd_Null                      0x0000
+#define TCmd_ClearTxCRC                        0x2000
+#define TCmd_SelectTicrTtsaData                0x4000
+#define TCmd_SelectTicrTxFifostatus    0x5000
+#define TCmd_SelectTicrIntLevel                0x6000
+#define TCmd_SelectTicrdma_level               0x7000
+#define TCmd_SendFrame                 0x8000
+#define TCmd_SendAbort                 0x9000
+#define TCmd_EnableDleInsertion                0xc000
+#define TCmd_DisableDleInsertion       0xd000
+#define TCmd_ClearEofEom               0xe000
+#define TCmd_SetEofEom                 0xf000
+
+#define RCmd_Null                      0x0000
+#define RCmd_ClearRxCRC                        0x2000
+#define RCmd_EnterHuntmode             0x3000
+#define RCmd_SelectRicrRtsaData                0x4000
+#define RCmd_SelectRicrRxFifostatus    0x5000
+#define RCmd_SelectRicrIntLevel                0x6000
+#define RCmd_SelectRicrdma_level               0x7000
+
+/*
+ * Bits for enabling and disabling IRQs in Interrupt Control Register (ICR)
+ */
+#define RECEIVE_STATUS         BIT5
+#define RECEIVE_DATA           BIT4
+#define TRANSMIT_STATUS                BIT3
+#define TRANSMIT_DATA          BIT2
+#define IO_PIN                 BIT1
+#define MISC                   BIT0
+
+
+/*
+ * Receive status Bits in Receive Command/status Register RCSR
+ */
+
+#define RXSTATUS_SHORT_FRAME           BIT8
+#define RXSTATUS_CODE_VIOLATION                BIT8
+#define RXSTATUS_EXITED_HUNT           BIT7
+#define RXSTATUS_IDLE_RECEIVED         BIT6
+#define RXSTATUS_BREAK_RECEIVED                BIT5
+#define RXSTATUS_ABORT_RECEIVED                BIT5
+#define RXSTATUS_RXBOUND               BIT4
+#define RXSTATUS_CRC_ERROR             BIT3
+#define RXSTATUS_FRAMING_ERROR         BIT3
+#define RXSTATUS_ABORT                 BIT2
+#define RXSTATUS_PARITY_ERROR          BIT2
+#define RXSTATUS_OVERRUN               BIT1
+#define RXSTATUS_DATA_AVAILABLE                BIT0
+#define RXSTATUS_ALL                   0x01f6
+#define usc_UnlatchRxstatusBits(a,b) usc_OutReg( (a), RCSR, (u16)((b) & RXSTATUS_ALL) )
+
+/*
+ * Values for setting transmit idle mode in 
+ * Transmit Control/status Register (TCSR)
+ */
+#define IDLEMODE_FLAGS                 0x0000
+#define IDLEMODE_ALT_ONE_ZERO          0x0100
+#define IDLEMODE_ZERO                  0x0200
+#define IDLEMODE_ONE                   0x0300
+#define IDLEMODE_ALT_MARK_SPACE                0x0500
+#define IDLEMODE_SPACE                 0x0600
+#define IDLEMODE_MARK                  0x0700
+#define IDLEMODE_MASK                  0x0700
+
+/*
+ * IUSC revision identifiers
+ */
+#define        IUSC_SL1660                     0x4d44
+#define IUSC_PRE_SL1660                        0x4553
+
+/*
+ * Transmit status Bits in Transmit Command/status Register (TCSR)
+ */
+
+#define TCSR_PRESERVE                  0x0F00
+
+#define TCSR_UNDERWAIT                 BIT11
+#define TXSTATUS_PREAMBLE_SENT         BIT7
+#define TXSTATUS_IDLE_SENT             BIT6
+#define TXSTATUS_ABORT_SENT            BIT5
+#define TXSTATUS_EOF_SENT              BIT4
+#define TXSTATUS_EOM_SENT              BIT4
+#define TXSTATUS_CRC_SENT              BIT3
+#define TXSTATUS_ALL_SENT              BIT2
+#define TXSTATUS_UNDERRUN              BIT1
+#define TXSTATUS_FIFO_EMPTY            BIT0
+#define TXSTATUS_ALL                   0x00fa
+#define usc_UnlatchTxstatusBits(a,b) usc_OutReg( (a), TCSR, (u16)((a)->tcsr_value + ((b) & 0x00FF)) )
+                               
+
+#define MISCSTATUS_RXC_LATCHED         BIT15
+#define MISCSTATUS_RXC                 BIT14
+#define MISCSTATUS_TXC_LATCHED         BIT13
+#define MISCSTATUS_TXC                 BIT12
+#define MISCSTATUS_RI_LATCHED          BIT11
+#define MISCSTATUS_RI                  BIT10
+#define MISCSTATUS_DSR_LATCHED         BIT9
+#define MISCSTATUS_DSR                 BIT8
+#define MISCSTATUS_DCD_LATCHED         BIT7
+#define MISCSTATUS_DCD                 BIT6
+#define MISCSTATUS_CTS_LATCHED         BIT5
+#define MISCSTATUS_CTS                 BIT4
+#define MISCSTATUS_RCC_UNDERRUN                BIT3
+#define MISCSTATUS_DPLL_NO_SYNC                BIT2
+#define MISCSTATUS_BRG1_ZERO           BIT1
+#define MISCSTATUS_BRG0_ZERO           BIT0
+
+#define usc_UnlatchIostatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0xaaa0))
+#define usc_UnlatchMiscstatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0x000f))
+
+#define SICR_RXC_ACTIVE                        BIT15
+#define SICR_RXC_INACTIVE              BIT14
+#define SICR_RXC                       (BIT15+BIT14)
+#define SICR_TXC_ACTIVE                        BIT13
+#define SICR_TXC_INACTIVE              BIT12
+#define SICR_TXC                       (BIT13+BIT12)
+#define SICR_RI_ACTIVE                 BIT11
+#define SICR_RI_INACTIVE               BIT10
+#define SICR_RI                                (BIT11+BIT10)
+#define SICR_DSR_ACTIVE                        BIT9
+#define SICR_DSR_INACTIVE              BIT8
+#define SICR_DSR                       (BIT9+BIT8)
+#define SICR_DCD_ACTIVE                        BIT7
+#define SICR_DCD_INACTIVE              BIT6
+#define SICR_DCD                       (BIT7+BIT6)
+#define SICR_CTS_ACTIVE                        BIT5
+#define SICR_CTS_INACTIVE              BIT4
+#define SICR_CTS                       (BIT5+BIT4)
+#define SICR_RCC_UNDERFLOW             BIT3
+#define SICR_DPLL_NO_SYNC              BIT2
+#define SICR_BRG1_ZERO                 BIT1
+#define SICR_BRG0_ZERO                 BIT0
+
+void usc_DisableMasterIrqBit( struct mgsl_struct *info );
+void usc_EnableMasterIrqBit( struct mgsl_struct *info );
+void usc_EnableInterrupts( struct mgsl_struct *info, u16 IrqMask );
+void usc_DisableInterrupts( struct mgsl_struct *info, u16 IrqMask );
+void usc_ClearIrqPendingBits( struct mgsl_struct *info, u16 IrqMask );
+
+#define usc_EnableInterrupts( a, b ) \
+       usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0xc0 + (b)) )
+
+#define usc_DisableInterrupts( a, b ) \
+       usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0x80 + (b)) )
+
+#define usc_EnableMasterIrqBit(a) \
+       usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0x0f00) + 0xb000) )
+
+#define usc_DisableMasterIrqBit(a) \
+       usc_OutReg( (a), ICR, (u16)(usc_InReg((a),ICR) & 0x7f00) )
+
+#define usc_ClearIrqPendingBits( a, b ) usc_OutReg( (a), DCCR, 0x40 + (b) )
+
+/*
+ * Transmit status Bits in Transmit Control status Register (TCSR)
+ * and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0)
+ */
+
+#define TXSTATUS_PREAMBLE_SENT BIT7
+#define TXSTATUS_IDLE_SENT     BIT6
+#define TXSTATUS_ABORT_SENT    BIT5
+#define TXSTATUS_EOF           BIT4
+#define TXSTATUS_CRC_SENT      BIT3
+#define TXSTATUS_ALL_SENT      BIT2
+#define TXSTATUS_UNDERRUN      BIT1
+#define TXSTATUS_FIFO_EMPTY    BIT0
+
+#define DICR_MASTER            BIT15
+#define DICR_TRANSMIT          BIT0
+#define DICR_RECEIVE           BIT1
+
+#define usc_EnableDmaInterrupts(a,b) \
+       usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) | (b)) )
+
+#define usc_DisableDmaInterrupts(a,b) \
+       usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) & ~(b)) )
+
+#define usc_EnableStatusIrqs(a,b) \
+       usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) | (b)) )
+
+#define usc_DisablestatusIrqs(a,b) \
+       usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) & ~(b)) )
+
+/* Transmit status Bits in Transmit Control status Register (TCSR) */
+/* and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0) */
+
+
+#define DISABLE_UNCONDITIONAL    0
+#define DISABLE_END_OF_FRAME     1
+#define ENABLE_UNCONDITIONAL     2
+#define ENABLE_AUTO_CTS          3
+#define ENABLE_AUTO_DCD          3
+#define usc_EnableTransmitter(a,b) \
+       usc_OutReg( (a), TMR, (u16)((usc_InReg((a),TMR) & 0xfffc) | (b)) )
+#define usc_EnableReceiver(a,b) \
+       usc_OutReg( (a), RMR, (u16)((usc_InReg((a),RMR) & 0xfffc) | (b)) )
+
+static u16  usc_InDmaReg( struct mgsl_struct *info, u16 Port );
+static void usc_OutDmaReg( struct mgsl_struct *info, u16 Port, u16 Value );
+static void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd );
+
+static u16  usc_InReg( struct mgsl_struct *info, u16 Port );
+static void usc_OutReg( struct mgsl_struct *info, u16 Port, u16 Value );
+static void usc_RTCmd( struct mgsl_struct *info, u16 Cmd );
+void usc_RCmd( struct mgsl_struct *info, u16 Cmd );
+void usc_TCmd( struct mgsl_struct *info, u16 Cmd );
+
+#define usc_TCmd(a,b) usc_OutReg((a), TCSR, (u16)((a)->tcsr_value + (b)))
+#define usc_RCmd(a,b) usc_OutReg((a), RCSR, (b))
+
+#define usc_SetTransmitSyncChars(a,s0,s1) usc_OutReg((a), TSR, (u16)(((u16)s0<<8)|(u16)s1))
+
+static void usc_process_rxoverrun_sync( struct mgsl_struct *info );
+static void usc_start_receiver( struct mgsl_struct *info );
+static void usc_stop_receiver( struct mgsl_struct *info );
+
+static void usc_start_transmitter( struct mgsl_struct *info );
+static void usc_stop_transmitter( struct mgsl_struct *info );
+static void usc_set_txidle( struct mgsl_struct *info );
+static void usc_load_txfifo( struct mgsl_struct *info );
+
+static void usc_enable_aux_clock( struct mgsl_struct *info, u32 DataRate );
+static void usc_enable_loopback( struct mgsl_struct *info, int enable );
+
+static void usc_get_serial_signals( struct mgsl_struct *info );
+static void usc_set_serial_signals( struct mgsl_struct *info );
+
+static void usc_reset( struct mgsl_struct *info );
+
+static void usc_set_sync_mode( struct mgsl_struct *info );
+static void usc_set_sdlc_mode( struct mgsl_struct *info );
+static void usc_set_async_mode( struct mgsl_struct *info );
+static void usc_enable_async_clock( struct mgsl_struct *info, u32 DataRate );
+
+static void usc_loopback_frame( struct mgsl_struct *info );
+
+static void mgsl_tx_timeout(unsigned long context);
+
+
+static void usc_loopmode_cancel_transmit( struct mgsl_struct * info );
+static void usc_loopmode_insert_request( struct mgsl_struct * info );
+static int usc_loopmode_active( struct mgsl_struct * info);
+static void usc_loopmode_send_done( struct mgsl_struct * info );
+
+static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg);
+
+#if SYNCLINK_GENERIC_HDLC
+#define dev_to_port(D) (dev_to_hdlc(D)->priv)
+static void hdlcdev_tx_done(struct mgsl_struct *info);
+static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size);
+static int  hdlcdev_init(struct mgsl_struct *info);
+static void hdlcdev_exit(struct mgsl_struct *info);
+#endif
+
+/*
+ * Defines a BUS descriptor value for the PCI adapter
+ * local bus address ranges.
+ */
+
+#define BUS_DESCRIPTOR( WrHold, WrDly, RdDly, Nwdd, Nwad, Nxda, Nrdd, Nrad ) \
+(0x00400020 + \
+((WrHold) << 30) + \
+((WrDly)  << 28) + \
+((RdDly)  << 26) + \
+((Nwdd)   << 20) + \
+((Nwad)   << 15) + \
+((Nxda)   << 13) + \
+((Nrdd)   << 11) + \
+((Nrad)   <<  6) )
+
+static void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit);
+
+/*
+ * Adapter diagnostic routines
+ */
+static bool mgsl_register_test( struct mgsl_struct *info );
+static bool mgsl_irq_test( struct mgsl_struct *info );
+static bool mgsl_dma_test( struct mgsl_struct *info );
+static bool mgsl_memory_test( struct mgsl_struct *info );
+static int mgsl_adapter_test( struct mgsl_struct *info );
+
+/*
+ * device and resource management routines
+ */
+static int mgsl_claim_resources(struct mgsl_struct *info);
+static void mgsl_release_resources(struct mgsl_struct *info);
+static void mgsl_add_device(struct mgsl_struct *info);
+static struct mgsl_struct* mgsl_allocate_device(void);
+
+/*
+ * DMA buffer manupulation functions.
+ */
+static void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex );
+static bool mgsl_get_rx_frame( struct mgsl_struct *info );
+static bool mgsl_get_raw_rx_frame( struct mgsl_struct *info );
+static void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info );
+static void mgsl_reset_tx_dma_buffers( struct mgsl_struct *info );
+static int num_free_tx_dma_buffers(struct mgsl_struct *info);
+static void mgsl_load_tx_dma_buffer( struct mgsl_struct *info, const char *Buffer, unsigned int BufferSize);
+static void mgsl_load_pci_memory(char* TargetPtr, const char* SourcePtr, unsigned short count);
+
+/*
+ * DMA and Shared Memory buffer allocation and formatting
+ */
+static int  mgsl_allocate_dma_buffers(struct mgsl_struct *info);
+static void mgsl_free_dma_buffers(struct mgsl_struct *info);
+static int  mgsl_alloc_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount);
+static void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount);
+static int  mgsl_alloc_buffer_list_memory(struct mgsl_struct *info);
+static void mgsl_free_buffer_list_memory(struct mgsl_struct *info);
+static int mgsl_alloc_intermediate_rxbuffer_memory(struct mgsl_struct *info);
+static void mgsl_free_intermediate_rxbuffer_memory(struct mgsl_struct *info);
+static int mgsl_alloc_intermediate_txbuffer_memory(struct mgsl_struct *info);
+static void mgsl_free_intermediate_txbuffer_memory(struct mgsl_struct *info);
+static bool load_next_tx_holding_buffer(struct mgsl_struct *info);
+static int save_tx_buffer_request(struct mgsl_struct *info,const char *Buffer, unsigned int BufferSize);
+
+/*
+ * Bottom half interrupt handlers
+ */
+static void mgsl_bh_handler(struct work_struct *work);
+static void mgsl_bh_receive(struct mgsl_struct *info);
+static void mgsl_bh_transmit(struct mgsl_struct *info);
+static void mgsl_bh_status(struct mgsl_struct *info);
+
+/*
+ * Interrupt handler routines and dispatch table.
+ */
+static void mgsl_isr_null( struct mgsl_struct *info );
+static void mgsl_isr_transmit_data( struct mgsl_struct *info );
+static void mgsl_isr_receive_data( struct mgsl_struct *info );
+static void mgsl_isr_receive_status( struct mgsl_struct *info );
+static void mgsl_isr_transmit_status( struct mgsl_struct *info );
+static void mgsl_isr_io_pin( struct mgsl_struct *info );
+static void mgsl_isr_misc( struct mgsl_struct *info );
+static void mgsl_isr_receive_dma( struct mgsl_struct *info );
+static void mgsl_isr_transmit_dma( struct mgsl_struct *info );
+
+typedef void (*isr_dispatch_func)(struct mgsl_struct *);
+
+static isr_dispatch_func UscIsrTable[7] =
+{
+       mgsl_isr_null,
+       mgsl_isr_misc,
+       mgsl_isr_io_pin,
+       mgsl_isr_transmit_data,
+       mgsl_isr_transmit_status,
+       mgsl_isr_receive_data,
+       mgsl_isr_receive_status
+};
+
+/*
+ * ioctl call handlers
+ */
+static int tiocmget(struct tty_struct *tty);
+static int tiocmset(struct tty_struct *tty,
+                   unsigned int set, unsigned int clear);
+static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount
+       __user *user_icount);
+static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS  __user *user_params);
+static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS  __user *new_params);
+static int mgsl_get_txidle(struct mgsl_struct * info, int __user *idle_mode);
+static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode);
+static int mgsl_txenable(struct mgsl_struct * info, int enable);
+static int mgsl_txabort(struct mgsl_struct * info);
+static int mgsl_rxenable(struct mgsl_struct * info, int enable);
+static int mgsl_wait_event(struct mgsl_struct * info, int __user *mask);
+static int mgsl_loopmode_send_done( struct mgsl_struct * info );
+
+/* set non-zero on successful registration with PCI subsystem */
+static bool pci_registered;
+
+/*
+ * Global linked list of SyncLink devices
+ */
+static struct mgsl_struct *mgsl_device_list;
+static int mgsl_device_count;
+
+/*
+ * Set this param to non-zero to load eax with the
+ * .text section address and breakpoint on module load.
+ * This is useful for use with gdb and add-symbol-file command.
+ */
+static int break_on_load;
+
+/*
+ * Driver major number, defaults to zero to get auto
+ * assigned major number. May be forced as module parameter.
+ */
+static int ttymajor;
+
+/*
+ * Array of user specified options for ISA adapters.
+ */
+static int io[MAX_ISA_DEVICES];
+static int irq[MAX_ISA_DEVICES];
+static int dma[MAX_ISA_DEVICES];
+static int debug_level;
+static int maxframe[MAX_TOTAL_DEVICES];
+static int txdmabufs[MAX_TOTAL_DEVICES];
+static int txholdbufs[MAX_TOTAL_DEVICES];
+       
+module_param(break_on_load, bool, 0);
+module_param(ttymajor, int, 0);
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(dma, int, NULL, 0);
+module_param(debug_level, int, 0);
+module_param_array(maxframe, int, NULL, 0);
+module_param_array(txdmabufs, int, NULL, 0);
+module_param_array(txholdbufs, int, NULL, 0);
+
+static char *driver_name = "SyncLink serial driver";
+static char *driver_version = "$Revision: 4.38 $";
+
+static int synclink_init_one (struct pci_dev *dev,
+                                    const struct pci_device_id *ent);
+static void synclink_remove_one (struct pci_dev *dev);
+
+static struct pci_device_id synclink_pci_tbl[] = {
+       { PCI_VENDOR_ID_MICROGATE, PCI_DEVICE_ID_MICROGATE_USC, PCI_ANY_ID, PCI_ANY_ID, },
+       { PCI_VENDOR_ID_MICROGATE, 0x0210, PCI_ANY_ID, PCI_ANY_ID, },
+       { 0, }, /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, synclink_pci_tbl);
+
+MODULE_LICENSE("GPL");
+
+static struct pci_driver synclink_pci_driver = {
+       .name           = "synclink",
+       .id_table       = synclink_pci_tbl,
+       .probe          = synclink_init_one,
+       .remove         = __devexit_p(synclink_remove_one),
+};
+
+static struct tty_driver *serial_driver;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+
+static void mgsl_change_params(struct mgsl_struct *info);
+static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout);
+
+/*
+ * 1st function defined in .text section. Calling this function in
+ * init_module() followed by a breakpoint allows a remote debugger
+ * (gdb) to get the .text address for the add-symbol-file command.
+ * This allows remote debugging of dynamically loadable modules.
+ */
+static void* mgsl_get_text_ptr(void)
+{
+       return mgsl_get_text_ptr;
+}
+
+static inline int mgsl_paranoia_check(struct mgsl_struct *info,
+                                       char *name, const char *routine)
+{
+#ifdef MGSL_PARANOIA_CHECK
+       static const char *badmagic =
+               "Warning: bad magic number for mgsl struct (%s) in %s\n";
+       static const char *badinfo =
+               "Warning: null mgsl_struct for (%s) in %s\n";
+
+       if (!info) {
+               printk(badinfo, name, routine);
+               return 1;
+       }
+       if (info->magic != MGSL_MAGIC) {
+               printk(badmagic, name, routine);
+               return 1;
+       }
+#else
+       if (!info)
+               return 1;
+#endif
+       return 0;
+}
+
+/**
+ * line discipline callback wrappers
+ *
+ * The wrappers maintain line discipline references
+ * while calling into the line discipline.
+ *
+ * ldisc_receive_buf  - pass receive data to line discipline
+ */
+
+static void ldisc_receive_buf(struct tty_struct *tty,
+                             const __u8 *data, char *flags, int count)
+{
+       struct tty_ldisc *ld;
+       if (!tty)
+               return;
+       ld = tty_ldisc_ref(tty);
+       if (ld) {
+               if (ld->ops->receive_buf)
+                       ld->ops->receive_buf(tty, data, flags, count);
+               tty_ldisc_deref(ld);
+       }
+}
+
+/* mgsl_stop()         throttle (stop) transmitter
+ *     
+ * Arguments:          tty     pointer to tty info structure
+ * Return Value:       None
+ */
+static void mgsl_stop(struct tty_struct *tty)
+{
+       struct mgsl_struct *info = tty->driver_data;
+       unsigned long flags;
+       
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_stop"))
+               return;
+       
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk("mgsl_stop(%s)\n",info->device_name);    
+               
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       if (info->tx_enabled)
+               usc_stop_transmitter(info);
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       
+}      /* end of mgsl_stop() */
+
+/* mgsl_start()                release (start) transmitter
+ *     
+ * Arguments:          tty     pointer to tty info structure
+ * Return Value:       None
+ */
+static void mgsl_start(struct tty_struct *tty)
+{
+       struct mgsl_struct *info = tty->driver_data;
+       unsigned long flags;
+       
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_start"))
+               return;
+       
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk("mgsl_start(%s)\n",info->device_name);   
+               
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       if (!info->tx_enabled)
+               usc_start_transmitter(info);
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       
+}      /* end of mgsl_start() */
+
+/*
+ * Bottom half work queue access functions
+ */
+
+/* mgsl_bh_action()    Return next bottom half action to perform.
+ * Return Value:       BH action code or 0 if nothing to do.
+ */
+static int mgsl_bh_action(struct mgsl_struct *info)
+{
+       unsigned long flags;
+       int rc = 0;
+       
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+
+       if (info->pending_bh & BH_RECEIVE) {
+               info->pending_bh &= ~BH_RECEIVE;
+               rc = BH_RECEIVE;
+       } else if (info->pending_bh & BH_TRANSMIT) {
+               info->pending_bh &= ~BH_TRANSMIT;
+               rc = BH_TRANSMIT;
+       } else if (info->pending_bh & BH_STATUS) {
+               info->pending_bh &= ~BH_STATUS;
+               rc = BH_STATUS;
+       }
+
+       if (!rc) {
+               /* Mark BH routine as complete */
+               info->bh_running = false;
+               info->bh_requested = false;
+       }
+       
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       
+       return rc;
+}
+
+/*
+ *     Perform bottom half processing of work items queued by ISR.
+ */
+static void mgsl_bh_handler(struct work_struct *work)
+{
+       struct mgsl_struct *info =
+               container_of(work, struct mgsl_struct, task);
+       int action;
+
+       if (!info)
+               return;
+               
+       if ( debug_level >= DEBUG_LEVEL_BH )
+               printk( "%s(%d):mgsl_bh_handler(%s) entry\n",
+                       __FILE__,__LINE__,info->device_name);
+       
+       info->bh_running = true;
+
+       while((action = mgsl_bh_action(info)) != 0) {
+       
+               /* Process work item */
+               if ( debug_level >= DEBUG_LEVEL_BH )
+                       printk( "%s(%d):mgsl_bh_handler() work item action=%d\n",
+                               __FILE__,__LINE__,action);
+
+               switch (action) {
+               
+               case BH_RECEIVE:
+                       mgsl_bh_receive(info);
+                       break;
+               case BH_TRANSMIT:
+                       mgsl_bh_transmit(info);
+                       break;
+               case BH_STATUS:
+                       mgsl_bh_status(info);
+                       break;
+               default:
+                       /* unknown work item ID */
+                       printk("Unknown work item ID=%08X!\n", action);
+                       break;
+               }
+       }
+
+       if ( debug_level >= DEBUG_LEVEL_BH )
+               printk( "%s(%d):mgsl_bh_handler(%s) exit\n",
+                       __FILE__,__LINE__,info->device_name);
+}
+
+static void mgsl_bh_receive(struct mgsl_struct *info)
+{
+       bool (*get_rx_frame)(struct mgsl_struct *info) =
+               (info->params.mode == MGSL_MODE_HDLC ? mgsl_get_rx_frame : mgsl_get_raw_rx_frame);
+
+       if ( debug_level >= DEBUG_LEVEL_BH )
+               printk( "%s(%d):mgsl_bh_receive(%s)\n",
+                       __FILE__,__LINE__,info->device_name);
+       
+       do
+       {
+               if (info->rx_rcc_underrun) {
+                       unsigned long flags;
+                       spin_lock_irqsave(&info->irq_spinlock,flags);
+                       usc_start_receiver(info);
+                       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+                       return;
+               }
+       } while(get_rx_frame(info));
+}
+
+static void mgsl_bh_transmit(struct mgsl_struct *info)
+{
+       struct tty_struct *tty = info->port.tty;
+       unsigned long flags;
+       
+       if ( debug_level >= DEBUG_LEVEL_BH )
+               printk( "%s(%d):mgsl_bh_transmit() entry on %s\n",
+                       __FILE__,__LINE__,info->device_name);
+
+       if (tty)
+               tty_wakeup(tty);
+
+       /* if transmitter idle and loopmode_send_done_requested
+        * then start echoing RxD to TxD
+        */
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       if ( !info->tx_active && info->loopmode_send_done_requested )
+               usc_loopmode_send_done( info );
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+}
+
+static void mgsl_bh_status(struct mgsl_struct *info)
+{
+       if ( debug_level >= DEBUG_LEVEL_BH )
+               printk( "%s(%d):mgsl_bh_status() entry on %s\n",
+                       __FILE__,__LINE__,info->device_name);
+
+       info->ri_chkcount = 0;
+       info->dsr_chkcount = 0;
+       info->dcd_chkcount = 0;
+       info->cts_chkcount = 0;
+}
+
+/* mgsl_isr_receive_status()
+ * 
+ *     Service a receive status interrupt. The type of status
+ *     interrupt is indicated by the state of the RCSR.
+ *     This is only used for HDLC mode.
+ *
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void mgsl_isr_receive_status( struct mgsl_struct *info )
+{
+       u16 status = usc_InReg( info, RCSR );
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )   
+               printk("%s(%d):mgsl_isr_receive_status status=%04X\n",
+                       __FILE__,__LINE__,status);
+                       
+       if ( (status & RXSTATUS_ABORT_RECEIVED) && 
+               info->loopmode_insert_requested &&
+               usc_loopmode_active(info) )
+       {
+               ++info->icount.rxabort;
+               info->loopmode_insert_requested = false;
+               /* clear CMR:13 to start echoing RxD to TxD */
+               info->cmr_value &= ~BIT13;
+               usc_OutReg(info, CMR, info->cmr_value);
+               /* disable received abort irq (no longer required) */
+               usc_OutReg(info, RICR,
+                       (usc_InReg(info, RICR) & ~RXSTATUS_ABORT_RECEIVED));
+       }
+
+       if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) {
+               if (status & RXSTATUS_EXITED_HUNT)
+                       info->icount.exithunt++;
+               if (status & RXSTATUS_IDLE_RECEIVED)
+                       info->icount.rxidle++;
+               wake_up_interruptible(&info->event_wait_q);
+       }
+
+       if (status & RXSTATUS_OVERRUN){
+               info->icount.rxover++;
+               usc_process_rxoverrun_sync( info );
+       }
+
+       usc_ClearIrqPendingBits( info, RECEIVE_STATUS );
+       usc_UnlatchRxstatusBits( info, status );
+
+}      /* end of mgsl_isr_receive_status() */
+
+/* mgsl_isr_transmit_status()
+ * 
+ *     Service a transmit status interrupt
+ *     HDLC mode :end of transmit frame
+ *     Async mode:all data is sent
+ *     transmit status is indicated by bits in the TCSR.
+ * 
+ * Arguments:          info           pointer to device instance data
+ * Return Value:       None
+ */
+static void mgsl_isr_transmit_status( struct mgsl_struct *info )
+{
+       u16 status = usc_InReg( info, TCSR );
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )   
+               printk("%s(%d):mgsl_isr_transmit_status status=%04X\n",
+                       __FILE__,__LINE__,status);
+       
+       usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
+       usc_UnlatchTxstatusBits( info, status );
+       
+       if ( status & (TXSTATUS_UNDERRUN | TXSTATUS_ABORT_SENT) )
+       {
+               /* finished sending HDLC abort. This may leave  */
+               /* the TxFifo with data from the aborted frame  */
+               /* so purge the TxFifo. Also shutdown the DMA   */
+               /* channel in case there is data remaining in   */
+               /* the DMA buffer                               */
+               usc_DmaCmd( info, DmaCmd_ResetTxChannel );
+               usc_RTCmd( info, RTCmd_PurgeTxFifo );
+       }
+       if ( status & TXSTATUS_EOF_SENT )
+               info->icount.txok++;
+       else if ( status & TXSTATUS_UNDERRUN )
+               info->icount.txunder++;
+       else if ( status & TXSTATUS_ABORT_SENT )
+               info->icount.txabort++;
+       else
+               info->icount.txunder++;
+                       
+       info->tx_active = false;
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       del_timer(&info->tx_timer);     
+       
+       if ( info->drop_rts_on_tx_done ) {
+               usc_get_serial_signals( info );
+               if ( info->serial_signals & SerialSignal_RTS ) {
+                       info->serial_signals &= ~SerialSignal_RTS;
+                       usc_set_serial_signals( info );
+               }
+               info->drop_rts_on_tx_done = false;
+       }
+
+#if SYNCLINK_GENERIC_HDLC
+       if (info->netcount)
+               hdlcdev_tx_done(info);
+       else 
+#endif
+       {
+               if (info->port.tty->stopped || info->port.tty->hw_stopped) {
+                       usc_stop_transmitter(info);
+                       return;
+               }
+               info->pending_bh |= BH_TRANSMIT;
+       }
+
+}      /* end of mgsl_isr_transmit_status() */
+
+/* mgsl_isr_io_pin()
+ * 
+ *     Service an Input/Output pin interrupt. The type of
+ *     interrupt is indicated by bits in the MISR
+ *     
+ * Arguments:          info           pointer to device instance data
+ * Return Value:       None
+ */
+static void mgsl_isr_io_pin( struct mgsl_struct *info )
+{
+       struct  mgsl_icount *icount;
+       u16 status = usc_InReg( info, MISR );
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )   
+               printk("%s(%d):mgsl_isr_io_pin status=%04X\n",
+                       __FILE__,__LINE__,status);
+                       
+       usc_ClearIrqPendingBits( info, IO_PIN );
+       usc_UnlatchIostatusBits( info, status );
+
+       if (status & (MISCSTATUS_CTS_LATCHED | MISCSTATUS_DCD_LATCHED |
+                     MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) {
+               icount = &info->icount;
+               /* update input line counters */
+               if (status & MISCSTATUS_RI_LATCHED) {
+                       if ((info->ri_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+                               usc_DisablestatusIrqs(info,SICR_RI);
+                       icount->rng++;
+                       if ( status & MISCSTATUS_RI )
+                               info->input_signal_events.ri_up++;      
+                       else
+                               info->input_signal_events.ri_down++;    
+               }
+               if (status & MISCSTATUS_DSR_LATCHED) {
+                       if ((info->dsr_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+                               usc_DisablestatusIrqs(info,SICR_DSR);
+                       icount->dsr++;
+                       if ( status & MISCSTATUS_DSR )
+                               info->input_signal_events.dsr_up++;
+                       else
+                               info->input_signal_events.dsr_down++;
+               }
+               if (status & MISCSTATUS_DCD_LATCHED) {
+                       if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+                               usc_DisablestatusIrqs(info,SICR_DCD);
+                       icount->dcd++;
+                       if (status & MISCSTATUS_DCD) {
+                               info->input_signal_events.dcd_up++;
+                       } else
+                               info->input_signal_events.dcd_down++;
+#if SYNCLINK_GENERIC_HDLC
+                       if (info->netcount) {
+                               if (status & MISCSTATUS_DCD)
+                                       netif_carrier_on(info->netdev);
+                               else
+                                       netif_carrier_off(info->netdev);
+                       }
+#endif
+               }
+               if (status & MISCSTATUS_CTS_LATCHED)
+               {
+                       if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+                               usc_DisablestatusIrqs(info,SICR_CTS);
+                       icount->cts++;
+                       if ( status & MISCSTATUS_CTS )
+                               info->input_signal_events.cts_up++;
+                       else
+                               info->input_signal_events.cts_down++;
+               }
+               wake_up_interruptible(&info->status_event_wait_q);
+               wake_up_interruptible(&info->event_wait_q);
+
+               if ( (info->port.flags & ASYNC_CHECK_CD) && 
+                    (status & MISCSTATUS_DCD_LATCHED) ) {
+                       if ( debug_level >= DEBUG_LEVEL_ISR )
+                               printk("%s CD now %s...", info->device_name,
+                                      (status & MISCSTATUS_DCD) ? "on" : "off");
+                       if (status & MISCSTATUS_DCD)
+                               wake_up_interruptible(&info->port.open_wait);
+                       else {
+                               if ( debug_level >= DEBUG_LEVEL_ISR )
+                                       printk("doing serial hangup...");
+                               if (info->port.tty)
+                                       tty_hangup(info->port.tty);
+                       }
+               }
+       
+               if ( (info->port.flags & ASYNC_CTS_FLOW) && 
+                    (status & MISCSTATUS_CTS_LATCHED) ) {
+                       if (info->port.tty->hw_stopped) {
+                               if (status & MISCSTATUS_CTS) {
+                                       if ( debug_level >= DEBUG_LEVEL_ISR )
+                                               printk("CTS tx start...");
+                                       if (info->port.tty)
+                                               info->port.tty->hw_stopped = 0;
+                                       usc_start_transmitter(info);
+                                       info->pending_bh |= BH_TRANSMIT;
+                                       return;
+                               }
+                       } else {
+                               if (!(status & MISCSTATUS_CTS)) {
+                                       if ( debug_level >= DEBUG_LEVEL_ISR )
+                                               printk("CTS tx stop...");
+                                       if (info->port.tty)
+                                               info->port.tty->hw_stopped = 1;
+                                       usc_stop_transmitter(info);
+                               }
+                       }
+               }
+       }
+
+       info->pending_bh |= BH_STATUS;
+       
+       /* for diagnostics set IRQ flag */
+       if ( status & MISCSTATUS_TXC_LATCHED ){
+               usc_OutReg( info, SICR,
+                       (unsigned short)(usc_InReg(info,SICR) & ~(SICR_TXC_ACTIVE+SICR_TXC_INACTIVE)) );
+               usc_UnlatchIostatusBits( info, MISCSTATUS_TXC_LATCHED );
+               info->irq_occurred = true;
+       }
+
+}      /* end of mgsl_isr_io_pin() */
+
+/* mgsl_isr_transmit_data()
+ * 
+ *     Service a transmit data interrupt (async mode only).
+ * 
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void mgsl_isr_transmit_data( struct mgsl_struct *info )
+{
+       if ( debug_level >= DEBUG_LEVEL_ISR )   
+               printk("%s(%d):mgsl_isr_transmit_data xmit_cnt=%d\n",
+                       __FILE__,__LINE__,info->xmit_cnt);
+                       
+       usc_ClearIrqPendingBits( info, TRANSMIT_DATA );
+       
+       if (info->port.tty->stopped || info->port.tty->hw_stopped) {
+               usc_stop_transmitter(info);
+               return;
+       }
+       
+       if ( info->xmit_cnt )
+               usc_load_txfifo( info );
+       else
+               info->tx_active = false;
+               
+       if (info->xmit_cnt < WAKEUP_CHARS)
+               info->pending_bh |= BH_TRANSMIT;
+
+}      /* end of mgsl_isr_transmit_data() */
+
+/* mgsl_isr_receive_data()
+ * 
+ *     Service a receive data interrupt. This occurs
+ *     when operating in asynchronous interrupt transfer mode.
+ *     The receive data FIFO is flushed to the receive data buffers. 
+ * 
+ * Arguments:          info            pointer to device instance data
+ * Return Value:       None
+ */
+static void mgsl_isr_receive_data( struct mgsl_struct *info )
+{
+       int Fifocount;
+       u16 status;
+       int work = 0;
+       unsigned char DataByte;
+       struct tty_struct *tty = info->port.tty;
+       struct  mgsl_icount *icount = &info->icount;
+       
+       if ( debug_level >= DEBUG_LEVEL_ISR )   
+               printk("%s(%d):mgsl_isr_receive_data\n",
+                       __FILE__,__LINE__);
+
+       usc_ClearIrqPendingBits( info, RECEIVE_DATA );
+       
+       /* select FIFO status for RICR readback */
+       usc_RCmd( info, RCmd_SelectRicrRxFifostatus );
+
+       /* clear the Wordstatus bit so that status readback */
+       /* only reflects the status of this byte */
+       usc_OutReg( info, RICR+LSBONLY, (u16)(usc_InReg(info, RICR+LSBONLY) & ~BIT3 ));
+
+       /* flush the receive FIFO */
+
+       while( (Fifocount = (usc_InReg(info,RICR) >> 8)) ) {
+               int flag;
+
+               /* read one byte from RxFIFO */
+               outw( (inw(info->io_base + CCAR) & 0x0780) | (RDR+LSBONLY),
+                     info->io_base + CCAR );
+               DataByte = inb( info->io_base + CCAR );
+
+               /* get the status of the received byte */
+               status = usc_InReg(info, RCSR);
+               if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR +
+                               RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) )
+                       usc_UnlatchRxstatusBits(info,RXSTATUS_ALL);
+               
+               icount->rx++;
+               
+               flag = 0;
+               if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR +
+                               RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) {
+                       printk("rxerr=%04X\n",status);                                  
+                       /* update error statistics */
+                       if ( status & RXSTATUS_BREAK_RECEIVED ) {
+                               status &= ~(RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR);
+                               icount->brk++;
+                       } else if (status & RXSTATUS_PARITY_ERROR) 
+                               icount->parity++;
+                       else if (status & RXSTATUS_FRAMING_ERROR)
+                               icount->frame++;
+                       else if (status & RXSTATUS_OVERRUN) {
+                               /* must issue purge fifo cmd before */
+                               /* 16C32 accepts more receive chars */
+                               usc_RTCmd(info,RTCmd_PurgeRxFifo);
+                               icount->overrun++;
+                       }
+
+                       /* discard char if tty control flags say so */                                  
+                       if (status & info->ignore_status_mask)
+                               continue;
+                               
+                       status &= info->read_status_mask;
+               
+                       if (status & RXSTATUS_BREAK_RECEIVED) {
+                               flag = TTY_BREAK;
+                               if (info->port.flags & ASYNC_SAK)
+                                       do_SAK(tty);
+                       } else if (status & RXSTATUS_PARITY_ERROR)
+                               flag = TTY_PARITY;
+                       else if (status & RXSTATUS_FRAMING_ERROR)
+                               flag = TTY_FRAME;
+               }       /* end of if (error) */
+               tty_insert_flip_char(tty, DataByte, flag);
+               if (status & RXSTATUS_OVERRUN) {
+                       /* Overrun is special, since it's
+                        * reported immediately, and doesn't
+                        * affect the current character
+                        */
+                       work += tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+               }
+       }
+
+       if ( debug_level >= DEBUG_LEVEL_ISR ) {
+               printk("%s(%d):rx=%d brk=%d parity=%d frame=%d overrun=%d\n",
+                       __FILE__,__LINE__,icount->rx,icount->brk,
+                       icount->parity,icount->frame,icount->overrun);
+       }
+                       
+       if(work)
+               tty_flip_buffer_push(tty);
+}
+
+/* mgsl_isr_misc()
+ * 
+ *     Service a miscellaneous interrupt source.
+ *     
+ * Arguments:          info            pointer to device extension (instance data)
+ * Return Value:       None
+ */
+static void mgsl_isr_misc( struct mgsl_struct *info )
+{
+       u16 status = usc_InReg( info, MISR );
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )   
+               printk("%s(%d):mgsl_isr_misc status=%04X\n",
+                       __FILE__,__LINE__,status);
+                       
+       if ((status & MISCSTATUS_RCC_UNDERRUN) &&
+           (info->params.mode == MGSL_MODE_HDLC)) {
+
+               /* turn off receiver and rx DMA */
+               usc_EnableReceiver(info,DISABLE_UNCONDITIONAL);
+               usc_DmaCmd(info, DmaCmd_ResetRxChannel);
+               usc_UnlatchRxstatusBits(info, RXSTATUS_ALL);
+               usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS);
+               usc_DisableInterrupts(info, RECEIVE_DATA + RECEIVE_STATUS);
+
+               /* schedule BH handler to restart receiver */
+               info->pending_bh |= BH_RECEIVE;
+               info->rx_rcc_underrun = true;
+       }
+
+       usc_ClearIrqPendingBits( info, MISC );
+       usc_UnlatchMiscstatusBits( info, status );
+
+}      /* end of mgsl_isr_misc() */
+
+/* mgsl_isr_null()
+ *
+ *     Services undefined interrupt vectors from the
+ *     USC. (hence this function SHOULD never be called)
+ * 
+ * Arguments:          info            pointer to device extension (instance data)
+ * Return Value:       None
+ */
+static void mgsl_isr_null( struct mgsl_struct *info )
+{
+
+}      /* end of mgsl_isr_null() */
+
+/* mgsl_isr_receive_dma()
+ * 
+ *     Service a receive DMA channel interrupt.
+ *     For this driver there are two sources of receive DMA interrupts
+ *     as identified in the Receive DMA mode Register (RDMR):
+ * 
+ *     BIT3    EOA/EOL         End of List, all receive buffers in receive
+ *                             buffer list have been filled (no more free buffers
+ *                             available). The DMA controller has shut down.
+ * 
+ *     BIT2    EOB             End of Buffer. This interrupt occurs when a receive
+ *                             DMA buffer is terminated in response to completion
+ *                             of a good frame or a frame with errors. The status
+ *                             of the frame is stored in the buffer entry in the
+ *                             list of receive buffer entries.
+ * 
+ * Arguments:          info            pointer to device instance data
+ * Return Value:       None
+ */
+static void mgsl_isr_receive_dma( struct mgsl_struct *info )
+{
+       u16 status;
+       
+       /* clear interrupt pending and IUS bit for Rx DMA IRQ */
+       usc_OutDmaReg( info, CDIR, BIT9+BIT1 );
+
+       /* Read the receive DMA status to identify interrupt type. */
+       /* This also clears the status bits. */
+       status = usc_InDmaReg( info, RDMR );
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )   
+               printk("%s(%d):mgsl_isr_receive_dma(%s) status=%04X\n",
+                       __FILE__,__LINE__,info->device_name,status);
+                       
+       info->pending_bh |= BH_RECEIVE;
+       
+       if ( status & BIT3 ) {
+               info->rx_overflow = true;
+               info->icount.buf_overrun++;
+       }
+
+}      /* end of mgsl_isr_receive_dma() */
+
+/* mgsl_isr_transmit_dma()
+ *
+ *     This function services a transmit DMA channel interrupt.
+ *
+ *     For this driver there is one source of transmit DMA interrupts
+ *     as identified in the Transmit DMA Mode Register (TDMR):
+ *
+ *             BIT2  EOB       End of Buffer. This interrupt occurs when a
+ *                             transmit DMA buffer has been emptied.
+ *
+ *             The driver maintains enough transmit DMA buffers to hold at least
+ *             one max frame size transmit frame. When operating in a buffered
+ *             transmit mode, there may be enough transmit DMA buffers to hold at
+ *             least two or more max frame size frames. On an EOB condition,
+ *             determine if there are any queued transmit buffers and copy into
+ *             transmit DMA buffers if we have room.
+ *
+ * Arguments:          info            pointer to device instance data
+ * Return Value:       None
+ */
+static void mgsl_isr_transmit_dma( struct mgsl_struct *info )
+{
+       u16 status;
+
+       /* clear interrupt pending and IUS bit for Tx DMA IRQ */
+       usc_OutDmaReg(info, CDIR, BIT8+BIT0 );
+
+       /* Read the transmit DMA status to identify interrupt type. */
+       /* This also clears the status bits. */
+
+       status = usc_InDmaReg( info, TDMR );
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk("%s(%d):mgsl_isr_transmit_dma(%s) status=%04X\n",
+                       __FILE__,__LINE__,info->device_name,status);
+
+       if ( status & BIT2 ) {
+               --info->tx_dma_buffers_used;
+
+               /* if there are transmit frames queued,
+                *  try to load the next one
+                */
+               if ( load_next_tx_holding_buffer(info) ) {
+                       /* if call returns non-zero value, we have
+                        * at least one free tx holding buffer
+                        */
+                       info->pending_bh |= BH_TRANSMIT;
+               }
+       }
+
+}      /* end of mgsl_isr_transmit_dma() */
+
+/* mgsl_interrupt()
+ * 
+ *     Interrupt service routine entry point.
+ *     
+ * Arguments:
+ * 
+ *     irq             interrupt number that caused interrupt
+ *     dev_id          device ID supplied during interrupt registration
+ *     
+ * Return Value: None
+ */
+static irqreturn_t mgsl_interrupt(int dummy, void *dev_id)
+{
+       struct mgsl_struct *info = dev_id;
+       u16 UscVector;
+       u16 DmaVector;
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )   
+               printk(KERN_DEBUG "%s(%d):mgsl_interrupt(%d)entry.\n",
+                       __FILE__, __LINE__, info->irq_level);
+
+       spin_lock(&info->irq_spinlock);
+
+       for(;;) {
+               /* Read the interrupt vectors from hardware. */
+               UscVector = usc_InReg(info, IVR) >> 9;
+               DmaVector = usc_InDmaReg(info, DIVR);
+               
+               if ( debug_level >= DEBUG_LEVEL_ISR )   
+                       printk("%s(%d):%s UscVector=%08X DmaVector=%08X\n",
+                               __FILE__,__LINE__,info->device_name,UscVector,DmaVector);
+                       
+               if ( !UscVector && !DmaVector )
+                       break;
+                       
+               /* Dispatch interrupt vector */
+               if ( UscVector )
+                       (*UscIsrTable[UscVector])(info);
+               else if ( (DmaVector&(BIT10|BIT9)) == BIT10)
+                       mgsl_isr_transmit_dma(info);
+               else
+                       mgsl_isr_receive_dma(info);
+
+               if ( info->isr_overflow ) {
+                       printk(KERN_ERR "%s(%d):%s isr overflow irq=%d\n",
+                               __FILE__, __LINE__, info->device_name, info->irq_level);
+                       usc_DisableMasterIrqBit(info);
+                       usc_DisableDmaInterrupts(info,DICR_MASTER);
+                       break;
+               }
+       }
+       
+       /* Request bottom half processing if there's something 
+        * for it to do and the bh is not already running
+        */
+
+       if ( info->pending_bh && !info->bh_running && !info->bh_requested ) {
+               if ( debug_level >= DEBUG_LEVEL_ISR )   
+                       printk("%s(%d):%s queueing bh task.\n",
+                               __FILE__,__LINE__,info->device_name);
+               schedule_work(&info->task);
+               info->bh_requested = true;
+       }
+
+       spin_unlock(&info->irq_spinlock);
+       
+       if ( debug_level >= DEBUG_LEVEL_ISR )   
+               printk(KERN_DEBUG "%s(%d):mgsl_interrupt(%d)exit.\n",
+                       __FILE__, __LINE__, info->irq_level);
+
+       return IRQ_HANDLED;
+}      /* end of mgsl_interrupt() */
+
+/* startup()
+ * 
+ *     Initialize and start device.
+ *     
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       0 if success, otherwise error code
+ */
+static int startup(struct mgsl_struct * info)
+{
+       int retval = 0;
+       
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk("%s(%d):mgsl_startup(%s)\n",__FILE__,__LINE__,info->device_name);
+               
+       if (info->port.flags & ASYNC_INITIALIZED)
+               return 0;
+       
+       if (!info->xmit_buf) {
+               /* allocate a page of memory for a transmit buffer */
+               info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
+               if (!info->xmit_buf) {
+                       printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n",
+                               __FILE__,__LINE__,info->device_name);
+                       return -ENOMEM;
+               }
+       }
+
+       info->pending_bh = 0;
+       
+       memset(&info->icount, 0, sizeof(info->icount));
+
+       setup_timer(&info->tx_timer, mgsl_tx_timeout, (unsigned long)info);
+       
+       /* Allocate and claim adapter resources */
+       retval = mgsl_claim_resources(info);
+       
+       /* perform existence check and diagnostics */
+       if ( !retval )
+               retval = mgsl_adapter_test(info);
+               
+       if ( retval ) {
+               if (capable(CAP_SYS_ADMIN) && info->port.tty)
+                       set_bit(TTY_IO_ERROR, &info->port.tty->flags);
+               mgsl_release_resources(info);
+               return retval;
+       }
+
+       /* program hardware for current parameters */
+       mgsl_change_params(info);
+       
+       if (info->port.tty)
+               clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
+
+       info->port.flags |= ASYNC_INITIALIZED;
+       
+       return 0;
+       
+}      /* end of startup() */
+
+/* shutdown()
+ *
+ * Called by mgsl_close() and mgsl_hangup() to shutdown hardware
+ *
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void shutdown(struct mgsl_struct * info)
+{
+       unsigned long flags;
+       
+       if (!(info->port.flags & ASYNC_INITIALIZED))
+               return;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_shutdown(%s)\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       /* clear status wait queue because status changes */
+       /* can't happen after shutting down the hardware */
+       wake_up_interruptible(&info->status_event_wait_q);
+       wake_up_interruptible(&info->event_wait_q);
+
+       del_timer_sync(&info->tx_timer);
+
+       if (info->xmit_buf) {
+               free_page((unsigned long) info->xmit_buf);
+               info->xmit_buf = NULL;
+       }
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       usc_DisableMasterIrqBit(info);
+       usc_stop_receiver(info);
+       usc_stop_transmitter(info);
+       usc_DisableInterrupts(info,RECEIVE_DATA + RECEIVE_STATUS +
+               TRANSMIT_DATA + TRANSMIT_STATUS + IO_PIN + MISC );
+       usc_DisableDmaInterrupts(info,DICR_MASTER + DICR_TRANSMIT + DICR_RECEIVE);
+       
+       /* Disable DMAEN (Port 7, Bit 14) */
+       /* This disconnects the DMA request signal from the ISA bus */
+       /* on the ISA adapter. This has no effect for the PCI adapter */
+       usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) | BIT14));
+       
+       /* Disable INTEN (Port 6, Bit12) */
+       /* This disconnects the IRQ request signal to the ISA bus */
+       /* on the ISA adapter. This has no effect for the PCI adapter */
+       usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) | BIT12));
+       
+       if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) {
+               info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
+               usc_set_serial_signals(info);
+       }
+       
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+       mgsl_release_resources(info);   
+       
+       if (info->port.tty)
+               set_bit(TTY_IO_ERROR, &info->port.tty->flags);
+
+       info->port.flags &= ~ASYNC_INITIALIZED;
+       
+}      /* end of shutdown() */
+
+static void mgsl_program_hw(struct mgsl_struct *info)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       
+       usc_stop_receiver(info);
+       usc_stop_transmitter(info);
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       
+       if (info->params.mode == MGSL_MODE_HDLC ||
+           info->params.mode == MGSL_MODE_RAW ||
+           info->netcount)
+               usc_set_sync_mode(info);
+       else
+               usc_set_async_mode(info);
+               
+       usc_set_serial_signals(info);
+       
+       info->dcd_chkcount = 0;
+       info->cts_chkcount = 0;
+       info->ri_chkcount = 0;
+       info->dsr_chkcount = 0;
+
+       usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI);          
+       usc_EnableInterrupts(info, IO_PIN);
+       usc_get_serial_signals(info);
+               
+       if (info->netcount || info->port.tty->termios->c_cflag & CREAD)
+               usc_start_receiver(info);
+               
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+}
+
+/* Reconfigure adapter based on new parameters
+ */
+static void mgsl_change_params(struct mgsl_struct *info)
+{
+       unsigned cflag;
+       int bits_per_char;
+
+       if (!info->port.tty || !info->port.tty->termios)
+               return;
+               
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_change_params(%s)\n",
+                        __FILE__,__LINE__, info->device_name );
+                        
+       cflag = info->port.tty->termios->c_cflag;
+
+       /* if B0 rate (hangup) specified then negate DTR and RTS */
+       /* otherwise assert DTR and RTS */
+       if (cflag & CBAUD)
+               info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+       else
+               info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+       
+       /* byte size and parity */
+       
+       switch (cflag & CSIZE) {
+             case CS5: info->params.data_bits = 5; break;
+             case CS6: info->params.data_bits = 6; break;
+             case CS7: info->params.data_bits = 7; break;
+             case CS8: info->params.data_bits = 8; break;
+             /* Never happens, but GCC is too dumb to figure it out */
+             default:  info->params.data_bits = 7; break;
+             }
+             
+       if (cflag & CSTOPB)
+               info->params.stop_bits = 2;
+       else
+               info->params.stop_bits = 1;
+
+       info->params.parity = ASYNC_PARITY_NONE;
+       if (cflag & PARENB) {
+               if (cflag & PARODD)
+                       info->params.parity = ASYNC_PARITY_ODD;
+               else
+                       info->params.parity = ASYNC_PARITY_EVEN;
+#ifdef CMSPAR
+               if (cflag & CMSPAR)
+                       info->params.parity = ASYNC_PARITY_SPACE;
+#endif
+       }
+
+       /* calculate number of jiffies to transmit a full
+        * FIFO (32 bytes) at specified data rate
+        */
+       bits_per_char = info->params.data_bits + 
+                       info->params.stop_bits + 1;
+
+       /* if port data rate is set to 460800 or less then
+        * allow tty settings to override, otherwise keep the
+        * current data rate.
+        */
+       if (info->params.data_rate <= 460800)
+               info->params.data_rate = tty_get_baud_rate(info->port.tty);
+       
+       if ( info->params.data_rate ) {
+               info->timeout = (32*HZ*bits_per_char) / 
+                               info->params.data_rate;
+       }
+       info->timeout += HZ/50;         /* Add .02 seconds of slop */
+
+       if (cflag & CRTSCTS)
+               info->port.flags |= ASYNC_CTS_FLOW;
+       else
+               info->port.flags &= ~ASYNC_CTS_FLOW;
+               
+       if (cflag & CLOCAL)
+               info->port.flags &= ~ASYNC_CHECK_CD;
+       else
+               info->port.flags |= ASYNC_CHECK_CD;
+
+       /* process tty input control flags */
+       
+       info->read_status_mask = RXSTATUS_OVERRUN;
+       if (I_INPCK(info->port.tty))
+               info->read_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR;
+       if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty))
+               info->read_status_mask |= RXSTATUS_BREAK_RECEIVED;
+       
+       if (I_IGNPAR(info->port.tty))
+               info->ignore_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR;
+       if (I_IGNBRK(info->port.tty)) {
+               info->ignore_status_mask |= RXSTATUS_BREAK_RECEIVED;
+               /* If ignoring parity and break indicators, ignore 
+                * overruns too.  (For real raw support).
+                */
+               if (I_IGNPAR(info->port.tty))
+                       info->ignore_status_mask |= RXSTATUS_OVERRUN;
+       }
+
+       mgsl_program_hw(info);
+
+}      /* end of mgsl_change_params() */
+
+/* mgsl_put_char()
+ * 
+ *     Add a character to the transmit buffer.
+ *     
+ * Arguments:          tty     pointer to tty information structure
+ *                     ch      character to add to transmit buffer
+ *             
+ * Return Value:       None
+ */
+static int mgsl_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct mgsl_struct *info = tty->driver_data;
+       unsigned long flags;
+       int ret = 0;
+
+       if (debug_level >= DEBUG_LEVEL_INFO) {
+               printk(KERN_DEBUG "%s(%d):mgsl_put_char(%d) on %s\n",
+                       __FILE__, __LINE__, ch, info->device_name);
+       }               
+       
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_put_char"))
+               return 0;
+
+       if (!info->xmit_buf)
+               return 0;
+
+       spin_lock_irqsave(&info->irq_spinlock, flags);
+       
+       if ((info->params.mode == MGSL_MODE_ASYNC ) || !info->tx_active) {
+               if (info->xmit_cnt < SERIAL_XMIT_SIZE - 1) {
+                       info->xmit_buf[info->xmit_head++] = ch;
+                       info->xmit_head &= SERIAL_XMIT_SIZE-1;
+                       info->xmit_cnt++;
+                       ret = 1;
+               }
+       }
+       spin_unlock_irqrestore(&info->irq_spinlock, flags);
+       return ret;
+       
+}      /* end of mgsl_put_char() */
+
+/* mgsl_flush_chars()
+ * 
+ *     Enable transmitter so remaining characters in the
+ *     transmit buffer are sent.
+ *     
+ * Arguments:          tty     pointer to tty information structure
+ * Return Value:       None
+ */
+static void mgsl_flush_chars(struct tty_struct *tty)
+{
+       struct mgsl_struct *info = tty->driver_data;
+       unsigned long flags;
+                               
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):mgsl_flush_chars() entry on %s xmit_cnt=%d\n",
+                       __FILE__,__LINE__,info->device_name,info->xmit_cnt);
+       
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_flush_chars"))
+               return;
+
+       if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+           !info->xmit_buf)
+               return;
+
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):mgsl_flush_chars() entry on %s starting transmitter\n",
+                       __FILE__,__LINE__,info->device_name );
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       
+       if (!info->tx_active) {
+               if ( (info->params.mode == MGSL_MODE_HDLC ||
+                       info->params.mode == MGSL_MODE_RAW) && info->xmit_cnt ) {
+                       /* operating in synchronous (frame oriented) mode */
+                       /* copy data from circular xmit_buf to */
+                       /* transmit DMA buffer. */
+                       mgsl_load_tx_dma_buffer(info,
+                                info->xmit_buf,info->xmit_cnt);
+               }
+               usc_start_transmitter(info);
+       }
+       
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       
+}      /* end of mgsl_flush_chars() */
+
+/* mgsl_write()
+ * 
+ *     Send a block of data
+ *     
+ * Arguments:
+ * 
+ *     tty             pointer to tty information structure
+ *     buf             pointer to buffer containing send data
+ *     count           size of send data in bytes
+ *     
+ * Return Value:       number of characters written
+ */
+static int mgsl_write(struct tty_struct * tty,
+                   const unsigned char *buf, int count)
+{
+       int     c, ret = 0;
+       struct mgsl_struct *info = tty->driver_data;
+       unsigned long flags;
+       
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):mgsl_write(%s) count=%d\n",
+                       __FILE__,__LINE__,info->device_name,count);
+       
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_write"))
+               goto cleanup;
+
+       if (!info->xmit_buf)
+               goto cleanup;
+
+       if ( info->params.mode == MGSL_MODE_HDLC ||
+                       info->params.mode == MGSL_MODE_RAW ) {
+               /* operating in synchronous (frame oriented) mode */
+               /* operating in synchronous (frame oriented) mode */
+               if (info->tx_active) {
+
+                       if ( info->params.mode == MGSL_MODE_HDLC ) {
+                               ret = 0;
+                               goto cleanup;
+                       }
+                       /* transmitter is actively sending data -
+                        * if we have multiple transmit dma and
+                        * holding buffers, attempt to queue this
+                        * frame for transmission at a later time.
+                        */
+                       if (info->tx_holding_count >= info->num_tx_holding_buffers ) {
+                               /* no tx holding buffers available */
+                               ret = 0;
+                               goto cleanup;
+                       }
+
+                       /* queue transmit frame request */
+                       ret = count;
+                       save_tx_buffer_request(info,buf,count);
+
+                       /* if we have sufficient tx dma buffers,
+                        * load the next buffered tx request
+                        */
+                       spin_lock_irqsave(&info->irq_spinlock,flags);
+                       load_next_tx_holding_buffer(info);
+                       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+                       goto cleanup;
+               }
+       
+               /* if operating in HDLC LoopMode and the adapter  */
+               /* has yet to be inserted into the loop, we can't */
+               /* transmit                                       */
+
+               if ( (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) &&
+                       !usc_loopmode_active(info) )
+               {
+                       ret = 0;
+                       goto cleanup;
+               }
+
+               if ( info->xmit_cnt ) {
+                       /* Send accumulated from send_char() calls */
+                       /* as frame and wait before accepting more data. */
+                       ret = 0;
+                       
+                       /* copy data from circular xmit_buf to */
+                       /* transmit DMA buffer. */
+                       mgsl_load_tx_dma_buffer(info,
+                               info->xmit_buf,info->xmit_cnt);
+                       if ( debug_level >= DEBUG_LEVEL_INFO )
+                               printk( "%s(%d):mgsl_write(%s) sync xmit_cnt flushing\n",
+                                       __FILE__,__LINE__,info->device_name);
+               } else {
+                       if ( debug_level >= DEBUG_LEVEL_INFO )
+                               printk( "%s(%d):mgsl_write(%s) sync transmit accepted\n",
+                                       __FILE__,__LINE__,info->device_name);
+                       ret = count;
+                       info->xmit_cnt = count;
+                       mgsl_load_tx_dma_buffer(info,buf,count);
+               }
+       } else {
+               while (1) {
+                       spin_lock_irqsave(&info->irq_spinlock,flags);
+                       c = min_t(int, count,
+                               min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                                   SERIAL_XMIT_SIZE - info->xmit_head));
+                       if (c <= 0) {
+                               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+                               break;
+                       }
+                       memcpy(info->xmit_buf + info->xmit_head, buf, c);
+                       info->xmit_head = ((info->xmit_head + c) &
+                                          (SERIAL_XMIT_SIZE-1));
+                       info->xmit_cnt += c;
+                       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+                       buf += c;
+                       count -= c;
+                       ret += c;
+               }
+       }       
+       
+       if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
+               spin_lock_irqsave(&info->irq_spinlock,flags);
+               if (!info->tx_active)
+                       usc_start_transmitter(info);
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       }
+cleanup:       
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):mgsl_write(%s) returning=%d\n",
+                       __FILE__,__LINE__,info->device_name,ret);
+                       
+       return ret;
+       
+}      /* end of mgsl_write() */
+
+/* mgsl_write_room()
+ *
+ *     Return the count of free bytes in transmit buffer
+ *     
+ * Arguments:          tty     pointer to tty info structure
+ * Return Value:       None
+ */
+static int mgsl_write_room(struct tty_struct *tty)
+{
+       struct mgsl_struct *info = tty->driver_data;
+       int     ret;
+                               
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_write_room"))
+               return 0;
+       ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+       if (ret < 0)
+               ret = 0;
+               
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_write_room(%s)=%d\n",
+                        __FILE__,__LINE__, info->device_name,ret );
+                        
+       if ( info->params.mode == MGSL_MODE_HDLC ||
+               info->params.mode == MGSL_MODE_RAW ) {
+               /* operating in synchronous (frame oriented) mode */
+               if ( info->tx_active )
+                       return 0;
+               else
+                       return HDLC_MAX_FRAME_SIZE;
+       }
+       
+       return ret;
+       
+}      /* end of mgsl_write_room() */
+
+/* mgsl_chars_in_buffer()
+ *
+ *     Return the count of bytes in transmit buffer
+ *     
+ * Arguments:          tty     pointer to tty info structure
+ * Return Value:       None
+ */
+static int mgsl_chars_in_buffer(struct tty_struct *tty)
+{
+       struct mgsl_struct *info = tty->driver_data;
+                        
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_chars_in_buffer(%s)\n",
+                        __FILE__,__LINE__, info->device_name );
+                        
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_chars_in_buffer"))
+               return 0;
+               
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_chars_in_buffer(%s)=%d\n",
+                        __FILE__,__LINE__, info->device_name,info->xmit_cnt );
+                        
+       if ( info->params.mode == MGSL_MODE_HDLC ||
+               info->params.mode == MGSL_MODE_RAW ) {
+               /* operating in synchronous (frame oriented) mode */
+               if ( info->tx_active )
+                       return info->max_frame_size;
+               else
+                       return 0;
+       }
+                        
+       return info->xmit_cnt;
+}      /* end of mgsl_chars_in_buffer() */
+
+/* mgsl_flush_buffer()
+ *
+ *     Discard all data in the send buffer
+ *     
+ * Arguments:          tty     pointer to tty info structure
+ * Return Value:       None
+ */
+static void mgsl_flush_buffer(struct tty_struct *tty)
+{
+       struct mgsl_struct *info = tty->driver_data;
+       unsigned long flags;
+       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_flush_buffer(%s) entry\n",
+                        __FILE__,__LINE__, info->device_name );
+       
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_flush_buffer"))
+               return;
+               
+       spin_lock_irqsave(&info->irq_spinlock,flags); 
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       del_timer(&info->tx_timer);     
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       
+       tty_wakeup(tty);
+}
+
+/* mgsl_send_xchar()
+ *
+ *     Send a high-priority XON/XOFF character
+ *     
+ * Arguments:          tty     pointer to tty info structure
+ *                     ch      character to send
+ * Return Value:       None
+ */
+static void mgsl_send_xchar(struct tty_struct *tty, char ch)
+{
+       struct mgsl_struct *info = tty->driver_data;
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_send_xchar(%s,%d)\n",
+                        __FILE__,__LINE__, info->device_name, ch );
+                        
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_send_xchar"))
+               return;
+
+       info->x_char = ch;
+       if (ch) {
+               /* Make sure transmit interrupts are on */
+               spin_lock_irqsave(&info->irq_spinlock,flags);
+               if (!info->tx_enabled)
+                       usc_start_transmitter(info);
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       }
+}      /* end of mgsl_send_xchar() */
+
+/* mgsl_throttle()
+ * 
+ *     Signal remote device to throttle send data (our receive data)
+ *     
+ * Arguments:          tty     pointer to tty info structure
+ * Return Value:       None
+ */
+static void mgsl_throttle(struct tty_struct * tty)
+{
+       struct mgsl_struct *info = tty->driver_data;
+       unsigned long flags;
+       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_throttle(%s) entry\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_throttle"))
+               return;
+       
+       if (I_IXOFF(tty))
+               mgsl_send_xchar(tty, STOP_CHAR(tty));
+       if (tty->termios->c_cflag & CRTSCTS) {
+               spin_lock_irqsave(&info->irq_spinlock,flags);
+               info->serial_signals &= ~SerialSignal_RTS;
+               usc_set_serial_signals(info);
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       }
+}      /* end of mgsl_throttle() */
+
+/* mgsl_unthrottle()
+ * 
+ *     Signal remote device to stop throttling send data (our receive data)
+ *     
+ * Arguments:          tty     pointer to tty info structure
+ * Return Value:       None
+ */
+static void mgsl_unthrottle(struct tty_struct * tty)
+{
+       struct mgsl_struct *info = tty->driver_data;
+       unsigned long flags;
+       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_unthrottle(%s) entry\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_unthrottle"))
+               return;
+       
+       if (I_IXOFF(tty)) {
+               if (info->x_char)
+                       info->x_char = 0;
+               else
+                       mgsl_send_xchar(tty, START_CHAR(tty));
+       }
+       
+       if (tty->termios->c_cflag & CRTSCTS) {
+               spin_lock_irqsave(&info->irq_spinlock,flags);
+               info->serial_signals |= SerialSignal_RTS;
+               usc_set_serial_signals(info);
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       }
+       
+}      /* end of mgsl_unthrottle() */
+
+/* mgsl_get_stats()
+ * 
+ *     get the current serial parameters information
+ *
+ * Arguments:  info            pointer to device instance data
+ *             user_icount     pointer to buffer to hold returned stats
+ *     
+ * Return Value:       0 if success, otherwise error code
+ */
+static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount __user *user_icount)
+{
+       int err;
+       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_get_params(%s)\n",
+                        __FILE__,__LINE__, info->device_name);
+                       
+       if (!user_icount) {
+               memset(&info->icount, 0, sizeof(info->icount));
+       } else {
+               mutex_lock(&info->port.mutex);
+               COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
+               mutex_unlock(&info->port.mutex);
+               if (err)
+                       return -EFAULT;
+       }
+       
+       return 0;
+       
+}      /* end of mgsl_get_stats() */
+
+/* mgsl_get_params()
+ * 
+ *     get the current serial parameters information
+ *
+ * Arguments:  info            pointer to device instance data
+ *             user_params     pointer to buffer to hold returned params
+ *     
+ * Return Value:       0 if success, otherwise error code
+ */
+static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS __user *user_params)
+{
+       int err;
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_get_params(%s)\n",
+                        __FILE__,__LINE__, info->device_name);
+                       
+       mutex_lock(&info->port.mutex);
+       COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
+       mutex_unlock(&info->port.mutex);
+       if (err) {
+               if ( debug_level >= DEBUG_LEVEL_INFO )
+                       printk( "%s(%d):mgsl_get_params(%s) user buffer copy failed\n",
+                               __FILE__,__LINE__,info->device_name);
+               return -EFAULT;
+       }
+       
+       return 0;
+       
+}      /* end of mgsl_get_params() */
+
+/* mgsl_set_params()
+ * 
+ *     set the serial parameters
+ *     
+ * Arguments:
+ * 
+ *     info            pointer to device instance data
+ *     new_params      user buffer containing new serial params
+ *
+ * Return Value:       0 if success, otherwise error code
+ */
+static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS __user *new_params)
+{
+       unsigned long flags;
+       MGSL_PARAMS tmp_params;
+       int err;
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_set_params %s\n", __FILE__,__LINE__,
+                       info->device_name );
+       COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS));
+       if (err) {
+               if ( debug_level >= DEBUG_LEVEL_INFO )
+                       printk( "%s(%d):mgsl_set_params(%s) user buffer copy failed\n",
+                               __FILE__,__LINE__,info->device_name);
+               return -EFAULT;
+       }
+       
+       mutex_lock(&info->port.mutex);
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       
+       mgsl_change_params(info);
+       mutex_unlock(&info->port.mutex);
+       
+       return 0;
+       
+}      /* end of mgsl_set_params() */
+
+/* mgsl_get_txidle()
+ * 
+ *     get the current transmit idle mode
+ *
+ * Arguments:  info            pointer to device instance data
+ *             idle_mode       pointer to buffer to hold returned idle mode
+ *     
+ * Return Value:       0 if success, otherwise error code
+ */
+static int mgsl_get_txidle(struct mgsl_struct * info, int __user *idle_mode)
+{
+       int err;
+       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_get_txidle(%s)=%d\n",
+                        __FILE__,__LINE__, info->device_name, info->idle_mode);
+                       
+       COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int));
+       if (err) {
+               if ( debug_level >= DEBUG_LEVEL_INFO )
+                       printk( "%s(%d):mgsl_get_txidle(%s) user buffer copy failed\n",
+                               __FILE__,__LINE__,info->device_name);
+               return -EFAULT;
+       }
+       
+       return 0;
+       
+}      /* end of mgsl_get_txidle() */
+
+/* mgsl_set_txidle()   service ioctl to set transmit idle mode
+ *     
+ * Arguments:          info            pointer to device instance data
+ *                     idle_mode       new idle mode
+ *
+ * Return Value:       0 if success, otherwise error code
+ */
+static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode)
+{
+       unsigned long flags;
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_set_txidle(%s,%d)\n", __FILE__,__LINE__,
+                       info->device_name, idle_mode );
+                       
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       info->idle_mode = idle_mode;
+       usc_set_txidle( info );
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       return 0;
+       
+}      /* end of mgsl_set_txidle() */
+
+/* mgsl_txenable()
+ * 
+ *     enable or disable the transmitter
+ *     
+ * Arguments:
+ * 
+ *     info            pointer to device instance data
+ *     enable          1 = enable, 0 = disable
+ *
+ * Return Value:       0 if success, otherwise error code
+ */
+static int mgsl_txenable(struct mgsl_struct * info, int enable)
+{
+       unsigned long flags;
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_txenable(%s,%d)\n", __FILE__,__LINE__,
+                       info->device_name, enable);
+                       
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       if ( enable ) {
+               if ( !info->tx_enabled ) {
+
+                       usc_start_transmitter(info);
+                       /*--------------------------------------------------
+                        * if HDLC/SDLC Loop mode, attempt to insert the
+                        * station in the 'loop' by setting CMR:13. Upon
+                        * receipt of the next GoAhead (RxAbort) sequence,
+                        * the OnLoop indicator (CCSR:7) should go active
+                        * to indicate that we are on the loop
+                        *--------------------------------------------------*/
+                       if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
+                               usc_loopmode_insert_request( info );
+               }
+       } else {
+               if ( info->tx_enabled )
+                       usc_stop_transmitter(info);
+       }
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       return 0;
+       
+}      /* end of mgsl_txenable() */
+
+/* mgsl_txabort()      abort send HDLC frame
+ *     
+ * Arguments:          info            pointer to device instance data
+ * Return Value:       0 if success, otherwise error code
+ */
+static int mgsl_txabort(struct mgsl_struct * info)
+{
+       unsigned long flags;
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_txabort(%s)\n", __FILE__,__LINE__,
+                       info->device_name);
+                       
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC )
+       {
+               if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
+                       usc_loopmode_cancel_transmit( info );
+               else
+                       usc_TCmd(info,TCmd_SendAbort);
+       }
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       return 0;
+       
+}      /* end of mgsl_txabort() */
+
+/* mgsl_rxenable()     enable or disable the receiver
+ *     
+ * Arguments:          info            pointer to device instance data
+ *                     enable          1 = enable, 0 = disable
+ * Return Value:       0 if success, otherwise error code
+ */
+static int mgsl_rxenable(struct mgsl_struct * info, int enable)
+{
+       unsigned long flags;
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_rxenable(%s,%d)\n", __FILE__,__LINE__,
+                       info->device_name, enable);
+                       
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       if ( enable ) {
+               if ( !info->rx_enabled )
+                       usc_start_receiver(info);
+       } else {
+               if ( info->rx_enabled )
+                       usc_stop_receiver(info);
+       }
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       return 0;
+       
+}      /* end of mgsl_rxenable() */
+
+/* mgsl_wait_event()   wait for specified event to occur
+ *     
+ * Arguments:          info    pointer to device instance data
+ *                     mask    pointer to bitmask of events to wait for
+ * Return Value:       0       if successful and bit mask updated with
+ *                             of events triggerred,
+ *                     otherwise error code
+ */
+static int mgsl_wait_event(struct mgsl_struct * info, int __user * mask_ptr)
+{
+       unsigned long flags;
+       int s;
+       int rc=0;
+       struct mgsl_icount cprev, cnow;
+       int events;
+       int mask;
+       struct  _input_signal_events oldsigs, newsigs;
+       DECLARE_WAITQUEUE(wait, current);
+
+       COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int));
+       if (rc) {
+               return  -EFAULT;
+       }
+                
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_wait_event(%s,%d)\n", __FILE__,__LINE__,
+                       info->device_name, mask);
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+
+       /* return immediately if state matches requested events */
+       usc_get_serial_signals(info);
+       s = info->serial_signals;
+       events = mask &
+               ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
+                 ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
+                 ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) +
+                 ((s & SerialSignal_RI)  ? MgslEvent_RiActive :MgslEvent_RiInactive) );
+       if (events) {
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+               goto exit;
+       }
+
+       /* save current irq counts */
+       cprev = info->icount;
+       oldsigs = info->input_signal_events;
+       
+       /* enable hunt and idle irqs if needed */
+       if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
+               u16 oldreg = usc_InReg(info,RICR);
+               u16 newreg = oldreg +
+                        (mask & MgslEvent_ExitHuntMode ? RXSTATUS_EXITED_HUNT:0) +
+                        (mask & MgslEvent_IdleReceived ? RXSTATUS_IDLE_RECEIVED:0);
+               if (oldreg != newreg)
+                       usc_OutReg(info, RICR, newreg);
+       }
+       
+       set_current_state(TASK_INTERRUPTIBLE);
+       add_wait_queue(&info->event_wait_q, &wait);
+       
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       
+
+       for(;;) {
+               schedule();
+               if (signal_pending(current)) {
+                       rc = -ERESTARTSYS;
+                       break;
+               }
+                       
+               /* get current irq counts */
+               spin_lock_irqsave(&info->irq_spinlock,flags);
+               cnow = info->icount;
+               newsigs = info->input_signal_events;
+               set_current_state(TASK_INTERRUPTIBLE);
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+               /* if no change, wait aborted for some reason */
+               if (newsigs.dsr_up   == oldsigs.dsr_up   &&
+                   newsigs.dsr_down == oldsigs.dsr_down &&
+                   newsigs.dcd_up   == oldsigs.dcd_up   &&
+                   newsigs.dcd_down == oldsigs.dcd_down &&
+                   newsigs.cts_up   == oldsigs.cts_up   &&
+                   newsigs.cts_down == oldsigs.cts_down &&
+                   newsigs.ri_up    == oldsigs.ri_up    &&
+                   newsigs.ri_down  == oldsigs.ri_down  &&
+                   cnow.exithunt    == cprev.exithunt   &&
+                   cnow.rxidle      == cprev.rxidle) {
+                       rc = -EIO;
+                       break;
+               }
+
+               events = mask &
+                       ( (newsigs.dsr_up   != oldsigs.dsr_up   ? MgslEvent_DsrActive:0)   +
+                       (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) +
+                       (newsigs.dcd_up   != oldsigs.dcd_up   ? MgslEvent_DcdActive:0)   +
+                       (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) +
+                       (newsigs.cts_up   != oldsigs.cts_up   ? MgslEvent_CtsActive:0)   +
+                       (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) +
+                       (newsigs.ri_up    != oldsigs.ri_up    ? MgslEvent_RiActive:0)    +
+                       (newsigs.ri_down  != oldsigs.ri_down  ? MgslEvent_RiInactive:0)  +
+                       (cnow.exithunt    != cprev.exithunt   ? MgslEvent_ExitHuntMode:0) +
+                         (cnow.rxidle      != cprev.rxidle     ? MgslEvent_IdleReceived:0) );
+               if (events)
+                       break;
+               
+               cprev = cnow;
+               oldsigs = newsigs;
+       }
+       
+       remove_wait_queue(&info->event_wait_q, &wait);
+       set_current_state(TASK_RUNNING);
+
+       if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
+               spin_lock_irqsave(&info->irq_spinlock,flags);
+               if (!waitqueue_active(&info->event_wait_q)) {
+                       /* disable enable exit hunt mode/idle rcvd IRQs */
+                       usc_OutReg(info, RICR, usc_InReg(info,RICR) &
+                               ~(RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED));
+               }
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       }
+exit:
+       if ( rc == 0 )
+               PUT_USER(rc, events, mask_ptr);
+               
+       return rc;
+       
+}      /* end of mgsl_wait_event() */
+
+static int modem_input_wait(struct mgsl_struct *info,int arg)
+{
+       unsigned long flags;
+       int rc;
+       struct mgsl_icount cprev, cnow;
+       DECLARE_WAITQUEUE(wait, current);
+
+       /* save current irq counts */
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       cprev = info->icount;
+       add_wait_queue(&info->status_event_wait_q, &wait);
+       set_current_state(TASK_INTERRUPTIBLE);
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+       for(;;) {
+               schedule();
+               if (signal_pending(current)) {
+                       rc = -ERESTARTSYS;
+                       break;
+               }
+
+               /* get new irq counts */
+               spin_lock_irqsave(&info->irq_spinlock,flags);
+               cnow = info->icount;
+               set_current_state(TASK_INTERRUPTIBLE);
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+               /* if no change, wait aborted for some reason */
+               if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+                   cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
+                       rc = -EIO;
+                       break;
+               }
+
+               /* check for change in caller specified modem input */
+               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)) {
+                       rc = 0;
+                       break;
+               }
+
+               cprev = cnow;
+       }
+       remove_wait_queue(&info->status_event_wait_q, &wait);
+       set_current_state(TASK_RUNNING);
+       return rc;
+}
+
+/* return the state of the serial control and status signals
+ */
+static int tiocmget(struct tty_struct *tty)
+{
+       struct mgsl_struct *info = tty->driver_data;
+       unsigned int result;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       usc_get_serial_signals(info);
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+       result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) +
+               ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) +
+               ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) +
+               ((info->serial_signals & SerialSignal_RI)  ? TIOCM_RNG:0) +
+               ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) +
+               ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0);
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s tiocmget() value=%08X\n",
+                        __FILE__,__LINE__, info->device_name, result );
+       return result;
+}
+
+/* set modem control signals (DTR/RTS)
+ */
+static int tiocmset(struct tty_struct *tty,
+                                   unsigned int set, unsigned int clear)
+{
+       struct mgsl_struct *info = tty->driver_data;
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s tiocmset(%x,%x)\n",
+                       __FILE__,__LINE__,info->device_name, set, clear);
+
+       if (set & TIOCM_RTS)
+               info->serial_signals |= SerialSignal_RTS;
+       if (set & TIOCM_DTR)
+               info->serial_signals |= SerialSignal_DTR;
+       if (clear & TIOCM_RTS)
+               info->serial_signals &= ~SerialSignal_RTS;
+       if (clear & TIOCM_DTR)
+               info->serial_signals &= ~SerialSignal_DTR;
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       usc_set_serial_signals(info);
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+       return 0;
+}
+
+/* mgsl_break()                Set or clear transmit break condition
+ *
+ * Arguments:          tty             pointer to tty instance data
+ *                     break_state     -1=set break condition, 0=clear
+ * Return Value:       error code
+ */
+static int mgsl_break(struct tty_struct *tty, int break_state)
+{
+       struct mgsl_struct * info = tty->driver_data;
+       unsigned long flags;
+       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_break(%s,%d)\n",
+                        __FILE__,__LINE__, info->device_name, break_state);
+                        
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_break"))
+               return -EINVAL;
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       if (break_state == -1)
+               usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) | BIT7));
+       else 
+               usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) & ~BIT7));
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       return 0;
+       
+}      /* end of mgsl_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.
+ */
+static int msgl_get_icount(struct tty_struct *tty,
+                               struct serial_icounter_struct *icount)
+
+{
+       struct mgsl_struct * info = tty->driver_data;
+       struct mgsl_icount cnow;        /* kernel counter temps */
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       cnow = info->icount;
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+       icount->cts = cnow.cts;
+       icount->dsr = cnow.dsr;
+       icount->rng = cnow.rng;
+       icount->dcd = cnow.dcd;
+       icount->rx = cnow.rx;
+       icount->tx = cnow.tx;
+       icount->frame = cnow.frame;
+       icount->overrun = cnow.overrun;
+       icount->parity = cnow.parity;
+       icount->brk = cnow.brk;
+       icount->buf_overrun = cnow.buf_overrun;
+       return 0;
+}
+
+/* mgsl_ioctl()        Service an IOCTL request
+ *     
+ * Arguments:
+ * 
+ *     tty     pointer to tty instance data
+ *     cmd     IOCTL command code
+ *     arg     command argument/context
+ *     
+ * Return Value:       0 if success, otherwise error code
+ */
+static int mgsl_ioctl(struct tty_struct *tty,
+                   unsigned int cmd, unsigned long arg)
+{
+       struct mgsl_struct * info = tty->driver_data;
+       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__,
+                       info->device_name, cmd );
+       
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_ioctl"))
+               return -ENODEV;
+
+       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+           (cmd != TIOCMIWAIT)) {
+               if (tty->flags & (1 << TTY_IO_ERROR))
+                   return -EIO;
+       }
+
+       return mgsl_ioctl_common(info, cmd, arg);
+}
+
+static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+       
+       switch (cmd) {
+               case MGSL_IOCGPARAMS:
+                       return mgsl_get_params(info, argp);
+               case MGSL_IOCSPARAMS:
+                       return mgsl_set_params(info, argp);
+               case MGSL_IOCGTXIDLE:
+                       return mgsl_get_txidle(info, argp);
+               case MGSL_IOCSTXIDLE:
+                       return mgsl_set_txidle(info,(int)arg);
+               case MGSL_IOCTXENABLE:
+                       return mgsl_txenable(info,(int)arg);
+               case MGSL_IOCRXENABLE:
+                       return mgsl_rxenable(info,(int)arg);
+               case MGSL_IOCTXABORT:
+                       return mgsl_txabort(info);
+               case MGSL_IOCGSTATS:
+                       return mgsl_get_stats(info, argp);
+               case MGSL_IOCWAITEVENT:
+                       return mgsl_wait_event(info, argp);
+               case MGSL_IOCLOOPTXDONE:
+                       return mgsl_loopmode_send_done(info);
+               /* Wait for modem input (DCD,RI,DSR,CTS) change
+                * as specified by mask in arg (TIOCM_RNG/DSR/CD/CTS)
+                */
+               case TIOCMIWAIT:
+                       return modem_input_wait(info,(int)arg);
+
+               default:
+                       return -ENOIOCTLCMD;
+       }
+       return 0;
+}
+
+/* mgsl_set_termios()
+ * 
+ *     Set new termios settings
+ *     
+ * Arguments:
+ * 
+ *     tty             pointer to tty structure
+ *     termios         pointer to buffer to hold returned old termios
+ *     
+ * Return Value:               None
+ */
+static void mgsl_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+       struct mgsl_struct *info = tty->driver_data;
+       unsigned long flags;
+       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_set_termios %s\n", __FILE__,__LINE__,
+                       tty->driver->name );
+       
+       mgsl_change_params(info);
+
+       /* Handle transition to B0 status */
+       if (old_termios->c_cflag & CBAUD &&
+           !(tty->termios->c_cflag & CBAUD)) {
+               info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+               spin_lock_irqsave(&info->irq_spinlock,flags);
+               usc_set_serial_signals(info);
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       }
+       
+       /* Handle transition away from B0 status */
+       if (!(old_termios->c_cflag & CBAUD) &&
+           tty->termios->c_cflag & CBAUD) {
+               info->serial_signals |= SerialSignal_DTR;
+               if (!(tty->termios->c_cflag & CRTSCTS) || 
+                   !test_bit(TTY_THROTTLED, &tty->flags)) {
+                       info->serial_signals |= SerialSignal_RTS;
+               }
+               spin_lock_irqsave(&info->irq_spinlock,flags);
+               usc_set_serial_signals(info);
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       }
+       
+       /* Handle turning off CRTSCTS */
+       if (old_termios->c_cflag & CRTSCTS &&
+           !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               mgsl_start(tty);
+       }
+
+}      /* end of mgsl_set_termios() */
+
+/* mgsl_close()
+ * 
+ *     Called when port is closed. Wait for remaining data to be
+ *     sent. Disable port and free resources.
+ *     
+ * Arguments:
+ * 
+ *     tty     pointer to open tty structure
+ *     filp    pointer to open file object
+ *     
+ * Return Value:       None
+ */
+static void mgsl_close(struct tty_struct *tty, struct file * filp)
+{
+       struct mgsl_struct * info = tty->driver_data;
+
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_close"))
+               return;
+       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_close(%s) entry, count=%d\n",
+                        __FILE__,__LINE__, info->device_name, info->port.count);
+
+       if (tty_port_close_start(&info->port, tty, filp) == 0)                   
+               goto cleanup;
+
+       mutex_lock(&info->port.mutex);
+       if (info->port.flags & ASYNC_INITIALIZED)
+               mgsl_wait_until_sent(tty, info->timeout);
+       mgsl_flush_buffer(tty);
+       tty_ldisc_flush(tty);
+       shutdown(info);
+       mutex_unlock(&info->port.mutex);
+
+       tty_port_close_end(&info->port, tty);   
+       info->port.tty = NULL;
+cleanup:                       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_close(%s) exit, count=%d\n", __FILE__,__LINE__,
+                       tty->driver->name, info->port.count);
+                       
+}      /* end of mgsl_close() */
+
+/* mgsl_wait_until_sent()
+ *
+ *     Wait until the transmitter is empty.
+ *
+ * Arguments:
+ *
+ *     tty             pointer to tty info structure
+ *     timeout         time to wait for send completion
+ *
+ * Return Value:       None
+ */
+static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       struct mgsl_struct * info = tty->driver_data;
+       unsigned long orig_jiffies, char_time;
+
+       if (!info )
+               return;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_wait_until_sent(%s) entry\n",
+                        __FILE__,__LINE__, info->device_name );
+      
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_wait_until_sent"))
+               return;
+
+       if (!(info->port.flags & ASYNC_INITIALIZED))
+               goto exit;
+        
+       orig_jiffies = jiffies;
+      
+       /* Set check interval to 1/5 of estimated time to
+        * send a character, and make it at least 1. The check
+        * interval should also be less than the timeout.
+        * Note: use tight timings here to satisfy the NIST-PCTS.
+        */ 
+
+       if ( info->params.data_rate ) {
+               char_time = info->timeout/(32 * 5);
+               if (!char_time)
+                       char_time++;
+       } else
+               char_time = 1;
+               
+       if (timeout)
+               char_time = min_t(unsigned long, char_time, timeout);
+               
+       if ( info->params.mode == MGSL_MODE_HDLC ||
+               info->params.mode == MGSL_MODE_RAW ) {
+               while (info->tx_active) {
+                       msleep_interruptible(jiffies_to_msecs(char_time));
+                       if (signal_pending(current))
+                               break;
+                       if (timeout && time_after(jiffies, orig_jiffies + timeout))
+                               break;
+               }
+       } else {
+               while (!(usc_InReg(info,TCSR) & TXSTATUS_ALL_SENT) &&
+                       info->tx_enabled) {
+                       msleep_interruptible(jiffies_to_msecs(char_time));
+                       if (signal_pending(current))
+                               break;
+                       if (timeout && time_after(jiffies, orig_jiffies + timeout))
+                               break;
+               }
+       }
+      
+exit:
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_wait_until_sent(%s) exit\n",
+                        __FILE__,__LINE__, info->device_name );
+                        
+}      /* end of mgsl_wait_until_sent() */
+
+/* mgsl_hangup()
+ *
+ *     Called by tty_hangup() when a hangup is signaled.
+ *     This is the same as to closing all open files for the port.
+ *
+ * Arguments:          tty     pointer to associated tty object
+ * Return Value:       None
+ */
+static void mgsl_hangup(struct tty_struct *tty)
+{
+       struct mgsl_struct * info = tty->driver_data;
+       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_hangup(%s)\n",
+                        __FILE__,__LINE__, info->device_name );
+                        
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_hangup"))
+               return;
+
+       mgsl_flush_buffer(tty);
+       shutdown(info);
+       
+       info->port.count = 0;   
+       info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
+       info->port.tty = NULL;
+
+       wake_up_interruptible(&info->port.open_wait);
+       
+}      /* end of mgsl_hangup() */
+
+/*
+ * carrier_raised()
+ *
+ *     Return true if carrier is raised
+ */
+
+static int carrier_raised(struct tty_port *port)
+{
+       unsigned long flags;
+       struct mgsl_struct *info = container_of(port, struct mgsl_struct, port);
+       
+       spin_lock_irqsave(&info->irq_spinlock, flags);
+       usc_get_serial_signals(info);
+       spin_unlock_irqrestore(&info->irq_spinlock, flags);
+       return (info->serial_signals & SerialSignal_DCD) ? 1 : 0;
+}
+
+static void dtr_rts(struct tty_port *port, int on)
+{
+       struct mgsl_struct *info = container_of(port, struct mgsl_struct, port);
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       if (on)
+               info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+       else
+               info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+       usc_set_serial_signals(info);
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+}
+
+
+/* block_til_ready()
+ * 
+ *     Block the current process until the specified port
+ *     is ready to be opened.
+ *     
+ * Arguments:
+ * 
+ *     tty             pointer to tty info structure
+ *     filp            pointer to open file object
+ *     info            pointer to device instance data
+ *     
+ * Return Value:       0 if success, otherwise error code
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+                          struct mgsl_struct *info)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       int             retval;
+       bool            do_clocal = false;
+       bool            extra_count = false;
+       unsigned long   flags;
+       int             dcd;
+       struct tty_port *port = &info->port;
+       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):block_til_ready on %s\n",
+                        __FILE__,__LINE__, tty->driver->name );
+
+       if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){
+               /* nonblock mode is set or port is not enabled */
+               port->flags |= ASYNC_NORMAL_ACTIVE;
+               return 0;
+       }
+
+       if (tty->termios->c_cflag & CLOCAL)
+               do_clocal = true;
+
+       /* Wait for carrier detect and the line to become
+        * free (i.e., not in use by the callout).  While we are in
+        * this loop, port->count is dropped by one, so that
+        * mgsl_close() knows when to free things.  We restore it upon
+        * exit, either normal or abnormal.
+        */
+        
+       retval = 0;
+       add_wait_queue(&port->open_wait, &wait);
+       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):block_til_ready before block on %s count=%d\n",
+                        __FILE__,__LINE__, tty->driver->name, port->count );
+
+       spin_lock_irqsave(&info->irq_spinlock, flags);
+       if (!tty_hung_up_p(filp)) {
+               extra_count = true;
+               port->count--;
+       }
+       spin_unlock_irqrestore(&info->irq_spinlock, flags);
+       port->blocked_open++;
+       
+       while (1) {
+               if (tty->termios->c_cflag & CBAUD)
+                       tty_port_raise_dtr_rts(port);
+               
+               set_current_state(TASK_INTERRUPTIBLE);
+               
+               if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){
+                       retval = (port->flags & ASYNC_HUP_NOTIFY) ?
+                                       -EAGAIN : -ERESTARTSYS;
+                       break;
+               }
+               
+               dcd = tty_port_carrier_raised(&info->port);
+               
+               if (!(port->flags & ASYNC_CLOSING) && (do_clocal || dcd))
+                       break;
+                       
+               if (signal_pending(current)) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+               
+               if (debug_level >= DEBUG_LEVEL_INFO)
+                       printk("%s(%d):block_til_ready blocking on %s count=%d\n",
+                                __FILE__,__LINE__, tty->driver->name, port->count );
+                                
+               tty_unlock();
+               schedule();
+               tty_lock();
+       }
+       
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&port->open_wait, &wait);
+       
+       /* FIXME: Racy on hangup during close wait */
+       if (extra_count)
+               port->count++;
+       port->blocked_open--;
+       
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):block_til_ready after blocking on %s count=%d\n",
+                        __FILE__,__LINE__, tty->driver->name, port->count );
+                        
+       if (!retval)
+               port->flags |= ASYNC_NORMAL_ACTIVE;
+               
+       return retval;
+       
+}      /* end of block_til_ready() */
+
+/* mgsl_open()
+ *
+ *     Called when a port is opened.  Init and enable port.
+ *     Perform serial-specific initialization for the tty structure.
+ *
+ * Arguments:          tty     pointer to tty info structure
+ *                     filp    associated file pointer
+ *
+ * Return Value:       0 if success, otherwise error code
+ */
+static int mgsl_open(struct tty_struct *tty, struct file * filp)
+{
+       struct mgsl_struct      *info;
+       int                     retval, line;
+       unsigned long flags;
+
+       /* verify range of specified line number */     
+       line = tty->index;
+       if ((line < 0) || (line >= mgsl_device_count)) {
+               printk("%s(%d):mgsl_open with invalid line #%d.\n",
+                       __FILE__,__LINE__,line);
+               return -ENODEV;
+       }
+
+       /* find the info structure for the specified line */
+       info = mgsl_device_list;
+       while(info && info->line != line)
+               info = info->next_device;
+       if (mgsl_paranoia_check(info, tty->name, "mgsl_open"))
+               return -ENODEV;
+       
+       tty->driver_data = info;
+       info->port.tty = tty;
+               
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_open(%s), old ref count = %d\n",
+                        __FILE__,__LINE__,tty->driver->name, info->port.count);
+
+       /* If port is closing, signal caller to try again */
+       if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){
+               if (info->port.flags & ASYNC_CLOSING)
+                       interruptible_sleep_on(&info->port.close_wait);
+               retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS);
+               goto cleanup;
+       }
+       
+       info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+       spin_lock_irqsave(&info->netlock, flags);
+       if (info->netcount) {
+               retval = -EBUSY;
+               spin_unlock_irqrestore(&info->netlock, flags);
+               goto cleanup;
+       }
+       info->port.count++;
+       spin_unlock_irqrestore(&info->netlock, flags);
+
+       if (info->port.count == 1) {
+               /* 1st open on this device, init hardware */
+               retval = startup(info);
+               if (retval < 0)
+                       goto cleanup;
+       }
+
+       retval = block_til_ready(tty, filp, info);
+       if (retval) {
+               if (debug_level >= DEBUG_LEVEL_INFO)
+                       printk("%s(%d):block_til_ready(%s) returned %d\n",
+                                __FILE__,__LINE__, info->device_name, retval);
+               goto cleanup;
+       }
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):mgsl_open(%s) success\n",
+                        __FILE__,__LINE__, info->device_name);
+       retval = 0;
+       
+cleanup:                       
+       if (retval) {
+               if (tty->count == 1)
+                       info->port.tty = NULL; /* tty layer will release tty struct */
+               if(info->port.count)
+                       info->port.count--;
+       }
+       
+       return retval;
+       
+}      /* end of mgsl_open() */
+
+/*
+ * /proc fs routines....
+ */
+
+static inline void line_info(struct seq_file *m, struct mgsl_struct *info)
+{
+       char    stat_buf[30];
+       unsigned long flags;
+
+       if (info->bus_type == MGSL_BUS_TYPE_PCI) {
+               seq_printf(m, "%s:PCI io:%04X irq:%d mem:%08X lcr:%08X",
+                       info->device_name, info->io_base, info->irq_level,
+                       info->phys_memory_base, info->phys_lcr_base);
+       } else {
+               seq_printf(m, "%s:(E)ISA io:%04X irq:%d dma:%d",
+                       info->device_name, info->io_base, 
+                       info->irq_level, info->dma_level);
+       }
+
+       /* output current serial signal states */
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       usc_get_serial_signals(info);
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       
+       stat_buf[0] = 0;
+       stat_buf[1] = 0;
+       if (info->serial_signals & SerialSignal_RTS)
+               strcat(stat_buf, "|RTS");
+       if (info->serial_signals & SerialSignal_CTS)
+               strcat(stat_buf, "|CTS");
+       if (info->serial_signals & SerialSignal_DTR)
+               strcat(stat_buf, "|DTR");
+       if (info->serial_signals & SerialSignal_DSR)
+               strcat(stat_buf, "|DSR");
+       if (info->serial_signals & SerialSignal_DCD)
+               strcat(stat_buf, "|CD");
+       if (info->serial_signals & SerialSignal_RI)
+               strcat(stat_buf, "|RI");
+
+       if (info->params.mode == MGSL_MODE_HDLC ||
+           info->params.mode == MGSL_MODE_RAW ) {
+               seq_printf(m, " HDLC txok:%d rxok:%d",
+                             info->icount.txok, info->icount.rxok);
+               if (info->icount.txunder)
+                       seq_printf(m, " txunder:%d", info->icount.txunder);
+               if (info->icount.txabort)
+                       seq_printf(m, " txabort:%d", info->icount.txabort);
+               if (info->icount.rxshort)
+                       seq_printf(m, " rxshort:%d", info->icount.rxshort);
+               if (info->icount.rxlong)
+                       seq_printf(m, " rxlong:%d", info->icount.rxlong);
+               if (info->icount.rxover)
+                       seq_printf(m, " rxover:%d", info->icount.rxover);
+               if (info->icount.rxcrc)
+                       seq_printf(m, " rxcrc:%d", info->icount.rxcrc);
+       } else {
+               seq_printf(m, " ASYNC tx:%d rx:%d",
+                             info->icount.tx, info->icount.rx);
+               if (info->icount.frame)
+                       seq_printf(m, " fe:%d", info->icount.frame);
+               if (info->icount.parity)
+                       seq_printf(m, " pe:%d", info->icount.parity);
+               if (info->icount.brk)
+                       seq_printf(m, " brk:%d", info->icount.brk);
+               if (info->icount.overrun)
+                       seq_printf(m, " oe:%d", info->icount.overrun);
+       }
+       
+       /* Append serial signal status to end */
+       seq_printf(m, " %s\n", stat_buf+1);
+       
+       seq_printf(m, "txactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
+        info->tx_active,info->bh_requested,info->bh_running,
+        info->pending_bh);
+        
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       {       
+       u16 Tcsr = usc_InReg( info, TCSR );
+       u16 Tdmr = usc_InDmaReg( info, TDMR );
+       u16 Ticr = usc_InReg( info, TICR );
+       u16 Rscr = usc_InReg( info, RCSR );
+       u16 Rdmr = usc_InDmaReg( info, RDMR );
+       u16 Ricr = usc_InReg( info, RICR );
+       u16 Icr = usc_InReg( info, ICR );
+       u16 Dccr = usc_InReg( info, DCCR );
+       u16 Tmr = usc_InReg( info, TMR );
+       u16 Tccr = usc_InReg( info, TCCR );
+       u16 Ccar = inw( info->io_base + CCAR );
+       seq_printf(m, "tcsr=%04X tdmr=%04X ticr=%04X rcsr=%04X rdmr=%04X\n"
+                        "ricr=%04X icr =%04X dccr=%04X tmr=%04X tccr=%04X ccar=%04X\n",
+                       Tcsr,Tdmr,Ticr,Rscr,Rdmr,Ricr,Icr,Dccr,Tmr,Tccr,Ccar );
+       }
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+}
+
+/* Called to print information about devices */
+static int mgsl_proc_show(struct seq_file *m, void *v)
+{
+       struct mgsl_struct *info;
+       
+       seq_printf(m, "synclink driver:%s\n", driver_version);
+       
+       info = mgsl_device_list;
+       while( info ) {
+               line_info(m, info);
+               info = info->next_device;
+       }
+       return 0;
+}
+
+static int mgsl_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mgsl_proc_show, NULL);
+}
+
+static const struct file_operations mgsl_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = mgsl_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/* mgsl_allocate_dma_buffers()
+ * 
+ *     Allocate and format DMA buffers (ISA adapter)
+ *     or format shared memory buffers (PCI adapter).
+ * 
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       0 if success, otherwise error
+ */
+static int mgsl_allocate_dma_buffers(struct mgsl_struct *info)
+{
+       unsigned short BuffersPerFrame;
+
+       info->last_mem_alloc = 0;
+
+       /* Calculate the number of DMA buffers necessary to hold the */
+       /* largest allowable frame size. Note: If the max frame size is */
+       /* not an even multiple of the DMA buffer size then we need to */
+       /* round the buffer count per frame up one. */
+
+       BuffersPerFrame = (unsigned short)(info->max_frame_size/DMABUFFERSIZE);
+       if ( info->max_frame_size % DMABUFFERSIZE )
+               BuffersPerFrame++;
+
+       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+               /*
+                * The PCI adapter has 256KBytes of shared memory to use.
+                * This is 64 PAGE_SIZE buffers.
+                *
+                * The first page is used for padding at this time so the
+                * buffer list does not begin at offset 0 of the PCI
+                * adapter's shared memory.
+                *
+                * The 2nd page is used for the buffer list. A 4K buffer
+                * list can hold 128 DMA_BUFFER structures at 32 bytes
+                * each.
+                *
+                * This leaves 62 4K pages.
+                *
+                * The next N pages are used for transmit frame(s). We
+                * reserve enough 4K page blocks to hold the required
+                * number of transmit dma buffers (num_tx_dma_buffers),
+                * each of MaxFrameSize size.
+                *
+                * Of the remaining pages (62-N), determine how many can
+                * be used to receive full MaxFrameSize inbound frames
+                */
+               info->tx_buffer_count = info->num_tx_dma_buffers * BuffersPerFrame;
+               info->rx_buffer_count = 62 - info->tx_buffer_count;
+       } else {
+               /* Calculate the number of PAGE_SIZE buffers needed for */
+               /* receive and transmit DMA buffers. */
+
+
+               /* Calculate the number of DMA buffers necessary to */
+               /* hold 7 max size receive frames and one max size transmit frame. */
+               /* The receive buffer count is bumped by one so we avoid an */
+               /* End of List condition if all receive buffers are used when */
+               /* using linked list DMA buffers. */
+
+               info->tx_buffer_count = info->num_tx_dma_buffers * BuffersPerFrame;
+               info->rx_buffer_count = (BuffersPerFrame * MAXRXFRAMES) + 6;
+               
+               /* 
+                * limit total TxBuffers & RxBuffers to 62 4K total 
+                * (ala PCI Allocation) 
+                */
+               
+               if ( (info->tx_buffer_count + info->rx_buffer_count) > 62 )
+                       info->rx_buffer_count = 62 - info->tx_buffer_count;
+
+       }
+
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk("%s(%d):Allocating %d TX and %d RX DMA buffers.\n",
+                       __FILE__,__LINE__, info->tx_buffer_count,info->rx_buffer_count);
+       
+       if ( mgsl_alloc_buffer_list_memory( info ) < 0 ||
+                 mgsl_alloc_frame_memory(info, info->rx_buffer_list, info->rx_buffer_count) < 0 || 
+                 mgsl_alloc_frame_memory(info, info->tx_buffer_list, info->tx_buffer_count) < 0 || 
+                 mgsl_alloc_intermediate_rxbuffer_memory(info) < 0  ||
+                 mgsl_alloc_intermediate_txbuffer_memory(info) < 0 ) {
+               printk("%s(%d):Can't allocate DMA buffer memory\n",__FILE__,__LINE__);
+               return -ENOMEM;
+       }
+       
+       mgsl_reset_rx_dma_buffers( info );
+       mgsl_reset_tx_dma_buffers( info );
+
+       return 0;
+
+}      /* end of mgsl_allocate_dma_buffers() */
+
+/*
+ * mgsl_alloc_buffer_list_memory()
+ * 
+ * Allocate a common DMA buffer for use as the
+ * receive and transmit buffer lists.
+ * 
+ * A buffer list is a set of buffer entries where each entry contains
+ * a pointer to an actual buffer and a pointer to the next buffer entry
+ * (plus some other info about the buffer).
+ * 
+ * The buffer entries for a list are built to form a circular list so
+ * that when the entire list has been traversed you start back at the
+ * beginning.
+ * 
+ * This function allocates memory for just the buffer entries.
+ * The links (pointer to next entry) are filled in with the physical
+ * address of the next entry so the adapter can navigate the list
+ * using bus master DMA. The pointers to the actual buffers are filled
+ * out later when the actual buffers are allocated.
+ * 
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       0 if success, otherwise error
+ */
+static int mgsl_alloc_buffer_list_memory( struct mgsl_struct *info )
+{
+       unsigned int i;
+
+       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+               /* PCI adapter uses shared memory. */
+               info->buffer_list = info->memory_base + info->last_mem_alloc;
+               info->buffer_list_phys = info->last_mem_alloc;
+               info->last_mem_alloc += BUFFERLISTSIZE;
+       } else {
+               /* ISA adapter uses system memory. */
+               /* The buffer lists are allocated as a common buffer that both */
+               /* the processor and adapter can access. This allows the driver to */
+               /* inspect portions of the buffer while other portions are being */
+               /* updated by the adapter using Bus Master DMA. */
+
+               info->buffer_list = dma_alloc_coherent(NULL, BUFFERLISTSIZE, &info->buffer_list_dma_addr, GFP_KERNEL);
+               if (info->buffer_list == NULL)
+                       return -ENOMEM;
+               info->buffer_list_phys = (u32)(info->buffer_list_dma_addr);
+       }
+
+       /* We got the memory for the buffer entry lists. */
+       /* Initialize the memory block to all zeros. */
+       memset( info->buffer_list, 0, BUFFERLISTSIZE );
+
+       /* Save virtual address pointers to the receive and */
+       /* transmit buffer lists. (Receive 1st). These pointers will */
+       /* be used by the processor to access the lists. */
+       info->rx_buffer_list = (DMABUFFERENTRY *)info->buffer_list;
+       info->tx_buffer_list = (DMABUFFERENTRY *)info->buffer_list;
+       info->tx_buffer_list += info->rx_buffer_count;
+
+       /*
+        * Build the links for the buffer entry lists such that
+        * two circular lists are built. (Transmit and Receive).
+        *
+        * Note: the links are physical addresses
+        * which are read by the adapter to determine the next
+        * buffer entry to use.
+        */
+
+       for ( i = 0; i < info->rx_buffer_count; i++ ) {
+               /* calculate and store physical address of this buffer entry */
+               info->rx_buffer_list[i].phys_entry =
+                       info->buffer_list_phys + (i * sizeof(DMABUFFERENTRY));
+
+               /* calculate and store physical address of */
+               /* next entry in cirular list of entries */
+
+               info->rx_buffer_list[i].link = info->buffer_list_phys;
+
+               if ( i < info->rx_buffer_count - 1 )
+                       info->rx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY);
+       }
+
+       for ( i = 0; i < info->tx_buffer_count; i++ ) {
+               /* calculate and store physical address of this buffer entry */
+               info->tx_buffer_list[i].phys_entry = info->buffer_list_phys +
+                       ((info->rx_buffer_count + i) * sizeof(DMABUFFERENTRY));
+
+               /* calculate and store physical address of */
+               /* next entry in cirular list of entries */
+
+               info->tx_buffer_list[i].link = info->buffer_list_phys +
+                       info->rx_buffer_count * sizeof(DMABUFFERENTRY);
+
+               if ( i < info->tx_buffer_count - 1 )
+                       info->tx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY);
+       }
+
+       return 0;
+
+}      /* end of mgsl_alloc_buffer_list_memory() */
+
+/* Free DMA buffers allocated for use as the
+ * receive and transmit buffer lists.
+ * Warning:
+ * 
+ *     The data transfer buffers associated with the buffer list
+ *     MUST be freed before freeing the buffer list itself because
+ *     the buffer list contains the information necessary to free
+ *     the individual buffers!
+ */
+static void mgsl_free_buffer_list_memory( struct mgsl_struct *info )
+{
+       if (info->buffer_list && info->bus_type != MGSL_BUS_TYPE_PCI)
+               dma_free_coherent(NULL, BUFFERLISTSIZE, info->buffer_list, info->buffer_list_dma_addr);
+               
+       info->buffer_list = NULL;
+       info->rx_buffer_list = NULL;
+       info->tx_buffer_list = NULL;
+
+}      /* end of mgsl_free_buffer_list_memory() */
+
+/*
+ * mgsl_alloc_frame_memory()
+ * 
+ *     Allocate the frame DMA buffers used by the specified buffer list.
+ *     Each DMA buffer will be one memory page in size. This is necessary
+ *     because memory can fragment enough that it may be impossible
+ *     contiguous pages.
+ * 
+ * Arguments:
+ * 
+ *     info            pointer to device instance data
+ *     BufferList      pointer to list of buffer entries
+ *     Buffercount     count of buffer entries in buffer list
+ * 
+ * Return Value:       0 if success, otherwise -ENOMEM
+ */
+static int mgsl_alloc_frame_memory(struct mgsl_struct *info,DMABUFFERENTRY *BufferList,int Buffercount)
+{
+       int i;
+       u32 phys_addr;
+
+       /* Allocate page sized buffers for the receive buffer list */
+
+       for ( i = 0; i < Buffercount; i++ ) {
+               if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+                       /* PCI adapter uses shared memory buffers. */
+                       BufferList[i].virt_addr = info->memory_base + info->last_mem_alloc;
+                       phys_addr = info->last_mem_alloc;
+                       info->last_mem_alloc += DMABUFFERSIZE;
+               } else {
+                       /* ISA adapter uses system memory. */
+                       BufferList[i].virt_addr = dma_alloc_coherent(NULL, DMABUFFERSIZE, &BufferList[i].dma_addr, GFP_KERNEL);
+                       if (BufferList[i].virt_addr == NULL)
+                               return -ENOMEM;
+                       phys_addr = (u32)(BufferList[i].dma_addr);
+               }
+               BufferList[i].phys_addr = phys_addr;
+       }
+
+       return 0;
+
+}      /* end of mgsl_alloc_frame_memory() */
+
+/*
+ * mgsl_free_frame_memory()
+ * 
+ *     Free the buffers associated with
+ *     each buffer entry of a buffer list.
+ * 
+ * Arguments:
+ * 
+ *     info            pointer to device instance data
+ *     BufferList      pointer to list of buffer entries
+ *     Buffercount     count of buffer entries in buffer list
+ * 
+ * Return Value:       None
+ */
+static void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList, int Buffercount)
+{
+       int i;
+
+       if ( BufferList ) {
+               for ( i = 0 ; i < Buffercount ; i++ ) {
+                       if ( BufferList[i].virt_addr ) {
+                               if ( info->bus_type != MGSL_BUS_TYPE_PCI )
+                                       dma_free_coherent(NULL, DMABUFFERSIZE, BufferList[i].virt_addr, BufferList[i].dma_addr);
+                               BufferList[i].virt_addr = NULL;
+                       }
+               }
+       }
+
+}      /* end of mgsl_free_frame_memory() */
+
+/* mgsl_free_dma_buffers()
+ * 
+ *     Free DMA buffers
+ *     
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void mgsl_free_dma_buffers( struct mgsl_struct *info )
+{
+       mgsl_free_frame_memory( info, info->rx_buffer_list, info->rx_buffer_count );
+       mgsl_free_frame_memory( info, info->tx_buffer_list, info->tx_buffer_count );
+       mgsl_free_buffer_list_memory( info );
+
+}      /* end of mgsl_free_dma_buffers() */
+
+
+/*
+ * mgsl_alloc_intermediate_rxbuffer_memory()
+ * 
+ *     Allocate a buffer large enough to hold max_frame_size. This buffer
+ *     is used to pass an assembled frame to the line discipline.
+ * 
+ * Arguments:
+ * 
+ *     info            pointer to device instance data
+ * 
+ * Return Value:       0 if success, otherwise -ENOMEM
+ */
+static int mgsl_alloc_intermediate_rxbuffer_memory(struct mgsl_struct *info)
+{
+       info->intermediate_rxbuffer = kmalloc(info->max_frame_size, GFP_KERNEL | GFP_DMA);
+       if ( info->intermediate_rxbuffer == NULL )
+               return -ENOMEM;
+
+       return 0;
+
+}      /* end of mgsl_alloc_intermediate_rxbuffer_memory() */
+
+/*
+ * mgsl_free_intermediate_rxbuffer_memory()
+ * 
+ * 
+ * Arguments:
+ * 
+ *     info            pointer to device instance data
+ * 
+ * Return Value:       None
+ */
+static void mgsl_free_intermediate_rxbuffer_memory(struct mgsl_struct *info)
+{
+       kfree(info->intermediate_rxbuffer);
+       info->intermediate_rxbuffer = NULL;
+
+}      /* end of mgsl_free_intermediate_rxbuffer_memory() */
+
+/*
+ * mgsl_alloc_intermediate_txbuffer_memory()
+ *
+ *     Allocate intermdiate transmit buffer(s) large enough to hold max_frame_size.
+ *     This buffer is used to load transmit frames into the adapter's dma transfer
+ *     buffers when there is sufficient space.
+ *
+ * Arguments:
+ *
+ *     info            pointer to device instance data
+ *
+ * Return Value:       0 if success, otherwise -ENOMEM
+ */
+static int mgsl_alloc_intermediate_txbuffer_memory(struct mgsl_struct *info)
+{
+       int i;
+
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk("%s %s(%d)  allocating %d tx holding buffers\n",
+                               info->device_name, __FILE__,__LINE__,info->num_tx_holding_buffers);
+
+       memset(info->tx_holding_buffers,0,sizeof(info->tx_holding_buffers));
+
+       for ( i=0; i<info->num_tx_holding_buffers; ++i) {
+               info->tx_holding_buffers[i].buffer =
+                       kmalloc(info->max_frame_size, GFP_KERNEL);
+               if (info->tx_holding_buffers[i].buffer == NULL) {
+                       for (--i; i >= 0; i--) {
+                               kfree(info->tx_holding_buffers[i].buffer);
+                               info->tx_holding_buffers[i].buffer = NULL;
+                       }
+                       return -ENOMEM;
+               }
+       }
+
+       return 0;
+
+}      /* end of mgsl_alloc_intermediate_txbuffer_memory() */
+
+/*
+ * mgsl_free_intermediate_txbuffer_memory()
+ *
+ *
+ * Arguments:
+ *
+ *     info            pointer to device instance data
+ *
+ * Return Value:       None
+ */
+static void mgsl_free_intermediate_txbuffer_memory(struct mgsl_struct *info)
+{
+       int i;
+
+       for ( i=0; i<info->num_tx_holding_buffers; ++i ) {
+               kfree(info->tx_holding_buffers[i].buffer);
+               info->tx_holding_buffers[i].buffer = NULL;
+       }
+
+       info->get_tx_holding_index = 0;
+       info->put_tx_holding_index = 0;
+       info->tx_holding_count = 0;
+
+}      /* end of mgsl_free_intermediate_txbuffer_memory() */
+
+
+/*
+ * load_next_tx_holding_buffer()
+ *
+ * attempts to load the next buffered tx request into the
+ * tx dma buffers
+ *
+ * Arguments:
+ *
+ *     info            pointer to device instance data
+ *
+ * Return Value:       true if next buffered tx request loaded
+ *                     into adapter's tx dma buffer,
+ *                     false otherwise
+ */
+static bool load_next_tx_holding_buffer(struct mgsl_struct *info)
+{
+       bool ret = false;
+
+       if ( info->tx_holding_count ) {
+               /* determine if we have enough tx dma buffers
+                * to accommodate the next tx frame
+                */
+               struct tx_holding_buffer *ptx =
+                       &info->tx_holding_buffers[info->get_tx_holding_index];
+               int num_free = num_free_tx_dma_buffers(info);
+               int num_needed = ptx->buffer_size / DMABUFFERSIZE;
+               if ( ptx->buffer_size % DMABUFFERSIZE )
+                       ++num_needed;
+
+               if (num_needed <= num_free) {
+                       info->xmit_cnt = ptx->buffer_size;
+                       mgsl_load_tx_dma_buffer(info,ptx->buffer,ptx->buffer_size);
+
+                       --info->tx_holding_count;
+                       if ( ++info->get_tx_holding_index >= info->num_tx_holding_buffers)
+                               info->get_tx_holding_index=0;
+
+                       /* restart transmit timer */
+                       mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(5000));
+
+                       ret = true;
+               }
+       }
+
+       return ret;
+}
+
+/*
+ * save_tx_buffer_request()
+ *
+ * attempt to store transmit frame request for later transmission
+ *
+ * Arguments:
+ *
+ *     info            pointer to device instance data
+ *     Buffer          pointer to buffer containing frame to load
+ *     BufferSize      size in bytes of frame in Buffer
+ *
+ * Return Value:       1 if able to store, 0 otherwise
+ */
+static int save_tx_buffer_request(struct mgsl_struct *info,const char *Buffer, unsigned int BufferSize)
+{
+       struct tx_holding_buffer *ptx;
+
+       if ( info->tx_holding_count >= info->num_tx_holding_buffers ) {
+               return 0;               /* all buffers in use */
+       }
+
+       ptx = &info->tx_holding_buffers[info->put_tx_holding_index];
+       ptx->buffer_size = BufferSize;
+       memcpy( ptx->buffer, Buffer, BufferSize);
+
+       ++info->tx_holding_count;
+       if ( ++info->put_tx_holding_index >= info->num_tx_holding_buffers)
+               info->put_tx_holding_index=0;
+
+       return 1;
+}
+
+static int mgsl_claim_resources(struct mgsl_struct *info)
+{
+       if (request_region(info->io_base,info->io_addr_size,"synclink") == NULL) {
+               printk( "%s(%d):I/O address conflict on device %s Addr=%08X\n",
+                       __FILE__,__LINE__,info->device_name, info->io_base);
+               return -ENODEV;
+       }
+       info->io_addr_requested = true;
+       
+       if ( request_irq(info->irq_level,mgsl_interrupt,info->irq_flags,
+               info->device_name, info ) < 0 ) {
+               printk( "%s(%d):Cant request interrupt on device %s IRQ=%d\n",
+                       __FILE__,__LINE__,info->device_name, info->irq_level );
+               goto errout;
+       }
+       info->irq_requested = true;
+       
+       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+               if (request_mem_region(info->phys_memory_base,0x40000,"synclink") == NULL) {
+                       printk( "%s(%d):mem addr conflict device %s Addr=%08X\n",
+                               __FILE__,__LINE__,info->device_name, info->phys_memory_base);
+                       goto errout;
+               }
+               info->shared_mem_requested = true;
+               if (request_mem_region(info->phys_lcr_base + info->lcr_offset,128,"synclink") == NULL) {
+                       printk( "%s(%d):lcr mem addr conflict device %s Addr=%08X\n",
+                               __FILE__,__LINE__,info->device_name, info->phys_lcr_base + info->lcr_offset);
+                       goto errout;
+               }
+               info->lcr_mem_requested = true;
+
+               info->memory_base = ioremap_nocache(info->phys_memory_base,
+                                                               0x40000);
+               if (!info->memory_base) {
+                       printk( "%s(%d):Cant map shared memory on device %s MemAddr=%08X\n",
+                               __FILE__,__LINE__,info->device_name, info->phys_memory_base );
+                       goto errout;
+               }
+               
+               if ( !mgsl_memory_test(info) ) {
+                       printk( "%s(%d):Failed shared memory test %s MemAddr=%08X\n",
+                               __FILE__,__LINE__,info->device_name, info->phys_memory_base );
+                       goto errout;
+               }
+               
+               info->lcr_base = ioremap_nocache(info->phys_lcr_base,
+                                                               PAGE_SIZE);
+               if (!info->lcr_base) {
+                       printk( "%s(%d):Cant map LCR memory on device %s MemAddr=%08X\n",
+                               __FILE__,__LINE__,info->device_name, info->phys_lcr_base );
+                       goto errout;
+               }
+               info->lcr_base += info->lcr_offset;
+               
+       } else {
+               /* claim DMA channel */
+               
+               if (request_dma(info->dma_level,info->device_name) < 0){
+                       printk( "%s(%d):Cant request DMA channel on device %s DMA=%d\n",
+                               __FILE__,__LINE__,info->device_name, info->dma_level );
+                       mgsl_release_resources( info );
+                       return -ENODEV;
+               }
+               info->dma_requested = true;
+
+               /* ISA adapter uses bus master DMA */           
+               set_dma_mode(info->dma_level,DMA_MODE_CASCADE);
+               enable_dma(info->dma_level);
+       }
+       
+       if ( mgsl_allocate_dma_buffers(info) < 0 ) {
+               printk( "%s(%d):Cant allocate DMA buffers on device %s DMA=%d\n",
+                       __FILE__,__LINE__,info->device_name, info->dma_level );
+               goto errout;
+       }       
+       
+       return 0;
+errout:
+       mgsl_release_resources(info);
+       return -ENODEV;
+
+}      /* end of mgsl_claim_resources() */
+
+static void mgsl_release_resources(struct mgsl_struct *info)
+{
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):mgsl_release_resources(%s) entry\n",
+                       __FILE__,__LINE__,info->device_name );
+                       
+       if ( info->irq_requested ) {
+               free_irq(info->irq_level, info);
+               info->irq_requested = false;
+       }
+       if ( info->dma_requested ) {
+               disable_dma(info->dma_level);
+               free_dma(info->dma_level);
+               info->dma_requested = false;
+       }
+       mgsl_free_dma_buffers(info);
+       mgsl_free_intermediate_rxbuffer_memory(info);
+       mgsl_free_intermediate_txbuffer_memory(info);
+       
+       if ( info->io_addr_requested ) {
+               release_region(info->io_base,info->io_addr_size);
+               info->io_addr_requested = false;
+       }
+       if ( info->shared_mem_requested ) {
+               release_mem_region(info->phys_memory_base,0x40000);
+               info->shared_mem_requested = false;
+       }
+       if ( info->lcr_mem_requested ) {
+               release_mem_region(info->phys_lcr_base + info->lcr_offset,128);
+               info->lcr_mem_requested = false;
+       }
+       if (info->memory_base){
+               iounmap(info->memory_base);
+               info->memory_base = NULL;
+       }
+       if (info->lcr_base){
+               iounmap(info->lcr_base - info->lcr_offset);
+               info->lcr_base = NULL;
+       }
+       
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):mgsl_release_resources(%s) exit\n",
+                       __FILE__,__LINE__,info->device_name );
+                       
+}      /* end of mgsl_release_resources() */
+
+/* mgsl_add_device()
+ * 
+ *     Add the specified device instance data structure to the
+ *     global linked list of devices and increment the device count.
+ *     
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void mgsl_add_device( struct mgsl_struct *info )
+{
+       info->next_device = NULL;
+       info->line = mgsl_device_count;
+       sprintf(info->device_name,"ttySL%d",info->line);
+       
+       if (info->line < MAX_TOTAL_DEVICES) {
+               if (maxframe[info->line])
+                       info->max_frame_size = maxframe[info->line];
+
+               if (txdmabufs[info->line]) {
+                       info->num_tx_dma_buffers = txdmabufs[info->line];
+                       if (info->num_tx_dma_buffers < 1)
+                               info->num_tx_dma_buffers = 1;
+               }
+
+               if (txholdbufs[info->line]) {
+                       info->num_tx_holding_buffers = txholdbufs[info->line];
+                       if (info->num_tx_holding_buffers < 1)
+                               info->num_tx_holding_buffers = 1;
+                       else if (info->num_tx_holding_buffers > MAX_TX_HOLDING_BUFFERS)
+                               info->num_tx_holding_buffers = MAX_TX_HOLDING_BUFFERS;
+               }
+       }
+
+       mgsl_device_count++;
+       
+       if ( !mgsl_device_list )
+               mgsl_device_list = info;
+       else {  
+               struct mgsl_struct *current_dev = mgsl_device_list;
+               while( current_dev->next_device )
+                       current_dev = current_dev->next_device;
+               current_dev->next_device = info;
+       }
+       
+       if ( info->max_frame_size < 4096 )
+               info->max_frame_size = 4096;
+       else if ( info->max_frame_size > 65535 )
+               info->max_frame_size = 65535;
+       
+       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+               printk( "SyncLink PCI v%d %s: IO=%04X IRQ=%d Mem=%08X,%08X MaxFrameSize=%u\n",
+                       info->hw_version + 1, info->device_name, info->io_base, info->irq_level,
+                       info->phys_memory_base, info->phys_lcr_base,
+                       info->max_frame_size );
+       } else {
+               printk( "SyncLink ISA %s: IO=%04X IRQ=%d DMA=%d MaxFrameSize=%u\n",
+                       info->device_name, info->io_base, info->irq_level, info->dma_level,
+                       info->max_frame_size );
+       }
+
+#if SYNCLINK_GENERIC_HDLC
+       hdlcdev_init(info);
+#endif
+
+}      /* end of mgsl_add_device() */
+
+static const struct tty_port_operations mgsl_port_ops = {
+       .carrier_raised = carrier_raised,
+       .dtr_rts = dtr_rts,
+};
+
+
+/* mgsl_allocate_device()
+ * 
+ *     Allocate and initialize a device instance structure
+ *     
+ * Arguments:          none
+ * Return Value:       pointer to mgsl_struct if success, otherwise NULL
+ */
+static struct mgsl_struct* mgsl_allocate_device(void)
+{
+       struct mgsl_struct *info;
+       
+       info = kzalloc(sizeof(struct mgsl_struct),
+                GFP_KERNEL);
+                
+       if (!info) {
+               printk("Error can't allocate device instance data\n");
+       } else {
+               tty_port_init(&info->port);
+               info->port.ops = &mgsl_port_ops;
+               info->magic = MGSL_MAGIC;
+               INIT_WORK(&info->task, mgsl_bh_handler);
+               info->max_frame_size = 4096;
+               info->port.close_delay = 5*HZ/10;
+               info->port.closing_wait = 30*HZ;
+               init_waitqueue_head(&info->status_event_wait_q);
+               init_waitqueue_head(&info->event_wait_q);
+               spin_lock_init(&info->irq_spinlock);
+               spin_lock_init(&info->netlock);
+               memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
+               info->idle_mode = HDLC_TXIDLE_FLAGS;            
+               info->num_tx_dma_buffers = 1;
+               info->num_tx_holding_buffers = 0;
+       }
+       
+       return info;
+
+}      /* end of mgsl_allocate_device()*/
+
+static const struct tty_operations mgsl_ops = {
+       .open = mgsl_open,
+       .close = mgsl_close,
+       .write = mgsl_write,
+       .put_char = mgsl_put_char,
+       .flush_chars = mgsl_flush_chars,
+       .write_room = mgsl_write_room,
+       .chars_in_buffer = mgsl_chars_in_buffer,
+       .flush_buffer = mgsl_flush_buffer,
+       .ioctl = mgsl_ioctl,
+       .throttle = mgsl_throttle,
+       .unthrottle = mgsl_unthrottle,
+       .send_xchar = mgsl_send_xchar,
+       .break_ctl = mgsl_break,
+       .wait_until_sent = mgsl_wait_until_sent,
+       .set_termios = mgsl_set_termios,
+       .stop = mgsl_stop,
+       .start = mgsl_start,
+       .hangup = mgsl_hangup,
+       .tiocmget = tiocmget,
+       .tiocmset = tiocmset,
+       .get_icount = msgl_get_icount,
+       .proc_fops = &mgsl_proc_fops,
+};
+
+/*
+ * perform tty device initialization
+ */
+static int mgsl_init_tty(void)
+{
+       int rc;
+
+       serial_driver = alloc_tty_driver(128);
+       if (!serial_driver)
+               return -ENOMEM;
+       
+       serial_driver->owner = THIS_MODULE;
+       serial_driver->driver_name = "synclink";
+       serial_driver->name = "ttySL";
+       serial_driver->major = ttymajor;
+       serial_driver->minor_start = 64;
+       serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
+       serial_driver->subtype = SERIAL_TYPE_NORMAL;
+       serial_driver->init_termios = tty_std_termios;
+       serial_driver->init_termios.c_cflag =
+               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       serial_driver->init_termios.c_ispeed = 9600;
+       serial_driver->init_termios.c_ospeed = 9600;
+       serial_driver->flags = TTY_DRIVER_REAL_RAW;
+       tty_set_operations(serial_driver, &mgsl_ops);
+       if ((rc = tty_register_driver(serial_driver)) < 0) {
+               printk("%s(%d):Couldn't register serial driver\n",
+                       __FILE__,__LINE__);
+               put_tty_driver(serial_driver);
+               serial_driver = NULL;
+               return rc;
+       }
+                       
+       printk("%s %s, tty major#%d\n",
+               driver_name, driver_version,
+               serial_driver->major);
+       return 0;
+}
+
+/* enumerate user specified ISA adapters
+ */
+static void mgsl_enum_isa_devices(void)
+{
+       struct mgsl_struct *info;
+       int i;
+               
+       /* Check for user specified ISA devices */
+       
+       for (i=0 ;(i < MAX_ISA_DEVICES) && io[i] && irq[i]; i++){
+               if ( debug_level >= DEBUG_LEVEL_INFO )
+                       printk("ISA device specified io=%04X,irq=%d,dma=%d\n",
+                               io[i], irq[i], dma[i] );
+               
+               info = mgsl_allocate_device();
+               if ( !info ) {
+                       /* error allocating device instance data */
+                       if ( debug_level >= DEBUG_LEVEL_ERROR )
+                               printk( "can't allocate device instance data.\n");
+                       continue;
+               }
+               
+               /* Copy user configuration info to device instance data */
+               info->io_base = (unsigned int)io[i];
+               info->irq_level = (unsigned int)irq[i];
+               info->irq_level = irq_canonicalize(info->irq_level);
+               info->dma_level = (unsigned int)dma[i];
+               info->bus_type = MGSL_BUS_TYPE_ISA;
+               info->io_addr_size = 16;
+               info->irq_flags = 0;
+               
+               mgsl_add_device( info );
+       }
+}
+
+static void synclink_cleanup(void)
+{
+       int rc;
+       struct mgsl_struct *info;
+       struct mgsl_struct *tmp;
+
+       printk("Unloading %s: %s\n", driver_name, driver_version);
+
+       if (serial_driver) {
+               if ((rc = tty_unregister_driver(serial_driver)))
+                       printk("%s(%d) failed to unregister tty driver err=%d\n",
+                              __FILE__,__LINE__,rc);
+               put_tty_driver(serial_driver);
+       }
+
+       info = mgsl_device_list;
+       while(info) {
+#if SYNCLINK_GENERIC_HDLC
+               hdlcdev_exit(info);
+#endif
+               mgsl_release_resources(info);
+               tmp = info;
+               info = info->next_device;
+               kfree(tmp);
+       }
+       
+       if (pci_registered)
+               pci_unregister_driver(&synclink_pci_driver);
+}
+
+static int __init synclink_init(void)
+{
+       int rc;
+
+       if (break_on_load) {
+               mgsl_get_text_ptr();
+               BREAKPOINT();
+       }
+
+       printk("%s %s\n", driver_name, driver_version);
+
+       mgsl_enum_isa_devices();
+       if ((rc = pci_register_driver(&synclink_pci_driver)) < 0)
+               printk("%s:failed to register PCI driver, error=%d\n",__FILE__,rc);
+       else
+               pci_registered = true;
+
+       if ((rc = mgsl_init_tty()) < 0)
+               goto error;
+
+       return 0;
+
+error:
+       synclink_cleanup();
+       return rc;
+}
+
+static void __exit synclink_exit(void)
+{
+       synclink_cleanup();
+}
+
+module_init(synclink_init);
+module_exit(synclink_exit);
+
+/*
+ * usc_RTCmd()
+ *
+ * Issue a USC Receive/Transmit command to the
+ * Channel Command/Address Register (CCAR).
+ *
+ * Notes:
+ *
+ *    The command is encoded in the most significant 5 bits <15..11>
+ *    of the CCAR value. Bits <10..7> of the CCAR must be preserved
+ *    and Bits <6..0> must be written as zeros.
+ *
+ * Arguments:
+ *
+ *    info   pointer to device information structure
+ *    Cmd    command mask (use symbolic macros)
+ *
+ * Return Value:
+ *
+ *    None
+ */
+static void usc_RTCmd( struct mgsl_struct *info, u16 Cmd )
+{
+       /* output command to CCAR in bits <15..11> */
+       /* preserve bits <10..7>, bits <6..0> must be zero */
+
+       outw( Cmd + info->loopback_bits, info->io_base + CCAR );
+
+       /* Read to flush write to CCAR */
+       if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+               inw( info->io_base + CCAR );
+
+}      /* end of usc_RTCmd() */
+
+/*
+ * usc_DmaCmd()
+ *
+ *    Issue a DMA command to the DMA Command/Address Register (DCAR).
+ *
+ * Arguments:
+ *
+ *    info   pointer to device information structure
+ *    Cmd    DMA command mask (usc_DmaCmd_XX Macros)
+ *
+ * Return Value:
+ *
+ *       None
+ */
+static void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd )
+{
+       /* write command mask to DCAR */
+       outw( Cmd + info->mbre_bit, info->io_base );
+
+       /* Read to flush write to DCAR */
+       if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+               inw( info->io_base );
+
+}      /* end of usc_DmaCmd() */
+
+/*
+ * usc_OutDmaReg()
+ *
+ *    Write a 16-bit value to a USC DMA register
+ *
+ * Arguments:
+ *
+ *    info      pointer to device info structure
+ *    RegAddr   register address (number) for write
+ *    RegValue  16-bit value to write to register
+ *
+ * Return Value:
+ *
+ *    None
+ *
+ */
+static void usc_OutDmaReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue )
+{
+       /* Note: The DCAR is located at the adapter base address */
+       /* Note: must preserve state of BIT8 in DCAR */
+
+       outw( RegAddr + info->mbre_bit, info->io_base );
+       outw( RegValue, info->io_base );
+
+       /* Read to flush write to DCAR */
+       if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+               inw( info->io_base );
+
+}      /* end of usc_OutDmaReg() */
+/*
+ * usc_InDmaReg()
+ *
+ *    Read a 16-bit value from a DMA register
+ *
+ * Arguments:
+ *
+ *    info     pointer to device info structure
+ *    RegAddr  register address (number) to read from
+ *
+ * Return Value:
+ *
+ *    The 16-bit value read from register
+ *
+ */
+static u16 usc_InDmaReg( struct mgsl_struct *info, u16 RegAddr )
+{
+       /* Note: The DCAR is located at the adapter base address */
+       /* Note: must preserve state of BIT8 in DCAR */
+
+       outw( RegAddr + info->mbre_bit, info->io_base );
+       return inw( info->io_base );
+
+}      /* end of usc_InDmaReg() */
+
+/*
+ *
+ * usc_OutReg()
+ *
+ *    Write a 16-bit value to a USC serial channel register 
+ *
+ * Arguments:
+ *
+ *    info      pointer to device info structure
+ *    RegAddr   register address (number) to write to
+ *    RegValue  16-bit value to write to register
+ *
+ * Return Value:
+ *
+ *    None
+ *
+ */
+static void usc_OutReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue )
+{
+       outw( RegAddr + info->loopback_bits, info->io_base + CCAR );
+       outw( RegValue, info->io_base + CCAR );
+
+       /* Read to flush write to CCAR */
+       if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+               inw( info->io_base + CCAR );
+
+}      /* end of usc_OutReg() */
+
+/*
+ * usc_InReg()
+ *
+ *    Reads a 16-bit value from a USC serial channel register
+ *
+ * Arguments:
+ *
+ *    info       pointer to device extension
+ *    RegAddr    register address (number) to read from
+ *
+ * Return Value:
+ *
+ *    16-bit value read from register
+ */
+static u16 usc_InReg( struct mgsl_struct *info, u16 RegAddr )
+{
+       outw( RegAddr + info->loopback_bits, info->io_base + CCAR );
+       return inw( info->io_base + CCAR );
+
+}      /* end of usc_InReg() */
+
+/* usc_set_sdlc_mode()
+ *
+ *    Set up the adapter for SDLC DMA communications.
+ *
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       NONE
+ */
+static void usc_set_sdlc_mode( struct mgsl_struct *info )
+{
+       u16 RegValue;
+       bool PreSL1660;
+       
+       /*
+        * determine if the IUSC on the adapter is pre-SL1660. If
+        * not, take advantage of the UnderWait feature of more
+        * modern chips. If an underrun occurs and this bit is set,
+        * the transmitter will idle the programmed idle pattern
+        * until the driver has time to service the underrun. Otherwise,
+        * the dma controller may get the cycles previously requested
+        * and begin transmitting queued tx data.
+        */
+       usc_OutReg(info,TMCR,0x1f);
+       RegValue=usc_InReg(info,TMDR);
+       PreSL1660 = (RegValue == IUSC_PRE_SL1660);
+
+       if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
+       {
+          /*
+          ** Channel Mode Register (CMR)
+          **
+          ** <15..14>    10    Tx Sub Modes, Send Flag on Underrun
+          ** <13>        0     0 = Transmit Disabled (initially)
+          ** <12>        0     1 = Consecutive Idles share common 0
+          ** <11..8>     1110  Transmitter Mode = HDLC/SDLC Loop
+          ** <7..4>      0000  Rx Sub Modes, addr/ctrl field handling
+          ** <3..0>      0110  Receiver Mode = HDLC/SDLC
+          **
+          ** 1000 1110 0000 0110 = 0x8e06
+          */
+          RegValue = 0x8e06;
+          /*--------------------------------------------------
+           * ignore user options for UnderRun Actions and
+           * preambles
+           *--------------------------------------------------*/
+       }
+       else
+       {       
+               /* Channel mode Register (CMR)
+                *
+                * <15..14>  00    Tx Sub modes, Underrun Action
+                * <13>      0     1 = Send Preamble before opening flag
+                * <12>      0     1 = Consecutive Idles share common 0
+                * <11..8>   0110  Transmitter mode = HDLC/SDLC
+                * <7..4>    0000  Rx Sub modes, addr/ctrl field handling
+                * <3..0>    0110  Receiver mode = HDLC/SDLC
+                *
+                * 0000 0110 0000 0110 = 0x0606
+                */
+               if (info->params.mode == MGSL_MODE_RAW) {
+                       RegValue = 0x0001;              /* Set Receive mode = external sync */
+
+                       usc_OutReg( info, IOCR,         /* Set IOCR DCD is RxSync Detect Input */
+                               (unsigned short)((usc_InReg(info, IOCR) & ~(BIT13|BIT12)) | BIT12));
+
+                       /*
+                        * TxSubMode:
+                        *      CMR <15>                0       Don't send CRC on Tx Underrun
+                        *      CMR <14>                x       undefined
+                        *      CMR <13>                0       Send preamble before openning sync
+                        *      CMR <12>                0       Send 8-bit syncs, 1=send Syncs per TxLength
+                        *
+                        * TxMode:
+                        *      CMR <11-8)      0100    MonoSync
+                        *
+                        *      0x00 0100 xxxx xxxx  04xx
+                        */
+                       RegValue |= 0x0400;
+               }
+               else {
+
+               RegValue = 0x0606;
+
+               if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 )
+                       RegValue |= BIT14;
+               else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG )
+                       RegValue |= BIT15;
+               else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC )
+                       RegValue |= BIT15 + BIT14;
+               }
+
+               if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE )
+                       RegValue |= BIT13;
+       }
+
+       if ( info->params.mode == MGSL_MODE_HDLC &&
+               (info->params.flags & HDLC_FLAG_SHARE_ZERO) )
+               RegValue |= BIT12;
+
+       if ( info->params.addr_filter != 0xff )
+       {
+               /* set up receive address filtering */
+               usc_OutReg( info, RSR, info->params.addr_filter );
+               RegValue |= BIT4;
+       }
+
+       usc_OutReg( info, CMR, RegValue );
+       info->cmr_value = RegValue;
+
+       /* Receiver mode Register (RMR)
+        *
+        * <15..13>  000    encoding
+        * <12..11>  00     FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1)
+        * <10>      1      1 = Set CRC to all 1s (use for SDLC/HDLC)
+        * <9>       0      1 = Include Receive chars in CRC
+        * <8>       1      1 = Use Abort/PE bit as abort indicator
+        * <7..6>    00     Even parity
+        * <5>       0      parity disabled
+        * <4..2>    000    Receive Char Length = 8 bits
+        * <1..0>    00     Disable Receiver
+        *
+        * 0000 0101 0000 0000 = 0x0500
+        */
+
+       RegValue = 0x0500;
+
+       switch ( info->params.encoding ) {
+       case HDLC_ENCODING_NRZB:               RegValue |= BIT13; break;
+       case HDLC_ENCODING_NRZI_MARK:          RegValue |= BIT14; break;
+       case HDLC_ENCODING_NRZI_SPACE:         RegValue |= BIT14 + BIT13; break;
+       case HDLC_ENCODING_BIPHASE_MARK:       RegValue |= BIT15; break;
+       case HDLC_ENCODING_BIPHASE_SPACE:      RegValue |= BIT15 + BIT13; break;
+       case HDLC_ENCODING_BIPHASE_LEVEL:      RegValue |= BIT15 + BIT14; break;
+       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break;
+       }
+
+       if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT )
+               RegValue |= BIT9;
+       else if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_32_CCITT )
+               RegValue |= ( BIT12 | BIT10 | BIT9 );
+
+       usc_OutReg( info, RMR, RegValue );
+
+       /* Set the Receive count Limit Register (RCLR) to 0xffff. */
+       /* When an opening flag of an SDLC frame is recognized the */
+       /* Receive Character count (RCC) is loaded with the value in */
+       /* RCLR. The RCC is decremented for each received byte.  The */
+       /* value of RCC is stored after the closing flag of the frame */
+       /* allowing the frame size to be computed. */
+
+       usc_OutReg( info, RCLR, RCLRVALUE );
+
+       usc_RCmd( info, RCmd_SelectRicrdma_level );
+
+       /* Receive Interrupt Control Register (RICR)
+        *
+        * <15..8>      ?       RxFIFO DMA Request Level
+        * <7>          0       Exited Hunt IA (Interrupt Arm)
+        * <6>          0       Idle Received IA
+        * <5>          0       Break/Abort IA
+        * <4>          0       Rx Bound IA
+        * <3>          1       Queued status reflects oldest 2 bytes in FIFO
+        * <2>          0       Abort/PE IA
+        * <1>          1       Rx Overrun IA
+        * <0>          0       Select TC0 value for readback
+        *
+        *      0000 0000 0000 1000 = 0x000a
+        */
+
+       /* Carry over the Exit Hunt and Idle Received bits */
+       /* in case they have been armed by usc_ArmEvents.   */
+
+       RegValue = usc_InReg( info, RICR ) & 0xc0;
+
+       if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+               usc_OutReg( info, RICR, (u16)(0x030a | RegValue) );
+       else
+               usc_OutReg( info, RICR, (u16)(0x140a | RegValue) );
+
+       /* Unlatch all Rx status bits and clear Rx status IRQ Pending */
+
+       usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
+       usc_ClearIrqPendingBits( info, RECEIVE_STATUS );
+
+       /* Transmit mode Register (TMR)
+        *      
+        * <15..13>     000     encoding
+        * <12..11>     00      FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1)
+        * <10>         1       1 = Start CRC as all 1s (use for SDLC/HDLC)
+        * <9>          0       1 = Tx CRC Enabled
+        * <8>          0       1 = Append CRC to end of transmit frame
+        * <7..6>       00      Transmit parity Even
+        * <5>          0       Transmit parity Disabled
+        * <4..2>       000     Tx Char Length = 8 bits
+        * <1..0>       00      Disable Transmitter
+        *
+        *      0000 0100 0000 0000 = 0x0400
+        */
+
+       RegValue = 0x0400;
+
+       switch ( info->params.encoding ) {
+       case HDLC_ENCODING_NRZB:               RegValue |= BIT13; break;
+       case HDLC_ENCODING_NRZI_MARK:          RegValue |= BIT14; break;
+       case HDLC_ENCODING_NRZI_SPACE:         RegValue |= BIT14 + BIT13; break;
+       case HDLC_ENCODING_BIPHASE_MARK:       RegValue |= BIT15; break;
+       case HDLC_ENCODING_BIPHASE_SPACE:      RegValue |= BIT15 + BIT13; break;
+       case HDLC_ENCODING_BIPHASE_LEVEL:      RegValue |= BIT15 + BIT14; break;
+       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break;
+       }
+
+       if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT )
+               RegValue |= BIT9 + BIT8;
+       else if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_32_CCITT )
+               RegValue |= ( BIT12 | BIT10 | BIT9 | BIT8);
+
+       usc_OutReg( info, TMR, RegValue );
+
+       usc_set_txidle( info );
+
+
+       usc_TCmd( info, TCmd_SelectTicrdma_level );
+
+       /* Transmit Interrupt Control Register (TICR)
+        *
+        * <15..8>      ?       Transmit FIFO DMA Level
+        * <7>          0       Present IA (Interrupt Arm)
+        * <6>          0       Idle Sent IA
+        * <5>          1       Abort Sent IA
+        * <4>          1       EOF/EOM Sent IA
+        * <3>          0       CRC Sent IA
+        * <2>          1       1 = Wait for SW Trigger to Start Frame
+        * <1>          1       Tx Underrun IA
+        * <0>          0       TC0 constant on read back
+        *
+        *      0000 0000 0011 0110 = 0x0036
+        */
+
+       if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+               usc_OutReg( info, TICR, 0x0736 );
+       else                                                            
+               usc_OutReg( info, TICR, 0x1436 );
+
+       usc_UnlatchTxstatusBits( info, TXSTATUS_ALL );
+       usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
+
+       /*
+       ** Transmit Command/Status Register (TCSR)
+       **
+       ** <15..12>     0000    TCmd
+       ** <11>         0/1     UnderWait
+       ** <10..08>     000     TxIdle
+       ** <7>          x       PreSent
+       ** <6>          x       IdleSent
+       ** <5>          x       AbortSent
+       ** <4>          x       EOF/EOM Sent
+       ** <3>          x       CRC Sent
+       ** <2>          x       All Sent
+       ** <1>          x       TxUnder
+       ** <0>          x       TxEmpty
+       ** 
+       ** 0000 0000 0000 0000 = 0x0000
+       */
+       info->tcsr_value = 0;
+
+       if ( !PreSL1660 )
+               info->tcsr_value |= TCSR_UNDERWAIT;
+               
+       usc_OutReg( info, TCSR, info->tcsr_value );
+
+       /* Clock mode Control Register (CMCR)
+        *
+        * <15..14>     00      counter 1 Source = Disabled
+        * <13..12>     00      counter 0 Source = Disabled
+        * <11..10>     11      BRG1 Input is TxC Pin
+        * <9..8>       11      BRG0 Input is TxC Pin
+        * <7..6>       01      DPLL Input is BRG1 Output
+        * <5..3>       XXX     TxCLK comes from Port 0
+        * <2..0>       XXX     RxCLK comes from Port 1
+        *
+        *      0000 1111 0111 0111 = 0x0f77
+        */
+
+       RegValue = 0x0f40;
+
+       if ( info->params.flags & HDLC_FLAG_RXC_DPLL )
+               RegValue |= 0x0003;     /* RxCLK from DPLL */
+       else if ( info->params.flags & HDLC_FLAG_RXC_BRG )
+               RegValue |= 0x0004;     /* RxCLK from BRG0 */
+       else if ( info->params.flags & HDLC_FLAG_RXC_TXCPIN)
+               RegValue |= 0x0006;     /* RxCLK from TXC Input */
+       else
+               RegValue |= 0x0007;     /* RxCLK from Port1 */
+
+       if ( info->params.flags & HDLC_FLAG_TXC_DPLL )
+               RegValue |= 0x0018;     /* TxCLK from DPLL */
+       else if ( info->params.flags & HDLC_FLAG_TXC_BRG )
+               RegValue |= 0x0020;     /* TxCLK from BRG0 */
+       else if ( info->params.flags & HDLC_FLAG_TXC_RXCPIN)
+               RegValue |= 0x0038;     /* RxCLK from TXC Input */
+       else
+               RegValue |= 0x0030;     /* TxCLK from Port0 */
+
+       usc_OutReg( info, CMCR, RegValue );
+
+
+       /* Hardware Configuration Register (HCR)
+        *
+        * <15..14>     00      CTR0 Divisor:00=32,01=16,10=8,11=4
+        * <13>         0       CTR1DSel:0=CTR0Div determines CTR0Div
+        * <12>         0       CVOK:0=report code violation in biphase
+        * <11..10>     00      DPLL Divisor:00=32,01=16,10=8,11=4
+        * <9..8>       XX      DPLL mode:00=disable,01=NRZ,10=Biphase,11=Biphase Level
+        * <7..6>       00      reserved
+        * <5>          0       BRG1 mode:0=continuous,1=single cycle
+        * <4>          X       BRG1 Enable
+        * <3..2>       00      reserved
+        * <1>          0       BRG0 mode:0=continuous,1=single cycle
+        * <0>          0       BRG0 Enable
+        */
+
+       RegValue = 0x0000;
+
+       if ( info->params.flags & (HDLC_FLAG_RXC_DPLL + HDLC_FLAG_TXC_DPLL) ) {
+               u32 XtalSpeed;
+               u32 DpllDivisor;
+               u16 Tc;
+
+               /*  DPLL is enabled. Use BRG1 to provide continuous reference clock  */
+               /*  for DPLL. DPLL mode in HCR is dependent on the encoding used. */
+
+               if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+                       XtalSpeed = 11059200;
+               else
+                       XtalSpeed = 14745600;
+
+               if ( info->params.flags & HDLC_FLAG_DPLL_DIV16 ) {
+                       DpllDivisor = 16;
+                       RegValue |= BIT10;
+               }
+               else if ( info->params.flags & HDLC_FLAG_DPLL_DIV8 ) {
+                       DpllDivisor = 8;
+                       RegValue |= BIT11;
+               }
+               else
+                       DpllDivisor = 32;
+
+               /*  Tc = (Xtal/Speed) - 1 */
+               /*  If twice the remainder of (Xtal/Speed) is greater than Speed */
+               /*  then rounding up gives a more precise time constant. Instead */
+               /*  of rounding up and then subtracting 1 we just don't subtract */
+               /*  the one in this case. */
+
+               /*--------------------------------------------------
+                * ejz: for DPLL mode, application should use the
+                * same clock speed as the partner system, even 
+                * though clocking is derived from the input RxData.
+                * In case the user uses a 0 for the clock speed,
+                * default to 0xffffffff and don't try to divide by
+                * zero
+                *--------------------------------------------------*/
+               if ( info->params.clock_speed )
+               {
+                       Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed);
+                       if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2)
+                              / info->params.clock_speed) )
+                               Tc--;
+               }
+               else
+                       Tc = -1;
+                                 
+
+               /* Write 16-bit Time Constant for BRG1 */
+               usc_OutReg( info, TC1R, Tc );
+
+               RegValue |= BIT4;               /* enable BRG1 */
+
+               switch ( info->params.encoding ) {
+               case HDLC_ENCODING_NRZ:
+               case HDLC_ENCODING_NRZB:
+               case HDLC_ENCODING_NRZI_MARK:
+               case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT8; break;
+               case HDLC_ENCODING_BIPHASE_MARK:
+               case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT9; break;
+               case HDLC_ENCODING_BIPHASE_LEVEL:
+               case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT9 + BIT8; break;
+               }
+       }
+
+       usc_OutReg( info, HCR, RegValue );
+
+
+       /* Channel Control/status Register (CCSR)
+        *
+        * <15>         X       RCC FIFO Overflow status (RO)
+        * <14>         X       RCC FIFO Not Empty status (RO)
+        * <13>         0       1 = Clear RCC FIFO (WO)
+        * <12>         X       DPLL Sync (RW)
+        * <11>         X       DPLL 2 Missed Clocks status (RO)
+        * <10>         X       DPLL 1 Missed Clock status (RO)
+        * <9..8>       00      DPLL Resync on rising and falling edges (RW)
+        * <7>          X       SDLC Loop On status (RO)
+        * <6>          X       SDLC Loop Send status (RO)
+        * <5>          1       Bypass counters for TxClk and RxClk (RW)
+        * <4..2>       000     Last Char of SDLC frame has 8 bits (RW)
+        * <1..0>       00      reserved
+        *
+        *      0000 0000 0010 0000 = 0x0020
+        */
+
+       usc_OutReg( info, CCSR, 0x1020 );
+
+
+       if ( info->params.flags & HDLC_FLAG_AUTO_CTS ) {
+               usc_OutReg( info, SICR,
+                           (u16)(usc_InReg(info,SICR) | SICR_CTS_INACTIVE) );
+       }
+       
+
+       /* enable Master Interrupt Enable bit (MIE) */
+       usc_EnableMasterIrqBit( info );
+
+       usc_ClearIrqPendingBits( info, RECEIVE_STATUS + RECEIVE_DATA +
+                               TRANSMIT_STATUS + TRANSMIT_DATA + MISC);
+
+       /* arm RCC underflow interrupt */
+       usc_OutReg(info, SICR, (u16)(usc_InReg(info,SICR) | BIT3));
+       usc_EnableInterrupts(info, MISC);
+
+       info->mbre_bit = 0;
+       outw( 0, info->io_base );                       /* clear Master Bus Enable (DCAR) */
+       usc_DmaCmd( info, DmaCmd_ResetAllChannels );    /* disable both DMA channels */
+       info->mbre_bit = BIT8;
+       outw( BIT8, info->io_base );                    /* set Master Bus Enable (DCAR) */
+
+       if (info->bus_type == MGSL_BUS_TYPE_ISA) {
+               /* Enable DMAEN (Port 7, Bit 14) */
+               /* This connects the DMA request signal to the ISA bus */
+               usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) & ~BIT14));
+       }
+
+       /* DMA Control Register (DCR)
+        *
+        * <15..14>     10      Priority mode = Alternating Tx/Rx
+        *              01      Rx has priority
+        *              00      Tx has priority
+        *
+        * <13>         1       Enable Priority Preempt per DCR<15..14>
+        *                      (WARNING DCR<11..10> must be 00 when this is 1)
+        *              0       Choose activate channel per DCR<11..10>
+        *
+        * <12>         0       Little Endian for Array/List
+        * <11..10>     00      Both Channels can use each bus grant
+        * <9..6>       0000    reserved
+        * <5>          0       7 CLK - Minimum Bus Re-request Interval
+        * <4>          0       1 = drive D/C and S/D pins
+        * <3>          1       1 = Add one wait state to all DMA cycles.
+        * <2>          0       1 = Strobe /UAS on every transfer.
+        * <1..0>       11      Addr incrementing only affects LS24 bits
+        *
+        *      0110 0000 0000 1011 = 0x600b
+        */
+
+       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+               /* PCI adapter does not need DMA wait state */
+               usc_OutDmaReg( info, DCR, 0xa00b );
+       }
+       else
+               usc_OutDmaReg( info, DCR, 0x800b );
+
+
+       /* Receive DMA mode Register (RDMR)
+        *
+        * <15..14>     11      DMA mode = Linked List Buffer mode
+        * <13>         1       RSBinA/L = store Rx status Block in Arrary/List entry
+        * <12>         1       Clear count of List Entry after fetching
+        * <11..10>     00      Address mode = Increment
+        * <9>          1       Terminate Buffer on RxBound
+        * <8>          0       Bus Width = 16bits
+        * <7..0>       ?       status Bits (write as 0s)
+        *
+        * 1111 0010 0000 0000 = 0xf200
+        */
+
+       usc_OutDmaReg( info, RDMR, 0xf200 );
+
+
+       /* Transmit DMA mode Register (TDMR)
+        *
+        * <15..14>     11      DMA mode = Linked List Buffer mode
+        * <13>         1       TCBinA/L = fetch Tx Control Block from List entry
+        * <12>         1       Clear count of List Entry after fetching
+        * <11..10>     00      Address mode = Increment
+        * <9>          1       Terminate Buffer on end of frame
+        * <8>          0       Bus Width = 16bits
+        * <7..0>       ?       status Bits (Read Only so write as 0)
+        *
+        *      1111 0010 0000 0000 = 0xf200
+        */
+
+       usc_OutDmaReg( info, TDMR, 0xf200 );
+
+
+       /* DMA Interrupt Control Register (DICR)
+        *
+        * <15>         1       DMA Interrupt Enable
+        * <14>         0       1 = Disable IEO from USC
+        * <13>         0       1 = Don't provide vector during IntAck
+        * <12>         1       1 = Include status in Vector
+        * <10..2>      0       reserved, Must be 0s
+        * <1>          0       1 = Rx DMA Interrupt Enabled
+        * <0>          0       1 = Tx DMA Interrupt Enabled
+        *
+        *      1001 0000 0000 0000 = 0x9000
+        */
+
+       usc_OutDmaReg( info, DICR, 0x9000 );
+
+       usc_InDmaReg( info, RDMR );             /* clear pending receive DMA IRQ bits */
+       usc_InDmaReg( info, TDMR );             /* clear pending transmit DMA IRQ bits */
+       usc_OutDmaReg( info, CDIR, 0x0303 );    /* clear IUS and Pending for Tx and Rx */
+
+       /* Channel Control Register (CCR)
+        *
+        * <15..14>     10      Use 32-bit Tx Control Blocks (TCBs)
+        * <13>         0       Trigger Tx on SW Command Disabled
+        * <12>         0       Flag Preamble Disabled
+        * <11..10>     00      Preamble Length
+        * <9..8>       00      Preamble Pattern
+        * <7..6>       10      Use 32-bit Rx status Blocks (RSBs)
+        * <5>          0       Trigger Rx on SW Command Disabled
+        * <4..0>       0       reserved
+        *
+        *      1000 0000 1000 0000 = 0x8080
+        */
+
+       RegValue = 0x8080;
+
+       switch ( info->params.preamble_length ) {
+       case HDLC_PREAMBLE_LENGTH_16BITS: RegValue |= BIT10; break;
+       case HDLC_PREAMBLE_LENGTH_32BITS: RegValue |= BIT11; break;
+       case HDLC_PREAMBLE_LENGTH_64BITS: RegValue |= BIT11 + BIT10; break;
+       }
+
+       switch ( info->params.preamble ) {
+       case HDLC_PREAMBLE_PATTERN_FLAGS: RegValue |= BIT8 + BIT12; break;
+       case HDLC_PREAMBLE_PATTERN_ONES:  RegValue |= BIT8; break;
+       case HDLC_PREAMBLE_PATTERN_10:    RegValue |= BIT9; break;
+       case HDLC_PREAMBLE_PATTERN_01:    RegValue |= BIT9 + BIT8; break;
+       }
+
+       usc_OutReg( info, CCR, RegValue );
+
+
+       /*
+        * Burst/Dwell Control Register
+        *
+        * <15..8>      0x20    Maximum number of transfers per bus grant
+        * <7..0>       0x00    Maximum number of clock cycles per bus grant
+        */
+
+       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+               /* don't limit bus occupancy on PCI adapter */
+               usc_OutDmaReg( info, BDCR, 0x0000 );
+       }
+       else
+               usc_OutDmaReg( info, BDCR, 0x2000 );
+
+       usc_stop_transmitter(info);
+       usc_stop_receiver(info);
+       
+}      /* end of usc_set_sdlc_mode() */
+
+/* usc_enable_loopback()
+ *
+ * Set the 16C32 for internal loopback mode.
+ * The TxCLK and RxCLK signals are generated from the BRG0 and
+ * the TxD is looped back to the RxD internally.
+ *
+ * Arguments:          info    pointer to device instance data
+ *                     enable  1 = enable loopback, 0 = disable
+ * Return Value:       None
+ */
+static void usc_enable_loopback(struct mgsl_struct *info, int enable)
+{
+       if (enable) {
+               /* blank external TXD output */
+               usc_OutReg(info,IOCR,usc_InReg(info,IOCR) | (BIT7+BIT6));
+       
+               /* Clock mode Control Register (CMCR)
+                *
+                * <15..14>     00      counter 1 Disabled
+                * <13..12>     00      counter 0 Disabled
+                * <11..10>     11      BRG1 Input is TxC Pin
+                * <9..8>       11      BRG0 Input is TxC Pin
+                * <7..6>       01      DPLL Input is BRG1 Output
+                * <5..3>       100     TxCLK comes from BRG0
+                * <2..0>       100     RxCLK comes from BRG0
+                *
+                * 0000 1111 0110 0100 = 0x0f64
+                */
+
+               usc_OutReg( info, CMCR, 0x0f64 );
+
+               /* Write 16-bit Time Constant for BRG0 */
+               /* use clock speed if available, otherwise use 8 for diagnostics */
+               if (info->params.clock_speed) {
+                       if (info->bus_type == MGSL_BUS_TYPE_PCI)
+                               usc_OutReg(info, TC0R, (u16)((11059200/info->params.clock_speed)-1));
+                       else
+                               usc_OutReg(info, TC0R, (u16)((14745600/info->params.clock_speed)-1));
+               } else
+                       usc_OutReg(info, TC0R, (u16)8);
+
+               /* Hardware Configuration Register (HCR) Clear Bit 1, BRG0
+                  mode = Continuous Set Bit 0 to enable BRG0.  */
+               usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) );
+
+               /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */
+               usc_OutReg(info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004));
+
+               /* set Internal Data loopback mode */
+               info->loopback_bits = 0x300;
+               outw( 0x0300, info->io_base + CCAR );
+       } else {
+               /* enable external TXD output */
+               usc_OutReg(info,IOCR,usc_InReg(info,IOCR) & ~(BIT7+BIT6));
+       
+               /* clear Internal Data loopback mode */
+               info->loopback_bits = 0;
+               outw( 0,info->io_base + CCAR );
+       }
+       
+}      /* end of usc_enable_loopback() */
+
+/* usc_enable_aux_clock()
+ *
+ * Enabled the AUX clock output at the specified frequency.
+ *
+ * Arguments:
+ *
+ *     info            pointer to device extension
+ *     data_rate       data rate of clock in bits per second
+ *                     A data rate of 0 disables the AUX clock.
+ *
+ * Return Value:       None
+ */
+static void usc_enable_aux_clock( struct mgsl_struct *info, u32 data_rate )
+{
+       u32 XtalSpeed;
+       u16 Tc;
+
+       if ( data_rate ) {
+               if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+                       XtalSpeed = 11059200;
+               else
+                       XtalSpeed = 14745600;
+
+
+               /* Tc = (Xtal/Speed) - 1 */
+               /* If twice the remainder of (Xtal/Speed) is greater than Speed */
+               /* then rounding up gives a more precise time constant. Instead */
+               /* of rounding up and then subtracting 1 we just don't subtract */
+               /* the one in this case. */
+
+
+               Tc = (u16)(XtalSpeed/data_rate);
+               if ( !(((XtalSpeed % data_rate) * 2) / data_rate) )
+                       Tc--;
+
+               /* Write 16-bit Time Constant for BRG0 */
+               usc_OutReg( info, TC0R, Tc );
+
+               /*
+                * Hardware Configuration Register (HCR)
+                * Clear Bit 1, BRG0 mode = Continuous
+                * Set Bit 0 to enable BRG0.
+                */
+
+               usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) );
+
+               /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */
+               usc_OutReg( info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) );
+       } else {
+               /* data rate == 0 so turn off BRG0 */
+               usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) );
+       }
+
+}      /* end of usc_enable_aux_clock() */
+
+/*
+ *
+ * usc_process_rxoverrun_sync()
+ *
+ *             This function processes a receive overrun by resetting the
+ *             receive DMA buffers and issuing a Purge Rx FIFO command
+ *             to allow the receiver to continue receiving.
+ *
+ * Arguments:
+ *
+ *     info            pointer to device extension
+ *
+ * Return Value: None
+ */
+static void usc_process_rxoverrun_sync( struct mgsl_struct *info )
+{
+       int start_index;
+       int end_index;
+       int frame_start_index;
+       bool start_of_frame_found = false;
+       bool end_of_frame_found = false;
+       bool reprogram_dma = false;
+
+       DMABUFFERENTRY *buffer_list = info->rx_buffer_list;
+       u32 phys_addr;
+
+       usc_DmaCmd( info, DmaCmd_PauseRxChannel );
+       usc_RCmd( info, RCmd_EnterHuntmode );
+       usc_RTCmd( info, RTCmd_PurgeRxFifo );
+
+       /* CurrentRxBuffer points to the 1st buffer of the next */
+       /* possibly available receive frame. */
+       
+       frame_start_index = start_index = end_index = info->current_rx_buffer;
+
+       /* Search for an unfinished string of buffers. This means */
+       /* that a receive frame started (at least one buffer with */
+       /* count set to zero) but there is no terminiting buffer */
+       /* (status set to non-zero). */
+
+       while( !buffer_list[end_index].count )
+       {
+               /* Count field has been reset to zero by 16C32. */
+               /* This buffer is currently in use. */
+
+               if ( !start_of_frame_found )
+               {
+                       start_of_frame_found = true;
+                       frame_start_index = end_index;
+                       end_of_frame_found = false;
+               }
+
+               if ( buffer_list[end_index].status )
+               {
+                       /* Status field has been set by 16C32. */
+                       /* This is the last buffer of a received frame. */
+
+                       /* We want to leave the buffers for this frame intact. */
+                       /* Move on to next possible frame. */
+
+                       start_of_frame_found = false;
+                       end_of_frame_found = true;
+               }
+
+               /* advance to next buffer entry in linked list */
+               end_index++;
+               if ( end_index == info->rx_buffer_count )
+                       end_index = 0;
+
+               if ( start_index == end_index )
+               {
+                       /* The entire list has been searched with all Counts == 0 and */
+                       /* all Status == 0. The receive buffers are */
+                       /* completely screwed, reset all receive buffers! */
+                       mgsl_reset_rx_dma_buffers( info );
+                       frame_start_index = 0;
+                       start_of_frame_found = false;
+                       reprogram_dma = true;
+                       break;
+               }
+       }
+
+       if ( start_of_frame_found && !end_of_frame_found )
+       {
+               /* There is an unfinished string of receive DMA buffers */
+               /* as a result of the receiver overrun. */
+
+               /* Reset the buffers for the unfinished frame */
+               /* and reprogram the receive DMA controller to start */
+               /* at the 1st buffer of unfinished frame. */
+
+               start_index = frame_start_index;
+
+               do
+               {
+                       *((unsigned long *)&(info->rx_buffer_list[start_index++].count)) = DMABUFFERSIZE;
+
+                       /* Adjust index for wrap around. */
+                       if ( start_index == info->rx_buffer_count )
+                               start_index = 0;
+
+               } while( start_index != end_index );
+
+               reprogram_dma = true;
+       }
+
+       if ( reprogram_dma )
+       {
+               usc_UnlatchRxstatusBits(info,RXSTATUS_ALL);
+               usc_ClearIrqPendingBits(info, RECEIVE_DATA|RECEIVE_STATUS);
+               usc_UnlatchRxstatusBits(info, RECEIVE_DATA|RECEIVE_STATUS);
+               
+               usc_EnableReceiver(info,DISABLE_UNCONDITIONAL);
+               
+               /* This empties the receive FIFO and loads the RCC with RCLR */
+               usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) );
+
+               /* program 16C32 with physical address of 1st DMA buffer entry */
+               phys_addr = info->rx_buffer_list[frame_start_index].phys_entry;
+               usc_OutDmaReg( info, NRARL, (u16)phys_addr );
+               usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) );
+
+               usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
+               usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS );
+               usc_EnableInterrupts( info, RECEIVE_STATUS );
+
+               /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */
+               /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */
+
+               usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 );
+               usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) );
+               usc_DmaCmd( info, DmaCmd_InitRxChannel );
+               if ( info->params.flags & HDLC_FLAG_AUTO_DCD )
+                       usc_EnableReceiver(info,ENABLE_AUTO_DCD);
+               else
+                       usc_EnableReceiver(info,ENABLE_UNCONDITIONAL);
+       }
+       else
+       {
+               /* This empties the receive FIFO and loads the RCC with RCLR */
+               usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) );
+               usc_RTCmd( info, RTCmd_PurgeRxFifo );
+       }
+
+}      /* end of usc_process_rxoverrun_sync() */
+
+/* usc_stop_receiver()
+ *
+ *     Disable USC receiver
+ *
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void usc_stop_receiver( struct mgsl_struct *info )
+{
+       if (debug_level >= DEBUG_LEVEL_ISR)
+               printk("%s(%d):usc_stop_receiver(%s)\n",
+                        __FILE__,__LINE__, info->device_name );
+                        
+       /* Disable receive DMA channel. */
+       /* This also disables receive DMA channel interrupts */
+       usc_DmaCmd( info, DmaCmd_ResetRxChannel );
+
+       usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
+       usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS );
+       usc_DisableInterrupts( info, RECEIVE_DATA + RECEIVE_STATUS );
+
+       usc_EnableReceiver(info,DISABLE_UNCONDITIONAL);
+
+       /* This empties the receive FIFO and loads the RCC with RCLR */
+       usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) );
+       usc_RTCmd( info, RTCmd_PurgeRxFifo );
+
+       info->rx_enabled = false;
+       info->rx_overflow = false;
+       info->rx_rcc_underrun = false;
+       
+}      /* end of stop_receiver() */
+
+/* usc_start_receiver()
+ *
+ *     Enable the USC receiver 
+ *
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void usc_start_receiver( struct mgsl_struct *info )
+{
+       u32 phys_addr;
+       
+       if (debug_level >= DEBUG_LEVEL_ISR)
+               printk("%s(%d):usc_start_receiver(%s)\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       mgsl_reset_rx_dma_buffers( info );
+       usc_stop_receiver( info );
+
+       usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) );
+       usc_RTCmd( info, RTCmd_PurgeRxFifo );
+
+       if ( info->params.mode == MGSL_MODE_HDLC ||
+               info->params.mode == MGSL_MODE_RAW ) {
+               /* DMA mode Transfers */
+               /* Program the DMA controller. */
+               /* Enable the DMA controller end of buffer interrupt. */
+
+               /* program 16C32 with physical address of 1st DMA buffer entry */
+               phys_addr = info->rx_buffer_list[0].phys_entry;
+               usc_OutDmaReg( info, NRARL, (u16)phys_addr );
+               usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) );
+
+               usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
+               usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS );
+               usc_EnableInterrupts( info, RECEIVE_STATUS );
+
+               /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */
+               /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */
+
+               usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 );
+               usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) );
+               usc_DmaCmd( info, DmaCmd_InitRxChannel );
+               if ( info->params.flags & HDLC_FLAG_AUTO_DCD )
+                       usc_EnableReceiver(info,ENABLE_AUTO_DCD);
+               else
+                       usc_EnableReceiver(info,ENABLE_UNCONDITIONAL);
+       } else {
+               usc_UnlatchRxstatusBits(info, RXSTATUS_ALL);
+               usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS);
+               usc_EnableInterrupts(info, RECEIVE_DATA);
+
+               usc_RTCmd( info, RTCmd_PurgeRxFifo );
+               usc_RCmd( info, RCmd_EnterHuntmode );
+
+               usc_EnableReceiver(info,ENABLE_UNCONDITIONAL);
+       }
+
+       usc_OutReg( info, CCSR, 0x1020 );
+
+       info->rx_enabled = true;
+
+}      /* end of usc_start_receiver() */
+
+/* usc_start_transmitter()
+ *
+ *     Enable the USC transmitter and send a transmit frame if
+ *     one is loaded in the DMA buffers.
+ *
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void usc_start_transmitter( struct mgsl_struct *info )
+{
+       u32 phys_addr;
+       unsigned int FrameSize;
+
+       if (debug_level >= DEBUG_LEVEL_ISR)
+               printk("%s(%d):usc_start_transmitter(%s)\n",
+                        __FILE__,__LINE__, info->device_name );
+                        
+       if ( info->xmit_cnt ) {
+
+               /* If auto RTS enabled and RTS is inactive, then assert */
+               /* RTS and set a flag indicating that the driver should */
+               /* negate RTS when the transmission completes. */
+
+               info->drop_rts_on_tx_done = false;
+
+               if ( info->params.flags & HDLC_FLAG_AUTO_RTS ) {
+                       usc_get_serial_signals( info );
+                       if ( !(info->serial_signals & SerialSignal_RTS) ) {
+                               info->serial_signals |= SerialSignal_RTS;
+                               usc_set_serial_signals( info );
+                               info->drop_rts_on_tx_done = true;
+                       }
+               }
+
+
+               if ( info->params.mode == MGSL_MODE_ASYNC ) {
+                       if ( !info->tx_active ) {
+                               usc_UnlatchTxstatusBits(info, TXSTATUS_ALL);
+                               usc_ClearIrqPendingBits(info, TRANSMIT_STATUS + TRANSMIT_DATA);
+                               usc_EnableInterrupts(info, TRANSMIT_DATA);
+                               usc_load_txfifo(info);
+                       }
+               } else {
+                       /* Disable transmit DMA controller while programming. */
+                       usc_DmaCmd( info, DmaCmd_ResetTxChannel );
+                       
+                       /* Transmit DMA buffer is loaded, so program USC */
+                       /* to send the frame contained in the buffers.   */
+
+                       FrameSize = info->tx_buffer_list[info->start_tx_dma_buffer].rcc;
+
+                       /* if operating in Raw sync mode, reset the rcc component
+                        * of the tx dma buffer entry, otherwise, the serial controller
+                        * will send a closing sync char after this count.
+                        */
+                       if ( info->params.mode == MGSL_MODE_RAW )
+                               info->tx_buffer_list[info->start_tx_dma_buffer].rcc = 0;
+
+                       /* Program the Transmit Character Length Register (TCLR) */
+                       /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */
+                       usc_OutReg( info, TCLR, (u16)FrameSize );
+
+                       usc_RTCmd( info, RTCmd_PurgeTxFifo );
+
+                       /* Program the address of the 1st DMA Buffer Entry in linked list */
+                       phys_addr = info->tx_buffer_list[info->start_tx_dma_buffer].phys_entry;
+                       usc_OutDmaReg( info, NTARL, (u16)phys_addr );
+                       usc_OutDmaReg( info, NTARU, (u16)(phys_addr >> 16) );
+
+                       usc_UnlatchTxstatusBits( info, TXSTATUS_ALL );
+                       usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
+                       usc_EnableInterrupts( info, TRANSMIT_STATUS );
+
+                       if ( info->params.mode == MGSL_MODE_RAW &&
+                                       info->num_tx_dma_buffers > 1 ) {
+                          /* When running external sync mode, attempt to 'stream' transmit  */
+                          /* by filling tx dma buffers as they become available. To do this */
+                          /* we need to enable Tx DMA EOB Status interrupts :               */
+                          /*                                                                */
+                          /* 1. Arm End of Buffer (EOB) Transmit DMA Interrupt (BIT2 of TDIAR) */
+                          /* 2. Enable Transmit DMA Interrupts (BIT0 of DICR) */
+
+                          usc_OutDmaReg( info, TDIAR, BIT2|BIT3 );
+                          usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT0) );
+                       }
+
+                       /* Initialize Transmit DMA Channel */
+                       usc_DmaCmd( info, DmaCmd_InitTxChannel );
+                       
+                       usc_TCmd( info, TCmd_SendFrame );
+                       
+                       mod_timer(&info->tx_timer, jiffies +
+                                       msecs_to_jiffies(5000));
+               }
+               info->tx_active = true;
+       }
+
+       if ( !info->tx_enabled ) {
+               info->tx_enabled = true;
+               if ( info->params.flags & HDLC_FLAG_AUTO_CTS )
+                       usc_EnableTransmitter(info,ENABLE_AUTO_CTS);
+               else
+                       usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL);
+       }
+
+}      /* end of usc_start_transmitter() */
+
+/* usc_stop_transmitter()
+ *
+ *     Stops the transmitter and DMA
+ *
+ * Arguments:          info    pointer to device isntance data
+ * Return Value:       None
+ */
+static void usc_stop_transmitter( struct mgsl_struct *info )
+{
+       if (debug_level >= DEBUG_LEVEL_ISR)
+               printk("%s(%d):usc_stop_transmitter(%s)\n",
+                        __FILE__,__LINE__, info->device_name );
+                        
+       del_timer(&info->tx_timer);     
+                        
+       usc_UnlatchTxstatusBits( info, TXSTATUS_ALL );
+       usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA );
+       usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA );
+
+       usc_EnableTransmitter(info,DISABLE_UNCONDITIONAL);
+       usc_DmaCmd( info, DmaCmd_ResetTxChannel );
+       usc_RTCmd( info, RTCmd_PurgeTxFifo );
+
+       info->tx_enabled = false;
+       info->tx_active = false;
+
+}      /* end of usc_stop_transmitter() */
+
+/* usc_load_txfifo()
+ *
+ *     Fill the transmit FIFO until the FIFO is full or
+ *     there is no more data to load.
+ *
+ * Arguments:          info    pointer to device extension (instance data)
+ * Return Value:       None
+ */
+static void usc_load_txfifo( struct mgsl_struct *info )
+{
+       int Fifocount;
+       u8 TwoBytes[2];
+       
+       if ( !info->xmit_cnt && !info->x_char )
+               return; 
+               
+       /* Select transmit FIFO status readback in TICR */
+       usc_TCmd( info, TCmd_SelectTicrTxFifostatus );
+
+       /* load the Transmit FIFO until FIFOs full or all data sent */
+
+       while( (Fifocount = usc_InReg(info, TICR) >> 8) && info->xmit_cnt ) {
+               /* there is more space in the transmit FIFO and */
+               /* there is more data in transmit buffer */
+
+               if ( (info->xmit_cnt > 1) && (Fifocount > 1) && !info->x_char ) {
+                       /* write a 16-bit word from transmit buffer to 16C32 */
+                               
+                       TwoBytes[0] = info->xmit_buf[info->xmit_tail++];
+                       info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+                       TwoBytes[1] = info->xmit_buf[info->xmit_tail++];
+                       info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+                       
+                       outw( *((u16 *)TwoBytes), info->io_base + DATAREG);
+                               
+                       info->xmit_cnt -= 2;
+                       info->icount.tx += 2;
+               } else {
+                       /* only 1 byte left to transmit or 1 FIFO slot left */
+                       
+                       outw( (inw( info->io_base + CCAR) & 0x0780) | (TDR+LSBONLY),
+                               info->io_base + CCAR );
+                       
+                       if (info->x_char) {
+                               /* transmit pending high priority char */
+                               outw( info->x_char,info->io_base + CCAR );
+                               info->x_char = 0;
+                       } else {
+                               outw( info->xmit_buf[info->xmit_tail++],info->io_base + CCAR );
+                               info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+                               info->xmit_cnt--;
+                       }
+                       info->icount.tx++;
+               }
+       }
+
+}      /* end of usc_load_txfifo() */
+
+/* usc_reset()
+ *
+ *     Reset the adapter to a known state and prepare it for further use.
+ *
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void usc_reset( struct mgsl_struct *info )
+{
+       if ( info->bus_type == MGSL_BUS_TYPE_PCI ) {
+               int i;
+               u32 readval;
+
+               /* Set BIT30 of Misc Control Register */
+               /* (Local Control Register 0x50) to force reset of USC. */
+
+               volatile u32 *MiscCtrl = (u32 *)(info->lcr_base + 0x50);
+               u32 *LCR0BRDR = (u32 *)(info->lcr_base + 0x28);
+
+               info->misc_ctrl_value |= BIT30;
+               *MiscCtrl = info->misc_ctrl_value;
+
+               /*
+                * Force at least 170ns delay before clearing 
+                * reset bit. Each read from LCR takes at least 
+                * 30ns so 10 times for 300ns to be safe.
+                */
+               for(i=0;i<10;i++)
+                       readval = *MiscCtrl;
+
+               info->misc_ctrl_value &= ~BIT30;
+               *MiscCtrl = info->misc_ctrl_value;
+
+               *LCR0BRDR = BUS_DESCRIPTOR(
+                       1,              // Write Strobe Hold (0-3)
+                       2,              // Write Strobe Delay (0-3)
+                       2,              // Read Strobe Delay  (0-3)
+                       0,              // NWDD (Write data-data) (0-3)
+                       4,              // NWAD (Write Addr-data) (0-31)
+                       0,              // NXDA (Read/Write Data-Addr) (0-3)
+                       0,              // NRDD (Read Data-Data) (0-3)
+                       5               // NRAD (Read Addr-Data) (0-31)
+                       );
+       } else {
+               /* do HW reset */
+               outb( 0,info->io_base + 8 );
+       }
+
+       info->mbre_bit = 0;
+       info->loopback_bits = 0;
+       info->usc_idle_mode = 0;
+
+       /*
+        * Program the Bus Configuration Register (BCR)
+        *
+        * <15>         0       Don't use separate address
+        * <14..6>      0       reserved
+        * <5..4>       00      IAckmode = Default, don't care
+        * <3>          1       Bus Request Totem Pole output
+        * <2>          1       Use 16 Bit data bus
+        * <1>          0       IRQ Totem Pole output
+        * <0>          0       Don't Shift Right Addr
+        *
+        * 0000 0000 0000 1100 = 0x000c
+        *
+        * By writing to io_base + SDPIN the Wait/Ack pin is
+        * programmed to work as a Wait pin.
+        */
+       
+       outw( 0x000c,info->io_base + SDPIN );
+
+
+       outw( 0,info->io_base );
+       outw( 0,info->io_base + CCAR );
+
+       /* select little endian byte ordering */
+       usc_RTCmd( info, RTCmd_SelectLittleEndian );
+
+
+       /* Port Control Register (PCR)
+        *
+        * <15..14>     11      Port 7 is Output (~DMAEN, Bit 14 : 0 = Enabled)
+        * <13..12>     11      Port 6 is Output (~INTEN, Bit 12 : 0 = Enabled)
+        * <11..10>     00      Port 5 is Input (No Connect, Don't Care)
+        * <9..8>       00      Port 4 is Input (No Connect, Don't Care)
+        * <7..6>       11      Port 3 is Output (~RTS, Bit 6 : 0 = Enabled )
+        * <5..4>       11      Port 2 is Output (~DTR, Bit 4 : 0 = Enabled )
+        * <3..2>       01      Port 1 is Input (Dedicated RxC)
+        * <1..0>       01      Port 0 is Input (Dedicated TxC)
+        *
+        *      1111 0000 1111 0101 = 0xf0f5
+        */
+
+       usc_OutReg( info, PCR, 0xf0f5 );
+
+
+       /*
+        * Input/Output Control Register
+        *
+        * <15..14>     00      CTS is active low input
+        * <13..12>     00      DCD is active low input
+        * <11..10>     00      TxREQ pin is input (DSR)
+        * <9..8>       00      RxREQ pin is input (RI)
+        * <7..6>       00      TxD is output (Transmit Data)
+        * <5..3>       000     TxC Pin in Input (14.7456MHz Clock)
+        * <2..0>       100     RxC is Output (drive with BRG0)
+        *
+        *      0000 0000 0000 0100 = 0x0004
+        */
+
+       usc_OutReg( info, IOCR, 0x0004 );
+
+}      /* end of usc_reset() */
+
+/* usc_set_async_mode()
+ *
+ *     Program adapter for asynchronous communications.
+ *
+ * Arguments:          info            pointer to device instance data
+ * Return Value:       None
+ */
+static void usc_set_async_mode( struct mgsl_struct *info )
+{
+       u16 RegValue;
+
+       /* disable interrupts while programming USC */
+       usc_DisableMasterIrqBit( info );
+
+       outw( 0, info->io_base );                       /* clear Master Bus Enable (DCAR) */
+       usc_DmaCmd( info, DmaCmd_ResetAllChannels );    /* disable both DMA channels */
+
+       usc_loopback_frame( info );
+
+       /* Channel mode Register (CMR)
+        *
+        * <15..14>     00      Tx Sub modes, 00 = 1 Stop Bit
+        * <13..12>     00                    00 = 16X Clock
+        * <11..8>      0000    Transmitter mode = Asynchronous
+        * <7..6>       00      reserved?
+        * <5..4>       00      Rx Sub modes, 00 = 16X Clock
+        * <3..0>       0000    Receiver mode = Asynchronous
+        *
+        * 0000 0000 0000 0000 = 0x0
+        */
+
+       RegValue = 0;
+       if ( info->params.stop_bits != 1 )
+               RegValue |= BIT14;
+       usc_OutReg( info, CMR, RegValue );
+
+       
+       /* Receiver mode Register (RMR)
+        *
+        * <15..13>     000     encoding = None
+        * <12..08>     00000   reserved (Sync Only)
+        * <7..6>       00      Even parity
+        * <5>          0       parity disabled
+        * <4..2>       000     Receive Char Length = 8 bits
+        * <1..0>       00      Disable Receiver
+        *
+        * 0000 0000 0000 0000 = 0x0
+        */
+
+       RegValue = 0;
+
+       if ( info->params.data_bits != 8 )
+               RegValue |= BIT4+BIT3+BIT2;
+
+       if ( info->params.parity != ASYNC_PARITY_NONE ) {
+               RegValue |= BIT5;
+               if ( info->params.parity != ASYNC_PARITY_ODD )
+                       RegValue |= BIT6;
+       }
+
+       usc_OutReg( info, RMR, RegValue );
+
+
+       /* Set IRQ trigger level */
+
+       usc_RCmd( info, RCmd_SelectRicrIntLevel );
+
+       
+       /* Receive Interrupt Control Register (RICR)
+        *
+        * <15..8>      ?               RxFIFO IRQ Request Level
+        *
+        * Note: For async mode the receive FIFO level must be set
+        * to 0 to avoid the situation where the FIFO contains fewer bytes
+        * than the trigger level and no more data is expected.
+        *
+        * <7>          0               Exited Hunt IA (Interrupt Arm)
+        * <6>          0               Idle Received IA
+        * <5>          0               Break/Abort IA
+        * <4>          0               Rx Bound IA
+        * <3>          0               Queued status reflects oldest byte in FIFO
+        * <2>          0               Abort/PE IA
+        * <1>          0               Rx Overrun IA
+        * <0>          0               Select TC0 value for readback
+        *
+        * 0000 0000 0100 0000 = 0x0000 + (FIFOLEVEL in MSB)
+        */
+       
+       usc_OutReg( info, RICR, 0x0000 );
+
+       usc_UnlatchRxstatusBits( info, RXSTATUS_ALL );
+       usc_ClearIrqPendingBits( info, RECEIVE_STATUS );
+
+       
+       /* Transmit mode Register (TMR)
+        *
+        * <15..13>     000     encoding = None
+        * <12..08>     00000   reserved (Sync Only)
+        * <7..6>       00      Transmit parity Even
+        * <5>          0       Transmit parity Disabled
+        * <4..2>       000     Tx Char Length = 8 bits
+        * <1..0>       00      Disable Transmitter
+        *
+        * 0000 0000 0000 0000 = 0x0
+        */
+
+       RegValue = 0;
+
+       if ( info->params.data_bits != 8 )
+               RegValue |= BIT4+BIT3+BIT2;
+
+       if ( info->params.parity != ASYNC_PARITY_NONE ) {
+               RegValue |= BIT5;
+               if ( info->params.parity != ASYNC_PARITY_ODD )
+                       RegValue |= BIT6;
+       }
+
+       usc_OutReg( info, TMR, RegValue );
+
+       usc_set_txidle( info );
+
+
+       /* Set IRQ trigger level */
+
+       usc_TCmd( info, TCmd_SelectTicrIntLevel );
+
+       
+       /* Transmit Interrupt Control Register (TICR)
+        *
+        * <15..8>      ?       Transmit FIFO IRQ Level
+        * <7>          0       Present IA (Interrupt Arm)
+        * <6>          1       Idle Sent IA
+        * <5>          0       Abort Sent IA
+        * <4>          0       EOF/EOM Sent IA
+        * <3>          0       CRC Sent IA
+        * <2>          0       1 = Wait for SW Trigger to Start Frame
+        * <1>          0       Tx Underrun IA
+        * <0>          0       TC0 constant on read back
+        *
+        *      0000 0000 0100 0000 = 0x0040
+        */
+
+       usc_OutReg( info, TICR, 0x1f40 );
+
+       usc_UnlatchTxstatusBits( info, TXSTATUS_ALL );
+       usc_ClearIrqPendingBits( info, TRANSMIT_STATUS );
+
+       usc_enable_async_clock( info, info->params.data_rate );
+
+       
+       /* Channel Control/status Register (CCSR)
+        *
+        * <15>         X       RCC FIFO Overflow status (RO)
+        * <14>         X       RCC FIFO Not Empty status (RO)
+        * <13>         0       1 = Clear RCC FIFO (WO)
+        * <12>         X       DPLL in Sync status (RO)
+        * <11>         X       DPLL 2 Missed Clocks status (RO)
+        * <10>         X       DPLL 1 Missed Clock status (RO)
+        * <9..8>       00      DPLL Resync on rising and falling edges (RW)
+        * <7>          X       SDLC Loop On status (RO)
+        * <6>          X       SDLC Loop Send status (RO)
+        * <5>          1       Bypass counters for TxClk and RxClk (RW)
+        * <4..2>       000     Last Char of SDLC frame has 8 bits (RW)
+        * <1..0>       00      reserved
+        *
+        *      0000 0000 0010 0000 = 0x0020
+        */
+       
+       usc_OutReg( info, CCSR, 0x0020 );
+
+       usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA +
+                             RECEIVE_DATA + RECEIVE_STATUS );
+
+       usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA +
+                               RECEIVE_DATA + RECEIVE_STATUS );
+
+       usc_EnableMasterIrqBit( info );
+
+       if (info->bus_type == MGSL_BUS_TYPE_ISA) {
+               /* Enable INTEN (Port 6, Bit12) */
+               /* This connects the IRQ request signal to the ISA bus */
+               usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12));
+       }
+
+       if (info->params.loopback) {
+               info->loopback_bits = 0x300;
+               outw(0x0300, info->io_base + CCAR);
+       }
+
+}      /* end of usc_set_async_mode() */
+
+/* usc_loopback_frame()
+ *
+ *     Loop back a small (2 byte) dummy SDLC frame.
+ *     Interrupts and DMA are NOT used. The purpose of this is to
+ *     clear any 'stale' status info left over from running in async mode.
+ *
+ *     The 16C32 shows the strange behaviour of marking the 1st
+ *     received SDLC frame with a CRC error even when there is no
+ *     CRC error. To get around this a small dummy from of 2 bytes
+ *     is looped back when switching from async to sync mode.
+ *
+ * Arguments:          info            pointer to device instance data
+ * Return Value:       None
+ */
+static void usc_loopback_frame( struct mgsl_struct *info )
+{
+       int i;
+       unsigned long oldmode = info->params.mode;
+
+       info->params.mode = MGSL_MODE_HDLC;
+       
+       usc_DisableMasterIrqBit( info );
+
+       usc_set_sdlc_mode( info );
+       usc_enable_loopback( info, 1 );
+
+       /* Write 16-bit Time Constant for BRG0 */
+       usc_OutReg( info, TC0R, 0 );
+       
+       /* Channel Control Register (CCR)
+        *
+        * <15..14>     00      Don't use 32-bit Tx Control Blocks (TCBs)
+        * <13>         0       Trigger Tx on SW Command Disabled
+        * <12>         0       Flag Preamble Disabled
+        * <11..10>     00      Preamble Length = 8-Bits
+        * <9..8>       01      Preamble Pattern = flags
+        * <7..6>       10      Don't use 32-bit Rx status Blocks (RSBs)
+        * <5>          0       Trigger Rx on SW Command Disabled
+        * <4..0>       0       reserved
+        *
+        *      0000 0001 0000 0000 = 0x0100
+        */
+
+       usc_OutReg( info, CCR, 0x0100 );
+
+       /* SETUP RECEIVER */
+       usc_RTCmd( info, RTCmd_PurgeRxFifo );
+       usc_EnableReceiver(info,ENABLE_UNCONDITIONAL);
+
+       /* SETUP TRANSMITTER */
+       /* Program the Transmit Character Length Register (TCLR) */
+       /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */
+       usc_OutReg( info, TCLR, 2 );
+       usc_RTCmd( info, RTCmd_PurgeTxFifo );
+
+       /* unlatch Tx status bits, and start transmit channel. */
+       usc_UnlatchTxstatusBits(info,TXSTATUS_ALL);
+       outw(0,info->io_base + DATAREG);
+
+       /* ENABLE TRANSMITTER */
+       usc_TCmd( info, TCmd_SendFrame );
+       usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL);
+                                                       
+       /* WAIT FOR RECEIVE COMPLETE */
+       for (i=0 ; i<1000 ; i++)
+               if (usc_InReg( info, RCSR ) & (BIT8 + BIT4 + BIT3 + BIT1))
+                       break;
+
+       /* clear Internal Data loopback mode */
+       usc_enable_loopback(info, 0);
+
+       usc_EnableMasterIrqBit(info);
+
+       info->params.mode = oldmode;
+
+}      /* end of usc_loopback_frame() */
+
+/* usc_set_sync_mode() Programs the USC for SDLC communications.
+ *
+ * Arguments:          info    pointer to adapter info structure
+ * Return Value:       None
+ */
+static void usc_set_sync_mode( struct mgsl_struct *info )
+{
+       usc_loopback_frame( info );
+       usc_set_sdlc_mode( info );
+
+       if (info->bus_type == MGSL_BUS_TYPE_ISA) {
+               /* Enable INTEN (Port 6, Bit12) */
+               /* This connects the IRQ request signal to the ISA bus */
+               usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12));
+       }
+
+       usc_enable_aux_clock(info, info->params.clock_speed);
+
+       if (info->params.loopback)
+               usc_enable_loopback(info,1);
+
+}      /* end of mgsl_set_sync_mode() */
+
+/* usc_set_txidle()    Set the HDLC idle mode for the transmitter.
+ *
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void usc_set_txidle( struct mgsl_struct *info )
+{
+       u16 usc_idle_mode = IDLEMODE_FLAGS;
+
+       /* Map API idle mode to USC register bits */
+
+       switch( info->idle_mode ){
+       case HDLC_TXIDLE_FLAGS:                 usc_idle_mode = IDLEMODE_FLAGS; break;
+       case HDLC_TXIDLE_ALT_ZEROS_ONES:        usc_idle_mode = IDLEMODE_ALT_ONE_ZERO; break;
+       case HDLC_TXIDLE_ZEROS:                 usc_idle_mode = IDLEMODE_ZERO; break;
+       case HDLC_TXIDLE_ONES:                  usc_idle_mode = IDLEMODE_ONE; break;
+       case HDLC_TXIDLE_ALT_MARK_SPACE:        usc_idle_mode = IDLEMODE_ALT_MARK_SPACE; break;
+       case HDLC_TXIDLE_SPACE:                 usc_idle_mode = IDLEMODE_SPACE; break;
+       case HDLC_TXIDLE_MARK:                  usc_idle_mode = IDLEMODE_MARK; break;
+       }
+
+       info->usc_idle_mode = usc_idle_mode;
+       //usc_OutReg(info, TCSR, usc_idle_mode);
+       info->tcsr_value &= ~IDLEMODE_MASK;     /* clear idle mode bits */
+       info->tcsr_value += usc_idle_mode;
+       usc_OutReg(info, TCSR, info->tcsr_value);
+
+       /*
+        * if SyncLink WAN adapter is running in external sync mode, the
+        * transmitter has been set to Monosync in order to try to mimic
+        * a true raw outbound bit stream. Monosync still sends an open/close
+        * sync char at the start/end of a frame. Try to match those sync
+        * patterns to the idle mode set here
+        */
+       if ( info->params.mode == MGSL_MODE_RAW ) {
+               unsigned char syncpat = 0;
+               switch( info->idle_mode ) {
+               case HDLC_TXIDLE_FLAGS:
+                       syncpat = 0x7e;
+                       break;
+               case HDLC_TXIDLE_ALT_ZEROS_ONES:
+                       syncpat = 0x55;
+                       break;
+               case HDLC_TXIDLE_ZEROS:
+               case HDLC_TXIDLE_SPACE:
+                       syncpat = 0x00;
+                       break;
+               case HDLC_TXIDLE_ONES:
+               case HDLC_TXIDLE_MARK:
+                       syncpat = 0xff;
+                       break;
+               case HDLC_TXIDLE_ALT_MARK_SPACE:
+                       syncpat = 0xaa;
+                       break;
+               }
+
+               usc_SetTransmitSyncChars(info,syncpat,syncpat);
+       }
+
+}      /* end of usc_set_txidle() */
+
+/* usc_get_serial_signals()
+ *
+ *     Query the adapter for the state of the V24 status (input) signals.
+ *
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void usc_get_serial_signals( struct mgsl_struct *info )
+{
+       u16 status;
+
+       /* clear all serial signals except DTR and RTS */
+       info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS;
+
+       /* Read the Misc Interrupt status Register (MISR) to get */
+       /* the V24 status signals. */
+
+       status = usc_InReg( info, MISR );
+
+       /* set serial signal bits to reflect MISR */
+
+       if ( status & MISCSTATUS_CTS )
+               info->serial_signals |= SerialSignal_CTS;
+
+       if ( status & MISCSTATUS_DCD )
+               info->serial_signals |= SerialSignal_DCD;
+
+       if ( status & MISCSTATUS_RI )
+               info->serial_signals |= SerialSignal_RI;
+
+       if ( status & MISCSTATUS_DSR )
+               info->serial_signals |= SerialSignal_DSR;
+
+}      /* end of usc_get_serial_signals() */
+
+/* usc_set_serial_signals()
+ *
+ *     Set the state of DTR and RTS based on contents of
+ *     serial_signals member of device extension.
+ *     
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void usc_set_serial_signals( struct mgsl_struct *info )
+{
+       u16 Control;
+       unsigned char V24Out = info->serial_signals;
+
+       /* get the current value of the Port Control Register (PCR) */
+
+       Control = usc_InReg( info, PCR );
+
+       if ( V24Out & SerialSignal_RTS )
+               Control &= ~(BIT6);
+       else
+               Control |= BIT6;
+
+       if ( V24Out & SerialSignal_DTR )
+               Control &= ~(BIT4);
+       else
+               Control |= BIT4;
+
+       usc_OutReg( info, PCR, Control );
+
+}      /* end of usc_set_serial_signals() */
+
+/* usc_enable_async_clock()
+ *
+ *     Enable the async clock at the specified frequency.
+ *
+ * Arguments:          info            pointer to device instance data
+ *                     data_rate       data rate of clock in bps
+ *                                     0 disables the AUX clock.
+ * Return Value:       None
+ */
+static void usc_enable_async_clock( struct mgsl_struct *info, u32 data_rate )
+{
+       if ( data_rate )        {
+               /*
+                * Clock mode Control Register (CMCR)
+                * 
+                * <15..14>     00      counter 1 Disabled
+                * <13..12>     00      counter 0 Disabled
+                * <11..10>     11      BRG1 Input is TxC Pin
+                * <9..8>       11      BRG0 Input is TxC Pin
+                * <7..6>       01      DPLL Input is BRG1 Output
+                * <5..3>       100     TxCLK comes from BRG0
+                * <2..0>       100     RxCLK comes from BRG0
+                *
+                * 0000 1111 0110 0100 = 0x0f64
+                */
+               
+               usc_OutReg( info, CMCR, 0x0f64 );
+
+
+               /*
+                * Write 16-bit Time Constant for BRG0
+                * Time Constant = (ClkSpeed / data_rate) - 1
+                * ClkSpeed = 921600 (ISA), 691200 (PCI)
+                */
+
+               if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+                       usc_OutReg( info, TC0R, (u16)((691200/data_rate) - 1) );
+               else
+                       usc_OutReg( info, TC0R, (u16)((921600/data_rate) - 1) );
+
+               
+               /*
+                * Hardware Configuration Register (HCR)
+                * Clear Bit 1, BRG0 mode = Continuous
+                * Set Bit 0 to enable BRG0.
+                */
+
+               usc_OutReg( info, HCR,
+                           (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) );
+
+
+               /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */
+
+               usc_OutReg( info, IOCR,
+                           (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) );
+       } else {
+               /* data rate == 0 so turn off BRG0 */
+               usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) );
+       }
+
+}      /* end of usc_enable_async_clock() */
+
+/*
+ * Buffer Structures:
+ *
+ * Normal memory access uses virtual addresses that can make discontiguous
+ * physical memory pages appear to be contiguous in the virtual address
+ * space (the processors memory mapping handles the conversions).
+ *
+ * DMA transfers require physically contiguous memory. This is because
+ * the DMA system controller and DMA bus masters deal with memory using
+ * only physical addresses.
+ *
+ * This causes a problem under Windows NT when large DMA buffers are
+ * needed. Fragmentation of the nonpaged pool prevents allocations of
+ * physically contiguous buffers larger than the PAGE_SIZE.
+ *
+ * However the 16C32 supports Bus Master Scatter/Gather DMA which
+ * allows DMA transfers to physically discontiguous buffers. Information
+ * about each data transfer buffer is contained in a memory structure
+ * called a 'buffer entry'. A list of buffer entries is maintained
+ * to track and control the use of the data transfer buffers.
+ *
+ * To support this strategy we will allocate sufficient PAGE_SIZE
+ * contiguous memory buffers to allow for the total required buffer
+ * space.
+ *
+ * The 16C32 accesses the list of buffer entries using Bus Master
+ * DMA. Control information is read from the buffer entries by the
+ * 16C32 to control data transfers. status information is written to
+ * the buffer entries by the 16C32 to indicate the status of completed
+ * transfers.
+ *
+ * The CPU writes control information to the buffer entries to control
+ * the 16C32 and reads status information from the buffer entries to
+ * determine information about received and transmitted frames.
+ *
+ * Because the CPU and 16C32 (adapter) both need simultaneous access
+ * to the buffer entries, the buffer entry memory is allocated with
+ * HalAllocateCommonBuffer(). This restricts the size of the buffer
+ * entry list to PAGE_SIZE.
+ *
+ * The actual data buffers on the other hand will only be accessed
+ * by the CPU or the adapter but not by both simultaneously. This allows
+ * Scatter/Gather packet based DMA procedures for using physically
+ * discontiguous pages.
+ */
+
+/*
+ * mgsl_reset_tx_dma_buffers()
+ *
+ *     Set the count for all transmit buffers to 0 to indicate the
+ *     buffer is available for use and set the current buffer to the
+ *     first buffer. This effectively makes all buffers free and
+ *     discards any data in buffers.
+ *
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void mgsl_reset_tx_dma_buffers( struct mgsl_struct *info )
+{
+       unsigned int i;
+
+       for ( i = 0; i < info->tx_buffer_count; i++ ) {
+               *((unsigned long *)&(info->tx_buffer_list[i].count)) = 0;
+       }
+
+       info->current_tx_buffer = 0;
+       info->start_tx_dma_buffer = 0;
+       info->tx_dma_buffers_used = 0;
+
+       info->get_tx_holding_index = 0;
+       info->put_tx_holding_index = 0;
+       info->tx_holding_count = 0;
+
+}      /* end of mgsl_reset_tx_dma_buffers() */
+
+/*
+ * num_free_tx_dma_buffers()
+ *
+ *     returns the number of free tx dma buffers available
+ *
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       number of free tx dma buffers
+ */
+static int num_free_tx_dma_buffers(struct mgsl_struct *info)
+{
+       return info->tx_buffer_count - info->tx_dma_buffers_used;
+}
+
+/*
+ * mgsl_reset_rx_dma_buffers()
+ * 
+ *     Set the count for all receive buffers to DMABUFFERSIZE
+ *     and set the current buffer to the first buffer. This effectively
+ *     makes all buffers free and discards any data in buffers.
+ * 
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       None
+ */
+static void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info )
+{
+       unsigned int i;
+
+       for ( i = 0; i < info->rx_buffer_count; i++ ) {
+               *((unsigned long *)&(info->rx_buffer_list[i].count)) = DMABUFFERSIZE;
+//             info->rx_buffer_list[i].count = DMABUFFERSIZE;
+//             info->rx_buffer_list[i].status = 0;
+       }
+
+       info->current_rx_buffer = 0;
+
+}      /* end of mgsl_reset_rx_dma_buffers() */
+
+/*
+ * mgsl_free_rx_frame_buffers()
+ * 
+ *     Free the receive buffers used by a received SDLC
+ *     frame such that the buffers can be reused.
+ * 
+ * Arguments:
+ * 
+ *     info                    pointer to device instance data
+ *     StartIndex              index of 1st receive buffer of frame
+ *     EndIndex                index of last receive buffer of frame
+ * 
+ * Return Value:       None
+ */
+static void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex )
+{
+       bool Done = false;
+       DMABUFFERENTRY *pBufEntry;
+       unsigned int Index;
+
+       /* Starting with 1st buffer entry of the frame clear the status */
+       /* field and set the count field to DMA Buffer Size. */
+
+       Index = StartIndex;
+
+       while( !Done ) {
+               pBufEntry = &(info->rx_buffer_list[Index]);
+
+               if ( Index == EndIndex ) {
+                       /* This is the last buffer of the frame! */
+                       Done = true;
+               }
+
+               /* reset current buffer for reuse */
+//             pBufEntry->status = 0;
+//             pBufEntry->count = DMABUFFERSIZE;
+               *((unsigned long *)&(pBufEntry->count)) = DMABUFFERSIZE;
+
+               /* advance to next buffer entry in linked list */
+               Index++;
+               if ( Index == info->rx_buffer_count )
+                       Index = 0;
+       }
+
+       /* set current buffer to next buffer after last buffer of frame */
+       info->current_rx_buffer = Index;
+
+}      /* end of free_rx_frame_buffers() */
+
+/* mgsl_get_rx_frame()
+ * 
+ *     This function attempts to return a received SDLC frame from the
+ *     receive DMA buffers. Only frames received without errors are returned.
+ *
+ * Arguments:          info    pointer to device extension
+ * Return Value:       true if frame returned, otherwise false
+ */
+static bool mgsl_get_rx_frame(struct mgsl_struct *info)
+{
+       unsigned int StartIndex, EndIndex;      /* index of 1st and last buffers of Rx frame */
+       unsigned short status;
+       DMABUFFERENTRY *pBufEntry;
+       unsigned int framesize = 0;
+       bool ReturnCode = false;
+       unsigned long flags;
+       struct tty_struct *tty = info->port.tty;
+       bool return_frame = false;
+       
+       /*
+        * current_rx_buffer points to the 1st buffer of the next available
+        * receive frame. To find the last buffer of the frame look for
+        * a non-zero status field in the buffer entries. (The status
+        * field is set by the 16C32 after completing a receive frame.
+        */
+
+       StartIndex = EndIndex = info->current_rx_buffer;
+
+       while( !info->rx_buffer_list[EndIndex].status ) {
+               /*
+                * If the count field of the buffer entry is non-zero then
+                * this buffer has not been used. (The 16C32 clears the count
+                * field when it starts using the buffer.) If an unused buffer
+                * is encountered then there are no frames available.
+                */
+
+               if ( info->rx_buffer_list[EndIndex].count )
+                       goto Cleanup;
+
+               /* advance to next buffer entry in linked list */
+               EndIndex++;
+               if ( EndIndex == info->rx_buffer_count )
+                       EndIndex = 0;
+
+               /* if entire list searched then no frame available */
+               if ( EndIndex == StartIndex ) {
+                       /* If this occurs then something bad happened,
+                        * all buffers have been 'used' but none mark
+                        * the end of a frame. Reset buffers and receiver.
+                        */
+
+                       if ( info->rx_enabled ){
+                               spin_lock_irqsave(&info->irq_spinlock,flags);
+                               usc_start_receiver(info);
+                               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+                       }
+                       goto Cleanup;
+               }
+       }
+
+
+       /* check status of receive frame */
+       
+       status = info->rx_buffer_list[EndIndex].status;
+
+       if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN +
+                       RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) {
+               if ( status & RXSTATUS_SHORT_FRAME )
+                       info->icount.rxshort++;
+               else if ( status & RXSTATUS_ABORT )
+                       info->icount.rxabort++;
+               else if ( status & RXSTATUS_OVERRUN )
+                       info->icount.rxover++;
+               else {
+                       info->icount.rxcrc++;
+                       if ( info->params.crc_type & HDLC_CRC_RETURN_EX )
+                               return_frame = true;
+               }
+               framesize = 0;
+#if SYNCLINK_GENERIC_HDLC
+               {
+                       info->netdev->stats.rx_errors++;
+                       info->netdev->stats.rx_frame_errors++;
+               }
+#endif
+       } else
+               return_frame = true;
+
+       if ( return_frame ) {
+               /* receive frame has no errors, get frame size.
+                * The frame size is the starting value of the RCC (which was
+                * set to 0xffff) minus the ending value of the RCC (decremented
+                * once for each receive character) minus 2 for the 16-bit CRC.
+                */
+
+               framesize = RCLRVALUE - info->rx_buffer_list[EndIndex].rcc;
+
+               /* adjust frame size for CRC if any */
+               if ( info->params.crc_type == HDLC_CRC_16_CCITT )
+                       framesize -= 2;
+               else if ( info->params.crc_type == HDLC_CRC_32_CCITT )
+                       framesize -= 4;         
+       }
+
+       if ( debug_level >= DEBUG_LEVEL_BH )
+               printk("%s(%d):mgsl_get_rx_frame(%s) status=%04X size=%d\n",
+                       __FILE__,__LINE__,info->device_name,status,framesize);
+                       
+       if ( debug_level >= DEBUG_LEVEL_DATA )
+               mgsl_trace_block(info,info->rx_buffer_list[StartIndex].virt_addr,
+                       min_t(int, framesize, DMABUFFERSIZE),0);
+               
+       if (framesize) {
+               if ( ( (info->params.crc_type & HDLC_CRC_RETURN_EX) &&
+                               ((framesize+1) > info->max_frame_size) ) ||
+                       (framesize > info->max_frame_size) )
+                       info->icount.rxlong++;
+               else {
+                       /* copy dma buffer(s) to contiguous intermediate buffer */
+                       int copy_count = framesize;
+                       int index = StartIndex;
+                       unsigned char *ptmp = info->intermediate_rxbuffer;
+
+                       if ( !(status & RXSTATUS_CRC_ERROR))
+                       info->icount.rxok++;
+                       
+                       while(copy_count) {
+                               int partial_count;
+                               if ( copy_count > DMABUFFERSIZE )
+                                       partial_count = DMABUFFERSIZE;
+                               else
+                                       partial_count = copy_count;
+                       
+                               pBufEntry = &(info->rx_buffer_list[index]);
+                               memcpy( ptmp, pBufEntry->virt_addr, partial_count );
+                               ptmp += partial_count;
+                               copy_count -= partial_count;
+                               
+                               if ( ++index == info->rx_buffer_count )
+                                       index = 0;
+                       }
+
+                       if ( info->params.crc_type & HDLC_CRC_RETURN_EX ) {
+                               ++framesize;
+                               *ptmp = (status & RXSTATUS_CRC_ERROR ?
+                                               RX_CRC_ERROR :
+                                               RX_OK);
+
+                               if ( debug_level >= DEBUG_LEVEL_DATA )
+                                       printk("%s(%d):mgsl_get_rx_frame(%s) rx frame status=%d\n",
+                                               __FILE__,__LINE__,info->device_name,
+                                               *ptmp);
+                       }
+
+#if SYNCLINK_GENERIC_HDLC
+                       if (info->netcount)
+                               hdlcdev_rx(info,info->intermediate_rxbuffer,framesize);
+                       else
+#endif
+                               ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
+               }
+       }
+       /* Free the buffers used by this frame. */
+       mgsl_free_rx_frame_buffers( info, StartIndex, EndIndex );
+
+       ReturnCode = true;
+
+Cleanup:
+
+       if ( info->rx_enabled && info->rx_overflow ) {
+               /* The receiver needs to restarted because of 
+                * a receive overflow (buffer or FIFO). If the 
+                * receive buffers are now empty, then restart receiver.
+                */
+
+               if ( !info->rx_buffer_list[EndIndex].status &&
+                       info->rx_buffer_list[EndIndex].count ) {
+                       spin_lock_irqsave(&info->irq_spinlock,flags);
+                       usc_start_receiver(info);
+                       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+               }
+       }
+
+       return ReturnCode;
+
+}      /* end of mgsl_get_rx_frame() */
+
+/* mgsl_get_raw_rx_frame()
+ *
+ *             This function attempts to return a received frame from the
+ *     receive DMA buffers when running in external loop mode. In this mode,
+ *     we will return at most one DMABUFFERSIZE frame to the application.
+ *     The USC receiver is triggering off of DCD going active to start a new
+ *     frame, and DCD going inactive to terminate the frame (similar to
+ *     processing a closing flag character).
+ *
+ *     In this routine, we will return DMABUFFERSIZE "chunks" at a time.
+ *     If DCD goes inactive, the last Rx DMA Buffer will have a non-zero
+ *     status field and the RCC field will indicate the length of the
+ *     entire received frame. We take this RCC field and get the modulus
+ *     of RCC and DMABUFFERSIZE to determine if number of bytes in the
+ *     last Rx DMA buffer and return that last portion of the frame.
+ *
+ * Arguments:          info    pointer to device extension
+ * Return Value:       true if frame returned, otherwise false
+ */
+static bool mgsl_get_raw_rx_frame(struct mgsl_struct *info)
+{
+       unsigned int CurrentIndex, NextIndex;
+       unsigned short status;
+       DMABUFFERENTRY *pBufEntry;
+       unsigned int framesize = 0;
+       bool ReturnCode = false;
+       unsigned long flags;
+       struct tty_struct *tty = info->port.tty;
+
+       /*
+        * current_rx_buffer points to the 1st buffer of the next available
+        * receive frame. The status field is set by the 16C32 after
+        * completing a receive frame. If the status field of this buffer
+        * is zero, either the USC is still filling this buffer or this
+        * is one of a series of buffers making up a received frame.
+        *
+        * If the count field of this buffer is zero, the USC is either
+        * using this buffer or has used this buffer. Look at the count
+        * field of the next buffer. If that next buffer's count is
+        * non-zero, the USC is still actively using the current buffer.
+        * Otherwise, if the next buffer's count field is zero, the
+        * current buffer is complete and the USC is using the next
+        * buffer.
+        */
+       CurrentIndex = NextIndex = info->current_rx_buffer;
+       ++NextIndex;
+       if ( NextIndex == info->rx_buffer_count )
+               NextIndex = 0;
+
+       if ( info->rx_buffer_list[CurrentIndex].status != 0 ||
+               (info->rx_buffer_list[CurrentIndex].count == 0 &&
+                       info->rx_buffer_list[NextIndex].count == 0)) {
+               /*
+                * Either the status field of this dma buffer is non-zero
+                * (indicating the last buffer of a receive frame) or the next
+                * buffer is marked as in use -- implying this buffer is complete
+                * and an intermediate buffer for this received frame.
+                */
+
+               status = info->rx_buffer_list[CurrentIndex].status;
+
+               if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN +
+                               RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) {
+                       if ( status & RXSTATUS_SHORT_FRAME )
+                               info->icount.rxshort++;
+                       else if ( status & RXSTATUS_ABORT )
+                               info->icount.rxabort++;
+                       else if ( status & RXSTATUS_OVERRUN )
+                               info->icount.rxover++;
+                       else
+                               info->icount.rxcrc++;
+                       framesize = 0;
+               } else {
+                       /*
+                        * A receive frame is available, get frame size and status.
+                        *
+                        * The frame size is the starting value of the RCC (which was
+                        * set to 0xffff) minus the ending value of the RCC (decremented
+                        * once for each receive character) minus 2 or 4 for the 16-bit
+                        * or 32-bit CRC.
+                        *
+                        * If the status field is zero, this is an intermediate buffer.
+                        * It's size is 4K.
+                        *
+                        * If the DMA Buffer Entry's Status field is non-zero, the
+                        * receive operation completed normally (ie: DCD dropped). The
+                        * RCC field is valid and holds the received frame size.
+                        * It is possible that the RCC field will be zero on a DMA buffer
+                        * entry with a non-zero status. This can occur if the total
+                        * frame size (number of bytes between the time DCD goes active
+                        * to the time DCD goes inactive) exceeds 65535 bytes. In this
+                        * case the 16C32 has underrun on the RCC count and appears to
+                        * stop updating this counter to let us know the actual received
+                        * frame size. If this happens (non-zero status and zero RCC),
+                        * simply return the entire RxDMA Buffer
+                        */
+                       if ( status ) {
+                               /*
+                                * In the event that the final RxDMA Buffer is
+                                * terminated with a non-zero status and the RCC
+                                * field is zero, we interpret this as the RCC
+                                * having underflowed (received frame > 65535 bytes).
+                                *
+                                * Signal the event to the user by passing back
+                                * a status of RxStatus_CrcError returning the full
+                                * buffer and let the app figure out what data is
+                                * actually valid
+                                */
+                               if ( info->rx_buffer_list[CurrentIndex].rcc )
+                                       framesize = RCLRVALUE - info->rx_buffer_list[CurrentIndex].rcc;
+                               else
+                                       framesize = DMABUFFERSIZE;
+                       }
+                       else
+                               framesize = DMABUFFERSIZE;
+               }
+
+               if ( framesize > DMABUFFERSIZE ) {
+                       /*
+                        * if running in raw sync mode, ISR handler for
+                        * End Of Buffer events terminates all buffers at 4K.
+                        * If this frame size is said to be >4K, get the
+                        * actual number of bytes of the frame in this buffer.
+                        */
+                       framesize = framesize % DMABUFFERSIZE;
+               }
+
+
+               if ( debug_level >= DEBUG_LEVEL_BH )
+                       printk("%s(%d):mgsl_get_raw_rx_frame(%s) status=%04X size=%d\n",
+                               __FILE__,__LINE__,info->device_name,status,framesize);
+
+               if ( debug_level >= DEBUG_LEVEL_DATA )
+                       mgsl_trace_block(info,info->rx_buffer_list[CurrentIndex].virt_addr,
+                               min_t(int, framesize, DMABUFFERSIZE),0);
+
+               if (framesize) {
+                       /* copy dma buffer(s) to contiguous intermediate buffer */
+                       /* NOTE: we never copy more than DMABUFFERSIZE bytes    */
+
+                       pBufEntry = &(info->rx_buffer_list[CurrentIndex]);
+                       memcpy( info->intermediate_rxbuffer, pBufEntry->virt_addr, framesize);
+                       info->icount.rxok++;
+
+                       ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
+               }
+
+               /* Free the buffers used by this frame. */
+               mgsl_free_rx_frame_buffers( info, CurrentIndex, CurrentIndex );
+
+               ReturnCode = true;
+       }
+
+
+       if ( info->rx_enabled && info->rx_overflow ) {
+               /* The receiver needs to restarted because of
+                * a receive overflow (buffer or FIFO). If the
+                * receive buffers are now empty, then restart receiver.
+                */
+
+               if ( !info->rx_buffer_list[CurrentIndex].status &&
+                       info->rx_buffer_list[CurrentIndex].count ) {
+                       spin_lock_irqsave(&info->irq_spinlock,flags);
+                       usc_start_receiver(info);
+                       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+               }
+       }
+
+       return ReturnCode;
+
+}      /* end of mgsl_get_raw_rx_frame() */
+
+/* mgsl_load_tx_dma_buffer()
+ * 
+ *     Load the transmit DMA buffer with the specified data.
+ * 
+ * Arguments:
+ * 
+ *     info            pointer to device extension
+ *     Buffer          pointer to buffer containing frame to load
+ *     BufferSize      size in bytes of frame in Buffer
+ * 
+ * Return Value:       None
+ */
+static void mgsl_load_tx_dma_buffer(struct mgsl_struct *info,
+               const char *Buffer, unsigned int BufferSize)
+{
+       unsigned short Copycount;
+       unsigned int i = 0;
+       DMABUFFERENTRY *pBufEntry;
+       
+       if ( debug_level >= DEBUG_LEVEL_DATA )
+               mgsl_trace_block(info,Buffer, min_t(int, BufferSize, DMABUFFERSIZE), 1);
+
+       if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) {
+               /* set CMR:13 to start transmit when
+                * next GoAhead (abort) is received
+                */
+               info->cmr_value |= BIT13;                         
+       }
+               
+       /* begin loading the frame in the next available tx dma
+        * buffer, remember it's starting location for setting
+        * up tx dma operation
+        */
+       i = info->current_tx_buffer;
+       info->start_tx_dma_buffer = i;
+
+       /* Setup the status and RCC (Frame Size) fields of the 1st */
+       /* buffer entry in the transmit DMA buffer list. */
+
+       info->tx_buffer_list[i].status = info->cmr_value & 0xf000;
+       info->tx_buffer_list[i].rcc    = BufferSize;
+       info->tx_buffer_list[i].count  = BufferSize;
+
+       /* Copy frame data from 1st source buffer to the DMA buffers. */
+       /* The frame data may span multiple DMA buffers. */
+
+       while( BufferSize ){
+               /* Get a pointer to next DMA buffer entry. */
+               pBufEntry = &info->tx_buffer_list[i++];
+                       
+               if ( i == info->tx_buffer_count )
+                       i=0;
+
+               /* Calculate the number of bytes that can be copied from */
+               /* the source buffer to this DMA buffer. */
+               if ( BufferSize > DMABUFFERSIZE )
+                       Copycount = DMABUFFERSIZE;
+               else
+                       Copycount = BufferSize;
+
+               /* Actually copy data from source buffer to DMA buffer. */
+               /* Also set the data count for this individual DMA buffer. */
+               if ( info->bus_type == MGSL_BUS_TYPE_PCI )
+                       mgsl_load_pci_memory(pBufEntry->virt_addr, Buffer,Copycount);
+               else
+                       memcpy(pBufEntry->virt_addr, Buffer, Copycount);
+
+               pBufEntry->count = Copycount;
+
+               /* Advance source pointer and reduce remaining data count. */
+               Buffer += Copycount;
+               BufferSize -= Copycount;
+
+               ++info->tx_dma_buffers_used;
+       }
+
+       /* remember next available tx dma buffer */
+       info->current_tx_buffer = i;
+
+}      /* end of mgsl_load_tx_dma_buffer() */
+
+/*
+ * mgsl_register_test()
+ * 
+ *     Performs a register test of the 16C32.
+ *     
+ * Arguments:          info    pointer to device instance data
+ * Return Value:               true if test passed, otherwise false
+ */
+static bool mgsl_register_test( struct mgsl_struct *info )
+{
+       static unsigned short BitPatterns[] =
+               { 0x0000, 0xffff, 0xaaaa, 0x5555, 0x1234, 0x6969, 0x9696, 0x0f0f };
+       static unsigned int Patterncount = ARRAY_SIZE(BitPatterns);
+       unsigned int i;
+       bool rc = true;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       usc_reset(info);
+
+       /* Verify the reset state of some registers. */
+
+       if ( (usc_InReg( info, SICR ) != 0) ||
+                 (usc_InReg( info, IVR  ) != 0) ||
+                 (usc_InDmaReg( info, DIVR ) != 0) ){
+               rc = false;
+       }
+
+       if ( rc ){
+               /* Write bit patterns to various registers but do it out of */
+               /* sync, then read back and verify values. */
+
+               for ( i = 0 ; i < Patterncount ; i++ ) {
+                       usc_OutReg( info, TC0R, BitPatterns[i] );
+                       usc_OutReg( info, TC1R, BitPatterns[(i+1)%Patterncount] );
+                       usc_OutReg( info, TCLR, BitPatterns[(i+2)%Patterncount] );
+                       usc_OutReg( info, RCLR, BitPatterns[(i+3)%Patterncount] );
+                       usc_OutReg( info, RSR,  BitPatterns[(i+4)%Patterncount] );
+                       usc_OutDmaReg( info, TBCR, BitPatterns[(i+5)%Patterncount] );
+
+                       if ( (usc_InReg( info, TC0R ) != BitPatterns[i]) ||
+                                 (usc_InReg( info, TC1R ) != BitPatterns[(i+1)%Patterncount]) ||
+                                 (usc_InReg( info, TCLR ) != BitPatterns[(i+2)%Patterncount]) ||
+                                 (usc_InReg( info, RCLR ) != BitPatterns[(i+3)%Patterncount]) ||
+                                 (usc_InReg( info, RSR )  != BitPatterns[(i+4)%Patterncount]) ||
+                                 (usc_InDmaReg( info, TBCR ) != BitPatterns[(i+5)%Patterncount]) ){
+                               rc = false;
+                               break;
+                       }
+               }
+       }
+
+       usc_reset(info);
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+       return rc;
+
+}      /* end of mgsl_register_test() */
+
+/* mgsl_irq_test()     Perform interrupt test of the 16C32.
+ * 
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       true if test passed, otherwise false
+ */
+static bool mgsl_irq_test( struct mgsl_struct *info )
+{
+       unsigned long EndTime;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       usc_reset(info);
+
+       /*
+        * Setup 16C32 to interrupt on TxC pin (14MHz clock) transition. 
+        * The ISR sets irq_occurred to true.
+        */
+
+       info->irq_occurred = false;
+
+       /* Enable INTEN gate for ISA adapter (Port 6, Bit12) */
+       /* Enable INTEN (Port 6, Bit12) */
+       /* This connects the IRQ request signal to the ISA bus */
+       /* on the ISA adapter. This has no effect for the PCI adapter */
+       usc_OutReg( info, PCR, (unsigned short)((usc_InReg(info, PCR) | BIT13) & ~BIT12) );
+
+       usc_EnableMasterIrqBit(info);
+       usc_EnableInterrupts(info, IO_PIN);
+       usc_ClearIrqPendingBits(info, IO_PIN);
+       
+       usc_UnlatchIostatusBits(info, MISCSTATUS_TXC_LATCHED);
+       usc_EnableStatusIrqs(info, SICR_TXC_ACTIVE + SICR_TXC_INACTIVE);
+
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+       EndTime=100;
+       while( EndTime-- && !info->irq_occurred ) {
+               msleep_interruptible(10);
+       }
+       
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       usc_reset(info);
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       
+       return info->irq_occurred;
+
+}      /* end of mgsl_irq_test() */
+
+/* mgsl_dma_test()
+ * 
+ *     Perform a DMA test of the 16C32. A small frame is
+ *     transmitted via DMA from a transmit buffer to a receive buffer
+ *     using single buffer DMA mode.
+ *     
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       true if test passed, otherwise false
+ */
+static bool mgsl_dma_test( struct mgsl_struct *info )
+{
+       unsigned short FifoLevel;
+       unsigned long phys_addr;
+       unsigned int FrameSize;
+       unsigned int i;
+       char *TmpPtr;
+       bool rc = true;
+       unsigned short status=0;
+       unsigned long EndTime;
+       unsigned long flags;
+       MGSL_PARAMS tmp_params;
+
+       /* save current port options */
+       memcpy(&tmp_params,&info->params,sizeof(MGSL_PARAMS));
+       /* load default port options */
+       memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
+       
+#define TESTFRAMESIZE 40
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       
+       /* setup 16C32 for SDLC DMA transfer mode */
+
+       usc_reset(info);
+       usc_set_sdlc_mode(info);
+       usc_enable_loopback(info,1);
+       
+       /* Reprogram the RDMR so that the 16C32 does NOT clear the count
+        * field of the buffer entry after fetching buffer address. This
+        * way we can detect a DMA failure for a DMA read (which should be
+        * non-destructive to system memory) before we try and write to
+        * memory (where a failure could corrupt system memory).
+        */
+
+       /* Receive DMA mode Register (RDMR)
+        * 
+        * <15..14>     11      DMA mode = Linked List Buffer mode
+        * <13>         1       RSBinA/L = store Rx status Block in List entry
+        * <12>         0       1 = Clear count of List Entry after fetching
+        * <11..10>     00      Address mode = Increment
+        * <9>          1       Terminate Buffer on RxBound
+        * <8>          0       Bus Width = 16bits
+        * <7..0>               ?       status Bits (write as 0s)
+        * 
+        * 1110 0010 0000 0000 = 0xe200
+        */
+
+       usc_OutDmaReg( info, RDMR, 0xe200 );
+       
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+
+       /* SETUP TRANSMIT AND RECEIVE DMA BUFFERS */
+
+       FrameSize = TESTFRAMESIZE;
+
+       /* setup 1st transmit buffer entry: */
+       /* with frame size and transmit control word */
+
+       info->tx_buffer_list[0].count  = FrameSize;
+       info->tx_buffer_list[0].rcc    = FrameSize;
+       info->tx_buffer_list[0].status = 0x4000;
+
+       /* build a transmit frame in 1st transmit DMA buffer */
+
+       TmpPtr = info->tx_buffer_list[0].virt_addr;
+       for (i = 0; i < FrameSize; i++ )
+               *TmpPtr++ = i;
+
+       /* setup 1st receive buffer entry: */
+       /* clear status, set max receive buffer size */
+
+       info->rx_buffer_list[0].status = 0;
+       info->rx_buffer_list[0].count = FrameSize + 4;
+
+       /* zero out the 1st receive buffer */
+
+       memset( info->rx_buffer_list[0].virt_addr, 0, FrameSize + 4 );
+
+       /* Set count field of next buffer entries to prevent */
+       /* 16C32 from using buffers after the 1st one. */
+
+       info->tx_buffer_list[1].count = 0;
+       info->rx_buffer_list[1].count = 0;
+       
+
+       /***************************/
+       /* Program 16C32 receiver. */
+       /***************************/
+       
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+
+       /* setup DMA transfers */
+       usc_RTCmd( info, RTCmd_PurgeRxFifo );
+
+       /* program 16C32 receiver with physical address of 1st DMA buffer entry */
+       phys_addr = info->rx_buffer_list[0].phys_entry;
+       usc_OutDmaReg( info, NRARL, (unsigned short)phys_addr );
+       usc_OutDmaReg( info, NRARU, (unsigned short)(phys_addr >> 16) );
+
+       /* Clear the Rx DMA status bits (read RDMR) and start channel */
+       usc_InDmaReg( info, RDMR );
+       usc_DmaCmd( info, DmaCmd_InitRxChannel );
+
+       /* Enable Receiver (RMR <1..0> = 10) */
+       usc_OutReg( info, RMR, (unsigned short)((usc_InReg(info, RMR) & 0xfffc) | 0x0002) );
+       
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+
+       /*************************************************************/
+       /* WAIT FOR RECEIVER TO DMA ALL PARAMETERS FROM BUFFER ENTRY */
+       /*************************************************************/
+
+       /* Wait 100ms for interrupt. */
+       EndTime = jiffies + msecs_to_jiffies(100);
+
+       for(;;) {
+               if (time_after(jiffies, EndTime)) {
+                       rc = false;
+                       break;
+               }
+
+               spin_lock_irqsave(&info->irq_spinlock,flags);
+               status = usc_InDmaReg( info, RDMR );
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+               if ( !(status & BIT4) && (status & BIT5) ) {
+                       /* INITG (BIT 4) is inactive (no entry read in progress) AND */
+                       /* BUSY  (BIT 5) is active (channel still active). */
+                       /* This means the buffer entry read has completed. */
+                       break;
+               }
+       }
+
+
+       /******************************/
+       /* Program 16C32 transmitter. */
+       /******************************/
+       
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+
+       /* Program the Transmit Character Length Register (TCLR) */
+       /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */
+
+       usc_OutReg( info, TCLR, (unsigned short)info->tx_buffer_list[0].count );
+       usc_RTCmd( info, RTCmd_PurgeTxFifo );
+
+       /* Program the address of the 1st DMA Buffer Entry in linked list */
+
+       phys_addr = info->tx_buffer_list[0].phys_entry;
+       usc_OutDmaReg( info, NTARL, (unsigned short)phys_addr );
+       usc_OutDmaReg( info, NTARU, (unsigned short)(phys_addr >> 16) );
+
+       /* unlatch Tx status bits, and start transmit channel. */
+
+       usc_OutReg( info, TCSR, (unsigned short)(( usc_InReg(info, TCSR) & 0x0f00) | 0xfa) );
+       usc_DmaCmd( info, DmaCmd_InitTxChannel );
+
+       /* wait for DMA controller to fill transmit FIFO */
+
+       usc_TCmd( info, TCmd_SelectTicrTxFifostatus );
+       
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+
+       /**********************************/
+       /* WAIT FOR TRANSMIT FIFO TO FILL */
+       /**********************************/
+       
+       /* Wait 100ms */
+       EndTime = jiffies + msecs_to_jiffies(100);
+
+       for(;;) {
+               if (time_after(jiffies, EndTime)) {
+                       rc = false;
+                       break;
+               }
+
+               spin_lock_irqsave(&info->irq_spinlock,flags);
+               FifoLevel = usc_InReg(info, TICR) >> 8;
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+                       
+               if ( FifoLevel < 16 )
+                       break;
+               else
+                       if ( FrameSize < 32 ) {
+                               /* This frame is smaller than the entire transmit FIFO */
+                               /* so wait for the entire frame to be loaded. */
+                               if ( FifoLevel <= (32 - FrameSize) )
+                                       break;
+                       }
+       }
+
+
+       if ( rc )
+       {
+               /* Enable 16C32 transmitter. */
+
+               spin_lock_irqsave(&info->irq_spinlock,flags);
+               
+               /* Transmit mode Register (TMR), <1..0> = 10, Enable Transmitter */
+               usc_TCmd( info, TCmd_SendFrame );
+               usc_OutReg( info, TMR, (unsigned short)((usc_InReg(info, TMR) & 0xfffc) | 0x0002) );
+               
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+                                               
+               /******************************/
+               /* WAIT FOR TRANSMIT COMPLETE */
+               /******************************/
+
+               /* Wait 100ms */
+               EndTime = jiffies + msecs_to_jiffies(100);
+
+               /* While timer not expired wait for transmit complete */
+
+               spin_lock_irqsave(&info->irq_spinlock,flags);
+               status = usc_InReg( info, TCSR );
+               spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+               while ( !(status & (BIT6+BIT5+BIT4+BIT2+BIT1)) ) {
+                       if (time_after(jiffies, EndTime)) {
+                               rc = false;
+                               break;
+                       }
+
+                       spin_lock_irqsave(&info->irq_spinlock,flags);
+                       status = usc_InReg( info, TCSR );
+                       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+               }
+       }
+
+
+       if ( rc ){
+               /* CHECK FOR TRANSMIT ERRORS */
+               if ( status & (BIT5 + BIT1) ) 
+                       rc = false;
+       }
+
+       if ( rc ) {
+               /* WAIT FOR RECEIVE COMPLETE */
+
+               /* Wait 100ms */
+               EndTime = jiffies + msecs_to_jiffies(100);
+
+               /* Wait for 16C32 to write receive status to buffer entry. */
+               status=info->rx_buffer_list[0].status;
+               while ( status == 0 ) {
+                       if (time_after(jiffies, EndTime)) {
+                               rc = false;
+                               break;
+                       }
+                       status=info->rx_buffer_list[0].status;
+               }
+       }
+
+
+       if ( rc ) {
+               /* CHECK FOR RECEIVE ERRORS */
+               status = info->rx_buffer_list[0].status;
+
+               if ( status & (BIT8 + BIT3 + BIT1) ) {
+                       /* receive error has occurred */
+                       rc = false;
+               } else {
+                       if ( memcmp( info->tx_buffer_list[0].virt_addr ,
+                               info->rx_buffer_list[0].virt_addr, FrameSize ) ){
+                               rc = false;
+                       }
+               }
+       }
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       usc_reset( info );
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+       /* restore current port options */
+       memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
+       
+       return rc;
+
+}      /* end of mgsl_dma_test() */
+
+/* mgsl_adapter_test()
+ * 
+ *     Perform the register, IRQ, and DMA tests for the 16C32.
+ *     
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       0 if success, otherwise -ENODEV
+ */
+static int mgsl_adapter_test( struct mgsl_struct *info )
+{
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):Testing device %s\n",
+                       __FILE__,__LINE__,info->device_name );
+                       
+       if ( !mgsl_register_test( info ) ) {
+               info->init_error = DiagStatus_AddressFailure;
+               printk( "%s(%d):Register test failure for device %s Addr=%04X\n",
+                       __FILE__,__LINE__,info->device_name, (unsigned short)(info->io_base) );
+               return -ENODEV;
+       }
+
+       if ( !mgsl_irq_test( info ) ) {
+               info->init_error = DiagStatus_IrqFailure;
+               printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n",
+                       __FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) );
+               return -ENODEV;
+       }
+
+       if ( !mgsl_dma_test( info ) ) {
+               info->init_error = DiagStatus_DmaFailure;
+               printk( "%s(%d):DMA test failure for device %s DMA=%d\n",
+                       __FILE__,__LINE__,info->device_name, (unsigned short)(info->dma_level) );
+               return -ENODEV;
+       }
+
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):device %s passed diagnostics\n",
+                       __FILE__,__LINE__,info->device_name );
+                       
+       return 0;
+
+}      /* end of mgsl_adapter_test() */
+
+/* mgsl_memory_test()
+ * 
+ *     Test the shared memory on a PCI adapter.
+ * 
+ * Arguments:          info    pointer to device instance data
+ * Return Value:       true if test passed, otherwise false
+ */
+static bool mgsl_memory_test( struct mgsl_struct *info )
+{
+       static unsigned long BitPatterns[] =
+               { 0x0, 0x55555555, 0xaaaaaaaa, 0x66666666, 0x99999999, 0xffffffff, 0x12345678 };
+       unsigned long Patterncount = ARRAY_SIZE(BitPatterns);
+       unsigned long i;
+       unsigned long TestLimit = SHARED_MEM_ADDRESS_SIZE/sizeof(unsigned long);
+       unsigned long * TestAddr;
+
+       if ( info->bus_type != MGSL_BUS_TYPE_PCI )
+               return true;
+
+       TestAddr = (unsigned long *)info->memory_base;
+
+       /* Test data lines with test pattern at one location. */
+
+       for ( i = 0 ; i < Patterncount ; i++ ) {
+               *TestAddr = BitPatterns[i];
+               if ( *TestAddr != BitPatterns[i] )
+                       return false;
+       }
+
+       /* Test address lines with incrementing pattern over */
+       /* entire address range. */
+
+       for ( i = 0 ; i < TestLimit ; i++ ) {
+               *TestAddr = i * 4;
+               TestAddr++;
+       }
+
+       TestAddr = (unsigned long *)info->memory_base;
+
+       for ( i = 0 ; i < TestLimit ; i++ ) {
+               if ( *TestAddr != i * 4 )
+                       return false;
+               TestAddr++;
+       }
+
+       memset( info->memory_base, 0, SHARED_MEM_ADDRESS_SIZE );
+
+       return true;
+
+}      /* End Of mgsl_memory_test() */
+
+
+/* mgsl_load_pci_memory()
+ * 
+ *     Load a large block of data into the PCI shared memory.
+ *     Use this instead of memcpy() or memmove() to move data
+ *     into the PCI shared memory.
+ * 
+ * Notes:
+ * 
+ *     This function prevents the PCI9050 interface chip from hogging
+ *     the adapter local bus, which can starve the 16C32 by preventing
+ *     16C32 bus master cycles.
+ * 
+ *     The PCI9050 documentation says that the 9050 will always release
+ *     control of the local bus after completing the current read
+ *     or write operation.
+ * 
+ *     It appears that as long as the PCI9050 write FIFO is full, the
+ *     PCI9050 treats all of the writes as a single burst transaction
+ *     and will not release the bus. This causes DMA latency problems
+ *     at high speeds when copying large data blocks to the shared
+ *     memory.
+ * 
+ *     This function in effect, breaks the a large shared memory write
+ *     into multiple transations by interleaving a shared memory read
+ *     which will flush the write FIFO and 'complete' the write
+ *     transation. This allows any pending DMA request to gain control
+ *     of the local bus in a timely fasion.
+ * 
+ * Arguments:
+ * 
+ *     TargetPtr       pointer to target address in PCI shared memory
+ *     SourcePtr       pointer to source buffer for data
+ *     count           count in bytes of data to copy
+ *
+ * Return Value:       None
+ */
+static void mgsl_load_pci_memory( char* TargetPtr, const char* SourcePtr,
+       unsigned short count )
+{
+       /* 16 32-bit writes @ 60ns each = 960ns max latency on local bus */
+#define PCI_LOAD_INTERVAL 64
+
+       unsigned short Intervalcount = count / PCI_LOAD_INTERVAL;
+       unsigned short Index;
+       unsigned long Dummy;
+
+       for ( Index = 0 ; Index < Intervalcount ; Index++ )
+       {
+               memcpy(TargetPtr, SourcePtr, PCI_LOAD_INTERVAL);
+               Dummy = *((volatile unsigned long *)TargetPtr);
+               TargetPtr += PCI_LOAD_INTERVAL;
+               SourcePtr += PCI_LOAD_INTERVAL;
+       }
+
+       memcpy( TargetPtr, SourcePtr, count % PCI_LOAD_INTERVAL );
+
+}      /* End Of mgsl_load_pci_memory() */
+
+static void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit)
+{
+       int i;
+       int linecount;
+       if (xmit)
+               printk("%s tx data:\n",info->device_name);
+       else
+               printk("%s rx data:\n",info->device_name);
+               
+       while(count) {
+               if (count > 16)
+                       linecount = 16;
+               else
+                       linecount = count;
+                       
+               for(i=0;i<linecount;i++)
+                       printk("%02X ",(unsigned char)data[i]);
+               for(;i<17;i++)
+                       printk("   ");
+               for(i=0;i<linecount;i++) {
+                       if (data[i]>=040 && data[i]<=0176)
+                               printk("%c",data[i]);
+                       else
+                               printk(".");
+               }
+               printk("\n");
+               
+               data  += linecount;
+               count -= linecount;
+       }
+}      /* end of mgsl_trace_block() */
+
+/* mgsl_tx_timeout()
+ * 
+ *     called when HDLC frame times out
+ *     update stats and do tx completion processing
+ *     
+ * Arguments:  context         pointer to device instance data
+ * Return Value:       None
+ */
+static void mgsl_tx_timeout(unsigned long context)
+{
+       struct mgsl_struct *info = (struct mgsl_struct*)context;
+       unsigned long flags;
+       
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):mgsl_tx_timeout(%s)\n",
+                       __FILE__,__LINE__,info->device_name);
+       if(info->tx_active &&
+          (info->params.mode == MGSL_MODE_HDLC ||
+           info->params.mode == MGSL_MODE_RAW) ) {
+               info->icount.txtimeout++;
+       }
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       info->tx_active = false;
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+       if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE )
+               usc_loopmode_cancel_transmit( info );
+
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+       
+#if SYNCLINK_GENERIC_HDLC
+       if (info->netcount)
+               hdlcdev_tx_done(info);
+       else
+#endif
+               mgsl_bh_transmit(info);
+       
+}      /* end of mgsl_tx_timeout() */
+
+/* signal that there are no more frames to send, so that
+ * line is 'released' by echoing RxD to TxD when current
+ * transmission is complete (or immediately if no tx in progress).
+ */
+static int mgsl_loopmode_send_done( struct mgsl_struct * info )
+{
+       unsigned long flags;
+       
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) {
+               if (info->tx_active)
+                       info->loopmode_send_done_requested = true;
+               else
+                       usc_loopmode_send_done(info);
+       }
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+       return 0;
+}
+
+/* release the line by echoing RxD to TxD
+ * upon completion of a transmit frame
+ */
+static void usc_loopmode_send_done( struct mgsl_struct * info )
+{
+       info->loopmode_send_done_requested = false;
+       /* clear CMR:13 to 0 to start echoing RxData to TxData */
+       info->cmr_value &= ~BIT13;                        
+       usc_OutReg(info, CMR, info->cmr_value);
+}
+
+/* abort a transmit in progress while in HDLC LoopMode
+ */
+static void usc_loopmode_cancel_transmit( struct mgsl_struct * info )
+{
+       /* reset tx dma channel and purge TxFifo */
+       usc_RTCmd( info, RTCmd_PurgeTxFifo );
+       usc_DmaCmd( info, DmaCmd_ResetTxChannel );
+       usc_loopmode_send_done( info );
+}
+
+/* for HDLC/SDLC LoopMode, setting CMR:13 after the transmitter is enabled
+ * is an Insert Into Loop action. Upon receipt of a GoAhead sequence (RxAbort)
+ * we must clear CMR:13 to begin repeating TxData to RxData
+ */
+static void usc_loopmode_insert_request( struct mgsl_struct * info )
+{
+       info->loopmode_insert_requested = true;
+       /* enable RxAbort irq. On next RxAbort, clear CMR:13 to
+        * begin repeating TxData on RxData (complete insertion)
+        */
+       usc_OutReg( info, RICR, 
+               (usc_InReg( info, RICR ) | RXSTATUS_ABORT_RECEIVED ) );
+               
+       /* set CMR:13 to insert into loop on next GoAhead (RxAbort) */
+       info->cmr_value |= BIT13;
+       usc_OutReg(info, CMR, info->cmr_value);
+}
+
+/* return 1 if station is inserted into the loop, otherwise 0
+ */
+static int usc_loopmode_active( struct mgsl_struct * info)
+{
+       return usc_InReg( info, CCSR ) & BIT7 ? 1 : 0 ;
+}
+
+#if SYNCLINK_GENERIC_HDLC
+
+/**
+ * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.)
+ * set encoding and frame check sequence (FCS) options
+ *
+ * dev       pointer to network device structure
+ * encoding  serial encoding setting
+ * parity    FCS setting
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
+                         unsigned short parity)
+{
+       struct mgsl_struct *info = dev_to_port(dev);
+       unsigned char  new_encoding;
+       unsigned short new_crctype;
+
+       /* return error if TTY interface open */
+       if (info->port.count)
+               return -EBUSY;
+
+       switch (encoding)
+       {
+       case ENCODING_NRZ:        new_encoding = HDLC_ENCODING_NRZ; break;
+       case ENCODING_NRZI:       new_encoding = HDLC_ENCODING_NRZI_SPACE; break;
+       case ENCODING_FM_MARK:    new_encoding = HDLC_ENCODING_BIPHASE_MARK; break;
+       case ENCODING_FM_SPACE:   new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break;
+       case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break;
+       default: return -EINVAL;
+       }
+
+       switch (parity)
+       {
+       case PARITY_NONE:            new_crctype = HDLC_CRC_NONE; break;
+       case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break;
+       case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break;
+       default: return -EINVAL;
+       }
+
+       info->params.encoding = new_encoding;
+       info->params.crc_type = new_crctype;
+
+       /* if network interface up, reprogram hardware */
+       if (info->netcount)
+               mgsl_program_hw(info);
+
+       return 0;
+}
+
+/**
+ * called by generic HDLC layer to send frame
+ *
+ * skb  socket buffer containing HDLC frame
+ * dev  pointer to network device structure
+ */
+static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
+                                     struct net_device *dev)
+{
+       struct mgsl_struct *info = dev_to_port(dev);
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk(KERN_INFO "%s:hdlc_xmit(%s)\n",__FILE__,dev->name);
+
+       /* stop sending until this frame completes */
+       netif_stop_queue(dev);
+
+       /* copy data to device buffers */
+       info->xmit_cnt = skb->len;
+       mgsl_load_tx_dma_buffer(info, skb->data, skb->len);
+
+       /* update network statistics */
+       dev->stats.tx_packets++;
+       dev->stats.tx_bytes += skb->len;
+
+       /* done with socket buffer, so free it */
+       dev_kfree_skb(skb);
+
+       /* save start time for transmit timeout detection */
+       dev->trans_start = jiffies;
+
+       /* start hardware transmitter if necessary */
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       if (!info->tx_active)
+               usc_start_transmitter(info);
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+       return NETDEV_TX_OK;
+}
+
+/**
+ * called by network layer when interface enabled
+ * claim resources and initialize hardware
+ *
+ * dev  pointer to network device structure
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_open(struct net_device *dev)
+{
+       struct mgsl_struct *info = dev_to_port(dev);
+       int rc;
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s:hdlcdev_open(%s)\n",__FILE__,dev->name);
+
+       /* generic HDLC layer open processing */
+       if ((rc = hdlc_open(dev)))
+               return rc;
+
+       /* arbitrate between network and tty opens */
+       spin_lock_irqsave(&info->netlock, flags);
+       if (info->port.count != 0 || info->netcount != 0) {
+               printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name);
+               spin_unlock_irqrestore(&info->netlock, flags);
+               return -EBUSY;
+       }
+       info->netcount=1;
+       spin_unlock_irqrestore(&info->netlock, flags);
+
+       /* claim resources and init adapter */
+       if ((rc = startup(info)) != 0) {
+               spin_lock_irqsave(&info->netlock, flags);
+               info->netcount=0;
+               spin_unlock_irqrestore(&info->netlock, flags);
+               return rc;
+       }
+
+       /* assert DTR and RTS, apply hardware settings */
+       info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+       mgsl_program_hw(info);
+
+       /* enable network layer transmit */
+       dev->trans_start = jiffies;
+       netif_start_queue(dev);
+
+       /* inform generic HDLC layer of current DCD status */
+       spin_lock_irqsave(&info->irq_spinlock, flags);
+       usc_get_serial_signals(info);
+       spin_unlock_irqrestore(&info->irq_spinlock, flags);
+       if (info->serial_signals & SerialSignal_DCD)
+               netif_carrier_on(dev);
+       else
+               netif_carrier_off(dev);
+       return 0;
+}
+
+/**
+ * called by network layer when interface is disabled
+ * shutdown hardware and release resources
+ *
+ * dev  pointer to network device structure
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_close(struct net_device *dev)
+{
+       struct mgsl_struct *info = dev_to_port(dev);
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s:hdlcdev_close(%s)\n",__FILE__,dev->name);
+
+       netif_stop_queue(dev);
+
+       /* shutdown adapter and release resources */
+       shutdown(info);
+
+       hdlc_close(dev);
+
+       spin_lock_irqsave(&info->netlock, flags);
+       info->netcount=0;
+       spin_unlock_irqrestore(&info->netlock, flags);
+
+       return 0;
+}
+
+/**
+ * called by network layer to process IOCTL call to network device
+ *
+ * dev  pointer to network device structure
+ * ifr  pointer to network interface request structure
+ * cmd  IOCTL command code
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       const size_t size = sizeof(sync_serial_settings);
+       sync_serial_settings new_line;
+       sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
+       struct mgsl_struct *info = dev_to_port(dev);
+       unsigned int flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s:hdlcdev_ioctl(%s)\n",__FILE__,dev->name);
+
+       /* return error if TTY interface open */
+       if (info->port.count)
+               return -EBUSY;
+
+       if (cmd != SIOCWANDEV)
+               return hdlc_ioctl(dev, ifr, cmd);
+
+       switch(ifr->ifr_settings.type) {
+       case IF_GET_IFACE: /* return current sync_serial_settings */
+
+               ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
+               if (ifr->ifr_settings.size < size) {
+                       ifr->ifr_settings.size = size; /* data size wanted */
+                       return -ENOBUFS;
+               }
+
+               flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
+                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
+                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
+                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
+
+               switch (flags){
+               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break;
+               case (HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_INT; break;
+               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_TXINT; break;
+               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break;
+               default: new_line.clock_type = CLOCK_DEFAULT;
+               }
+
+               new_line.clock_rate = info->params.clock_speed;
+               new_line.loopback   = info->params.loopback ? 1:0;
+
+               if (copy_to_user(line, &new_line, size))
+                       return -EFAULT;
+               return 0;
+
+       case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */
+
+               if(!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               if (copy_from_user(&new_line, line, size))
+                       return -EFAULT;
+
+               switch (new_line.clock_type)
+               {
+               case CLOCK_EXT:      flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break;
+               case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break;
+               case CLOCK_INT:      flags = HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG;    break;
+               case CLOCK_TXINT:    flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG;    break;
+               case CLOCK_DEFAULT:  flags = info->params.flags &
+                                            (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
+                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
+                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
+                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN); break;
+               default: return -EINVAL;
+               }
+
+               if (new_line.loopback != 0 && new_line.loopback != 1)
+                       return -EINVAL;
+
+               info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
+                                       HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
+                                       HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
+                                       HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
+               info->params.flags |= flags;
+
+               info->params.loopback = new_line.loopback;
+
+               if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG))
+                       info->params.clock_speed = new_line.clock_rate;
+               else
+                       info->params.clock_speed = 0;
+
+               /* if network interface up, reprogram hardware */
+               if (info->netcount)
+                       mgsl_program_hw(info);
+               return 0;
+
+       default:
+               return hdlc_ioctl(dev, ifr, cmd);
+       }
+}
+
+/**
+ * called by network layer when transmit timeout is detected
+ *
+ * dev  pointer to network device structure
+ */
+static void hdlcdev_tx_timeout(struct net_device *dev)
+{
+       struct mgsl_struct *info = dev_to_port(dev);
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("hdlcdev_tx_timeout(%s)\n",dev->name);
+
+       dev->stats.tx_errors++;
+       dev->stats.tx_aborted_errors++;
+
+       spin_lock_irqsave(&info->irq_spinlock,flags);
+       usc_stop_transmitter(info);
+       spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+       netif_wake_queue(dev);
+}
+
+/**
+ * called by device driver when transmit completes
+ * reenable network layer transmit if stopped
+ *
+ * info  pointer to device instance information
+ */
+static void hdlcdev_tx_done(struct mgsl_struct *info)
+{
+       if (netif_queue_stopped(info->netdev))
+               netif_wake_queue(info->netdev);
+}
+
+/**
+ * called by device driver when frame received
+ * pass frame to network layer
+ *
+ * info  pointer to device instance information
+ * buf   pointer to buffer contianing frame data
+ * size  count of data bytes in buf
+ */
+static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size)
+{
+       struct sk_buff *skb = dev_alloc_skb(size);
+       struct net_device *dev = info->netdev;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("hdlcdev_rx(%s)\n", dev->name);
+
+       if (skb == NULL) {
+               printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n",
+                      dev->name);
+               dev->stats.rx_dropped++;
+               return;
+       }
+
+       memcpy(skb_put(skb, size), buf, size);
+
+       skb->protocol = hdlc_type_trans(skb, dev);
+
+       dev->stats.rx_packets++;
+       dev->stats.rx_bytes += size;
+
+       netif_rx(skb);
+}
+
+static const struct net_device_ops hdlcdev_ops = {
+       .ndo_open       = hdlcdev_open,
+       .ndo_stop       = hdlcdev_close,
+       .ndo_change_mtu = hdlc_change_mtu,
+       .ndo_start_xmit = hdlc_start_xmit,
+       .ndo_do_ioctl   = hdlcdev_ioctl,
+       .ndo_tx_timeout = hdlcdev_tx_timeout,
+};
+
+/**
+ * called by device driver when adding device instance
+ * do generic HDLC initialization
+ *
+ * info  pointer to device instance information
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_init(struct mgsl_struct *info)
+{
+       int rc;
+       struct net_device *dev;
+       hdlc_device *hdlc;
+
+       /* allocate and initialize network and HDLC layer objects */
+
+       if (!(dev = alloc_hdlcdev(info))) {
+               printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__);
+               return -ENOMEM;
+       }
+
+       /* for network layer reporting purposes only */
+       dev->base_addr = info->io_base;
+       dev->irq       = info->irq_level;
+       dev->dma       = info->dma_level;
+
+       /* network layer callbacks and settings */
+       dev->netdev_ops     = &hdlcdev_ops;
+       dev->watchdog_timeo = 10 * HZ;
+       dev->tx_queue_len   = 50;
+
+       /* generic HDLC layer callbacks and settings */
+       hdlc         = dev_to_hdlc(dev);
+       hdlc->attach = hdlcdev_attach;
+       hdlc->xmit   = hdlcdev_xmit;
+
+       /* register objects with HDLC layer */
+       if ((rc = register_hdlc_device(dev))) {
+               printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__);
+               free_netdev(dev);
+               return rc;
+       }
+
+       info->netdev = dev;
+       return 0;
+}
+
+/**
+ * called by device driver when removing device instance
+ * do generic HDLC cleanup
+ *
+ * info  pointer to device instance information
+ */
+static void hdlcdev_exit(struct mgsl_struct *info)
+{
+       unregister_hdlc_device(info->netdev);
+       free_netdev(info->netdev);
+       info->netdev = NULL;
+}
+
+#endif /* CONFIG_HDLC */
+
+
+static int __devinit synclink_init_one (struct pci_dev *dev,
+                                       const struct pci_device_id *ent)
+{
+       struct mgsl_struct *info;
+
+       if (pci_enable_device(dev)) {
+               printk("error enabling pci device %p\n", dev);
+               return -EIO;
+       }
+
+       if (!(info = mgsl_allocate_device())) {
+               printk("can't allocate device instance data.\n");
+               return -EIO;
+       }
+
+        /* Copy user configuration info to device instance data */
+               
+       info->io_base = pci_resource_start(dev, 2);
+       info->irq_level = dev->irq;
+       info->phys_memory_base = pci_resource_start(dev, 3);
+                               
+        /* Because veremap only works on page boundaries we must map
+        * a larger area than is actually implemented for the LCR
+        * memory range. We map a full page starting at the page boundary.
+        */
+       info->phys_lcr_base = pci_resource_start(dev, 0);
+       info->lcr_offset    = info->phys_lcr_base & (PAGE_SIZE-1);
+       info->phys_lcr_base &= ~(PAGE_SIZE-1);
+                               
+       info->bus_type = MGSL_BUS_TYPE_PCI;
+       info->io_addr_size = 8;
+       info->irq_flags = IRQF_SHARED;
+
+       if (dev->device == 0x0210) {
+               /* Version 1 PCI9030 based universal PCI adapter */
+               info->misc_ctrl_value = 0x007c4080;
+               info->hw_version = 1;
+       } else {
+               /* Version 0 PCI9050 based 5V PCI adapter
+                * A PCI9050 bug prevents reading LCR registers if 
+                * LCR base address bit 7 is set. Maintain shadow
+                * value so we can write to LCR misc control reg.
+                */
+               info->misc_ctrl_value = 0x087e4546;
+               info->hw_version = 0;
+       }
+                               
+       mgsl_add_device(info);
+
+       return 0;
+}
+
+static void __devexit synclink_remove_one (struct pci_dev *dev)
+{
+}
+
diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c
new file mode 100644 (file)
index 0000000..a35dd54
--- /dev/null
@@ -0,0 +1,5161 @@
+/*
+ * Device driver for Microgate SyncLink GT serial adapters.
+ *
+ * written by Paul Fulghum for Microgate Corporation
+ * paulkf@microgate.com
+ *
+ * Microgate and SyncLink are trademarks of Microgate Corporation
+ *
+ * This code is released under the GNU General Public License (GPL)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * DEBUG OUTPUT DEFINITIONS
+ *
+ * uncomment lines below to enable specific types of debug output
+ *
+ * DBGINFO   information - most verbose output
+ * DBGERR    serious errors
+ * DBGBH     bottom half service routine debugging
+ * DBGISR    interrupt service routine debugging
+ * DBGDATA   output receive and transmit data
+ * DBGTBUF   output transmit DMA buffers and registers
+ * DBGRBUF   output receive DMA buffers and registers
+ */
+
+#define DBGINFO(fmt) if (debug_level >= DEBUG_LEVEL_INFO) printk fmt
+#define DBGERR(fmt) if (debug_level >= DEBUG_LEVEL_ERROR) printk fmt
+#define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt
+#define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt
+#define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label))
+/*#define DBGTBUF(info) dump_tbufs(info)*/
+/*#define DBGRBUF(info) dump_rbufs(info)*/
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ioctl.h>
+#include <linux/termios.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/hdlc.h>
+#include <linux/synclink.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+
+#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE))
+#define SYNCLINK_GENERIC_HDLC 1
+#else
+#define SYNCLINK_GENERIC_HDLC 0
+#endif
+
+/*
+ * module identification
+ */
+static char *driver_name     = "SyncLink GT";
+static char *tty_driver_name = "synclink_gt";
+static char *tty_dev_prefix  = "ttySLG";
+MODULE_LICENSE("GPL");
+#define MGSL_MAGIC 0x5401
+#define MAX_DEVICES 32
+
+static struct pci_device_id pci_table[] = {
+       {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+       {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT2_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+       {PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT4_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+       {PCI_VENDOR_ID_MICROGATE, SYNCLINK_AC_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+       {0,}, /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, pci_table);
+
+static int  init_one(struct pci_dev *dev,const struct pci_device_id *ent);
+static void remove_one(struct pci_dev *dev);
+static struct pci_driver pci_driver = {
+       .name           = "synclink_gt",
+       .id_table       = pci_table,
+       .probe          = init_one,
+       .remove         = __devexit_p(remove_one),
+};
+
+static bool pci_registered;
+
+/*
+ * module configuration and status
+ */
+static struct slgt_info *slgt_device_list;
+static int slgt_device_count;
+
+static int ttymajor;
+static int debug_level;
+static int maxframe[MAX_DEVICES];
+
+module_param(ttymajor, int, 0);
+module_param(debug_level, int, 0);
+module_param_array(maxframe, int, NULL, 0);
+
+MODULE_PARM_DESC(ttymajor, "TTY major device number override: 0=auto assigned");
+MODULE_PARM_DESC(debug_level, "Debug syslog output: 0=disabled, 1 to 5=increasing detail");
+MODULE_PARM_DESC(maxframe, "Maximum frame size used by device (4096 to 65535)");
+
+/*
+ * tty support and callbacks
+ */
+static struct tty_driver *serial_driver;
+
+static int  open(struct tty_struct *tty, struct file * filp);
+static void close(struct tty_struct *tty, struct file * filp);
+static void hangup(struct tty_struct *tty);
+static void set_termios(struct tty_struct *tty, struct ktermios *old_termios);
+
+static int  write(struct tty_struct *tty, const unsigned char *buf, int count);
+static int put_char(struct tty_struct *tty, unsigned char ch);
+static void send_xchar(struct tty_struct *tty, char ch);
+static void wait_until_sent(struct tty_struct *tty, int timeout);
+static int  write_room(struct tty_struct *tty);
+static void flush_chars(struct tty_struct *tty);
+static void flush_buffer(struct tty_struct *tty);
+static void tx_hold(struct tty_struct *tty);
+static void tx_release(struct tty_struct *tty);
+
+static int  ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
+static int  chars_in_buffer(struct tty_struct *tty);
+static void throttle(struct tty_struct * tty);
+static void unthrottle(struct tty_struct * tty);
+static int set_break(struct tty_struct *tty, int break_state);
+
+/*
+ * generic HDLC support and callbacks
+ */
+#if SYNCLINK_GENERIC_HDLC
+#define dev_to_port(D) (dev_to_hdlc(D)->priv)
+static void hdlcdev_tx_done(struct slgt_info *info);
+static void hdlcdev_rx(struct slgt_info *info, char *buf, int size);
+static int  hdlcdev_init(struct slgt_info *info);
+static void hdlcdev_exit(struct slgt_info *info);
+#endif
+
+
+/*
+ * device specific structures, macros and functions
+ */
+
+#define SLGT_MAX_PORTS 4
+#define SLGT_REG_SIZE  256
+
+/*
+ * conditional wait facility
+ */
+struct cond_wait {
+       struct cond_wait *next;
+       wait_queue_head_t q;
+       wait_queue_t wait;
+       unsigned int data;
+};
+static void init_cond_wait(struct cond_wait *w, unsigned int data);
+static void add_cond_wait(struct cond_wait **head, struct cond_wait *w);
+static void remove_cond_wait(struct cond_wait **head, struct cond_wait *w);
+static void flush_cond_wait(struct cond_wait **head);
+
+/*
+ * DMA buffer descriptor and access macros
+ */
+struct slgt_desc
+{
+       __le16 count;
+       __le16 status;
+       __le32 pbuf;  /* physical address of data buffer */
+       __le32 next;  /* physical address of next descriptor */
+
+       /* driver book keeping */
+       char *buf;          /* virtual  address of data buffer */
+       unsigned int pdesc; /* physical address of this descriptor */
+       dma_addr_t buf_dma_addr;
+       unsigned short buf_count;
+};
+
+#define set_desc_buffer(a,b) (a).pbuf = cpu_to_le32((unsigned int)(b))
+#define set_desc_next(a,b) (a).next   = cpu_to_le32((unsigned int)(b))
+#define set_desc_count(a,b)(a).count  = cpu_to_le16((unsigned short)(b))
+#define set_desc_eof(a,b)  (a).status = cpu_to_le16((b) ? (le16_to_cpu((a).status) | BIT0) : (le16_to_cpu((a).status) & ~BIT0))
+#define set_desc_status(a, b) (a).status = cpu_to_le16((unsigned short)(b))
+#define desc_count(a)      (le16_to_cpu((a).count))
+#define desc_status(a)     (le16_to_cpu((a).status))
+#define desc_complete(a)   (le16_to_cpu((a).status) & BIT15)
+#define desc_eof(a)        (le16_to_cpu((a).status) & BIT2)
+#define desc_crc_error(a)  (le16_to_cpu((a).status) & BIT1)
+#define desc_abort(a)      (le16_to_cpu((a).status) & BIT0)
+#define desc_residue(a)    ((le16_to_cpu((a).status) & 0x38) >> 3)
+
+struct _input_signal_events {
+       int ri_up;
+       int ri_down;
+       int dsr_up;
+       int dsr_down;
+       int dcd_up;
+       int dcd_down;
+       int cts_up;
+       int cts_down;
+};
+
+/*
+ * device instance data structure
+ */
+struct slgt_info {
+       void *if_ptr;           /* General purpose pointer (used by SPPP) */
+       struct tty_port port;
+
+       struct slgt_info *next_device;  /* device list link */
+
+       int magic;
+
+       char device_name[25];
+       struct pci_dev *pdev;
+
+       int port_count;  /* count of ports on adapter */
+       int adapter_num; /* adapter instance number */
+       int port_num;    /* port instance number */
+
+       /* array of pointers to port contexts on this adapter */
+       struct slgt_info *port_array[SLGT_MAX_PORTS];
+
+       int                     line;           /* tty line instance number */
+
+       struct mgsl_icount      icount;
+
+       int                     timeout;
+       int                     x_char;         /* xon/xoff character */
+       unsigned int            read_status_mask;
+       unsigned int            ignore_status_mask;
+
+       wait_queue_head_t       status_event_wait_q;
+       wait_queue_head_t       event_wait_q;
+       struct timer_list       tx_timer;
+       struct timer_list       rx_timer;
+
+       unsigned int            gpio_present;
+       struct cond_wait        *gpio_wait_q;
+
+       spinlock_t lock;        /* spinlock for synchronizing with ISR */
+
+       struct work_struct task;
+       u32 pending_bh;
+       bool bh_requested;
+       bool bh_running;
+
+       int isr_overflow;
+       bool irq_requested;     /* true if IRQ requested */
+       bool irq_occurred;      /* for diagnostics use */
+
+       /* device configuration */
+
+       unsigned int bus_type;
+       unsigned int irq_level;
+       unsigned long irq_flags;
+
+       unsigned char __iomem * reg_addr;  /* memory mapped registers address */
+       u32 phys_reg_addr;
+       bool reg_addr_requested;
+
+       MGSL_PARAMS params;       /* communications parameters */
+       u32 idle_mode;
+       u32 max_frame_size;       /* as set by device config */
+
+       unsigned int rbuf_fill_level;
+       unsigned int rx_pio;
+       unsigned int if_mode;
+       unsigned int base_clock;
+       unsigned int xsync;
+       unsigned int xctrl;
+
+       /* device status */
+
+       bool rx_enabled;
+       bool rx_restart;
+
+       bool tx_enabled;
+       bool tx_active;
+
+       unsigned char signals;    /* serial signal states */
+       int init_error;  /* initialization error */
+
+       unsigned char *tx_buf;
+       int tx_count;
+
+       char flag_buf[MAX_ASYNC_BUFFER_SIZE];
+       char char_buf[MAX_ASYNC_BUFFER_SIZE];
+       bool drop_rts_on_tx_done;
+       struct  _input_signal_events    input_signal_events;
+
+       int dcd_chkcount;       /* check counts to prevent */
+       int cts_chkcount;       /* too many IRQs if a signal */
+       int dsr_chkcount;       /* is floating */
+       int ri_chkcount;
+
+       char *bufs;             /* virtual address of DMA buffer lists */
+       dma_addr_t bufs_dma_addr; /* physical address of buffer descriptors */
+
+       unsigned int rbuf_count;
+       struct slgt_desc *rbufs;
+       unsigned int rbuf_current;
+       unsigned int rbuf_index;
+       unsigned int rbuf_fill_index;
+       unsigned short rbuf_fill_count;
+
+       unsigned int tbuf_count;
+       struct slgt_desc *tbufs;
+       unsigned int tbuf_current;
+       unsigned int tbuf_start;
+
+       unsigned char *tmp_rbuf;
+       unsigned int tmp_rbuf_count;
+
+       /* SPPP/Cisco HDLC device parts */
+
+       int netcount;
+       spinlock_t netlock;
+#if SYNCLINK_GENERIC_HDLC
+       struct net_device *netdev;
+#endif
+
+};
+
+static MGSL_PARAMS default_params = {
+       .mode            = MGSL_MODE_HDLC,
+       .loopback        = 0,
+       .flags           = HDLC_FLAG_UNDERRUN_ABORT15,
+       .encoding        = HDLC_ENCODING_NRZI_SPACE,
+       .clock_speed     = 0,
+       .addr_filter     = 0xff,
+       .crc_type        = HDLC_CRC_16_CCITT,
+       .preamble_length = HDLC_PREAMBLE_LENGTH_8BITS,
+       .preamble        = HDLC_PREAMBLE_PATTERN_NONE,
+       .data_rate       = 9600,
+       .data_bits       = 8,
+       .stop_bits       = 1,
+       .parity          = ASYNC_PARITY_NONE
+};
+
+
+#define BH_RECEIVE  1
+#define BH_TRANSMIT 2
+#define BH_STATUS   4
+#define IO_PIN_SHUTDOWN_LIMIT 100
+
+#define DMABUFSIZE 256
+#define DESC_LIST_SIZE 4096
+
+#define MASK_PARITY  BIT1
+#define MASK_FRAMING BIT0
+#define MASK_BREAK   BIT14
+#define MASK_OVERRUN BIT4
+
+#define GSR   0x00 /* global status */
+#define JCR   0x04 /* JTAG control */
+#define IODR  0x08 /* GPIO direction */
+#define IOER  0x0c /* GPIO interrupt enable */
+#define IOVR  0x10 /* GPIO value */
+#define IOSR  0x14 /* GPIO interrupt status */
+#define TDR   0x80 /* tx data */
+#define RDR   0x80 /* rx data */
+#define TCR   0x82 /* tx control */
+#define TIR   0x84 /* tx idle */
+#define TPR   0x85 /* tx preamble */
+#define RCR   0x86 /* rx control */
+#define VCR   0x88 /* V.24 control */
+#define CCR   0x89 /* clock control */
+#define BDR   0x8a /* baud divisor */
+#define SCR   0x8c /* serial control */
+#define SSR   0x8e /* serial status */
+#define RDCSR 0x90 /* rx DMA control/status */
+#define TDCSR 0x94 /* tx DMA control/status */
+#define RDDAR 0x98 /* rx DMA descriptor address */
+#define TDDAR 0x9c /* tx DMA descriptor address */
+#define XSR   0x40 /* extended sync pattern */
+#define XCR   0x44 /* extended control */
+
+#define RXIDLE      BIT14
+#define RXBREAK     BIT14
+#define IRQ_TXDATA  BIT13
+#define IRQ_TXIDLE  BIT12
+#define IRQ_TXUNDER BIT11 /* HDLC */
+#define IRQ_RXDATA  BIT10
+#define IRQ_RXIDLE  BIT9  /* HDLC */
+#define IRQ_RXBREAK BIT9  /* async */
+#define IRQ_RXOVER  BIT8
+#define IRQ_DSR     BIT7
+#define IRQ_CTS     BIT6
+#define IRQ_DCD     BIT5
+#define IRQ_RI      BIT4
+#define IRQ_ALL     0x3ff0
+#define IRQ_MASTER  BIT0
+
+#define slgt_irq_on(info, mask) \
+       wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) | (mask)))
+#define slgt_irq_off(info, mask) \
+       wr_reg16((info), SCR, (unsigned short)(rd_reg16((info), SCR) & ~(mask)))
+
+static __u8  rd_reg8(struct slgt_info *info, unsigned int addr);
+static void  wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value);
+static __u16 rd_reg16(struct slgt_info *info, unsigned int addr);
+static void  wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value);
+static __u32 rd_reg32(struct slgt_info *info, unsigned int addr);
+static void  wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value);
+
+static void  msc_set_vcr(struct slgt_info *info);
+
+static int  startup(struct slgt_info *info);
+static int  block_til_ready(struct tty_struct *tty, struct file * filp,struct slgt_info *info);
+static void shutdown(struct slgt_info *info);
+static void program_hw(struct slgt_info *info);
+static void change_params(struct slgt_info *info);
+
+static int  register_test(struct slgt_info *info);
+static int  irq_test(struct slgt_info *info);
+static int  loopback_test(struct slgt_info *info);
+static int  adapter_test(struct slgt_info *info);
+
+static void reset_adapter(struct slgt_info *info);
+static void reset_port(struct slgt_info *info);
+static void async_mode(struct slgt_info *info);
+static void sync_mode(struct slgt_info *info);
+
+static void rx_stop(struct slgt_info *info);
+static void rx_start(struct slgt_info *info);
+static void reset_rbufs(struct slgt_info *info);
+static void free_rbufs(struct slgt_info *info, unsigned int first, unsigned int last);
+static void rdma_reset(struct slgt_info *info);
+static bool rx_get_frame(struct slgt_info *info);
+static bool rx_get_buf(struct slgt_info *info);
+
+static void tx_start(struct slgt_info *info);
+static void tx_stop(struct slgt_info *info);
+static void tx_set_idle(struct slgt_info *info);
+static unsigned int free_tbuf_count(struct slgt_info *info);
+static unsigned int tbuf_bytes(struct slgt_info *info);
+static void reset_tbufs(struct slgt_info *info);
+static void tdma_reset(struct slgt_info *info);
+static bool tx_load(struct slgt_info *info, const char *buf, unsigned int count);
+
+static void get_signals(struct slgt_info *info);
+static void set_signals(struct slgt_info *info);
+static void enable_loopback(struct slgt_info *info);
+static void set_rate(struct slgt_info *info, u32 data_rate);
+
+static int  bh_action(struct slgt_info *info);
+static void bh_handler(struct work_struct *work);
+static void bh_transmit(struct slgt_info *info);
+static void isr_serial(struct slgt_info *info);
+static void isr_rdma(struct slgt_info *info);
+static void isr_txeom(struct slgt_info *info, unsigned short status);
+static void isr_tdma(struct slgt_info *info);
+
+static int  alloc_dma_bufs(struct slgt_info *info);
+static void free_dma_bufs(struct slgt_info *info);
+static int  alloc_desc(struct slgt_info *info);
+static void free_desc(struct slgt_info *info);
+static int  alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count);
+static void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count);
+
+static int  alloc_tmp_rbuf(struct slgt_info *info);
+static void free_tmp_rbuf(struct slgt_info *info);
+
+static void tx_timeout(unsigned long context);
+static void rx_timeout(unsigned long context);
+
+/*
+ * ioctl handlers
+ */
+static int  get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount);
+static int  get_params(struct slgt_info *info, MGSL_PARAMS __user *params);
+static int  set_params(struct slgt_info *info, MGSL_PARAMS __user *params);
+static int  get_txidle(struct slgt_info *info, int __user *idle_mode);
+static int  set_txidle(struct slgt_info *info, int idle_mode);
+static int  tx_enable(struct slgt_info *info, int enable);
+static int  tx_abort(struct slgt_info *info);
+static int  rx_enable(struct slgt_info *info, int enable);
+static int  modem_input_wait(struct slgt_info *info,int arg);
+static int  wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr);
+static int  tiocmget(struct tty_struct *tty);
+static int  tiocmset(struct tty_struct *tty,
+                               unsigned int set, unsigned int clear);
+static int set_break(struct tty_struct *tty, int break_state);
+static int  get_interface(struct slgt_info *info, int __user *if_mode);
+static int  set_interface(struct slgt_info *info, int if_mode);
+static int  set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
+static int  get_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
+static int  wait_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
+static int  get_xsync(struct slgt_info *info, int __user *if_mode);
+static int  set_xsync(struct slgt_info *info, int if_mode);
+static int  get_xctrl(struct slgt_info *info, int __user *if_mode);
+static int  set_xctrl(struct slgt_info *info, int if_mode);
+
+/*
+ * driver functions
+ */
+static void add_device(struct slgt_info *info);
+static void device_init(int adapter_num, struct pci_dev *pdev);
+static int  claim_resources(struct slgt_info *info);
+static void release_resources(struct slgt_info *info);
+
+/*
+ * DEBUG OUTPUT CODE
+ */
+#ifndef DBGINFO
+#define DBGINFO(fmt)
+#endif
+#ifndef DBGERR
+#define DBGERR(fmt)
+#endif
+#ifndef DBGBH
+#define DBGBH(fmt)
+#endif
+#ifndef DBGISR
+#define DBGISR(fmt)
+#endif
+
+#ifdef DBGDATA
+static void trace_block(struct slgt_info *info, const char *data, int count, const char *label)
+{
+       int i;
+       int linecount;
+       printk("%s %s data:\n",info->device_name, label);
+       while(count) {
+               linecount = (count > 16) ? 16 : count;
+               for(i=0; i < linecount; i++)
+                       printk("%02X ",(unsigned char)data[i]);
+               for(;i<17;i++)
+                       printk("   ");
+               for(i=0;i<linecount;i++) {
+                       if (data[i]>=040 && data[i]<=0176)
+                               printk("%c",data[i]);
+                       else
+                               printk(".");
+               }
+               printk("\n");
+               data  += linecount;
+               count -= linecount;
+       }
+}
+#else
+#define DBGDATA(info, buf, size, label)
+#endif
+
+#ifdef DBGTBUF
+static void dump_tbufs(struct slgt_info *info)
+{
+       int i;
+       printk("tbuf_current=%d\n", info->tbuf_current);
+       for (i=0 ; i < info->tbuf_count ; i++) {
+               printk("%d: count=%04X status=%04X\n",
+                       i, le16_to_cpu(info->tbufs[i].count), le16_to_cpu(info->tbufs[i].status));
+       }
+}
+#else
+#define DBGTBUF(info)
+#endif
+
+#ifdef DBGRBUF
+static void dump_rbufs(struct slgt_info *info)
+{
+       int i;
+       printk("rbuf_current=%d\n", info->rbuf_current);
+       for (i=0 ; i < info->rbuf_count ; i++) {
+               printk("%d: count=%04X status=%04X\n",
+                       i, le16_to_cpu(info->rbufs[i].count), le16_to_cpu(info->rbufs[i].status));
+       }
+}
+#else
+#define DBGRBUF(info)
+#endif
+
+static inline int sanity_check(struct slgt_info *info, char *devname, const char *name)
+{
+#ifdef SANITY_CHECK
+       if (!info) {
+               printk("null struct slgt_info for (%s) in %s\n", devname, name);
+               return 1;
+       }
+       if (info->magic != MGSL_MAGIC) {
+               printk("bad magic number struct slgt_info (%s) in %s\n", devname, name);
+               return 1;
+       }
+#else
+       if (!info)
+               return 1;
+#endif
+       return 0;
+}
+
+/**
+ * line discipline callback wrappers
+ *
+ * The wrappers maintain line discipline references
+ * while calling into the line discipline.
+ *
+ * ldisc_receive_buf  - pass receive data to line discipline
+ */
+static void ldisc_receive_buf(struct tty_struct *tty,
+                             const __u8 *data, char *flags, int count)
+{
+       struct tty_ldisc *ld;
+       if (!tty)
+               return;
+       ld = tty_ldisc_ref(tty);
+       if (ld) {
+               if (ld->ops->receive_buf)
+                       ld->ops->receive_buf(tty, data, flags, count);
+               tty_ldisc_deref(ld);
+       }
+}
+
+/* tty callbacks */
+
+static int open(struct tty_struct *tty, struct file *filp)
+{
+       struct slgt_info *info;
+       int retval, line;
+       unsigned long flags;
+
+       line = tty->index;
+       if ((line < 0) || (line >= slgt_device_count)) {
+               DBGERR(("%s: open with invalid line #%d.\n", driver_name, line));
+               return -ENODEV;
+       }
+
+       info = slgt_device_list;
+       while(info && info->line != line)
+               info = info->next_device;
+       if (sanity_check(info, tty->name, "open"))
+               return -ENODEV;
+       if (info->init_error) {
+               DBGERR(("%s init error=%d\n", info->device_name, info->init_error));
+               return -ENODEV;
+       }
+
+       tty->driver_data = info;
+       info->port.tty = tty;
+
+       DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count));
+
+       /* If port is closing, signal caller to try again */
+       if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){
+               if (info->port.flags & ASYNC_CLOSING)
+                       interruptible_sleep_on(&info->port.close_wait);
+               retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS);
+               goto cleanup;
+       }
+
+       mutex_lock(&info->port.mutex);
+       info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+       spin_lock_irqsave(&info->netlock, flags);
+       if (info->netcount) {
+               retval = -EBUSY;
+               spin_unlock_irqrestore(&info->netlock, flags);
+               mutex_unlock(&info->port.mutex);
+               goto cleanup;
+       }
+       info->port.count++;
+       spin_unlock_irqrestore(&info->netlock, flags);
+
+       if (info->port.count == 1) {
+               /* 1st open on this device, init hardware */
+               retval = startup(info);
+               if (retval < 0) {
+                       mutex_unlock(&info->port.mutex);
+                       goto cleanup;
+               }
+       }
+       mutex_unlock(&info->port.mutex);
+       retval = block_til_ready(tty, filp, info);
+       if (retval) {
+               DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval));
+               goto cleanup;
+       }
+
+       retval = 0;
+
+cleanup:
+       if (retval) {
+               if (tty->count == 1)
+                       info->port.tty = NULL; /* tty layer will release tty struct */
+               if(info->port.count)
+                       info->port.count--;
+       }
+
+       DBGINFO(("%s open rc=%d\n", info->device_name, retval));
+       return retval;
+}
+
+static void close(struct tty_struct *tty, struct file *filp)
+{
+       struct slgt_info *info = tty->driver_data;
+
+       if (sanity_check(info, tty->name, "close"))
+               return;
+       DBGINFO(("%s close entry, count=%d\n", info->device_name, info->port.count));
+
+       if (tty_port_close_start(&info->port, tty, filp) == 0)
+               goto cleanup;
+
+       mutex_lock(&info->port.mutex);
+       if (info->port.flags & ASYNC_INITIALIZED)
+               wait_until_sent(tty, info->timeout);
+       flush_buffer(tty);
+       tty_ldisc_flush(tty);
+
+       shutdown(info);
+       mutex_unlock(&info->port.mutex);
+
+       tty_port_close_end(&info->port, tty);
+       info->port.tty = NULL;
+cleanup:
+       DBGINFO(("%s close exit, count=%d\n", tty->driver->name, info->port.count));
+}
+
+static void hangup(struct tty_struct *tty)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned long flags;
+
+       if (sanity_check(info, tty->name, "hangup"))
+               return;
+       DBGINFO(("%s hangup\n", info->device_name));
+
+       flush_buffer(tty);
+
+       mutex_lock(&info->port.mutex);
+       shutdown(info);
+
+       spin_lock_irqsave(&info->port.lock, flags);
+       info->port.count = 0;
+       info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
+       info->port.tty = NULL;
+       spin_unlock_irqrestore(&info->port.lock, flags);
+       mutex_unlock(&info->port.mutex);
+
+       wake_up_interruptible(&info->port.open_wait);
+}
+
+static void set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned long flags;
+
+       DBGINFO(("%s set_termios\n", tty->driver->name));
+
+       change_params(info);
+
+       /* Handle transition to B0 status */
+       if (old_termios->c_cflag & CBAUD &&
+           !(tty->termios->c_cflag & CBAUD)) {
+               info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+               spin_lock_irqsave(&info->lock,flags);
+               set_signals(info);
+               spin_unlock_irqrestore(&info->lock,flags);
+       }
+
+       /* Handle transition away from B0 status */
+       if (!(old_termios->c_cflag & CBAUD) &&
+           tty->termios->c_cflag & CBAUD) {
+               info->signals |= SerialSignal_DTR;
+               if (!(tty->termios->c_cflag & CRTSCTS) ||
+                   !test_bit(TTY_THROTTLED, &tty->flags)) {
+                       info->signals |= SerialSignal_RTS;
+               }
+               spin_lock_irqsave(&info->lock,flags);
+               set_signals(info);
+               spin_unlock_irqrestore(&info->lock,flags);
+       }
+
+       /* Handle turning off CRTSCTS */
+       if (old_termios->c_cflag & CRTSCTS &&
+           !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               tx_release(tty);
+       }
+}
+
+static void update_tx_timer(struct slgt_info *info)
+{
+       /*
+        * use worst case speed of 1200bps to calculate transmit timeout
+        * based on data in buffers (tbuf_bytes) and FIFO (128 bytes)
+        */
+       if (info->params.mode == MGSL_MODE_HDLC) {
+               int timeout  = (tbuf_bytes(info) * 7) + 1000;
+               mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout));
+       }
+}
+
+static int write(struct tty_struct *tty,
+                const unsigned char *buf, int count)
+{
+       int ret = 0;
+       struct slgt_info *info = tty->driver_data;
+       unsigned long flags;
+
+       if (sanity_check(info, tty->name, "write"))
+               return -EIO;
+
+       DBGINFO(("%s write count=%d\n", info->device_name, count));
+
+       if (!info->tx_buf || (count > info->max_frame_size))
+               return -EIO;
+
+       if (!count || tty->stopped || tty->hw_stopped)
+               return 0;
+
+       spin_lock_irqsave(&info->lock, flags);
+
+       if (info->tx_count) {
+               /* send accumulated data from send_char() */
+               if (!tx_load(info, info->tx_buf, info->tx_count))
+                       goto cleanup;
+               info->tx_count = 0;
+       }
+
+       if (tx_load(info, buf, count))
+               ret = count;
+
+cleanup:
+       spin_unlock_irqrestore(&info->lock, flags);
+       DBGINFO(("%s write rc=%d\n", info->device_name, ret));
+       return ret;
+}
+
+static int put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned long flags;
+       int ret = 0;
+
+       if (sanity_check(info, tty->name, "put_char"))
+               return 0;
+       DBGINFO(("%s put_char(%d)\n", info->device_name, ch));
+       if (!info->tx_buf)
+               return 0;
+       spin_lock_irqsave(&info->lock,flags);
+       if (info->tx_count < info->max_frame_size) {
+               info->tx_buf[info->tx_count++] = ch;
+               ret = 1;
+       }
+       spin_unlock_irqrestore(&info->lock,flags);
+       return ret;
+}
+
+static void send_xchar(struct tty_struct *tty, char ch)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned long flags;
+
+       if (sanity_check(info, tty->name, "send_xchar"))
+               return;
+       DBGINFO(("%s send_xchar(%d)\n", info->device_name, ch));
+       info->x_char = ch;
+       if (ch) {
+               spin_lock_irqsave(&info->lock,flags);
+               if (!info->tx_enabled)
+                       tx_start(info);
+               spin_unlock_irqrestore(&info->lock,flags);
+       }
+}
+
+static void wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned long orig_jiffies, char_time;
+
+       if (!info )
+               return;
+       if (sanity_check(info, tty->name, "wait_until_sent"))
+               return;
+       DBGINFO(("%s wait_until_sent entry\n", info->device_name));
+       if (!(info->port.flags & ASYNC_INITIALIZED))
+               goto exit;
+
+       orig_jiffies = jiffies;
+
+       /* Set check interval to 1/5 of estimated time to
+        * send a character, and make it at least 1. The check
+        * interval should also be less than the timeout.
+        * Note: use tight timings here to satisfy the NIST-PCTS.
+        */
+
+       if (info->params.data_rate) {
+               char_time = info->timeout/(32 * 5);
+               if (!char_time)
+                       char_time++;
+       } else
+               char_time = 1;
+
+       if (timeout)
+               char_time = min_t(unsigned long, char_time, timeout);
+
+       while (info->tx_active) {
+               msleep_interruptible(jiffies_to_msecs(char_time));
+               if (signal_pending(current))
+                       break;
+               if (timeout && time_after(jiffies, orig_jiffies + timeout))
+                       break;
+       }
+exit:
+       DBGINFO(("%s wait_until_sent exit\n", info->device_name));
+}
+
+static int write_room(struct tty_struct *tty)
+{
+       struct slgt_info *info = tty->driver_data;
+       int ret;
+
+       if (sanity_check(info, tty->name, "write_room"))
+               return 0;
+       ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE;
+       DBGINFO(("%s write_room=%d\n", info->device_name, ret));
+       return ret;
+}
+
+static void flush_chars(struct tty_struct *tty)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned long flags;
+
+       if (sanity_check(info, tty->name, "flush_chars"))
+               return;
+       DBGINFO(("%s flush_chars entry tx_count=%d\n", info->device_name, info->tx_count));
+
+       if (info->tx_count <= 0 || tty->stopped ||
+           tty->hw_stopped || !info->tx_buf)
+               return;
+
+       DBGINFO(("%s flush_chars start transmit\n", info->device_name));
+
+       spin_lock_irqsave(&info->lock,flags);
+       if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count))
+               info->tx_count = 0;
+       spin_unlock_irqrestore(&info->lock,flags);
+}
+
+static void flush_buffer(struct tty_struct *tty)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned long flags;
+
+       if (sanity_check(info, tty->name, "flush_buffer"))
+               return;
+       DBGINFO(("%s flush_buffer\n", info->device_name));
+
+       spin_lock_irqsave(&info->lock, flags);
+       info->tx_count = 0;
+       spin_unlock_irqrestore(&info->lock, flags);
+
+       tty_wakeup(tty);
+}
+
+/*
+ * throttle (stop) transmitter
+ */
+static void tx_hold(struct tty_struct *tty)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned long flags;
+
+       if (sanity_check(info, tty->name, "tx_hold"))
+               return;
+       DBGINFO(("%s tx_hold\n", info->device_name));
+       spin_lock_irqsave(&info->lock,flags);
+       if (info->tx_enabled && info->params.mode == MGSL_MODE_ASYNC)
+               tx_stop(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/*
+ * release (start) transmitter
+ */
+static void tx_release(struct tty_struct *tty)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned long flags;
+
+       if (sanity_check(info, tty->name, "tx_release"))
+               return;
+       DBGINFO(("%s tx_release\n", info->device_name));
+       spin_lock_irqsave(&info->lock, flags);
+       if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count))
+               info->tx_count = 0;
+       spin_unlock_irqrestore(&info->lock, flags);
+}
+
+/*
+ * Service an IOCTL request
+ *
+ * Arguments
+ *
+ *     tty     pointer to tty instance data
+ *     cmd     IOCTL command code
+ *     arg     command argument/context
+ *
+ * Return 0 if success, otherwise error code
+ */
+static int ioctl(struct tty_struct *tty,
+                unsigned int cmd, unsigned long arg)
+{
+       struct slgt_info *info = tty->driver_data;
+       void __user *argp = (void __user *)arg;
+       int ret;
+
+       if (sanity_check(info, tty->name, "ioctl"))
+               return -ENODEV;
+       DBGINFO(("%s ioctl() cmd=%08X\n", info->device_name, cmd));
+
+       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+           (cmd != TIOCMIWAIT)) {
+               if (tty->flags & (1 << TTY_IO_ERROR))
+                   return -EIO;
+       }
+
+       switch (cmd) {
+       case MGSL_IOCWAITEVENT:
+               return wait_mgsl_event(info, argp);
+       case TIOCMIWAIT:
+               return modem_input_wait(info,(int)arg);
+       case MGSL_IOCSGPIO:
+               return set_gpio(info, argp);
+       case MGSL_IOCGGPIO:
+               return get_gpio(info, argp);
+       case MGSL_IOCWAITGPIO:
+               return wait_gpio(info, argp);
+       case MGSL_IOCGXSYNC:
+               return get_xsync(info, argp);
+       case MGSL_IOCSXSYNC:
+               return set_xsync(info, (int)arg);
+       case MGSL_IOCGXCTRL:
+               return get_xctrl(info, argp);
+       case MGSL_IOCSXCTRL:
+               return set_xctrl(info, (int)arg);
+       }
+       mutex_lock(&info->port.mutex);
+       switch (cmd) {
+       case MGSL_IOCGPARAMS:
+               ret = get_params(info, argp);
+               break;
+       case MGSL_IOCSPARAMS:
+               ret = set_params(info, argp);
+               break;
+       case MGSL_IOCGTXIDLE:
+               ret = get_txidle(info, argp);
+               break;
+       case MGSL_IOCSTXIDLE:
+               ret = set_txidle(info, (int)arg);
+               break;
+       case MGSL_IOCTXENABLE:
+               ret = tx_enable(info, (int)arg);
+               break;
+       case MGSL_IOCRXENABLE:
+               ret = rx_enable(info, (int)arg);
+               break;
+       case MGSL_IOCTXABORT:
+               ret = tx_abort(info);
+               break;
+       case MGSL_IOCGSTATS:
+               ret = get_stats(info, argp);
+               break;
+       case MGSL_IOCGIF:
+               ret = get_interface(info, argp);
+               break;
+       case MGSL_IOCSIF:
+               ret = set_interface(info,(int)arg);
+               break;
+       default:
+               ret = -ENOIOCTLCMD;
+       }
+       mutex_unlock(&info->port.mutex);
+       return ret;
+}
+
+static int get_icount(struct tty_struct *tty,
+                               struct serial_icounter_struct *icount)
+
+{
+       struct slgt_info *info = tty->driver_data;
+       struct mgsl_icount cnow;        /* kernel counter temps */
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->lock,flags);
+       cnow = info->icount;
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       icount->cts = cnow.cts;
+       icount->dsr = cnow.dsr;
+       icount->rng = cnow.rng;
+       icount->dcd = cnow.dcd;
+       icount->rx = cnow.rx;
+       icount->tx = cnow.tx;
+       icount->frame = cnow.frame;
+       icount->overrun = cnow.overrun;
+       icount->parity = cnow.parity;
+       icount->brk = cnow.brk;
+       icount->buf_overrun = cnow.buf_overrun;
+
+       return 0;
+}
+
+/*
+ * support for 32 bit ioctl calls on 64 bit systems
+ */
+#ifdef CONFIG_COMPAT
+static long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params)
+{
+       struct MGSL_PARAMS32 tmp_params;
+
+       DBGINFO(("%s get_params32\n", info->device_name));
+       memset(&tmp_params, 0, sizeof(tmp_params));
+       tmp_params.mode            = (compat_ulong_t)info->params.mode;
+       tmp_params.loopback        = info->params.loopback;
+       tmp_params.flags           = info->params.flags;
+       tmp_params.encoding        = info->params.encoding;
+       tmp_params.clock_speed     = (compat_ulong_t)info->params.clock_speed;
+       tmp_params.addr_filter     = info->params.addr_filter;
+       tmp_params.crc_type        = info->params.crc_type;
+       tmp_params.preamble_length = info->params.preamble_length;
+       tmp_params.preamble        = info->params.preamble;
+       tmp_params.data_rate       = (compat_ulong_t)info->params.data_rate;
+       tmp_params.data_bits       = info->params.data_bits;
+       tmp_params.stop_bits       = info->params.stop_bits;
+       tmp_params.parity          = info->params.parity;
+       if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32)))
+               return -EFAULT;
+       return 0;
+}
+
+static long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params)
+{
+       struct MGSL_PARAMS32 tmp_params;
+
+       DBGINFO(("%s set_params32\n", info->device_name));
+       if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32)))
+               return -EFAULT;
+
+       spin_lock(&info->lock);
+       if (tmp_params.mode == MGSL_MODE_BASE_CLOCK) {
+               info->base_clock = tmp_params.clock_speed;
+       } else {
+               info->params.mode            = tmp_params.mode;
+               info->params.loopback        = tmp_params.loopback;
+               info->params.flags           = tmp_params.flags;
+               info->params.encoding        = tmp_params.encoding;
+               info->params.clock_speed     = tmp_params.clock_speed;
+               info->params.addr_filter     = tmp_params.addr_filter;
+               info->params.crc_type        = tmp_params.crc_type;
+               info->params.preamble_length = tmp_params.preamble_length;
+               info->params.preamble        = tmp_params.preamble;
+               info->params.data_rate       = tmp_params.data_rate;
+               info->params.data_bits       = tmp_params.data_bits;
+               info->params.stop_bits       = tmp_params.stop_bits;
+               info->params.parity          = tmp_params.parity;
+       }
+       spin_unlock(&info->lock);
+
+       program_hw(info);
+
+       return 0;
+}
+
+static long slgt_compat_ioctl(struct tty_struct *tty,
+                        unsigned int cmd, unsigned long arg)
+{
+       struct slgt_info *info = tty->driver_data;
+       int rc = -ENOIOCTLCMD;
+
+       if (sanity_check(info, tty->name, "compat_ioctl"))
+               return -ENODEV;
+       DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd));
+
+       switch (cmd) {
+
+       case MGSL_IOCSPARAMS32:
+               rc = set_params32(info, compat_ptr(arg));
+               break;
+
+       case MGSL_IOCGPARAMS32:
+               rc = get_params32(info, compat_ptr(arg));
+               break;
+
+       case MGSL_IOCGPARAMS:
+       case MGSL_IOCSPARAMS:
+       case MGSL_IOCGTXIDLE:
+       case MGSL_IOCGSTATS:
+       case MGSL_IOCWAITEVENT:
+       case MGSL_IOCGIF:
+       case MGSL_IOCSGPIO:
+       case MGSL_IOCGGPIO:
+       case MGSL_IOCWAITGPIO:
+       case MGSL_IOCGXSYNC:
+       case MGSL_IOCGXCTRL:
+       case MGSL_IOCSTXIDLE:
+       case MGSL_IOCTXENABLE:
+       case MGSL_IOCRXENABLE:
+       case MGSL_IOCTXABORT:
+       case TIOCMIWAIT:
+       case MGSL_IOCSIF:
+       case MGSL_IOCSXSYNC:
+       case MGSL_IOCSXCTRL:
+               rc = ioctl(tty, cmd, arg);
+               break;
+       }
+
+       DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc));
+       return rc;
+}
+#else
+#define slgt_compat_ioctl NULL
+#endif /* ifdef CONFIG_COMPAT */
+
+/*
+ * proc fs support
+ */
+static inline void line_info(struct seq_file *m, struct slgt_info *info)
+{
+       char stat_buf[30];
+       unsigned long flags;
+
+       seq_printf(m, "%s: IO=%08X IRQ=%d MaxFrameSize=%u\n",
+                     info->device_name, info->phys_reg_addr,
+                     info->irq_level, info->max_frame_size);
+
+       /* output current serial signal states */
+       spin_lock_irqsave(&info->lock,flags);
+       get_signals(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       stat_buf[0] = 0;
+       stat_buf[1] = 0;
+       if (info->signals & SerialSignal_RTS)
+               strcat(stat_buf, "|RTS");
+       if (info->signals & SerialSignal_CTS)
+               strcat(stat_buf, "|CTS");
+       if (info->signals & SerialSignal_DTR)
+               strcat(stat_buf, "|DTR");
+       if (info->signals & SerialSignal_DSR)
+               strcat(stat_buf, "|DSR");
+       if (info->signals & SerialSignal_DCD)
+               strcat(stat_buf, "|CD");
+       if (info->signals & SerialSignal_RI)
+               strcat(stat_buf, "|RI");
+
+       if (info->params.mode != MGSL_MODE_ASYNC) {
+               seq_printf(m, "\tHDLC txok:%d rxok:%d",
+                              info->icount.txok, info->icount.rxok);
+               if (info->icount.txunder)
+                       seq_printf(m, " txunder:%d", info->icount.txunder);
+               if (info->icount.txabort)
+                       seq_printf(m, " txabort:%d", info->icount.txabort);
+               if (info->icount.rxshort)
+                       seq_printf(m, " rxshort:%d", info->icount.rxshort);
+               if (info->icount.rxlong)
+                       seq_printf(m, " rxlong:%d", info->icount.rxlong);
+               if (info->icount.rxover)
+                       seq_printf(m, " rxover:%d", info->icount.rxover);
+               if (info->icount.rxcrc)
+                       seq_printf(m, " rxcrc:%d", info->icount.rxcrc);
+       } else {
+               seq_printf(m, "\tASYNC tx:%d rx:%d",
+                              info->icount.tx, info->icount.rx);
+               if (info->icount.frame)
+                       seq_printf(m, " fe:%d", info->icount.frame);
+               if (info->icount.parity)
+                       seq_printf(m, " pe:%d", info->icount.parity);
+               if (info->icount.brk)
+                       seq_printf(m, " brk:%d", info->icount.brk);
+               if (info->icount.overrun)
+                       seq_printf(m, " oe:%d", info->icount.overrun);
+       }
+
+       /* Append serial signal status to end */
+       seq_printf(m, " %s\n", stat_buf+1);
+
+       seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
+                      info->tx_active,info->bh_requested,info->bh_running,
+                      info->pending_bh);
+}
+
+/* Called to print information about devices
+ */
+static int synclink_gt_proc_show(struct seq_file *m, void *v)
+{
+       struct slgt_info *info;
+
+       seq_puts(m, "synclink_gt driver\n");
+
+       info = slgt_device_list;
+       while( info ) {
+               line_info(m, info);
+               info = info->next_device;
+       }
+       return 0;
+}
+
+static int synclink_gt_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, synclink_gt_proc_show, NULL);
+}
+
+static const struct file_operations synclink_gt_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = synclink_gt_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/*
+ * return count of bytes in transmit buffer
+ */
+static int chars_in_buffer(struct tty_struct *tty)
+{
+       struct slgt_info *info = tty->driver_data;
+       int count;
+       if (sanity_check(info, tty->name, "chars_in_buffer"))
+               return 0;
+       count = tbuf_bytes(info);
+       DBGINFO(("%s chars_in_buffer()=%d\n", info->device_name, count));
+       return count;
+}
+
+/*
+ * signal remote device to throttle send data (our receive data)
+ */
+static void throttle(struct tty_struct * tty)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned long flags;
+
+       if (sanity_check(info, tty->name, "throttle"))
+               return;
+       DBGINFO(("%s throttle\n", info->device_name));
+       if (I_IXOFF(tty))
+               send_xchar(tty, STOP_CHAR(tty));
+       if (tty->termios->c_cflag & CRTSCTS) {
+               spin_lock_irqsave(&info->lock,flags);
+               info->signals &= ~SerialSignal_RTS;
+               set_signals(info);
+               spin_unlock_irqrestore(&info->lock,flags);
+       }
+}
+
+/*
+ * signal remote device to stop throttling send data (our receive data)
+ */
+static void unthrottle(struct tty_struct * tty)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned long flags;
+
+       if (sanity_check(info, tty->name, "unthrottle"))
+               return;
+       DBGINFO(("%s unthrottle\n", info->device_name));
+       if (I_IXOFF(tty)) {
+               if (info->x_char)
+                       info->x_char = 0;
+               else
+                       send_xchar(tty, START_CHAR(tty));
+       }
+       if (tty->termios->c_cflag & CRTSCTS) {
+               spin_lock_irqsave(&info->lock,flags);
+               info->signals |= SerialSignal_RTS;
+               set_signals(info);
+               spin_unlock_irqrestore(&info->lock,flags);
+       }
+}
+
+/*
+ * set or clear transmit break condition
+ * break_state -1=set break condition, 0=clear
+ */
+static int set_break(struct tty_struct *tty, int break_state)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned short value;
+       unsigned long flags;
+
+       if (sanity_check(info, tty->name, "set_break"))
+               return -EINVAL;
+       DBGINFO(("%s set_break(%d)\n", info->device_name, break_state));
+
+       spin_lock_irqsave(&info->lock,flags);
+       value = rd_reg16(info, TCR);
+       if (break_state == -1)
+               value |= BIT6;
+       else
+               value &= ~BIT6;
+       wr_reg16(info, TCR, value);
+       spin_unlock_irqrestore(&info->lock,flags);
+       return 0;
+}
+
+#if SYNCLINK_GENERIC_HDLC
+
+/**
+ * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.)
+ * set encoding and frame check sequence (FCS) options
+ *
+ * dev       pointer to network device structure
+ * encoding  serial encoding setting
+ * parity    FCS setting
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
+                         unsigned short parity)
+{
+       struct slgt_info *info = dev_to_port(dev);
+       unsigned char  new_encoding;
+       unsigned short new_crctype;
+
+       /* return error if TTY interface open */
+       if (info->port.count)
+               return -EBUSY;
+
+       DBGINFO(("%s hdlcdev_attach\n", info->device_name));
+
+       switch (encoding)
+       {
+       case ENCODING_NRZ:        new_encoding = HDLC_ENCODING_NRZ; break;
+       case ENCODING_NRZI:       new_encoding = HDLC_ENCODING_NRZI_SPACE; break;
+       case ENCODING_FM_MARK:    new_encoding = HDLC_ENCODING_BIPHASE_MARK; break;
+       case ENCODING_FM_SPACE:   new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break;
+       case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break;
+       default: return -EINVAL;
+       }
+
+       switch (parity)
+       {
+       case PARITY_NONE:            new_crctype = HDLC_CRC_NONE; break;
+       case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break;
+       case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break;
+       default: return -EINVAL;
+       }
+
+       info->params.encoding = new_encoding;
+       info->params.crc_type = new_crctype;
+
+       /* if network interface up, reprogram hardware */
+       if (info->netcount)
+               program_hw(info);
+
+       return 0;
+}
+
+/**
+ * called by generic HDLC layer to send frame
+ *
+ * skb  socket buffer containing HDLC frame
+ * dev  pointer to network device structure
+ */
+static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
+                                     struct net_device *dev)
+{
+       struct slgt_info *info = dev_to_port(dev);
+       unsigned long flags;
+
+       DBGINFO(("%s hdlc_xmit\n", dev->name));
+
+       if (!skb->len)
+               return NETDEV_TX_OK;
+
+       /* stop sending until this frame completes */
+       netif_stop_queue(dev);
+
+       /* update network statistics */
+       dev->stats.tx_packets++;
+       dev->stats.tx_bytes += skb->len;
+
+       /* save start time for transmit timeout detection */
+       dev->trans_start = jiffies;
+
+       spin_lock_irqsave(&info->lock, flags);
+       tx_load(info, skb->data, skb->len);
+       spin_unlock_irqrestore(&info->lock, flags);
+
+       /* done with socket buffer, so free it */
+       dev_kfree_skb(skb);
+
+       return NETDEV_TX_OK;
+}
+
+/**
+ * called by network layer when interface enabled
+ * claim resources and initialize hardware
+ *
+ * dev  pointer to network device structure
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_open(struct net_device *dev)
+{
+       struct slgt_info *info = dev_to_port(dev);
+       int rc;
+       unsigned long flags;
+
+       if (!try_module_get(THIS_MODULE))
+               return -EBUSY;
+
+       DBGINFO(("%s hdlcdev_open\n", dev->name));
+
+       /* generic HDLC layer open processing */
+       if ((rc = hdlc_open(dev)))
+               return rc;
+
+       /* arbitrate between network and tty opens */
+       spin_lock_irqsave(&info->netlock, flags);
+       if (info->port.count != 0 || info->netcount != 0) {
+               DBGINFO(("%s hdlc_open busy\n", dev->name));
+               spin_unlock_irqrestore(&info->netlock, flags);
+               return -EBUSY;
+       }
+       info->netcount=1;
+       spin_unlock_irqrestore(&info->netlock, flags);
+
+       /* claim resources and init adapter */
+       if ((rc = startup(info)) != 0) {
+               spin_lock_irqsave(&info->netlock, flags);
+               info->netcount=0;
+               spin_unlock_irqrestore(&info->netlock, flags);
+               return rc;
+       }
+
+       /* assert DTR and RTS, apply hardware settings */
+       info->signals |= SerialSignal_RTS + SerialSignal_DTR;
+       program_hw(info);
+
+       /* enable network layer transmit */
+       dev->trans_start = jiffies;
+       netif_start_queue(dev);
+
+       /* inform generic HDLC layer of current DCD status */
+       spin_lock_irqsave(&info->lock, flags);
+       get_signals(info);
+       spin_unlock_irqrestore(&info->lock, flags);
+       if (info->signals & SerialSignal_DCD)
+               netif_carrier_on(dev);
+       else
+               netif_carrier_off(dev);
+       return 0;
+}
+
+/**
+ * called by network layer when interface is disabled
+ * shutdown hardware and release resources
+ *
+ * dev  pointer to network device structure
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_close(struct net_device *dev)
+{
+       struct slgt_info *info = dev_to_port(dev);
+       unsigned long flags;
+
+       DBGINFO(("%s hdlcdev_close\n", dev->name));
+
+       netif_stop_queue(dev);
+
+       /* shutdown adapter and release resources */
+       shutdown(info);
+
+       hdlc_close(dev);
+
+       spin_lock_irqsave(&info->netlock, flags);
+       info->netcount=0;
+       spin_unlock_irqrestore(&info->netlock, flags);
+
+       module_put(THIS_MODULE);
+       return 0;
+}
+
+/**
+ * called by network layer to process IOCTL call to network device
+ *
+ * dev  pointer to network device structure
+ * ifr  pointer to network interface request structure
+ * cmd  IOCTL command code
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       const size_t size = sizeof(sync_serial_settings);
+       sync_serial_settings new_line;
+       sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
+       struct slgt_info *info = dev_to_port(dev);
+       unsigned int flags;
+
+       DBGINFO(("%s hdlcdev_ioctl\n", dev->name));
+
+       /* return error if TTY interface open */
+       if (info->port.count)
+               return -EBUSY;
+
+       if (cmd != SIOCWANDEV)
+               return hdlc_ioctl(dev, ifr, cmd);
+
+       memset(&new_line, 0, sizeof(new_line));
+
+       switch(ifr->ifr_settings.type) {
+       case IF_GET_IFACE: /* return current sync_serial_settings */
+
+               ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
+               if (ifr->ifr_settings.size < size) {
+                       ifr->ifr_settings.size = size; /* data size wanted */
+                       return -ENOBUFS;
+               }
+
+               flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
+                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
+                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
+                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
+
+               switch (flags){
+               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break;
+               case (HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_INT; break;
+               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_TXINT; break;
+               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break;
+               default: new_line.clock_type = CLOCK_DEFAULT;
+               }
+
+               new_line.clock_rate = info->params.clock_speed;
+               new_line.loopback   = info->params.loopback ? 1:0;
+
+               if (copy_to_user(line, &new_line, size))
+                       return -EFAULT;
+               return 0;
+
+       case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */
+
+               if(!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               if (copy_from_user(&new_line, line, size))
+                       return -EFAULT;
+
+               switch (new_line.clock_type)
+               {
+               case CLOCK_EXT:      flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break;
+               case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break;
+               case CLOCK_INT:      flags = HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG;    break;
+               case CLOCK_TXINT:    flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG;    break;
+               case CLOCK_DEFAULT:  flags = info->params.flags &
+                                            (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
+                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
+                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
+                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN); break;
+               default: return -EINVAL;
+               }
+
+               if (new_line.loopback != 0 && new_line.loopback != 1)
+                       return -EINVAL;
+
+               info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
+                                       HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
+                                       HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
+                                       HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
+               info->params.flags |= flags;
+
+               info->params.loopback = new_line.loopback;
+
+               if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG))
+                       info->params.clock_speed = new_line.clock_rate;
+               else
+                       info->params.clock_speed = 0;
+
+               /* if network interface up, reprogram hardware */
+               if (info->netcount)
+                       program_hw(info);
+               return 0;
+
+       default:
+               return hdlc_ioctl(dev, ifr, cmd);
+       }
+}
+
+/**
+ * called by network layer when transmit timeout is detected
+ *
+ * dev  pointer to network device structure
+ */
+static void hdlcdev_tx_timeout(struct net_device *dev)
+{
+       struct slgt_info *info = dev_to_port(dev);
+       unsigned long flags;
+
+       DBGINFO(("%s hdlcdev_tx_timeout\n", dev->name));
+
+       dev->stats.tx_errors++;
+       dev->stats.tx_aborted_errors++;
+
+       spin_lock_irqsave(&info->lock,flags);
+       tx_stop(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       netif_wake_queue(dev);
+}
+
+/**
+ * called by device driver when transmit completes
+ * reenable network layer transmit if stopped
+ *
+ * info  pointer to device instance information
+ */
+static void hdlcdev_tx_done(struct slgt_info *info)
+{
+       if (netif_queue_stopped(info->netdev))
+               netif_wake_queue(info->netdev);
+}
+
+/**
+ * called by device driver when frame received
+ * pass frame to network layer
+ *
+ * info  pointer to device instance information
+ * buf   pointer to buffer contianing frame data
+ * size  count of data bytes in buf
+ */
+static void hdlcdev_rx(struct slgt_info *info, char *buf, int size)
+{
+       struct sk_buff *skb = dev_alloc_skb(size);
+       struct net_device *dev = info->netdev;
+
+       DBGINFO(("%s hdlcdev_rx\n", dev->name));
+
+       if (skb == NULL) {
+               DBGERR(("%s: can't alloc skb, drop packet\n", dev->name));
+               dev->stats.rx_dropped++;
+               return;
+       }
+
+       memcpy(skb_put(skb, size), buf, size);
+
+       skb->protocol = hdlc_type_trans(skb, dev);
+
+       dev->stats.rx_packets++;
+       dev->stats.rx_bytes += size;
+
+       netif_rx(skb);
+}
+
+static const struct net_device_ops hdlcdev_ops = {
+       .ndo_open       = hdlcdev_open,
+       .ndo_stop       = hdlcdev_close,
+       .ndo_change_mtu = hdlc_change_mtu,
+       .ndo_start_xmit = hdlc_start_xmit,
+       .ndo_do_ioctl   = hdlcdev_ioctl,
+       .ndo_tx_timeout = hdlcdev_tx_timeout,
+};
+
+/**
+ * called by device driver when adding device instance
+ * do generic HDLC initialization
+ *
+ * info  pointer to device instance information
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_init(struct slgt_info *info)
+{
+       int rc;
+       struct net_device *dev;
+       hdlc_device *hdlc;
+
+       /* allocate and initialize network and HDLC layer objects */
+
+       if (!(dev = alloc_hdlcdev(info))) {
+               printk(KERN_ERR "%s hdlc device alloc failure\n", info->device_name);
+               return -ENOMEM;
+       }
+
+       /* for network layer reporting purposes only */
+       dev->mem_start = info->phys_reg_addr;
+       dev->mem_end   = info->phys_reg_addr + SLGT_REG_SIZE - 1;
+       dev->irq       = info->irq_level;
+
+       /* network layer callbacks and settings */
+       dev->netdev_ops     = &hdlcdev_ops;
+       dev->watchdog_timeo = 10 * HZ;
+       dev->tx_queue_len   = 50;
+
+       /* generic HDLC layer callbacks and settings */
+       hdlc         = dev_to_hdlc(dev);
+       hdlc->attach = hdlcdev_attach;
+       hdlc->xmit   = hdlcdev_xmit;
+
+       /* register objects with HDLC layer */
+       if ((rc = register_hdlc_device(dev))) {
+               printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__);
+               free_netdev(dev);
+               return rc;
+       }
+
+       info->netdev = dev;
+       return 0;
+}
+
+/**
+ * called by device driver when removing device instance
+ * do generic HDLC cleanup
+ *
+ * info  pointer to device instance information
+ */
+static void hdlcdev_exit(struct slgt_info *info)
+{
+       unregister_hdlc_device(info->netdev);
+       free_netdev(info->netdev);
+       info->netdev = NULL;
+}
+
+#endif /* ifdef CONFIG_HDLC */
+
+/*
+ * get async data from rx DMA buffers
+ */
+static void rx_async(struct slgt_info *info)
+{
+       struct tty_struct *tty = info->port.tty;
+       struct mgsl_icount *icount = &info->icount;
+       unsigned int start, end;
+       unsigned char *p;
+       unsigned char status;
+       struct slgt_desc *bufs = info->rbufs;
+       int i, count;
+       int chars = 0;
+       int stat;
+       unsigned char ch;
+
+       start = end = info->rbuf_current;
+
+       while(desc_complete(bufs[end])) {
+               count = desc_count(bufs[end]) - info->rbuf_index;
+               p     = bufs[end].buf + info->rbuf_index;
+
+               DBGISR(("%s rx_async count=%d\n", info->device_name, count));
+               DBGDATA(info, p, count, "rx");
+
+               for(i=0 ; i < count; i+=2, p+=2) {
+                       ch = *p;
+                       icount->rx++;
+
+                       stat = 0;
+
+                       if ((status = *(p+1) & (BIT1 + BIT0))) {
+                               if (status & BIT1)
+                                       icount->parity++;
+                               else if (status & BIT0)
+                                       icount->frame++;
+                               /* discard char if tty control flags say so */
+                               if (status & info->ignore_status_mask)
+                                       continue;
+                               if (status & BIT1)
+                                       stat = TTY_PARITY;
+                               else if (status & BIT0)
+                                       stat = TTY_FRAME;
+                       }
+                       if (tty) {
+                               tty_insert_flip_char(tty, ch, stat);
+                               chars++;
+                       }
+               }
+
+               if (i < count) {
+                       /* receive buffer not completed */
+                       info->rbuf_index += i;
+                       mod_timer(&info->rx_timer, jiffies + 1);
+                       break;
+               }
+
+               info->rbuf_index = 0;
+               free_rbufs(info, end, end);
+
+               if (++end == info->rbuf_count)
+                       end = 0;
+
+               /* if entire list searched then no frame available */
+               if (end == start)
+                       break;
+       }
+
+       if (tty && chars)
+               tty_flip_buffer_push(tty);
+}
+
+/*
+ * return next bottom half action to perform
+ */
+static int bh_action(struct slgt_info *info)
+{
+       unsigned long flags;
+       int rc;
+
+       spin_lock_irqsave(&info->lock,flags);
+
+       if (info->pending_bh & BH_RECEIVE) {
+               info->pending_bh &= ~BH_RECEIVE;
+               rc = BH_RECEIVE;
+       } else if (info->pending_bh & BH_TRANSMIT) {
+               info->pending_bh &= ~BH_TRANSMIT;
+               rc = BH_TRANSMIT;
+       } else if (info->pending_bh & BH_STATUS) {
+               info->pending_bh &= ~BH_STATUS;
+               rc = BH_STATUS;
+       } else {
+               /* Mark BH routine as complete */
+               info->bh_running = false;
+               info->bh_requested = false;
+               rc = 0;
+       }
+
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       return rc;
+}
+
+/*
+ * perform bottom half processing
+ */
+static void bh_handler(struct work_struct *work)
+{
+       struct slgt_info *info = container_of(work, struct slgt_info, task);
+       int action;
+
+       if (!info)
+               return;
+       info->bh_running = true;
+
+       while((action = bh_action(info))) {
+               switch (action) {
+               case BH_RECEIVE:
+                       DBGBH(("%s bh receive\n", info->device_name));
+                       switch(info->params.mode) {
+                       case MGSL_MODE_ASYNC:
+                               rx_async(info);
+                               break;
+                       case MGSL_MODE_HDLC:
+                               while(rx_get_frame(info));
+                               break;
+                       case MGSL_MODE_RAW:
+                       case MGSL_MODE_MONOSYNC:
+                       case MGSL_MODE_BISYNC:
+                       case MGSL_MODE_XSYNC:
+                               while(rx_get_buf(info));
+                               break;
+                       }
+                       /* restart receiver if rx DMA buffers exhausted */
+                       if (info->rx_restart)
+                               rx_start(info);
+                       break;
+               case BH_TRANSMIT:
+                       bh_transmit(info);
+                       break;
+               case BH_STATUS:
+                       DBGBH(("%s bh status\n", info->device_name));
+                       info->ri_chkcount = 0;
+                       info->dsr_chkcount = 0;
+                       info->dcd_chkcount = 0;
+                       info->cts_chkcount = 0;
+                       break;
+               default:
+                       DBGBH(("%s unknown action\n", info->device_name));
+                       break;
+               }
+       }
+       DBGBH(("%s bh_handler exit\n", info->device_name));
+}
+
+static void bh_transmit(struct slgt_info *info)
+{
+       struct tty_struct *tty = info->port.tty;
+
+       DBGBH(("%s bh_transmit\n", info->device_name));
+       if (tty)
+               tty_wakeup(tty);
+}
+
+static void dsr_change(struct slgt_info *info, unsigned short status)
+{
+       if (status & BIT3) {
+               info->signals |= SerialSignal_DSR;
+               info->input_signal_events.dsr_up++;
+       } else {
+               info->signals &= ~SerialSignal_DSR;
+               info->input_signal_events.dsr_down++;
+       }
+       DBGISR(("dsr_change %s signals=%04X\n", info->device_name, info->signals));
+       if ((info->dsr_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
+               slgt_irq_off(info, IRQ_DSR);
+               return;
+       }
+       info->icount.dsr++;
+       wake_up_interruptible(&info->status_event_wait_q);
+       wake_up_interruptible(&info->event_wait_q);
+       info->pending_bh |= BH_STATUS;
+}
+
+static void cts_change(struct slgt_info *info, unsigned short status)
+{
+       if (status & BIT2) {
+               info->signals |= SerialSignal_CTS;
+               info->input_signal_events.cts_up++;
+       } else {
+               info->signals &= ~SerialSignal_CTS;
+               info->input_signal_events.cts_down++;
+       }
+       DBGISR(("cts_change %s signals=%04X\n", info->device_name, info->signals));
+       if ((info->cts_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
+               slgt_irq_off(info, IRQ_CTS);
+               return;
+       }
+       info->icount.cts++;
+       wake_up_interruptible(&info->status_event_wait_q);
+       wake_up_interruptible(&info->event_wait_q);
+       info->pending_bh |= BH_STATUS;
+
+       if (info->port.flags & ASYNC_CTS_FLOW) {
+               if (info->port.tty) {
+                       if (info->port.tty->hw_stopped) {
+                               if (info->signals & SerialSignal_CTS) {
+                                       info->port.tty->hw_stopped = 0;
+                                       info->pending_bh |= BH_TRANSMIT;
+                                       return;
+                               }
+                       } else {
+                               if (!(info->signals & SerialSignal_CTS))
+                                       info->port.tty->hw_stopped = 1;
+                       }
+               }
+       }
+}
+
+static void dcd_change(struct slgt_info *info, unsigned short status)
+{
+       if (status & BIT1) {
+               info->signals |= SerialSignal_DCD;
+               info->input_signal_events.dcd_up++;
+       } else {
+               info->signals &= ~SerialSignal_DCD;
+               info->input_signal_events.dcd_down++;
+       }
+       DBGISR(("dcd_change %s signals=%04X\n", info->device_name, info->signals));
+       if ((info->dcd_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
+               slgt_irq_off(info, IRQ_DCD);
+               return;
+       }
+       info->icount.dcd++;
+#if SYNCLINK_GENERIC_HDLC
+       if (info->netcount) {
+               if (info->signals & SerialSignal_DCD)
+                       netif_carrier_on(info->netdev);
+               else
+                       netif_carrier_off(info->netdev);
+       }
+#endif
+       wake_up_interruptible(&info->status_event_wait_q);
+       wake_up_interruptible(&info->event_wait_q);
+       info->pending_bh |= BH_STATUS;
+
+       if (info->port.flags & ASYNC_CHECK_CD) {
+               if (info->signals & SerialSignal_DCD)
+                       wake_up_interruptible(&info->port.open_wait);
+               else {
+                       if (info->port.tty)
+                               tty_hangup(info->port.tty);
+               }
+       }
+}
+
+static void ri_change(struct slgt_info *info, unsigned short status)
+{
+       if (status & BIT0) {
+               info->signals |= SerialSignal_RI;
+               info->input_signal_events.ri_up++;
+       } else {
+               info->signals &= ~SerialSignal_RI;
+               info->input_signal_events.ri_down++;
+       }
+       DBGISR(("ri_change %s signals=%04X\n", info->device_name, info->signals));
+       if ((info->ri_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
+               slgt_irq_off(info, IRQ_RI);
+               return;
+       }
+       info->icount.rng++;
+       wake_up_interruptible(&info->status_event_wait_q);
+       wake_up_interruptible(&info->event_wait_q);
+       info->pending_bh |= BH_STATUS;
+}
+
+static void isr_rxdata(struct slgt_info *info)
+{
+       unsigned int count = info->rbuf_fill_count;
+       unsigned int i = info->rbuf_fill_index;
+       unsigned short reg;
+
+       while (rd_reg16(info, SSR) & IRQ_RXDATA) {
+               reg = rd_reg16(info, RDR);
+               DBGISR(("isr_rxdata %s RDR=%04X\n", info->device_name, reg));
+               if (desc_complete(info->rbufs[i])) {
+                       /* all buffers full */
+                       rx_stop(info);
+                       info->rx_restart = 1;
+                       continue;
+               }
+               info->rbufs[i].buf[count++] = (unsigned char)reg;
+               /* async mode saves status byte to buffer for each data byte */
+               if (info->params.mode == MGSL_MODE_ASYNC)
+                       info->rbufs[i].buf[count++] = (unsigned char)(reg >> 8);
+               if (count == info->rbuf_fill_level || (reg & BIT10)) {
+                       /* buffer full or end of frame */
+                       set_desc_count(info->rbufs[i], count);
+                       set_desc_status(info->rbufs[i], BIT15 | (reg >> 8));
+                       info->rbuf_fill_count = count = 0;
+                       if (++i == info->rbuf_count)
+                               i = 0;
+                       info->pending_bh |= BH_RECEIVE;
+               }
+       }
+
+       info->rbuf_fill_index = i;
+       info->rbuf_fill_count = count;
+}
+
+static void isr_serial(struct slgt_info *info)
+{
+       unsigned short status = rd_reg16(info, SSR);
+
+       DBGISR(("%s isr_serial status=%04X\n", info->device_name, status));
+
+       wr_reg16(info, SSR, status); /* clear pending */
+
+       info->irq_occurred = true;
+
+       if (info->params.mode == MGSL_MODE_ASYNC) {
+               if (status & IRQ_TXIDLE) {
+                       if (info->tx_active)
+                               isr_txeom(info, status);
+               }
+               if (info->rx_pio && (status & IRQ_RXDATA))
+                       isr_rxdata(info);
+               if ((status & IRQ_RXBREAK) && (status & RXBREAK)) {
+                       info->icount.brk++;
+                       /* process break detection if tty control allows */
+                       if (info->port.tty) {
+                               if (!(status & info->ignore_status_mask)) {
+                                       if (info->read_status_mask & MASK_BREAK) {
+                                               tty_insert_flip_char(info->port.tty, 0, TTY_BREAK);
+                                               if (info->port.flags & ASYNC_SAK)
+                                                       do_SAK(info->port.tty);
+                                       }
+                               }
+                       }
+               }
+       } else {
+               if (status & (IRQ_TXIDLE + IRQ_TXUNDER))
+                       isr_txeom(info, status);
+               if (info->rx_pio && (status & IRQ_RXDATA))
+                       isr_rxdata(info);
+               if (status & IRQ_RXIDLE) {
+                       if (status & RXIDLE)
+                               info->icount.rxidle++;
+                       else
+                               info->icount.exithunt++;
+                       wake_up_interruptible(&info->event_wait_q);
+               }
+
+               if (status & IRQ_RXOVER)
+                       rx_start(info);
+       }
+
+       if (status & IRQ_DSR)
+               dsr_change(info, status);
+       if (status & IRQ_CTS)
+               cts_change(info, status);
+       if (status & IRQ_DCD)
+               dcd_change(info, status);
+       if (status & IRQ_RI)
+               ri_change(info, status);
+}
+
+static void isr_rdma(struct slgt_info *info)
+{
+       unsigned int status = rd_reg32(info, RDCSR);
+
+       DBGISR(("%s isr_rdma status=%08x\n", info->device_name, status));
+
+       /* RDCSR (rx DMA control/status)
+        *
+        * 31..07  reserved
+        * 06      save status byte to DMA buffer
+        * 05      error
+        * 04      eol (end of list)
+        * 03      eob (end of buffer)
+        * 02      IRQ enable
+        * 01      reset
+        * 00      enable
+        */
+       wr_reg32(info, RDCSR, status);  /* clear pending */
+
+       if (status & (BIT5 + BIT4)) {
+               DBGISR(("%s isr_rdma rx_restart=1\n", info->device_name));
+               info->rx_restart = true;
+       }
+       info->pending_bh |= BH_RECEIVE;
+}
+
+static void isr_tdma(struct slgt_info *info)
+{
+       unsigned int status = rd_reg32(info, TDCSR);
+
+       DBGISR(("%s isr_tdma status=%08x\n", info->device_name, status));
+
+       /* TDCSR (tx DMA control/status)
+        *
+        * 31..06  reserved
+        * 05      error
+        * 04      eol (end of list)
+        * 03      eob (end of buffer)
+        * 02      IRQ enable
+        * 01      reset
+        * 00      enable
+        */
+       wr_reg32(info, TDCSR, status);  /* clear pending */
+
+       if (status & (BIT5 + BIT4 + BIT3)) {
+               // another transmit buffer has completed
+               // run bottom half to get more send data from user
+               info->pending_bh |= BH_TRANSMIT;
+       }
+}
+
+/*
+ * return true if there are unsent tx DMA buffers, otherwise false
+ *
+ * if there are unsent buffers then info->tbuf_start
+ * is set to index of first unsent buffer
+ */
+static bool unsent_tbufs(struct slgt_info *info)
+{
+       unsigned int i = info->tbuf_current;
+       bool rc = false;
+
+       /*
+        * search backwards from last loaded buffer (precedes tbuf_current)
+        * for first unsent buffer (desc_count > 0)
+        */
+
+       do {
+               if (i)
+                       i--;
+               else
+                       i = info->tbuf_count - 1;
+               if (!desc_count(info->tbufs[i]))
+                       break;
+               info->tbuf_start = i;
+               rc = true;
+       } while (i != info->tbuf_current);
+
+       return rc;
+}
+
+static void isr_txeom(struct slgt_info *info, unsigned short status)
+{
+       DBGISR(("%s txeom status=%04x\n", info->device_name, status));
+
+       slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER);
+       tdma_reset(info);
+       if (status & IRQ_TXUNDER) {
+               unsigned short val = rd_reg16(info, TCR);
+               wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */
+               wr_reg16(info, TCR, val); /* clear reset bit */
+       }
+
+       if (info->tx_active) {
+               if (info->params.mode != MGSL_MODE_ASYNC) {
+                       if (status & IRQ_TXUNDER)
+                               info->icount.txunder++;
+                       else if (status & IRQ_TXIDLE)
+                               info->icount.txok++;
+               }
+
+               if (unsent_tbufs(info)) {
+                       tx_start(info);
+                       update_tx_timer(info);
+                       return;
+               }
+               info->tx_active = false;
+
+               del_timer(&info->tx_timer);
+
+               if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done) {
+                       info->signals &= ~SerialSignal_RTS;
+                       info->drop_rts_on_tx_done = false;
+                       set_signals(info);
+               }
+
+#if SYNCLINK_GENERIC_HDLC
+               if (info->netcount)
+                       hdlcdev_tx_done(info);
+               else
+#endif
+               {
+                       if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) {
+                               tx_stop(info);
+                               return;
+                       }
+                       info->pending_bh |= BH_TRANSMIT;
+               }
+       }
+}
+
+static void isr_gpio(struct slgt_info *info, unsigned int changed, unsigned int state)
+{
+       struct cond_wait *w, *prev;
+
+       /* wake processes waiting for specific transitions */
+       for (w = info->gpio_wait_q, prev = NULL ; w != NULL ; w = w->next) {
+               if (w->data & changed) {
+                       w->data = state;
+                       wake_up_interruptible(&w->q);
+                       if (prev != NULL)
+                               prev->next = w->next;
+                       else
+                               info->gpio_wait_q = w->next;
+               } else
+                       prev = w;
+       }
+}
+
+/* interrupt service routine
+ *
+ *     irq     interrupt number
+ *     dev_id  device ID supplied during interrupt registration
+ */
+static irqreturn_t slgt_interrupt(int dummy, void *dev_id)
+{
+       struct slgt_info *info = dev_id;
+       unsigned int gsr;
+       unsigned int i;
+
+       DBGISR(("slgt_interrupt irq=%d entry\n", info->irq_level));
+
+       while((gsr = rd_reg32(info, GSR) & 0xffffff00)) {
+               DBGISR(("%s gsr=%08x\n", info->device_name, gsr));
+               info->irq_occurred = true;
+               for(i=0; i < info->port_count ; i++) {
+                       if (info->port_array[i] == NULL)
+                               continue;
+                       spin_lock(&info->port_array[i]->lock);
+                       if (gsr & (BIT8 << i))
+                               isr_serial(info->port_array[i]);
+                       if (gsr & (BIT16 << (i*2)))
+                               isr_rdma(info->port_array[i]);
+                       if (gsr & (BIT17 << (i*2)))
+                               isr_tdma(info->port_array[i]);
+                       spin_unlock(&info->port_array[i]->lock);
+               }
+       }
+
+       if (info->gpio_present) {
+               unsigned int state;
+               unsigned int changed;
+               spin_lock(&info->lock);
+               while ((changed = rd_reg32(info, IOSR)) != 0) {
+                       DBGISR(("%s iosr=%08x\n", info->device_name, changed));
+                       /* read latched state of GPIO signals */
+                       state = rd_reg32(info, IOVR);
+                       /* clear pending GPIO interrupt bits */
+                       wr_reg32(info, IOSR, changed);
+                       for (i=0 ; i < info->port_count ; i++) {
+                               if (info->port_array[i] != NULL)
+                                       isr_gpio(info->port_array[i], changed, state);
+                       }
+               }
+               spin_unlock(&info->lock);
+       }
+
+       for(i=0; i < info->port_count ; i++) {
+               struct slgt_info *port = info->port_array[i];
+               if (port == NULL)
+                       continue;
+               spin_lock(&port->lock);
+               if ((port->port.count || port->netcount) &&
+                   port->pending_bh && !port->bh_running &&
+                   !port->bh_requested) {
+                       DBGISR(("%s bh queued\n", port->device_name));
+                       schedule_work(&port->task);
+                       port->bh_requested = true;
+               }
+               spin_unlock(&port->lock);
+       }
+
+       DBGISR(("slgt_interrupt irq=%d exit\n", info->irq_level));
+       return IRQ_HANDLED;
+}
+
+static int startup(struct slgt_info *info)
+{
+       DBGINFO(("%s startup\n", info->device_name));
+
+       if (info->port.flags & ASYNC_INITIALIZED)
+               return 0;
+
+       if (!info->tx_buf) {
+               info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL);
+               if (!info->tx_buf) {
+                       DBGERR(("%s can't allocate tx buffer\n", info->device_name));
+                       return -ENOMEM;
+               }
+       }
+
+       info->pending_bh = 0;
+
+       memset(&info->icount, 0, sizeof(info->icount));
+
+       /* program hardware for current parameters */
+       change_params(info);
+
+       if (info->port.tty)
+               clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
+
+       info->port.flags |= ASYNC_INITIALIZED;
+
+       return 0;
+}
+
+/*
+ *  called by close() and hangup() to shutdown hardware
+ */
+static void shutdown(struct slgt_info *info)
+{
+       unsigned long flags;
+
+       if (!(info->port.flags & ASYNC_INITIALIZED))
+               return;
+
+       DBGINFO(("%s shutdown\n", info->device_name));
+
+       /* clear status wait queue because status changes */
+       /* can't happen after shutting down the hardware */
+       wake_up_interruptible(&info->status_event_wait_q);
+       wake_up_interruptible(&info->event_wait_q);
+
+       del_timer_sync(&info->tx_timer);
+       del_timer_sync(&info->rx_timer);
+
+       kfree(info->tx_buf);
+       info->tx_buf = NULL;
+
+       spin_lock_irqsave(&info->lock,flags);
+
+       tx_stop(info);
+       rx_stop(info);
+
+       slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
+
+       if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) {
+               info->signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
+               set_signals(info);
+       }
+
+       flush_cond_wait(&info->gpio_wait_q);
+
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       if (info->port.tty)
+               set_bit(TTY_IO_ERROR, &info->port.tty->flags);
+
+       info->port.flags &= ~ASYNC_INITIALIZED;
+}
+
+static void program_hw(struct slgt_info *info)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->lock,flags);
+
+       rx_stop(info);
+       tx_stop(info);
+
+       if (info->params.mode != MGSL_MODE_ASYNC ||
+           info->netcount)
+               sync_mode(info);
+       else
+               async_mode(info);
+
+       set_signals(info);
+
+       info->dcd_chkcount = 0;
+       info->cts_chkcount = 0;
+       info->ri_chkcount = 0;
+       info->dsr_chkcount = 0;
+
+       slgt_irq_on(info, IRQ_DCD | IRQ_CTS | IRQ_DSR | IRQ_RI);
+       get_signals(info);
+
+       if (info->netcount ||
+           (info->port.tty && info->port.tty->termios->c_cflag & CREAD))
+               rx_start(info);
+
+       spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/*
+ * reconfigure adapter based on new parameters
+ */
+static void change_params(struct slgt_info *info)
+{
+       unsigned cflag;
+       int bits_per_char;
+
+       if (!info->port.tty || !info->port.tty->termios)
+               return;
+       DBGINFO(("%s change_params\n", info->device_name));
+
+       cflag = info->port.tty->termios->c_cflag;
+
+       /* if B0 rate (hangup) specified then negate DTR and RTS */
+       /* otherwise assert DTR and RTS */
+       if (cflag & CBAUD)
+               info->signals |= SerialSignal_RTS + SerialSignal_DTR;
+       else
+               info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+
+       /* byte size and parity */
+
+       switch (cflag & CSIZE) {
+       case CS5: info->params.data_bits = 5; break;
+       case CS6: info->params.data_bits = 6; break;
+       case CS7: info->params.data_bits = 7; break;
+       case CS8: info->params.data_bits = 8; break;
+       default:  info->params.data_bits = 7; break;
+       }
+
+       info->params.stop_bits = (cflag & CSTOPB) ? 2 : 1;
+
+       if (cflag & PARENB)
+               info->params.parity = (cflag & PARODD) ? ASYNC_PARITY_ODD : ASYNC_PARITY_EVEN;
+       else
+               info->params.parity = ASYNC_PARITY_NONE;
+
+       /* calculate number of jiffies to transmit a full
+        * FIFO (32 bytes) at specified data rate
+        */
+       bits_per_char = info->params.data_bits +
+                       info->params.stop_bits + 1;
+
+       info->params.data_rate = tty_get_baud_rate(info->port.tty);
+
+       if (info->params.data_rate) {
+               info->timeout = (32*HZ*bits_per_char) /
+                               info->params.data_rate;
+       }
+       info->timeout += HZ/50;         /* Add .02 seconds of slop */
+
+       if (cflag & CRTSCTS)
+               info->port.flags |= ASYNC_CTS_FLOW;
+       else
+               info->port.flags &= ~ASYNC_CTS_FLOW;
+
+       if (cflag & CLOCAL)
+               info->port.flags &= ~ASYNC_CHECK_CD;
+       else
+               info->port.flags |= ASYNC_CHECK_CD;
+
+       /* process tty input control flags */
+
+       info->read_status_mask = IRQ_RXOVER;
+       if (I_INPCK(info->port.tty))
+               info->read_status_mask |= MASK_PARITY | MASK_FRAMING;
+       if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty))
+               info->read_status_mask |= MASK_BREAK;
+       if (I_IGNPAR(info->port.tty))
+               info->ignore_status_mask |= MASK_PARITY | MASK_FRAMING;
+       if (I_IGNBRK(info->port.tty)) {
+               info->ignore_status_mask |= MASK_BREAK;
+               /* If ignoring parity and break indicators, ignore
+                * overruns too.  (For real raw support).
+                */
+               if (I_IGNPAR(info->port.tty))
+                       info->ignore_status_mask |= MASK_OVERRUN;
+       }
+
+       program_hw(info);
+}
+
+static int get_stats(struct slgt_info *info, struct mgsl_icount __user *user_icount)
+{
+       DBGINFO(("%s get_stats\n",  info->device_name));
+       if (!user_icount) {
+               memset(&info->icount, 0, sizeof(info->icount));
+       } else {
+               if (copy_to_user(user_icount, &info->icount, sizeof(struct mgsl_icount)))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+static int get_params(struct slgt_info *info, MGSL_PARAMS __user *user_params)
+{
+       DBGINFO(("%s get_params\n", info->device_name));
+       if (copy_to_user(user_params, &info->params, sizeof(MGSL_PARAMS)))
+               return -EFAULT;
+       return 0;
+}
+
+static int set_params(struct slgt_info *info, MGSL_PARAMS __user *new_params)
+{
+       unsigned long flags;
+       MGSL_PARAMS tmp_params;
+
+       DBGINFO(("%s set_params\n", info->device_name));
+       if (copy_from_user(&tmp_params, new_params, sizeof(MGSL_PARAMS)))
+               return -EFAULT;
+
+       spin_lock_irqsave(&info->lock, flags);
+       if (tmp_params.mode == MGSL_MODE_BASE_CLOCK)
+               info->base_clock = tmp_params.clock_speed;
+       else
+               memcpy(&info->params, &tmp_params, sizeof(MGSL_PARAMS));
+       spin_unlock_irqrestore(&info->lock, flags);
+
+       program_hw(info);
+
+       return 0;
+}
+
+static int get_txidle(struct slgt_info *info, int __user *idle_mode)
+{
+       DBGINFO(("%s get_txidle=%d\n", info->device_name, info->idle_mode));
+       if (put_user(info->idle_mode, idle_mode))
+               return -EFAULT;
+       return 0;
+}
+
+static int set_txidle(struct slgt_info *info, int idle_mode)
+{
+       unsigned long flags;
+       DBGINFO(("%s set_txidle(%d)\n", info->device_name, idle_mode));
+       spin_lock_irqsave(&info->lock,flags);
+       info->idle_mode = idle_mode;
+       if (info->params.mode != MGSL_MODE_ASYNC)
+               tx_set_idle(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+       return 0;
+}
+
+static int tx_enable(struct slgt_info *info, int enable)
+{
+       unsigned long flags;
+       DBGINFO(("%s tx_enable(%d)\n", info->device_name, enable));
+       spin_lock_irqsave(&info->lock,flags);
+       if (enable) {
+               if (!info->tx_enabled)
+                       tx_start(info);
+       } else {
+               if (info->tx_enabled)
+                       tx_stop(info);
+       }
+       spin_unlock_irqrestore(&info->lock,flags);
+       return 0;
+}
+
+/*
+ * abort transmit HDLC frame
+ */
+static int tx_abort(struct slgt_info *info)
+{
+       unsigned long flags;
+       DBGINFO(("%s tx_abort\n", info->device_name));
+       spin_lock_irqsave(&info->lock,flags);
+       tdma_reset(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+       return 0;
+}
+
+static int rx_enable(struct slgt_info *info, int enable)
+{
+       unsigned long flags;
+       unsigned int rbuf_fill_level;
+       DBGINFO(("%s rx_enable(%08x)\n", info->device_name, enable));
+       spin_lock_irqsave(&info->lock,flags);
+       /*
+        * enable[31..16] = receive DMA buffer fill level
+        * 0 = noop (leave fill level unchanged)
+        * fill level must be multiple of 4 and <= buffer size
+        */
+       rbuf_fill_level = ((unsigned int)enable) >> 16;
+       if (rbuf_fill_level) {
+               if ((rbuf_fill_level > DMABUFSIZE) || (rbuf_fill_level % 4)) {
+                       spin_unlock_irqrestore(&info->lock, flags);
+                       return -EINVAL;
+               }
+               info->rbuf_fill_level = rbuf_fill_level;
+               if (rbuf_fill_level < 128)
+                       info->rx_pio = 1; /* PIO mode */
+               else
+                       info->rx_pio = 0; /* DMA mode */
+               rx_stop(info); /* restart receiver to use new fill level */
+       }
+
+       /*
+        * enable[1..0] = receiver enable command
+        * 0 = disable
+        * 1 = enable
+        * 2 = enable or force hunt mode if already enabled
+        */
+       enable &= 3;
+       if (enable) {
+               if (!info->rx_enabled)
+                       rx_start(info);
+               else if (enable == 2) {
+                       /* force hunt mode (write 1 to RCR[3]) */
+                       wr_reg16(info, RCR, rd_reg16(info, RCR) | BIT3);
+               }
+       } else {
+               if (info->rx_enabled)
+                       rx_stop(info);
+       }
+       spin_unlock_irqrestore(&info->lock,flags);
+       return 0;
+}
+
+/*
+ *  wait for specified event to occur
+ */
+static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr)
+{
+       unsigned long flags;
+       int s;
+       int rc=0;
+       struct mgsl_icount cprev, cnow;
+       int events;
+       int mask;
+       struct  _input_signal_events oldsigs, newsigs;
+       DECLARE_WAITQUEUE(wait, current);
+
+       if (get_user(mask, mask_ptr))
+               return -EFAULT;
+
+       DBGINFO(("%s wait_mgsl_event(%d)\n", info->device_name, mask));
+
+       spin_lock_irqsave(&info->lock,flags);
+
+       /* return immediately if state matches requested events */
+       get_signals(info);
+       s = info->signals;
+
+       events = mask &
+               ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
+                 ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
+                 ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) +
+                 ((s & SerialSignal_RI)  ? MgslEvent_RiActive :MgslEvent_RiInactive) );
+       if (events) {
+               spin_unlock_irqrestore(&info->lock,flags);
+               goto exit;
+       }
+
+       /* save current irq counts */
+       cprev = info->icount;
+       oldsigs = info->input_signal_events;
+
+       /* enable hunt and idle irqs if needed */
+       if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) {
+               unsigned short val = rd_reg16(info, SCR);
+               if (!(val & IRQ_RXIDLE))
+                       wr_reg16(info, SCR, (unsigned short)(val | IRQ_RXIDLE));
+       }
+
+       set_current_state(TASK_INTERRUPTIBLE);
+       add_wait_queue(&info->event_wait_q, &wait);
+
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       for(;;) {
+               schedule();
+               if (signal_pending(current)) {
+                       rc = -ERESTARTSYS;
+                       break;
+               }
+
+               /* get current irq counts */
+               spin_lock_irqsave(&info->lock,flags);
+               cnow = info->icount;
+               newsigs = info->input_signal_events;
+               set_current_state(TASK_INTERRUPTIBLE);
+               spin_unlock_irqrestore(&info->lock,flags);
+
+               /* if no change, wait aborted for some reason */
+               if (newsigs.dsr_up   == oldsigs.dsr_up   &&
+                   newsigs.dsr_down == oldsigs.dsr_down &&
+                   newsigs.dcd_up   == oldsigs.dcd_up   &&
+                   newsigs.dcd_down == oldsigs.dcd_down &&
+                   newsigs.cts_up   == oldsigs.cts_up   &&
+                   newsigs.cts_down == oldsigs.cts_down &&
+                   newsigs.ri_up    == oldsigs.ri_up    &&
+                   newsigs.ri_down  == oldsigs.ri_down  &&
+                   cnow.exithunt    == cprev.exithunt   &&
+                   cnow.rxidle      == cprev.rxidle) {
+                       rc = -EIO;
+                       break;
+               }
+
+               events = mask &
+                       ( (newsigs.dsr_up   != oldsigs.dsr_up   ? MgslEvent_DsrActive:0)   +
+                         (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) +
+                         (newsigs.dcd_up   != oldsigs.dcd_up   ? MgslEvent_DcdActive:0)   +
+                         (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) +
+                         (newsigs.cts_up   != oldsigs.cts_up   ? MgslEvent_CtsActive:0)   +
+                         (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) +
+                         (newsigs.ri_up    != oldsigs.ri_up    ? MgslEvent_RiActive:0)    +
+                         (newsigs.ri_down  != oldsigs.ri_down  ? MgslEvent_RiInactive:0)  +
+                         (cnow.exithunt    != cprev.exithunt   ? MgslEvent_ExitHuntMode:0) +
+                         (cnow.rxidle      != cprev.rxidle     ? MgslEvent_IdleReceived:0) );
+               if (events)
+                       break;
+
+               cprev = cnow;
+               oldsigs = newsigs;
+       }
+
+       remove_wait_queue(&info->event_wait_q, &wait);
+       set_current_state(TASK_RUNNING);
+
+
+       if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
+               spin_lock_irqsave(&info->lock,flags);
+               if (!waitqueue_active(&info->event_wait_q)) {
+                       /* disable enable exit hunt mode/idle rcvd IRQs */
+                       wr_reg16(info, SCR,
+                               (unsigned short)(rd_reg16(info, SCR) & ~IRQ_RXIDLE));
+               }
+               spin_unlock_irqrestore(&info->lock,flags);
+       }
+exit:
+       if (rc == 0)
+               rc = put_user(events, mask_ptr);
+       return rc;
+}
+
+static int get_interface(struct slgt_info *info, int __user *if_mode)
+{
+       DBGINFO(("%s get_interface=%x\n", info->device_name, info->if_mode));
+       if (put_user(info->if_mode, if_mode))
+               return -EFAULT;
+       return 0;
+}
+
+static int set_interface(struct slgt_info *info, int if_mode)
+{
+       unsigned long flags;
+       unsigned short val;
+
+       DBGINFO(("%s set_interface=%x)\n", info->device_name, if_mode));
+       spin_lock_irqsave(&info->lock,flags);
+       info->if_mode = if_mode;
+
+       msc_set_vcr(info);
+
+       /* TCR (tx control) 07  1=RTS driver control */
+       val = rd_reg16(info, TCR);
+       if (info->if_mode & MGSL_INTERFACE_RTS_EN)
+               val |= BIT7;
+       else
+               val &= ~BIT7;
+       wr_reg16(info, TCR, val);
+
+       spin_unlock_irqrestore(&info->lock,flags);
+       return 0;
+}
+
+static int get_xsync(struct slgt_info *info, int __user *xsync)
+{
+       DBGINFO(("%s get_xsync=%x\n", info->device_name, info->xsync));
+       if (put_user(info->xsync, xsync))
+               return -EFAULT;
+       return 0;
+}
+
+/*
+ * set extended sync pattern (1 to 4 bytes) for extended sync mode
+ *
+ * sync pattern is contained in least significant bytes of value
+ * most significant byte of sync pattern is oldest (1st sent/detected)
+ */
+static int set_xsync(struct slgt_info *info, int xsync)
+{
+       unsigned long flags;
+
+       DBGINFO(("%s set_xsync=%x)\n", info->device_name, xsync));
+       spin_lock_irqsave(&info->lock, flags);
+       info->xsync = xsync;
+       wr_reg32(info, XSR, xsync);
+       spin_unlock_irqrestore(&info->lock, flags);
+       return 0;
+}
+
+static int get_xctrl(struct slgt_info *info, int __user *xctrl)
+{
+       DBGINFO(("%s get_xctrl=%x\n", info->device_name, info->xctrl));
+       if (put_user(info->xctrl, xctrl))
+               return -EFAULT;
+       return 0;
+}
+
+/*
+ * set extended control options
+ *
+ * xctrl[31:19] reserved, must be zero
+ * xctrl[18:17] extended sync pattern length in bytes
+ *              00 = 1 byte  in xsr[7:0]
+ *              01 = 2 bytes in xsr[15:0]
+ *              10 = 3 bytes in xsr[23:0]
+ *              11 = 4 bytes in xsr[31:0]
+ * xctrl[16]    1 = enable terminal count, 0=disabled
+ * xctrl[15:0]  receive terminal count for fixed length packets
+ *              value is count minus one (0 = 1 byte packet)
+ *              when terminal count is reached, receiver
+ *              automatically returns to hunt mode and receive
+ *              FIFO contents are flushed to DMA buffers with
+ *              end of frame (EOF) status
+ */
+static int set_xctrl(struct slgt_info *info, int xctrl)
+{
+       unsigned long flags;
+
+       DBGINFO(("%s set_xctrl=%x)\n", info->device_name, xctrl));
+       spin_lock_irqsave(&info->lock, flags);
+       info->xctrl = xctrl;
+       wr_reg32(info, XCR, xctrl);
+       spin_unlock_irqrestore(&info->lock, flags);
+       return 0;
+}
+
+/*
+ * set general purpose IO pin state and direction
+ *
+ * user_gpio fields:
+ * state   each bit indicates a pin state
+ * smask   set bit indicates pin state to set
+ * dir     each bit indicates a pin direction (0=input, 1=output)
+ * dmask   set bit indicates pin direction to set
+ */
+static int set_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio)
+{
+       unsigned long flags;
+       struct gpio_desc gpio;
+       __u32 data;
+
+       if (!info->gpio_present)
+               return -EINVAL;
+       if (copy_from_user(&gpio, user_gpio, sizeof(gpio)))
+               return -EFAULT;
+       DBGINFO(("%s set_gpio state=%08x smask=%08x dir=%08x dmask=%08x\n",
+                info->device_name, gpio.state, gpio.smask,
+                gpio.dir, gpio.dmask));
+
+       spin_lock_irqsave(&info->port_array[0]->lock, flags);
+       if (gpio.dmask) {
+               data = rd_reg32(info, IODR);
+               data |= gpio.dmask & gpio.dir;
+               data &= ~(gpio.dmask & ~gpio.dir);
+               wr_reg32(info, IODR, data);
+       }
+       if (gpio.smask) {
+               data = rd_reg32(info, IOVR);
+               data |= gpio.smask & gpio.state;
+               data &= ~(gpio.smask & ~gpio.state);
+               wr_reg32(info, IOVR, data);
+       }
+       spin_unlock_irqrestore(&info->port_array[0]->lock, flags);
+
+       return 0;
+}
+
+/*
+ * get general purpose IO pin state and direction
+ */
+static int get_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio)
+{
+       struct gpio_desc gpio;
+       if (!info->gpio_present)
+               return -EINVAL;
+       gpio.state = rd_reg32(info, IOVR);
+       gpio.smask = 0xffffffff;
+       gpio.dir   = rd_reg32(info, IODR);
+       gpio.dmask = 0xffffffff;
+       if (copy_to_user(user_gpio, &gpio, sizeof(gpio)))
+               return -EFAULT;
+       DBGINFO(("%s get_gpio state=%08x dir=%08x\n",
+                info->device_name, gpio.state, gpio.dir));
+       return 0;
+}
+
+/*
+ * conditional wait facility
+ */
+static void init_cond_wait(struct cond_wait *w, unsigned int data)
+{
+       init_waitqueue_head(&w->q);
+       init_waitqueue_entry(&w->wait, current);
+       w->data = data;
+}
+
+static void add_cond_wait(struct cond_wait **head, struct cond_wait *w)
+{
+       set_current_state(TASK_INTERRUPTIBLE);
+       add_wait_queue(&w->q, &w->wait);
+       w->next = *head;
+       *head = w;
+}
+
+static void remove_cond_wait(struct cond_wait **head, struct cond_wait *cw)
+{
+       struct cond_wait *w, *prev;
+       remove_wait_queue(&cw->q, &cw->wait);
+       set_current_state(TASK_RUNNING);
+       for (w = *head, prev = NULL ; w != NULL ; prev = w, w = w->next) {
+               if (w == cw) {
+                       if (prev != NULL)
+                               prev->next = w->next;
+                       else
+                               *head = w->next;
+                       break;
+               }
+       }
+}
+
+static void flush_cond_wait(struct cond_wait **head)
+{
+       while (*head != NULL) {
+               wake_up_interruptible(&(*head)->q);
+               *head = (*head)->next;
+       }
+}
+
+/*
+ * wait for general purpose I/O pin(s) to enter specified state
+ *
+ * user_gpio fields:
+ * state - bit indicates target pin state
+ * smask - set bit indicates watched pin
+ *
+ * The wait ends when at least one watched pin enters the specified
+ * state. When 0 (no error) is returned, user_gpio->state is set to the
+ * state of all GPIO pins when the wait ends.
+ *
+ * Note: Each pin may be a dedicated input, dedicated output, or
+ * configurable input/output. The number and configuration of pins
+ * varies with the specific adapter model. Only input pins (dedicated
+ * or configured) can be monitored with this function.
+ */
+static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio)
+{
+       unsigned long flags;
+       int rc = 0;
+       struct gpio_desc gpio;
+       struct cond_wait wait;
+       u32 state;
+
+       if (!info->gpio_present)
+               return -EINVAL;
+       if (copy_from_user(&gpio, user_gpio, sizeof(gpio)))
+               return -EFAULT;
+       DBGINFO(("%s wait_gpio() state=%08x smask=%08x\n",
+                info->device_name, gpio.state, gpio.smask));
+       /* ignore output pins identified by set IODR bit */
+       if ((gpio.smask &= ~rd_reg32(info, IODR)) == 0)
+               return -EINVAL;
+       init_cond_wait(&wait, gpio.smask);
+
+       spin_lock_irqsave(&info->port_array[0]->lock, flags);
+       /* enable interrupts for watched pins */
+       wr_reg32(info, IOER, rd_reg32(info, IOER) | gpio.smask);
+       /* get current pin states */
+       state = rd_reg32(info, IOVR);
+
+       if (gpio.smask & ~(state ^ gpio.state)) {
+               /* already in target state */
+               gpio.state = state;
+       } else {
+               /* wait for target state */
+               add_cond_wait(&info->gpio_wait_q, &wait);
+               spin_unlock_irqrestore(&info->port_array[0]->lock, flags);
+               schedule();
+               if (signal_pending(current))
+                       rc = -ERESTARTSYS;
+               else
+                       gpio.state = wait.data;
+               spin_lock_irqsave(&info->port_array[0]->lock, flags);
+               remove_cond_wait(&info->gpio_wait_q, &wait);
+       }
+
+       /* disable all GPIO interrupts if no waiting processes */
+       if (info->gpio_wait_q == NULL)
+               wr_reg32(info, IOER, 0);
+       spin_unlock_irqrestore(&info->port_array[0]->lock, flags);
+
+       if ((rc == 0) && copy_to_user(user_gpio, &gpio, sizeof(gpio)))
+               rc = -EFAULT;
+       return rc;
+}
+
+static int modem_input_wait(struct slgt_info *info,int arg)
+{
+       unsigned long flags;
+       int rc;
+       struct mgsl_icount cprev, cnow;
+       DECLARE_WAITQUEUE(wait, current);
+
+       /* save current irq counts */
+       spin_lock_irqsave(&info->lock,flags);
+       cprev = info->icount;
+       add_wait_queue(&info->status_event_wait_q, &wait);
+       set_current_state(TASK_INTERRUPTIBLE);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       for(;;) {
+               schedule();
+               if (signal_pending(current)) {
+                       rc = -ERESTARTSYS;
+                       break;
+               }
+
+               /* get new irq counts */
+               spin_lock_irqsave(&info->lock,flags);
+               cnow = info->icount;
+               set_current_state(TASK_INTERRUPTIBLE);
+               spin_unlock_irqrestore(&info->lock,flags);
+
+               /* if no change, wait aborted for some reason */
+               if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+                   cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
+                       rc = -EIO;
+                       break;
+               }
+
+               /* check for change in caller specified modem input */
+               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)) {
+                       rc = 0;
+                       break;
+               }
+
+               cprev = cnow;
+       }
+       remove_wait_queue(&info->status_event_wait_q, &wait);
+       set_current_state(TASK_RUNNING);
+       return rc;
+}
+
+/*
+ *  return state of serial control and status signals
+ */
+static int tiocmget(struct tty_struct *tty)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned int result;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->lock,flags);
+       get_signals(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       result = ((info->signals & SerialSignal_RTS) ? TIOCM_RTS:0) +
+               ((info->signals & SerialSignal_DTR) ? TIOCM_DTR:0) +
+               ((info->signals & SerialSignal_DCD) ? TIOCM_CAR:0) +
+               ((info->signals & SerialSignal_RI)  ? TIOCM_RNG:0) +
+               ((info->signals & SerialSignal_DSR) ? TIOCM_DSR:0) +
+               ((info->signals & SerialSignal_CTS) ? TIOCM_CTS:0);
+
+       DBGINFO(("%s tiocmget value=%08X\n", info->device_name, result));
+       return result;
+}
+
+/*
+ * set modem control signals (DTR/RTS)
+ *
+ *     cmd     signal command: TIOCMBIS = set bit TIOCMBIC = clear bit
+ *             TIOCMSET = set/clear signal values
+ *     value   bit mask for command
+ */
+static int tiocmset(struct tty_struct *tty,
+                   unsigned int set, unsigned int clear)
+{
+       struct slgt_info *info = tty->driver_data;
+       unsigned long flags;
+
+       DBGINFO(("%s tiocmset(%x,%x)\n", info->device_name, set, clear));
+
+       if (set & TIOCM_RTS)
+               info->signals |= SerialSignal_RTS;
+       if (set & TIOCM_DTR)
+               info->signals |= SerialSignal_DTR;
+       if (clear & TIOCM_RTS)
+               info->signals &= ~SerialSignal_RTS;
+       if (clear & TIOCM_DTR)
+               info->signals &= ~SerialSignal_DTR;
+
+       spin_lock_irqsave(&info->lock,flags);
+       set_signals(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+       return 0;
+}
+
+static int carrier_raised(struct tty_port *port)
+{
+       unsigned long flags;
+       struct slgt_info *info = container_of(port, struct slgt_info, port);
+
+       spin_lock_irqsave(&info->lock,flags);
+       get_signals(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+       return (info->signals & SerialSignal_DCD) ? 1 : 0;
+}
+
+static void dtr_rts(struct tty_port *port, int on)
+{
+       unsigned long flags;
+       struct slgt_info *info = container_of(port, struct slgt_info, port);
+
+       spin_lock_irqsave(&info->lock,flags);
+       if (on)
+               info->signals |= SerialSignal_RTS + SerialSignal_DTR;
+       else
+               info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+       set_signals(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+}
+
+
+/*
+ *  block current process until the device is ready to open
+ */
+static int block_til_ready(struct tty_struct *tty, struct file *filp,
+                          struct slgt_info *info)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       int             retval;
+       bool            do_clocal = false;
+       bool            extra_count = false;
+       unsigned long   flags;
+       int             cd;
+       struct tty_port *port = &info->port;
+
+       DBGINFO(("%s block_til_ready\n", tty->driver->name));
+
+       if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){
+               /* nonblock mode is set or port is not enabled */
+               port->flags |= ASYNC_NORMAL_ACTIVE;
+               return 0;
+       }
+
+       if (tty->termios->c_cflag & CLOCAL)
+               do_clocal = true;
+
+       /* Wait for carrier detect and the line to become
+        * free (i.e., not in use by the callout).  While we are in
+        * this loop, port->count is dropped by one, so that
+        * close() knows when to free things.  We restore it upon
+        * exit, either normal or abnormal.
+        */
+
+       retval = 0;
+       add_wait_queue(&port->open_wait, &wait);
+
+       spin_lock_irqsave(&info->lock, flags);
+       if (!tty_hung_up_p(filp)) {
+               extra_count = true;
+               port->count--;
+       }
+       spin_unlock_irqrestore(&info->lock, flags);
+       port->blocked_open++;
+
+       while (1) {
+               if ((tty->termios->c_cflag & CBAUD))
+                       tty_port_raise_dtr_rts(port);
+
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){
+                       retval = (port->flags & ASYNC_HUP_NOTIFY) ?
+                                       -EAGAIN : -ERESTARTSYS;
+                       break;
+               }
+
+               cd = tty_port_carrier_raised(port);
+
+               if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd ))
+                       break;
+
+               if (signal_pending(current)) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+
+               DBGINFO(("%s block_til_ready wait\n", tty->driver->name));
+               tty_unlock();
+               schedule();
+               tty_lock();
+       }
+
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&port->open_wait, &wait);
+
+       if (extra_count)
+               port->count++;
+       port->blocked_open--;
+
+       if (!retval)
+               port->flags |= ASYNC_NORMAL_ACTIVE;
+
+       DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval));
+       return retval;
+}
+
+static int alloc_tmp_rbuf(struct slgt_info *info)
+{
+       info->tmp_rbuf = kmalloc(info->max_frame_size + 5, GFP_KERNEL);
+       if (info->tmp_rbuf == NULL)
+               return -ENOMEM;
+       return 0;
+}
+
+static void free_tmp_rbuf(struct slgt_info *info)
+{
+       kfree(info->tmp_rbuf);
+       info->tmp_rbuf = NULL;
+}
+
+/*
+ * allocate DMA descriptor lists.
+ */
+static int alloc_desc(struct slgt_info *info)
+{
+       unsigned int i;
+       unsigned int pbufs;
+
+       /* allocate memory to hold descriptor lists */
+       info->bufs = pci_alloc_consistent(info->pdev, DESC_LIST_SIZE, &info->bufs_dma_addr);
+       if (info->bufs == NULL)
+               return -ENOMEM;
+
+       memset(info->bufs, 0, DESC_LIST_SIZE);
+
+       info->rbufs = (struct slgt_desc*)info->bufs;
+       info->tbufs = ((struct slgt_desc*)info->bufs) + info->rbuf_count;
+
+       pbufs = (unsigned int)info->bufs_dma_addr;
+
+       /*
+        * Build circular lists of descriptors
+        */
+
+       for (i=0; i < info->rbuf_count; i++) {
+               /* physical address of this descriptor */
+               info->rbufs[i].pdesc = pbufs + (i * sizeof(struct slgt_desc));
+
+               /* physical address of next descriptor */
+               if (i == info->rbuf_count - 1)
+                       info->rbufs[i].next = cpu_to_le32(pbufs);
+               else
+                       info->rbufs[i].next = cpu_to_le32(pbufs + ((i+1) * sizeof(struct slgt_desc)));
+               set_desc_count(info->rbufs[i], DMABUFSIZE);
+       }
+
+       for (i=0; i < info->tbuf_count; i++) {
+               /* physical address of this descriptor */
+               info->tbufs[i].pdesc = pbufs + ((info->rbuf_count + i) * sizeof(struct slgt_desc));
+
+               /* physical address of next descriptor */
+               if (i == info->tbuf_count - 1)
+                       info->tbufs[i].next = cpu_to_le32(pbufs + info->rbuf_count * sizeof(struct slgt_desc));
+               else
+                       info->tbufs[i].next = cpu_to_le32(pbufs + ((info->rbuf_count + i + 1) * sizeof(struct slgt_desc)));
+       }
+
+       return 0;
+}
+
+static void free_desc(struct slgt_info *info)
+{
+       if (info->bufs != NULL) {
+               pci_free_consistent(info->pdev, DESC_LIST_SIZE, info->bufs, info->bufs_dma_addr);
+               info->bufs  = NULL;
+               info->rbufs = NULL;
+               info->tbufs = NULL;
+       }
+}
+
+static int alloc_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count)
+{
+       int i;
+       for (i=0; i < count; i++) {
+               if ((bufs[i].buf = pci_alloc_consistent(info->pdev, DMABUFSIZE, &bufs[i].buf_dma_addr)) == NULL)
+                       return -ENOMEM;
+               bufs[i].pbuf  = cpu_to_le32((unsigned int)bufs[i].buf_dma_addr);
+       }
+       return 0;
+}
+
+static void free_bufs(struct slgt_info *info, struct slgt_desc *bufs, int count)
+{
+       int i;
+       for (i=0; i < count; i++) {
+               if (bufs[i].buf == NULL)
+                       continue;
+               pci_free_consistent(info->pdev, DMABUFSIZE, bufs[i].buf, bufs[i].buf_dma_addr);
+               bufs[i].buf = NULL;
+       }
+}
+
+static int alloc_dma_bufs(struct slgt_info *info)
+{
+       info->rbuf_count = 32;
+       info->tbuf_count = 32;
+
+       if (alloc_desc(info) < 0 ||
+           alloc_bufs(info, info->rbufs, info->rbuf_count) < 0 ||
+           alloc_bufs(info, info->tbufs, info->tbuf_count) < 0 ||
+           alloc_tmp_rbuf(info) < 0) {
+               DBGERR(("%s DMA buffer alloc fail\n", info->device_name));
+               return -ENOMEM;
+       }
+       reset_rbufs(info);
+       return 0;
+}
+
+static void free_dma_bufs(struct slgt_info *info)
+{
+       if (info->bufs) {
+               free_bufs(info, info->rbufs, info->rbuf_count);
+               free_bufs(info, info->tbufs, info->tbuf_count);
+               free_desc(info);
+       }
+       free_tmp_rbuf(info);
+}
+
+static int claim_resources(struct slgt_info *info)
+{
+       if (request_mem_region(info->phys_reg_addr, SLGT_REG_SIZE, "synclink_gt") == NULL) {
+               DBGERR(("%s reg addr conflict, addr=%08X\n",
+                       info->device_name, info->phys_reg_addr));
+               info->init_error = DiagStatus_AddressConflict;
+               goto errout;
+       }
+       else
+               info->reg_addr_requested = true;
+
+       info->reg_addr = ioremap_nocache(info->phys_reg_addr, SLGT_REG_SIZE);
+       if (!info->reg_addr) {
+               DBGERR(("%s cant map device registers, addr=%08X\n",
+                       info->device_name, info->phys_reg_addr));
+               info->init_error = DiagStatus_CantAssignPciResources;
+               goto errout;
+       }
+       return 0;
+
+errout:
+       release_resources(info);
+       return -ENODEV;
+}
+
+static void release_resources(struct slgt_info *info)
+{
+       if (info->irq_requested) {
+               free_irq(info->irq_level, info);
+               info->irq_requested = false;
+       }
+
+       if (info->reg_addr_requested) {
+               release_mem_region(info->phys_reg_addr, SLGT_REG_SIZE);
+               info->reg_addr_requested = false;
+       }
+
+       if (info->reg_addr) {
+               iounmap(info->reg_addr);
+               info->reg_addr = NULL;
+       }
+}
+
+/* Add the specified device instance data structure to the
+ * global linked list of devices and increment the device count.
+ */
+static void add_device(struct slgt_info *info)
+{
+       char *devstr;
+
+       info->next_device = NULL;
+       info->line = slgt_device_count;
+       sprintf(info->device_name, "%s%d", tty_dev_prefix, info->line);
+
+       if (info->line < MAX_DEVICES) {
+               if (maxframe[info->line])
+                       info->max_frame_size = maxframe[info->line];
+       }
+
+       slgt_device_count++;
+
+       if (!slgt_device_list)
+               slgt_device_list = info;
+       else {
+               struct slgt_info *current_dev = slgt_device_list;
+               while(current_dev->next_device)
+                       current_dev = current_dev->next_device;
+               current_dev->next_device = info;
+       }
+
+       if (info->max_frame_size < 4096)
+               info->max_frame_size = 4096;
+       else if (info->max_frame_size > 65535)
+               info->max_frame_size = 65535;
+
+       switch(info->pdev->device) {
+       case SYNCLINK_GT_DEVICE_ID:
+               devstr = "GT";
+               break;
+       case SYNCLINK_GT2_DEVICE_ID:
+               devstr = "GT2";
+               break;
+       case SYNCLINK_GT4_DEVICE_ID:
+               devstr = "GT4";
+               break;
+       case SYNCLINK_AC_DEVICE_ID:
+               devstr = "AC";
+               info->params.mode = MGSL_MODE_ASYNC;
+               break;
+       default:
+               devstr = "(unknown model)";
+       }
+       printk("SyncLink %s %s IO=%08x IRQ=%d MaxFrameSize=%u\n",
+               devstr, info->device_name, info->phys_reg_addr,
+               info->irq_level, info->max_frame_size);
+
+#if SYNCLINK_GENERIC_HDLC
+       hdlcdev_init(info);
+#endif
+}
+
+static const struct tty_port_operations slgt_port_ops = {
+       .carrier_raised = carrier_raised,
+       .dtr_rts = dtr_rts,
+};
+
+/*
+ *  allocate device instance structure, return NULL on failure
+ */
+static struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev)
+{
+       struct slgt_info *info;
+
+       info = kzalloc(sizeof(struct slgt_info), GFP_KERNEL);
+
+       if (!info) {
+               DBGERR(("%s device alloc failed adapter=%d port=%d\n",
+                       driver_name, adapter_num, port_num));
+       } else {
+               tty_port_init(&info->port);
+               info->port.ops = &slgt_port_ops;
+               info->magic = MGSL_MAGIC;
+               INIT_WORK(&info->task, bh_handler);
+               info->max_frame_size = 4096;
+               info->base_clock = 14745600;
+               info->rbuf_fill_level = DMABUFSIZE;
+               info->port.close_delay = 5*HZ/10;
+               info->port.closing_wait = 30*HZ;
+               init_waitqueue_head(&info->status_event_wait_q);
+               init_waitqueue_head(&info->event_wait_q);
+               spin_lock_init(&info->netlock);
+               memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
+               info->idle_mode = HDLC_TXIDLE_FLAGS;
+               info->adapter_num = adapter_num;
+               info->port_num = port_num;
+
+               setup_timer(&info->tx_timer, tx_timeout, (unsigned long)info);
+               setup_timer(&info->rx_timer, rx_timeout, (unsigned long)info);
+
+               /* Copy configuration info to device instance data */
+               info->pdev = pdev;
+               info->irq_level = pdev->irq;
+               info->phys_reg_addr = pci_resource_start(pdev,0);
+
+               info->bus_type = MGSL_BUS_TYPE_PCI;
+               info->irq_flags = IRQF_SHARED;
+
+               info->init_error = -1; /* assume error, set to 0 on successful init */
+       }
+
+       return info;
+}
+
+static void device_init(int adapter_num, struct pci_dev *pdev)
+{
+       struct slgt_info *port_array[SLGT_MAX_PORTS];
+       int i;
+       int port_count = 1;
+
+       if (pdev->device == SYNCLINK_GT2_DEVICE_ID)
+               port_count = 2;
+       else if (pdev->device == SYNCLINK_GT4_DEVICE_ID)
+               port_count = 4;
+
+       /* allocate device instances for all ports */
+       for (i=0; i < port_count; ++i) {
+               port_array[i] = alloc_dev(adapter_num, i, pdev);
+               if (port_array[i] == NULL) {
+                       for (--i; i >= 0; --i)
+                               kfree(port_array[i]);
+                       return;
+               }
+       }
+
+       /* give copy of port_array to all ports and add to device list  */
+       for (i=0; i < port_count; ++i) {
+               memcpy(port_array[i]->port_array, port_array, sizeof(port_array));
+               add_device(port_array[i]);
+               port_array[i]->port_count = port_count;
+               spin_lock_init(&port_array[i]->lock);
+       }
+
+       /* Allocate and claim adapter resources */
+       if (!claim_resources(port_array[0])) {
+
+               alloc_dma_bufs(port_array[0]);
+
+               /* copy resource information from first port to others */
+               for (i = 1; i < port_count; ++i) {
+                       port_array[i]->irq_level = port_array[0]->irq_level;
+                       port_array[i]->reg_addr  = port_array[0]->reg_addr;
+                       alloc_dma_bufs(port_array[i]);
+               }
+
+               if (request_irq(port_array[0]->irq_level,
+                                       slgt_interrupt,
+                                       port_array[0]->irq_flags,
+                                       port_array[0]->device_name,
+                                       port_array[0]) < 0) {
+                       DBGERR(("%s request_irq failed IRQ=%d\n",
+                               port_array[0]->device_name,
+                               port_array[0]->irq_level));
+               } else {
+                       port_array[0]->irq_requested = true;
+                       adapter_test(port_array[0]);
+                       for (i=1 ; i < port_count ; i++) {
+                               port_array[i]->init_error = port_array[0]->init_error;
+                               port_array[i]->gpio_present = port_array[0]->gpio_present;
+                       }
+               }
+       }
+
+       for (i=0; i < port_count; ++i)
+               tty_register_device(serial_driver, port_array[i]->line, &(port_array[i]->pdev->dev));
+}
+
+static int __devinit init_one(struct pci_dev *dev,
+                             const struct pci_device_id *ent)
+{
+       if (pci_enable_device(dev)) {
+               printk("error enabling pci device %p\n", dev);
+               return -EIO;
+       }
+       pci_set_master(dev);
+       device_init(slgt_device_count, dev);
+       return 0;
+}
+
+static void __devexit remove_one(struct pci_dev *dev)
+{
+}
+
+static const struct tty_operations ops = {
+       .open = open,
+       .close = close,
+       .write = write,
+       .put_char = put_char,
+       .flush_chars = flush_chars,
+       .write_room = write_room,
+       .chars_in_buffer = chars_in_buffer,
+       .flush_buffer = flush_buffer,
+       .ioctl = ioctl,
+       .compat_ioctl = slgt_compat_ioctl,
+       .throttle = throttle,
+       .unthrottle = unthrottle,
+       .send_xchar = send_xchar,
+       .break_ctl = set_break,
+       .wait_until_sent = wait_until_sent,
+       .set_termios = set_termios,
+       .stop = tx_hold,
+       .start = tx_release,
+       .hangup = hangup,
+       .tiocmget = tiocmget,
+       .tiocmset = tiocmset,
+       .get_icount = get_icount,
+       .proc_fops = &synclink_gt_proc_fops,
+};
+
+static void slgt_cleanup(void)
+{
+       int rc;
+       struct slgt_info *info;
+       struct slgt_info *tmp;
+
+       printk(KERN_INFO "unload %s\n", driver_name);
+
+       if (serial_driver) {
+               for (info=slgt_device_list ; info != NULL ; info=info->next_device)
+                       tty_unregister_device(serial_driver, info->line);
+               if ((rc = tty_unregister_driver(serial_driver)))
+                       DBGERR(("tty_unregister_driver error=%d\n", rc));
+               put_tty_driver(serial_driver);
+       }
+
+       /* reset devices */
+       info = slgt_device_list;
+       while(info) {
+               reset_port(info);
+               info = info->next_device;
+       }
+
+       /* release devices */
+       info = slgt_device_list;
+       while(info) {
+#if SYNCLINK_GENERIC_HDLC
+               hdlcdev_exit(info);
+#endif
+               free_dma_bufs(info);
+               free_tmp_rbuf(info);
+               if (info->port_num == 0)
+                       release_resources(info);
+               tmp = info;
+               info = info->next_device;
+               kfree(tmp);
+       }
+
+       if (pci_registered)
+               pci_unregister_driver(&pci_driver);
+}
+
+/*
+ *  Driver initialization entry point.
+ */
+static int __init slgt_init(void)
+{
+       int rc;
+
+       printk(KERN_INFO "%s\n", driver_name);
+
+       serial_driver = alloc_tty_driver(MAX_DEVICES);
+       if (!serial_driver) {
+               printk("%s can't allocate tty driver\n", driver_name);
+               return -ENOMEM;
+       }
+
+       /* Initialize the tty_driver structure */
+
+       serial_driver->owner = THIS_MODULE;
+       serial_driver->driver_name = tty_driver_name;
+       serial_driver->name = tty_dev_prefix;
+       serial_driver->major = ttymajor;
+       serial_driver->minor_start = 64;
+       serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
+       serial_driver->subtype = SERIAL_TYPE_NORMAL;
+       serial_driver->init_termios = tty_std_termios;
+       serial_driver->init_termios.c_cflag =
+               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       serial_driver->init_termios.c_ispeed = 9600;
+       serial_driver->init_termios.c_ospeed = 9600;
+       serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+       tty_set_operations(serial_driver, &ops);
+       if ((rc = tty_register_driver(serial_driver)) < 0) {
+               DBGERR(("%s can't register serial driver\n", driver_name));
+               put_tty_driver(serial_driver);
+               serial_driver = NULL;
+               goto error;
+       }
+
+       printk(KERN_INFO "%s, tty major#%d\n",
+              driver_name, serial_driver->major);
+
+       slgt_device_count = 0;
+       if ((rc = pci_register_driver(&pci_driver)) < 0) {
+               printk("%s pci_register_driver error=%d\n", driver_name, rc);
+               goto error;
+       }
+       pci_registered = true;
+
+       if (!slgt_device_list)
+               printk("%s no devices found\n",driver_name);
+
+       return 0;
+
+error:
+       slgt_cleanup();
+       return rc;
+}
+
+static void __exit slgt_exit(void)
+{
+       slgt_cleanup();
+}
+
+module_init(slgt_init);
+module_exit(slgt_exit);
+
+/*
+ * register access routines
+ */
+
+#define CALC_REGADDR() \
+       unsigned long reg_addr = ((unsigned long)info->reg_addr) + addr; \
+       if (addr >= 0x80) \
+               reg_addr += (info->port_num) * 32; \
+       else if (addr >= 0x40)  \
+               reg_addr += (info->port_num) * 16;
+
+static __u8 rd_reg8(struct slgt_info *info, unsigned int addr)
+{
+       CALC_REGADDR();
+       return readb((void __iomem *)reg_addr);
+}
+
+static void wr_reg8(struct slgt_info *info, unsigned int addr, __u8 value)
+{
+       CALC_REGADDR();
+       writeb(value, (void __iomem *)reg_addr);
+}
+
+static __u16 rd_reg16(struct slgt_info *info, unsigned int addr)
+{
+       CALC_REGADDR();
+       return readw((void __iomem *)reg_addr);
+}
+
+static void wr_reg16(struct slgt_info *info, unsigned int addr, __u16 value)
+{
+       CALC_REGADDR();
+       writew(value, (void __iomem *)reg_addr);
+}
+
+static __u32 rd_reg32(struct slgt_info *info, unsigned int addr)
+{
+       CALC_REGADDR();
+       return readl((void __iomem *)reg_addr);
+}
+
+static void wr_reg32(struct slgt_info *info, unsigned int addr, __u32 value)
+{
+       CALC_REGADDR();
+       writel(value, (void __iomem *)reg_addr);
+}
+
+static void rdma_reset(struct slgt_info *info)
+{
+       unsigned int i;
+
+       /* set reset bit */
+       wr_reg32(info, RDCSR, BIT1);
+
+       /* wait for enable bit cleared */
+       for(i=0 ; i < 1000 ; i++)
+               if (!(rd_reg32(info, RDCSR) & BIT0))
+                       break;
+}
+
+static void tdma_reset(struct slgt_info *info)
+{
+       unsigned int i;
+
+       /* set reset bit */
+       wr_reg32(info, TDCSR, BIT1);
+
+       /* wait for enable bit cleared */
+       for(i=0 ; i < 1000 ; i++)
+               if (!(rd_reg32(info, TDCSR) & BIT0))
+                       break;
+}
+
+/*
+ * enable internal loopback
+ * TxCLK and RxCLK are generated from BRG
+ * and TxD is looped back to RxD internally.
+ */
+static void enable_loopback(struct slgt_info *info)
+{
+       /* SCR (serial control) BIT2=looopback enable */
+       wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT2));
+
+       if (info->params.mode != MGSL_MODE_ASYNC) {
+               /* CCR (clock control)
+                * 07..05  tx clock source (010 = BRG)
+                * 04..02  rx clock source (010 = BRG)
+                * 01      auxclk enable   (0 = disable)
+                * 00      BRG enable      (1 = enable)
+                *
+                * 0100 1001
+                */
+               wr_reg8(info, CCR, 0x49);
+
+               /* set speed if available, otherwise use default */
+               if (info->params.clock_speed)
+                       set_rate(info, info->params.clock_speed);
+               else
+                       set_rate(info, 3686400);
+       }
+}
+
+/*
+ *  set baud rate generator to specified rate
+ */
+static void set_rate(struct slgt_info *info, u32 rate)
+{
+       unsigned int div;
+       unsigned int osc = info->base_clock;
+
+       /* div = osc/rate - 1
+        *
+        * Round div up if osc/rate is not integer to
+        * force to next slowest rate.
+        */
+
+       if (rate) {
+               div = osc/rate;
+               if (!(osc % rate) && div)
+                       div--;
+               wr_reg16(info, BDR, (unsigned short)div);
+       }
+}
+
+static void rx_stop(struct slgt_info *info)
+{
+       unsigned short val;
+
+       /* disable and reset receiver */
+       val = rd_reg16(info, RCR) & ~BIT1;          /* clear enable bit */
+       wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */
+       wr_reg16(info, RCR, val);                  /* clear reset bit */
+
+       slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA + IRQ_RXIDLE);
+
+       /* clear pending rx interrupts */
+       wr_reg16(info, SSR, IRQ_RXIDLE + IRQ_RXOVER);
+
+       rdma_reset(info);
+
+       info->rx_enabled = false;
+       info->rx_restart = false;
+}
+
+static void rx_start(struct slgt_info *info)
+{
+       unsigned short val;
+
+       slgt_irq_off(info, IRQ_RXOVER + IRQ_RXDATA);
+
+       /* clear pending rx overrun IRQ */
+       wr_reg16(info, SSR, IRQ_RXOVER);
+
+       /* reset and disable receiver */
+       val = rd_reg16(info, RCR) & ~BIT1; /* clear enable bit */
+       wr_reg16(info, RCR, (unsigned short)(val | BIT2)); /* set reset bit */
+       wr_reg16(info, RCR, val);                  /* clear reset bit */
+
+       rdma_reset(info);
+       reset_rbufs(info);
+
+       if (info->rx_pio) {
+               /* rx request when rx FIFO not empty */
+               wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) & ~BIT14));
+               slgt_irq_on(info, IRQ_RXDATA);
+               if (info->params.mode == MGSL_MODE_ASYNC) {
+                       /* enable saving of rx status */
+                       wr_reg32(info, RDCSR, BIT6);
+               }
+       } else {
+               /* rx request when rx FIFO half full */
+               wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT14));
+               /* set 1st descriptor address */
+               wr_reg32(info, RDDAR, info->rbufs[0].pdesc);
+
+               if (info->params.mode != MGSL_MODE_ASYNC) {
+                       /* enable rx DMA and DMA interrupt */
+                       wr_reg32(info, RDCSR, (BIT2 + BIT0));
+               } else {
+                       /* enable saving of rx status, rx DMA and DMA interrupt */
+                       wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0));
+               }
+       }
+
+       slgt_irq_on(info, IRQ_RXOVER);
+
+       /* enable receiver */
+       wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | BIT1));
+
+       info->rx_restart = false;
+       info->rx_enabled = true;
+}
+
+static void tx_start(struct slgt_info *info)
+{
+       if (!info->tx_enabled) {
+               wr_reg16(info, TCR,
+                        (unsigned short)((rd_reg16(info, TCR) | BIT1) & ~BIT2));
+               info->tx_enabled = true;
+       }
+
+       if (desc_count(info->tbufs[info->tbuf_start])) {
+               info->drop_rts_on_tx_done = false;
+
+               if (info->params.mode != MGSL_MODE_ASYNC) {
+                       if (info->params.flags & HDLC_FLAG_AUTO_RTS) {
+                               get_signals(info);
+                               if (!(info->signals & SerialSignal_RTS)) {
+                                       info->signals |= SerialSignal_RTS;
+                                       set_signals(info);
+                                       info->drop_rts_on_tx_done = true;
+                               }
+                       }
+
+                       slgt_irq_off(info, IRQ_TXDATA);
+                       slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE);
+                       /* clear tx idle and underrun status bits */
+                       wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
+               } else {
+                       slgt_irq_off(info, IRQ_TXDATA);
+                       slgt_irq_on(info, IRQ_TXIDLE);
+                       /* clear tx idle status bit */
+                       wr_reg16(info, SSR, IRQ_TXIDLE);
+               }
+               /* set 1st descriptor address and start DMA */
+               wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
+               wr_reg32(info, TDCSR, BIT2 + BIT0);
+               info->tx_active = true;
+       }
+}
+
+static void tx_stop(struct slgt_info *info)
+{
+       unsigned short val;
+
+       del_timer(&info->tx_timer);
+
+       tdma_reset(info);
+
+       /* reset and disable transmitter */
+       val = rd_reg16(info, TCR) & ~BIT1;          /* clear enable bit */
+       wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */
+
+       slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER);
+
+       /* clear tx idle and underrun status bit */
+       wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
+
+       reset_tbufs(info);
+
+       info->tx_enabled = false;
+       info->tx_active = false;
+}
+
+static void reset_port(struct slgt_info *info)
+{
+       if (!info->reg_addr)
+               return;
+
+       tx_stop(info);
+       rx_stop(info);
+
+       info->signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
+       set_signals(info);
+
+       slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
+}
+
+static void reset_adapter(struct slgt_info *info)
+{
+       int i;
+       for (i=0; i < info->port_count; ++i) {
+               if (info->port_array[i])
+                       reset_port(info->port_array[i]);
+       }
+}
+
+static void async_mode(struct slgt_info *info)
+{
+       unsigned short val;
+
+       slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
+       tx_stop(info);
+       rx_stop(info);
+
+       /* TCR (tx control)
+        *
+        * 15..13  mode, 010=async
+        * 12..10  encoding, 000=NRZ
+        * 09      parity enable
+        * 08      1=odd parity, 0=even parity
+        * 07      1=RTS driver control
+        * 06      1=break enable
+        * 05..04  character length
+        *         00=5 bits
+        *         01=6 bits
+        *         10=7 bits
+        *         11=8 bits
+        * 03      0=1 stop bit, 1=2 stop bits
+        * 02      reset
+        * 01      enable
+        * 00      auto-CTS enable
+        */
+       val = 0x4000;
+
+       if (info->if_mode & MGSL_INTERFACE_RTS_EN)
+               val |= BIT7;
+
+       if (info->params.parity != ASYNC_PARITY_NONE) {
+               val |= BIT9;
+               if (info->params.parity == ASYNC_PARITY_ODD)
+                       val |= BIT8;
+       }
+
+       switch (info->params.data_bits)
+       {
+       case 6: val |= BIT4; break;
+       case 7: val |= BIT5; break;
+       case 8: val |= BIT5 + BIT4; break;
+       }
+
+       if (info->params.stop_bits != 1)
+               val |= BIT3;
+
+       if (info->params.flags & HDLC_FLAG_AUTO_CTS)
+               val |= BIT0;
+
+       wr_reg16(info, TCR, val);
+
+       /* RCR (rx control)
+        *
+        * 15..13  mode, 010=async
+        * 12..10  encoding, 000=NRZ
+        * 09      parity enable
+        * 08      1=odd parity, 0=even parity
+        * 07..06  reserved, must be 0
+        * 05..04  character length
+        *         00=5 bits
+        *         01=6 bits
+        *         10=7 bits
+        *         11=8 bits
+        * 03      reserved, must be zero
+        * 02      reset
+        * 01      enable
+        * 00      auto-DCD enable
+        */
+       val = 0x4000;
+
+       if (info->params.parity != ASYNC_PARITY_NONE) {
+               val |= BIT9;
+               if (info->params.parity == ASYNC_PARITY_ODD)
+                       val |= BIT8;
+       }
+
+       switch (info->params.data_bits)
+       {
+       case 6: val |= BIT4; break;
+       case 7: val |= BIT5; break;
+       case 8: val |= BIT5 + BIT4; break;
+       }
+
+       if (info->params.flags & HDLC_FLAG_AUTO_DCD)
+               val |= BIT0;
+
+       wr_reg16(info, RCR, val);
+
+       /* CCR (clock control)
+        *
+        * 07..05  011 = tx clock source is BRG/16
+        * 04..02  010 = rx clock source is BRG
+        * 01      0 = auxclk disabled
+        * 00      1 = BRG enabled
+        *
+        * 0110 1001
+        */
+       wr_reg8(info, CCR, 0x69);
+
+       msc_set_vcr(info);
+
+       /* SCR (serial control)
+        *
+        * 15  1=tx req on FIFO half empty
+        * 14  1=rx req on FIFO half full
+        * 13  tx data  IRQ enable
+        * 12  tx idle  IRQ enable
+        * 11  rx break on IRQ enable
+        * 10  rx data  IRQ enable
+        * 09  rx break off IRQ enable
+        * 08  overrun  IRQ enable
+        * 07  DSR      IRQ enable
+        * 06  CTS      IRQ enable
+        * 05  DCD      IRQ enable
+        * 04  RI       IRQ enable
+        * 03  0=16x sampling, 1=8x sampling
+        * 02  1=txd->rxd internal loopback enable
+        * 01  reserved, must be zero
+        * 00  1=master IRQ enable
+        */
+       val = BIT15 + BIT14 + BIT0;
+       /* JCR[8] : 1 = x8 async mode feature available */
+       if ((rd_reg32(info, JCR) & BIT8) && info->params.data_rate &&
+           ((info->base_clock < (info->params.data_rate * 16)) ||
+            (info->base_clock % (info->params.data_rate * 16)))) {
+               /* use 8x sampling */
+               val |= BIT3;
+               set_rate(info, info->params.data_rate * 8);
+       } else {
+               /* use 16x sampling */
+               set_rate(info, info->params.data_rate * 16);
+       }
+       wr_reg16(info, SCR, val);
+
+       slgt_irq_on(info, IRQ_RXBREAK | IRQ_RXOVER);
+
+       if (info->params.loopback)
+               enable_loopback(info);
+}
+
+static void sync_mode(struct slgt_info *info)
+{
+       unsigned short val;
+
+       slgt_irq_off(info, IRQ_ALL | IRQ_MASTER);
+       tx_stop(info);
+       rx_stop(info);
+
+       /* TCR (tx control)
+        *
+        * 15..13  mode
+        *         000=HDLC/SDLC
+        *         001=raw bit synchronous
+        *         010=asynchronous/isochronous
+        *         011=monosync byte synchronous
+        *         100=bisync byte synchronous
+        *         101=xsync byte synchronous
+        * 12..10  encoding
+        * 09      CRC enable
+        * 08      CRC32
+        * 07      1=RTS driver control
+        * 06      preamble enable
+        * 05..04  preamble length
+        * 03      share open/close flag
+        * 02      reset
+        * 01      enable
+        * 00      auto-CTS enable
+        */
+       val = BIT2;
+
+       switch(info->params.mode) {
+       case MGSL_MODE_XSYNC:
+               val |= BIT15 + BIT13;
+               break;
+       case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break;
+       case MGSL_MODE_BISYNC:   val |= BIT15; break;
+       case MGSL_MODE_RAW:      val |= BIT13; break;
+       }
+       if (info->if_mode & MGSL_INTERFACE_RTS_EN)
+               val |= BIT7;
+
+       switch(info->params.encoding)
+       {
+       case HDLC_ENCODING_NRZB:          val |= BIT10; break;
+       case HDLC_ENCODING_NRZI_MARK:     val |= BIT11; break;
+       case HDLC_ENCODING_NRZI:          val |= BIT11 + BIT10; break;
+       case HDLC_ENCODING_BIPHASE_MARK:  val |= BIT12; break;
+       case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break;
+       case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break;
+       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break;
+       }
+
+       switch (info->params.crc_type & HDLC_CRC_MASK)
+       {
+       case HDLC_CRC_16_CCITT: val |= BIT9; break;
+       case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break;
+       }
+
+       if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE)
+               val |= BIT6;
+
+       switch (info->params.preamble_length)
+       {
+       case HDLC_PREAMBLE_LENGTH_16BITS: val |= BIT5; break;
+       case HDLC_PREAMBLE_LENGTH_32BITS: val |= BIT4; break;
+       case HDLC_PREAMBLE_LENGTH_64BITS: val |= BIT5 + BIT4; break;
+       }
+
+       if (info->params.flags & HDLC_FLAG_AUTO_CTS)
+               val |= BIT0;
+
+       wr_reg16(info, TCR, val);
+
+       /* TPR (transmit preamble) */
+
+       switch (info->params.preamble)
+       {
+       case HDLC_PREAMBLE_PATTERN_FLAGS: val = 0x7e; break;
+       case HDLC_PREAMBLE_PATTERN_ONES:  val = 0xff; break;
+       case HDLC_PREAMBLE_PATTERN_ZEROS: val = 0x00; break;
+       case HDLC_PREAMBLE_PATTERN_10:    val = 0x55; break;
+       case HDLC_PREAMBLE_PATTERN_01:    val = 0xaa; break;
+       default:                          val = 0x7e; break;
+       }
+       wr_reg8(info, TPR, (unsigned char)val);
+
+       /* RCR (rx control)
+        *
+        * 15..13  mode
+        *         000=HDLC/SDLC
+        *         001=raw bit synchronous
+        *         010=asynchronous/isochronous
+        *         011=monosync byte synchronous
+        *         100=bisync byte synchronous
+        *         101=xsync byte synchronous
+        * 12..10  encoding
+        * 09      CRC enable
+        * 08      CRC32
+        * 07..03  reserved, must be 0
+        * 02      reset
+        * 01      enable
+        * 00      auto-DCD enable
+        */
+       val = 0;
+
+       switch(info->params.mode) {
+       case MGSL_MODE_XSYNC:
+               val |= BIT15 + BIT13;
+               break;
+       case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break;
+       case MGSL_MODE_BISYNC:   val |= BIT15; break;
+       case MGSL_MODE_RAW:      val |= BIT13; break;
+       }
+
+       switch(info->params.encoding)
+       {
+       case HDLC_ENCODING_NRZB:          val |= BIT10; break;
+       case HDLC_ENCODING_NRZI_MARK:     val |= BIT11; break;
+       case HDLC_ENCODING_NRZI:          val |= BIT11 + BIT10; break;
+       case HDLC_ENCODING_BIPHASE_MARK:  val |= BIT12; break;
+       case HDLC_ENCODING_BIPHASE_SPACE: val |= BIT12 + BIT10; break;
+       case HDLC_ENCODING_BIPHASE_LEVEL: val |= BIT12 + BIT11; break;
+       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: val |= BIT12 + BIT11 + BIT10; break;
+       }
+
+       switch (info->params.crc_type & HDLC_CRC_MASK)
+       {
+       case HDLC_CRC_16_CCITT: val |= BIT9; break;
+       case HDLC_CRC_32_CCITT: val |= BIT9 + BIT8; break;
+       }
+
+       if (info->params.flags & HDLC_FLAG_AUTO_DCD)
+               val |= BIT0;
+
+       wr_reg16(info, RCR, val);
+
+       /* CCR (clock control)
+        *
+        * 07..05  tx clock source
+        * 04..02  rx clock source
+        * 01      auxclk enable
+        * 00      BRG enable
+        */
+       val = 0;
+
+       if (info->params.flags & HDLC_FLAG_TXC_BRG)
+       {
+               // when RxC source is DPLL, BRG generates 16X DPLL
+               // reference clock, so take TxC from BRG/16 to get
+               // transmit clock at actual data rate
+               if (info->params.flags & HDLC_FLAG_RXC_DPLL)
+                       val |= BIT6 + BIT5;     /* 011, txclk = BRG/16 */
+               else
+                       val |= BIT6;    /* 010, txclk = BRG */
+       }
+       else if (info->params.flags & HDLC_FLAG_TXC_DPLL)
+               val |= BIT7;    /* 100, txclk = DPLL Input */
+       else if (info->params.flags & HDLC_FLAG_TXC_RXCPIN)
+               val |= BIT5;    /* 001, txclk = RXC Input */
+
+       if (info->params.flags & HDLC_FLAG_RXC_BRG)
+               val |= BIT3;    /* 010, rxclk = BRG */
+       else if (info->params.flags & HDLC_FLAG_RXC_DPLL)
+               val |= BIT4;    /* 100, rxclk = DPLL */
+       else if (info->params.flags & HDLC_FLAG_RXC_TXCPIN)
+               val |= BIT2;    /* 001, rxclk = TXC Input */
+
+       if (info->params.clock_speed)
+               val |= BIT1 + BIT0;
+
+       wr_reg8(info, CCR, (unsigned char)val);
+
+       if (info->params.flags & (HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL))
+       {
+               // program DPLL mode
+               switch(info->params.encoding)
+               {
+               case HDLC_ENCODING_BIPHASE_MARK:
+               case HDLC_ENCODING_BIPHASE_SPACE:
+                       val = BIT7; break;
+               case HDLC_ENCODING_BIPHASE_LEVEL:
+               case HDLC_ENCODING_DIFF_BIPHASE_LEVEL:
+                       val = BIT7 + BIT6; break;
+               default: val = BIT6;    // NRZ encodings
+               }
+               wr_reg16(info, RCR, (unsigned short)(rd_reg16(info, RCR) | val));
+
+               // DPLL requires a 16X reference clock from BRG
+               set_rate(info, info->params.clock_speed * 16);
+       }
+       else
+               set_rate(info, info->params.clock_speed);
+
+       tx_set_idle(info);
+
+       msc_set_vcr(info);
+
+       /* SCR (serial control)
+        *
+        * 15  1=tx req on FIFO half empty
+        * 14  1=rx req on FIFO half full
+        * 13  tx data  IRQ enable
+        * 12  tx idle  IRQ enable
+        * 11  underrun IRQ enable
+        * 10  rx data  IRQ enable
+        * 09  rx idle  IRQ enable
+        * 08  overrun  IRQ enable
+        * 07  DSR      IRQ enable
+        * 06  CTS      IRQ enable
+        * 05  DCD      IRQ enable
+        * 04  RI       IRQ enable
+        * 03  reserved, must be zero
+        * 02  1=txd->rxd internal loopback enable
+        * 01  reserved, must be zero
+        * 00  1=master IRQ enable
+        */
+       wr_reg16(info, SCR, BIT15 + BIT14 + BIT0);
+
+       if (info->params.loopback)
+               enable_loopback(info);
+}
+
+/*
+ *  set transmit idle mode
+ */
+static void tx_set_idle(struct slgt_info *info)
+{
+       unsigned char val;
+       unsigned short tcr;
+
+       /* if preamble enabled (tcr[6] == 1) then tx idle size = 8 bits
+        * else tcr[5:4] = tx idle size: 00 = 8 bits, 01 = 16 bits
+        */
+       tcr = rd_reg16(info, TCR);
+       if (info->idle_mode & HDLC_TXIDLE_CUSTOM_16) {
+               /* disable preamble, set idle size to 16 bits */
+               tcr = (tcr & ~(BIT6 + BIT5)) | BIT4;
+               /* MSB of 16 bit idle specified in tx preamble register (TPR) */
+               wr_reg8(info, TPR, (unsigned char)((info->idle_mode >> 8) & 0xff));
+       } else if (!(tcr & BIT6)) {
+               /* preamble is disabled, set idle size to 8 bits */
+               tcr &= ~(BIT5 + BIT4);
+       }
+       wr_reg16(info, TCR, tcr);
+
+       if (info->idle_mode & (HDLC_TXIDLE_CUSTOM_8 | HDLC_TXIDLE_CUSTOM_16)) {
+               /* LSB of custom tx idle specified in tx idle register */
+               val = (unsigned char)(info->idle_mode & 0xff);
+       } else {
+               /* standard 8 bit idle patterns */
+               switch(info->idle_mode)
+               {
+               case HDLC_TXIDLE_FLAGS:          val = 0x7e; break;
+               case HDLC_TXIDLE_ALT_ZEROS_ONES:
+               case HDLC_TXIDLE_ALT_MARK_SPACE: val = 0xaa; break;
+               case HDLC_TXIDLE_ZEROS:
+               case HDLC_TXIDLE_SPACE:          val = 0x00; break;
+               default:                         val = 0xff;
+               }
+       }
+
+       wr_reg8(info, TIR, val);
+}
+
+/*
+ * get state of V24 status (input) signals
+ */
+static void get_signals(struct slgt_info *info)
+{
+       unsigned short status = rd_reg16(info, SSR);
+
+       /* clear all serial signals except DTR and RTS */
+       info->signals &= SerialSignal_DTR + SerialSignal_RTS;
+
+       if (status & BIT3)
+               info->signals |= SerialSignal_DSR;
+       if (status & BIT2)
+               info->signals |= SerialSignal_CTS;
+       if (status & BIT1)
+               info->signals |= SerialSignal_DCD;
+       if (status & BIT0)
+               info->signals |= SerialSignal_RI;
+}
+
+/*
+ * set V.24 Control Register based on current configuration
+ */
+static void msc_set_vcr(struct slgt_info *info)
+{
+       unsigned char val = 0;
+
+       /* VCR (V.24 control)
+        *
+        * 07..04  serial IF select
+        * 03      DTR
+        * 02      RTS
+        * 01      LL
+        * 00      RL
+        */
+
+       switch(info->if_mode & MGSL_INTERFACE_MASK)
+       {
+       case MGSL_INTERFACE_RS232:
+               val |= BIT5; /* 0010 */
+               break;
+       case MGSL_INTERFACE_V35:
+               val |= BIT7 + BIT6 + BIT5; /* 1110 */
+               break;
+       case MGSL_INTERFACE_RS422:
+               val |= BIT6; /* 0100 */
+               break;
+       }
+
+       if (info->if_mode & MGSL_INTERFACE_MSB_FIRST)
+               val |= BIT4;
+       if (info->signals & SerialSignal_DTR)
+               val |= BIT3;
+       if (info->signals & SerialSignal_RTS)
+               val |= BIT2;
+       if (info->if_mode & MGSL_INTERFACE_LL)
+               val |= BIT1;
+       if (info->if_mode & MGSL_INTERFACE_RL)
+               val |= BIT0;
+       wr_reg8(info, VCR, val);
+}
+
+/*
+ * set state of V24 control (output) signals
+ */
+static void set_signals(struct slgt_info *info)
+{
+       unsigned char val = rd_reg8(info, VCR);
+       if (info->signals & SerialSignal_DTR)
+               val |= BIT3;
+       else
+               val &= ~BIT3;
+       if (info->signals & SerialSignal_RTS)
+               val |= BIT2;
+       else
+               val &= ~BIT2;
+       wr_reg8(info, VCR, val);
+}
+
+/*
+ * free range of receive DMA buffers (i to last)
+ */
+static void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last)
+{
+       int done = 0;
+
+       while(!done) {
+               /* reset current buffer for reuse */
+               info->rbufs[i].status = 0;
+               set_desc_count(info->rbufs[i], info->rbuf_fill_level);
+               if (i == last)
+                       done = 1;
+               if (++i == info->rbuf_count)
+                       i = 0;
+       }
+       info->rbuf_current = i;
+}
+
+/*
+ * mark all receive DMA buffers as free
+ */
+static void reset_rbufs(struct slgt_info *info)
+{
+       free_rbufs(info, 0, info->rbuf_count - 1);
+       info->rbuf_fill_index = 0;
+       info->rbuf_fill_count = 0;
+}
+
+/*
+ * pass receive HDLC frame to upper layer
+ *
+ * return true if frame available, otherwise false
+ */
+static bool rx_get_frame(struct slgt_info *info)
+{
+       unsigned int start, end;
+       unsigned short status;
+       unsigned int framesize = 0;
+       unsigned long flags;
+       struct tty_struct *tty = info->port.tty;
+       unsigned char addr_field = 0xff;
+       unsigned int crc_size = 0;
+
+       switch (info->params.crc_type & HDLC_CRC_MASK) {
+       case HDLC_CRC_16_CCITT: crc_size = 2; break;
+       case HDLC_CRC_32_CCITT: crc_size = 4; break;
+       }
+
+check_again:
+
+       framesize = 0;
+       addr_field = 0xff;
+       start = end = info->rbuf_current;
+
+       for (;;) {
+               if (!desc_complete(info->rbufs[end]))
+                       goto cleanup;
+
+               if (framesize == 0 && info->params.addr_filter != 0xff)
+                       addr_field = info->rbufs[end].buf[0];
+
+               framesize += desc_count(info->rbufs[end]);
+
+               if (desc_eof(info->rbufs[end]))
+                       break;
+
+               if (++end == info->rbuf_count)
+                       end = 0;
+
+               if (end == info->rbuf_current) {
+                       if (info->rx_enabled){
+                               spin_lock_irqsave(&info->lock,flags);
+                               rx_start(info);
+                               spin_unlock_irqrestore(&info->lock,flags);
+                       }
+                       goto cleanup;
+               }
+       }
+
+       /* status
+        *
+        * 15      buffer complete
+        * 14..06  reserved
+        * 05..04  residue
+        * 02      eof (end of frame)
+        * 01      CRC error
+        * 00      abort
+        */
+       status = desc_status(info->rbufs[end]);
+
+       /* ignore CRC bit if not using CRC (bit is undefined) */
+       if ((info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_NONE)
+               status &= ~BIT1;
+
+       if (framesize == 0 ||
+                (addr_field != 0xff && addr_field != info->params.addr_filter)) {
+               free_rbufs(info, start, end);
+               goto check_again;
+       }
+
+       if (framesize < (2 + crc_size) || status & BIT0) {
+               info->icount.rxshort++;
+               framesize = 0;
+       } else if (status & BIT1) {
+               info->icount.rxcrc++;
+               if (!(info->params.crc_type & HDLC_CRC_RETURN_EX))
+                       framesize = 0;
+       }
+
+#if SYNCLINK_GENERIC_HDLC
+       if (framesize == 0) {
+               info->netdev->stats.rx_errors++;
+               info->netdev->stats.rx_frame_errors++;
+       }
+#endif
+
+       DBGBH(("%s rx frame status=%04X size=%d\n",
+               info->device_name, status, framesize));
+       DBGDATA(info, info->rbufs[start].buf, min_t(int, framesize, info->rbuf_fill_level), "rx");
+
+       if (framesize) {
+               if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) {
+                       framesize -= crc_size;
+                       crc_size = 0;
+               }
+
+               if (framesize > info->max_frame_size + crc_size)
+                       info->icount.rxlong++;
+               else {
+                       /* copy dma buffer(s) to contiguous temp buffer */
+                       int copy_count = framesize;
+                       int i = start;
+                       unsigned char *p = info->tmp_rbuf;
+                       info->tmp_rbuf_count = framesize;
+
+                       info->icount.rxok++;
+
+                       while(copy_count) {
+                               int partial_count = min_t(int, copy_count, info->rbuf_fill_level);
+                               memcpy(p, info->rbufs[i].buf, partial_count);
+                               p += partial_count;
+                               copy_count -= partial_count;
+                               if (++i == info->rbuf_count)
+                                       i = 0;
+                       }
+
+                       if (info->params.crc_type & HDLC_CRC_RETURN_EX) {
+                               *p = (status & BIT1) ? RX_CRC_ERROR : RX_OK;
+                               framesize++;
+                       }
+
+#if SYNCLINK_GENERIC_HDLC
+                       if (info->netcount)
+                               hdlcdev_rx(info,info->tmp_rbuf, framesize);
+                       else
+#endif
+                               ldisc_receive_buf(tty, info->tmp_rbuf, info->flag_buf, framesize);
+               }
+       }
+       free_rbufs(info, start, end);
+       return true;
+
+cleanup:
+       return false;
+}
+
+/*
+ * pass receive buffer (RAW synchronous mode) to tty layer
+ * return true if buffer available, otherwise false
+ */
+static bool rx_get_buf(struct slgt_info *info)
+{
+       unsigned int i = info->rbuf_current;
+       unsigned int count;
+
+       if (!desc_complete(info->rbufs[i]))
+               return false;
+       count = desc_count(info->rbufs[i]);
+       switch(info->params.mode) {
+       case MGSL_MODE_MONOSYNC:
+       case MGSL_MODE_BISYNC:
+       case MGSL_MODE_XSYNC:
+               /* ignore residue in byte synchronous modes */
+               if (desc_residue(info->rbufs[i]))
+                       count--;
+               break;
+       }
+       DBGDATA(info, info->rbufs[i].buf, count, "rx");
+       DBGINFO(("rx_get_buf size=%d\n", count));
+       if (count)
+               ldisc_receive_buf(info->port.tty, info->rbufs[i].buf,
+                                 info->flag_buf, count);
+       free_rbufs(info, i, i);
+       return true;
+}
+
+static void reset_tbufs(struct slgt_info *info)
+{
+       unsigned int i;
+       info->tbuf_current = 0;
+       for (i=0 ; i < info->tbuf_count ; i++) {
+               info->tbufs[i].status = 0;
+               info->tbufs[i].count  = 0;
+       }
+}
+
+/*
+ * return number of free transmit DMA buffers
+ */
+static unsigned int free_tbuf_count(struct slgt_info *info)
+{
+       unsigned int count = 0;
+       unsigned int i = info->tbuf_current;
+
+       do
+       {
+               if (desc_count(info->tbufs[i]))
+                       break; /* buffer in use */
+               ++count;
+               if (++i == info->tbuf_count)
+                       i=0;
+       } while (i != info->tbuf_current);
+
+       /* if tx DMA active, last zero count buffer is in use */
+       if (count && (rd_reg32(info, TDCSR) & BIT0))
+               --count;
+
+       return count;
+}
+
+/*
+ * return number of bytes in unsent transmit DMA buffers
+ * and the serial controller tx FIFO
+ */
+static unsigned int tbuf_bytes(struct slgt_info *info)
+{
+       unsigned int total_count = 0;
+       unsigned int i = info->tbuf_current;
+       unsigned int reg_value;
+       unsigned int count;
+       unsigned int active_buf_count = 0;
+
+       /*
+        * Add descriptor counts for all tx DMA buffers.
+        * If count is zero (cleared by DMA controller after read),
+        * the buffer is complete or is actively being read from.
+        *
+        * Record buf_count of last buffer with zero count starting
+        * from current ring position. buf_count is mirror
+        * copy of count and is not cleared by serial controller.
+        * If DMA controller is active, that buffer is actively
+        * being read so add to total.
+        */
+       do {
+               count = desc_count(info->tbufs[i]);
+               if (count)
+                       total_count += count;
+               else if (!total_count)
+                       active_buf_count = info->tbufs[i].buf_count;
+               if (++i == info->tbuf_count)
+                       i = 0;
+       } while (i != info->tbuf_current);
+
+       /* read tx DMA status register */
+       reg_value = rd_reg32(info, TDCSR);
+
+       /* if tx DMA active, last zero count buffer is in use */
+       if (reg_value & BIT0)
+               total_count += active_buf_count;
+
+       /* add tx FIFO count = reg_value[15..8] */
+       total_count += (reg_value >> 8) & 0xff;
+
+       /* if transmitter active add one byte for shift register */
+       if (info->tx_active)
+               total_count++;
+
+       return total_count;
+}
+
+/*
+ * load data into transmit DMA buffer ring and start transmitter if needed
+ * return true if data accepted, otherwise false (buffers full)
+ */
+static bool tx_load(struct slgt_info *info, const char *buf, unsigned int size)
+{
+       unsigned short count;
+       unsigned int i;
+       struct slgt_desc *d;
+
+       /* check required buffer space */
+       if (DIV_ROUND_UP(size, DMABUFSIZE) > free_tbuf_count(info))
+               return false;
+
+       DBGDATA(info, buf, size, "tx");
+
+       /*
+        * copy data to one or more DMA buffers in circular ring
+        * tbuf_start   = first buffer for this data
+        * tbuf_current = next free buffer
+        *
+        * Copy all data before making data visible to DMA controller by
+        * setting descriptor count of the first buffer.
+        * This prevents an active DMA controller from reading the first DMA
+        * buffers of a frame and stopping before the final buffers are filled.
+        */
+
+       info->tbuf_start = i = info->tbuf_current;
+
+       while (size) {
+               d = &info->tbufs[i];
+
+               count = (unsigned short)((size > DMABUFSIZE) ? DMABUFSIZE : size);
+               memcpy(d->buf, buf, count);
+
+               size -= count;
+               buf  += count;
+
+               /*
+                * set EOF bit for last buffer of HDLC frame or
+                * for every buffer in raw mode
+                */
+               if ((!size && info->params.mode == MGSL_MODE_HDLC) ||
+                   info->params.mode == MGSL_MODE_RAW)
+                       set_desc_eof(*d, 1);
+               else
+                       set_desc_eof(*d, 0);
+
+               /* set descriptor count for all but first buffer */
+               if (i != info->tbuf_start)
+                       set_desc_count(*d, count);
+               d->buf_count = count;
+
+               if (++i == info->tbuf_count)
+                       i = 0;
+       }
+
+       info->tbuf_current = i;
+
+       /* set first buffer count to make new data visible to DMA controller */
+       d = &info->tbufs[info->tbuf_start];
+       set_desc_count(*d, d->buf_count);
+
+       /* start transmitter if needed and update transmit timeout */
+       if (!info->tx_active)
+               tx_start(info);
+       update_tx_timer(info);
+
+       return true;
+}
+
+static int register_test(struct slgt_info *info)
+{
+       static unsigned short patterns[] =
+               {0x0000, 0xffff, 0xaaaa, 0x5555, 0x6969, 0x9696};
+       static unsigned int count = ARRAY_SIZE(patterns);
+       unsigned int i;
+       int rc = 0;
+
+       for (i=0 ; i < count ; i++) {
+               wr_reg16(info, TIR, patterns[i]);
+               wr_reg16(info, BDR, patterns[(i+1)%count]);
+               if ((rd_reg16(info, TIR) != patterns[i]) ||
+                   (rd_reg16(info, BDR) != patterns[(i+1)%count])) {
+                       rc = -ENODEV;
+                       break;
+               }
+       }
+       info->gpio_present = (rd_reg32(info, JCR) & BIT5) ? 1 : 0;
+       info->init_error = rc ? 0 : DiagStatus_AddressFailure;
+       return rc;
+}
+
+static int irq_test(struct slgt_info *info)
+{
+       unsigned long timeout;
+       unsigned long flags;
+       struct tty_struct *oldtty = info->port.tty;
+       u32 speed = info->params.data_rate;
+
+       info->params.data_rate = 921600;
+       info->port.tty = NULL;
+
+       spin_lock_irqsave(&info->lock, flags);
+       async_mode(info);
+       slgt_irq_on(info, IRQ_TXIDLE);
+
+       /* enable transmitter */
+       wr_reg16(info, TCR,
+               (unsigned short)(rd_reg16(info, TCR) | BIT1));
+
+       /* write one byte and wait for tx idle */
+       wr_reg16(info, TDR, 0);
+
+       /* assume failure */
+       info->init_error = DiagStatus_IrqFailure;
+       info->irq_occurred = false;
+
+       spin_unlock_irqrestore(&info->lock, flags);
+
+       timeout=100;
+       while(timeout-- && !info->irq_occurred)
+               msleep_interruptible(10);
+
+       spin_lock_irqsave(&info->lock,flags);
+       reset_port(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       info->params.data_rate = speed;
+       info->port.tty = oldtty;
+
+       info->init_error = info->irq_occurred ? 0 : DiagStatus_IrqFailure;
+       return info->irq_occurred ? 0 : -ENODEV;
+}
+
+static int loopback_test_rx(struct slgt_info *info)
+{
+       unsigned char *src, *dest;
+       int count;
+
+       if (desc_complete(info->rbufs[0])) {
+               count = desc_count(info->rbufs[0]);
+               src   = info->rbufs[0].buf;
+               dest  = info->tmp_rbuf;
+
+               for( ; count ; count-=2, src+=2) {
+                       /* src=data byte (src+1)=status byte */
+                       if (!(*(src+1) & (BIT9 + BIT8))) {
+                               *dest = *src;
+                               dest++;
+                               info->tmp_rbuf_count++;
+                       }
+               }
+               DBGDATA(info, info->tmp_rbuf, info->tmp_rbuf_count, "rx");
+               return 1;
+       }
+       return 0;
+}
+
+static int loopback_test(struct slgt_info *info)
+{
+#define TESTFRAMESIZE 20
+
+       unsigned long timeout;
+       u16 count = TESTFRAMESIZE;
+       unsigned char buf[TESTFRAMESIZE];
+       int rc = -ENODEV;
+       unsigned long flags;
+
+       struct tty_struct *oldtty = info->port.tty;
+       MGSL_PARAMS params;
+
+       memcpy(&params, &info->params, sizeof(params));
+
+       info->params.mode = MGSL_MODE_ASYNC;
+       info->params.data_rate = 921600;
+       info->params.loopback = 1;
+       info->port.tty = NULL;
+
+       /* build and send transmit frame */
+       for (count = 0; count < TESTFRAMESIZE; ++count)
+               buf[count] = (unsigned char)count;
+
+       info->tmp_rbuf_count = 0;
+       memset(info->tmp_rbuf, 0, TESTFRAMESIZE);
+
+       /* program hardware for HDLC and enabled receiver */
+       spin_lock_irqsave(&info->lock,flags);
+       async_mode(info);
+       rx_start(info);
+       tx_load(info, buf, count);
+       spin_unlock_irqrestore(&info->lock, flags);
+
+       /* wait for receive complete */
+       for (timeout = 100; timeout; --timeout) {
+               msleep_interruptible(10);
+               if (loopback_test_rx(info)) {
+                       rc = 0;
+                       break;
+               }
+       }
+
+       /* verify received frame length and contents */
+       if (!rc && (info->tmp_rbuf_count != count ||
+                 memcmp(buf, info->tmp_rbuf, count))) {
+               rc = -ENODEV;
+       }
+
+       spin_lock_irqsave(&info->lock,flags);
+       reset_adapter(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       memcpy(&info->params, &params, sizeof(info->params));
+       info->port.tty = oldtty;
+
+       info->init_error = rc ? DiagStatus_DmaFailure : 0;
+       return rc;
+}
+
+static int adapter_test(struct slgt_info *info)
+{
+       DBGINFO(("testing %s\n", info->device_name));
+       if (register_test(info) < 0) {
+               printk("register test failure %s addr=%08X\n",
+                       info->device_name, info->phys_reg_addr);
+       } else if (irq_test(info) < 0) {
+               printk("IRQ test failure %s IRQ=%d\n",
+                       info->device_name, info->irq_level);
+       } else if (loopback_test(info) < 0) {
+               printk("loopback test failure %s\n", info->device_name);
+       }
+       return info->init_error;
+}
+
+/*
+ * transmit timeout handler
+ */
+static void tx_timeout(unsigned long context)
+{
+       struct slgt_info *info = (struct slgt_info*)context;
+       unsigned long flags;
+
+       DBGINFO(("%s tx_timeout\n", info->device_name));
+       if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) {
+               info->icount.txtimeout++;
+       }
+       spin_lock_irqsave(&info->lock,flags);
+       tx_stop(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+#if SYNCLINK_GENERIC_HDLC
+       if (info->netcount)
+               hdlcdev_tx_done(info);
+       else
+#endif
+               bh_transmit(info);
+}
+
+/*
+ * receive buffer polling timer
+ */
+static void rx_timeout(unsigned long context)
+{
+       struct slgt_info *info = (struct slgt_info*)context;
+       unsigned long flags;
+
+       DBGINFO(("%s rx_timeout\n", info->device_name));
+       spin_lock_irqsave(&info->lock, flags);
+       info->pending_bh |= BH_RECEIVE;
+       spin_unlock_irqrestore(&info->lock, flags);
+       bh_handler(&info->task);
+}
+
diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c
new file mode 100644 (file)
index 0000000..3273436
--- /dev/null
@@ -0,0 +1,5600 @@
+/*
+ * $Id: synclinkmp.c,v 4.38 2005/07/15 13:29:44 paulkf Exp $
+ *
+ * Device driver for Microgate SyncLink Multiport
+ * high speed multiprotocol serial adapter.
+ *
+ * written by Paul Fulghum for Microgate Corporation
+ * paulkf@microgate.com
+ *
+ * Microgate and SyncLink are trademarks of Microgate Corporation
+ *
+ * Derived from serial.c written by Theodore Ts'o and Linus Torvalds
+ * This code is released under the GNU General Public License (GPL)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq))
+#if defined(__i386__)
+#  define BREAKPOINT() asm("   int $3");
+#else
+#  define BREAKPOINT() { }
+#endif
+
+#define MAX_DEVICES 12
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ioctl.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <linux/bitops.h>
+#include <asm/types.h>
+#include <linux/termios.h>
+#include <linux/workqueue.h>
+#include <linux/hdlc.h>
+#include <linux/synclink.h>
+
+#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINKMP_MODULE))
+#define SYNCLINK_GENERIC_HDLC 1
+#else
+#define SYNCLINK_GENERIC_HDLC 0
+#endif
+
+#define GET_USER(error,value,addr) error = get_user(value,addr)
+#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0
+#define PUT_USER(error,value,addr) error = put_user(value,addr)
+#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0
+
+#include <asm/uaccess.h>
+
+static MGSL_PARAMS default_params = {
+       MGSL_MODE_HDLC,                 /* unsigned long mode */
+       0,                              /* unsigned char loopback; */
+       HDLC_FLAG_UNDERRUN_ABORT15,     /* unsigned short flags; */
+       HDLC_ENCODING_NRZI_SPACE,       /* unsigned char encoding; */
+       0,                              /* unsigned long clock_speed; */
+       0xff,                           /* unsigned char addr_filter; */
+       HDLC_CRC_16_CCITT,              /* unsigned short crc_type; */
+       HDLC_PREAMBLE_LENGTH_8BITS,     /* unsigned char preamble_length; */
+       HDLC_PREAMBLE_PATTERN_NONE,     /* unsigned char preamble; */
+       9600,                           /* unsigned long data_rate; */
+       8,                              /* unsigned char data_bits; */
+       1,                              /* unsigned char stop_bits; */
+       ASYNC_PARITY_NONE               /* unsigned char parity; */
+};
+
+/* size in bytes of DMA data buffers */
+#define SCABUFSIZE     1024
+#define SCA_MEM_SIZE   0x40000
+#define SCA_BASE_SIZE   512
+#define SCA_REG_SIZE    16
+#define SCA_MAX_PORTS   4
+#define SCAMAXDESC     128
+
+#define        BUFFERLISTSIZE  4096
+
+/* SCA-I style DMA buffer descriptor */
+typedef struct _SCADESC
+{
+       u16     next;           /* lower l6 bits of next descriptor addr */
+       u16     buf_ptr;        /* lower 16 bits of buffer addr */
+       u8      buf_base;       /* upper 8 bits of buffer addr */
+       u8      pad1;
+       u16     length;         /* length of buffer */
+       u8      status;         /* status of buffer */
+       u8      pad2;
+} SCADESC, *PSCADESC;
+
+typedef struct _SCADESC_EX
+{
+       /* device driver bookkeeping section */
+       char    *virt_addr;     /* virtual address of data buffer */
+       u16     phys_entry;     /* lower 16-bits of physical address of this descriptor */
+} SCADESC_EX, *PSCADESC_EX;
+
+/* The queue of BH actions to be performed */
+
+#define BH_RECEIVE  1
+#define BH_TRANSMIT 2
+#define BH_STATUS   4
+
+#define IO_PIN_SHUTDOWN_LIMIT 100
+
+struct _input_signal_events {
+       int     ri_up;
+       int     ri_down;
+       int     dsr_up;
+       int     dsr_down;
+       int     dcd_up;
+       int     dcd_down;
+       int     cts_up;
+       int     cts_down;
+};
+
+/*
+ * Device instance data structure
+ */
+typedef struct _synclinkmp_info {
+       void *if_ptr;                           /* General purpose pointer (used by SPPP) */
+       int                     magic;
+       struct tty_port         port;
+       int                     line;
+       unsigned short          close_delay;
+       unsigned short          closing_wait;   /* time to wait before closing */
+
+       struct mgsl_icount      icount;
+
+       int                     timeout;
+       int                     x_char;         /* xon/xoff character */
+       u16                     read_status_mask1;  /* break detection (SR1 indications) */
+       u16                     read_status_mask2;  /* parity/framing/overun (SR2 indications) */
+       unsigned char           ignore_status_mask1;  /* break detection (SR1 indications) */
+       unsigned char           ignore_status_mask2;  /* parity/framing/overun (SR2 indications) */
+       unsigned char           *tx_buf;
+       int                     tx_put;
+       int                     tx_get;
+       int                     tx_count;
+
+       wait_queue_head_t       status_event_wait_q;
+       wait_queue_head_t       event_wait_q;
+       struct timer_list       tx_timer;       /* HDLC transmit timeout timer */
+       struct _synclinkmp_info *next_device;   /* device list link */
+       struct timer_list       status_timer;   /* input signal status check timer */
+
+       spinlock_t lock;                /* spinlock for synchronizing with ISR */
+       struct work_struct task;                        /* task structure for scheduling bh */
+
+       u32 max_frame_size;                     /* as set by device config */
+
+       u32 pending_bh;
+
+       bool bh_running;                                /* Protection from multiple */
+       int isr_overflow;
+       bool bh_requested;
+
+       int dcd_chkcount;                       /* check counts to prevent */
+       int cts_chkcount;                       /* too many IRQs if a signal */
+       int dsr_chkcount;                       /* is floating */
+       int ri_chkcount;
+
+       char *buffer_list;                      /* virtual address of Rx & Tx buffer lists */
+       unsigned long buffer_list_phys;
+
+       unsigned int rx_buf_count;              /* count of total allocated Rx buffers */
+       SCADESC *rx_buf_list;                   /* list of receive buffer entries */
+       SCADESC_EX rx_buf_list_ex[SCAMAXDESC]; /* list of receive buffer entries */
+       unsigned int current_rx_buf;
+
+       unsigned int tx_buf_count;              /* count of total allocated Tx buffers */
+       SCADESC *tx_buf_list;           /* list of transmit buffer entries */
+       SCADESC_EX tx_buf_list_ex[SCAMAXDESC]; /* list of transmit buffer entries */
+       unsigned int last_tx_buf;
+
+       unsigned char *tmp_rx_buf;
+       unsigned int tmp_rx_buf_count;
+
+       bool rx_enabled;
+       bool rx_overflow;
+
+       bool tx_enabled;
+       bool tx_active;
+       u32 idle_mode;
+
+       unsigned char ie0_value;
+       unsigned char ie1_value;
+       unsigned char ie2_value;
+       unsigned char ctrlreg_value;
+       unsigned char old_signals;
+
+       char device_name[25];                   /* device instance name */
+
+       int port_count;
+       int adapter_num;
+       int port_num;
+
+       struct _synclinkmp_info *port_array[SCA_MAX_PORTS];
+
+       unsigned int bus_type;                  /* expansion bus type (ISA,EISA,PCI) */
+
+       unsigned int irq_level;                 /* interrupt level */
+       unsigned long irq_flags;
+       bool irq_requested;                     /* true if IRQ requested */
+
+       MGSL_PARAMS params;                     /* communications parameters */
+
+       unsigned char serial_signals;           /* current serial signal states */
+
+       bool irq_occurred;                      /* for diagnostics use */
+       unsigned int init_error;                /* Initialization startup error */
+
+       u32 last_mem_alloc;
+       unsigned char* memory_base;             /* shared memory address (PCI only) */
+       u32 phys_memory_base;
+       int shared_mem_requested;
+
+       unsigned char* sca_base;                /* HD64570 SCA Memory address */
+       u32 phys_sca_base;
+       u32 sca_offset;
+       bool sca_base_requested;
+
+       unsigned char* lcr_base;                /* local config registers (PCI only) */
+       u32 phys_lcr_base;
+       u32 lcr_offset;
+       int lcr_mem_requested;
+
+       unsigned char* statctrl_base;           /* status/control register memory */
+       u32 phys_statctrl_base;
+       u32 statctrl_offset;
+       bool sca_statctrl_requested;
+
+       u32 misc_ctrl_value;
+       char flag_buf[MAX_ASYNC_BUFFER_SIZE];
+       char char_buf[MAX_ASYNC_BUFFER_SIZE];
+       bool drop_rts_on_tx_done;
+
+       struct  _input_signal_events    input_signal_events;
+
+       /* SPPP/Cisco HDLC device parts */
+       int netcount;
+       spinlock_t netlock;
+
+#if SYNCLINK_GENERIC_HDLC
+       struct net_device *netdev;
+#endif
+
+} SLMP_INFO;
+
+#define MGSL_MAGIC 0x5401
+
+/*
+ * define serial signal status change macros
+ */
+#define        MISCSTATUS_DCD_LATCHED  (SerialSignal_DCD<<8)   /* indicates change in DCD */
+#define MISCSTATUS_RI_LATCHED  (SerialSignal_RI<<8)    /* indicates change in RI */
+#define MISCSTATUS_CTS_LATCHED (SerialSignal_CTS<<8)   /* indicates change in CTS */
+#define MISCSTATUS_DSR_LATCHED (SerialSignal_DSR<<8)   /* change in DSR */
+
+/* Common Register macros */
+#define LPR    0x00
+#define PABR0  0x02
+#define PABR1  0x03
+#define WCRL   0x04
+#define WCRM   0x05
+#define WCRH   0x06
+#define DPCR   0x08
+#define DMER   0x09
+#define ISR0   0x10
+#define ISR1   0x11
+#define ISR2   0x12
+#define IER0   0x14
+#define IER1   0x15
+#define IER2   0x16
+#define ITCR   0x18
+#define INTVR  0x1a
+#define IMVR   0x1c
+
+/* MSCI Register macros */
+#define TRB    0x20
+#define TRBL   0x20
+#define TRBH   0x21
+#define SR0    0x22
+#define SR1    0x23
+#define SR2    0x24
+#define SR3    0x25
+#define FST    0x26
+#define IE0    0x28
+#define IE1    0x29
+#define IE2    0x2a
+#define FIE    0x2b
+#define CMD    0x2c
+#define MD0    0x2e
+#define MD1    0x2f
+#define MD2    0x30
+#define CTL    0x31
+#define SA0    0x32
+#define SA1    0x33
+#define IDL    0x34
+#define TMC    0x35
+#define RXS    0x36
+#define TXS    0x37
+#define TRC0   0x38
+#define TRC1   0x39
+#define RRC    0x3a
+#define CST0   0x3c
+#define CST1   0x3d
+
+/* Timer Register Macros */
+#define TCNT   0x60
+#define TCNTL  0x60
+#define TCNTH  0x61
+#define TCONR  0x62
+#define TCONRL 0x62
+#define TCONRH 0x63
+#define TMCS   0x64
+#define TEPR   0x65
+
+/* DMA Controller Register macros */
+#define DARL   0x80
+#define DARH   0x81
+#define DARB   0x82
+#define BAR    0x80
+#define BARL   0x80
+#define BARH   0x81
+#define BARB   0x82
+#define SAR    0x84
+#define SARL   0x84
+#define SARH   0x85
+#define SARB   0x86
+#define CPB    0x86
+#define CDA    0x88
+#define CDAL   0x88
+#define CDAH   0x89
+#define EDA    0x8a
+#define EDAL   0x8a
+#define EDAH   0x8b
+#define BFL    0x8c
+#define BFLL   0x8c
+#define BFLH   0x8d
+#define BCR    0x8e
+#define BCRL   0x8e
+#define BCRH   0x8f
+#define DSR    0x90
+#define DMR    0x91
+#define FCT    0x93
+#define DIR    0x94
+#define DCMD   0x95
+
+/* combine with timer or DMA register address */
+#define TIMER0 0x00
+#define TIMER1 0x08
+#define TIMER2 0x10
+#define TIMER3 0x18
+#define RXDMA  0x00
+#define TXDMA  0x20
+
+/* SCA Command Codes */
+#define NOOP           0x00
+#define TXRESET                0x01
+#define TXENABLE       0x02
+#define TXDISABLE      0x03
+#define TXCRCINIT      0x04
+#define TXCRCEXCL      0x05
+#define TXEOM          0x06
+#define TXABORT                0x07
+#define MPON           0x08
+#define TXBUFCLR       0x09
+#define RXRESET                0x11
+#define RXENABLE       0x12
+#define RXDISABLE      0x13
+#define RXCRCINIT      0x14
+#define RXREJECT       0x15
+#define SEARCHMP       0x16
+#define RXCRCEXCL      0x17
+#define RXCRCCALC      0x18
+#define CHRESET                0x21
+#define HUNT           0x31
+
+/* DMA command codes */
+#define SWABORT                0x01
+#define FEICLEAR       0x02
+
+/* IE0 */
+#define TXINTE                 BIT7
+#define RXINTE                 BIT6
+#define TXRDYE                 BIT1
+#define RXRDYE                 BIT0
+
+/* IE1 & SR1 */
+#define UDRN           BIT7
+#define IDLE           BIT6
+#define SYNCD          BIT4
+#define FLGD           BIT4
+#define CCTS           BIT3
+#define CDCD           BIT2
+#define BRKD           BIT1
+#define ABTD           BIT1
+#define GAPD           BIT1
+#define BRKE           BIT0
+#define IDLD   BIT0
+
+/* IE2 & SR2 */
+#define EOM    BIT7
+#define PMP    BIT6
+#define SHRT   BIT6
+#define PE     BIT5
+#define ABT    BIT5
+#define FRME   BIT4
+#define RBIT   BIT4
+#define OVRN   BIT3
+#define CRCE   BIT2
+
+
+/*
+ * Global linked list of SyncLink devices
+ */
+static SLMP_INFO *synclinkmp_device_list = NULL;
+static int synclinkmp_adapter_count = -1;
+static int synclinkmp_device_count = 0;
+
+/*
+ * Set this param to non-zero to load eax with the
+ * .text section address and breakpoint on module load.
+ * This is useful for use with gdb and add-symbol-file command.
+ */
+static int break_on_load = 0;
+
+/*
+ * Driver major number, defaults to zero to get auto
+ * assigned major number. May be forced as module parameter.
+ */
+static int ttymajor = 0;
+
+/*
+ * Array of user specified options for ISA adapters.
+ */
+static int debug_level = 0;
+static int maxframe[MAX_DEVICES] = {0,};
+
+module_param(break_on_load, bool, 0);
+module_param(ttymajor, int, 0);
+module_param(debug_level, int, 0);
+module_param_array(maxframe, int, NULL, 0);
+
+static char *driver_name = "SyncLink MultiPort driver";
+static char *driver_version = "$Revision: 4.38 $";
+
+static int synclinkmp_init_one(struct pci_dev *dev,const struct pci_device_id *ent);
+static void synclinkmp_remove_one(struct pci_dev *dev);
+
+static struct pci_device_id synclinkmp_pci_tbl[] = {
+       { PCI_VENDOR_ID_MICROGATE, PCI_DEVICE_ID_MICROGATE_SCA, PCI_ANY_ID, PCI_ANY_ID, },
+       { 0, }, /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, synclinkmp_pci_tbl);
+
+MODULE_LICENSE("GPL");
+
+static struct pci_driver synclinkmp_pci_driver = {
+       .name           = "synclinkmp",
+       .id_table       = synclinkmp_pci_tbl,
+       .probe          = synclinkmp_init_one,
+       .remove         = __devexit_p(synclinkmp_remove_one),
+};
+
+
+static struct tty_driver *serial_driver;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+
+/* tty callbacks */
+
+static int  open(struct tty_struct *tty, struct file * filp);
+static void close(struct tty_struct *tty, struct file * filp);
+static void hangup(struct tty_struct *tty);
+static void set_termios(struct tty_struct *tty, struct ktermios *old_termios);
+
+static int  write(struct tty_struct *tty, const unsigned char *buf, int count);
+static int put_char(struct tty_struct *tty, unsigned char ch);
+static void send_xchar(struct tty_struct *tty, char ch);
+static void wait_until_sent(struct tty_struct *tty, int timeout);
+static int  write_room(struct tty_struct *tty);
+static void flush_chars(struct tty_struct *tty);
+static void flush_buffer(struct tty_struct *tty);
+static void tx_hold(struct tty_struct *tty);
+static void tx_release(struct tty_struct *tty);
+
+static int  ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
+static int  chars_in_buffer(struct tty_struct *tty);
+static void throttle(struct tty_struct * tty);
+static void unthrottle(struct tty_struct * tty);
+static int set_break(struct tty_struct *tty, int break_state);
+
+#if SYNCLINK_GENERIC_HDLC
+#define dev_to_port(D) (dev_to_hdlc(D)->priv)
+static void hdlcdev_tx_done(SLMP_INFO *info);
+static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size);
+static int  hdlcdev_init(SLMP_INFO *info);
+static void hdlcdev_exit(SLMP_INFO *info);
+#endif
+
+/* ioctl handlers */
+
+static int  get_stats(SLMP_INFO *info, struct mgsl_icount __user *user_icount);
+static int  get_params(SLMP_INFO *info, MGSL_PARAMS __user *params);
+static int  set_params(SLMP_INFO *info, MGSL_PARAMS __user *params);
+static int  get_txidle(SLMP_INFO *info, int __user *idle_mode);
+static int  set_txidle(SLMP_INFO *info, int idle_mode);
+static int  tx_enable(SLMP_INFO *info, int enable);
+static int  tx_abort(SLMP_INFO *info);
+static int  rx_enable(SLMP_INFO *info, int enable);
+static int  modem_input_wait(SLMP_INFO *info,int arg);
+static int  wait_mgsl_event(SLMP_INFO *info, int __user *mask_ptr);
+static int  tiocmget(struct tty_struct *tty);
+static int  tiocmset(struct tty_struct *tty,
+                       unsigned int set, unsigned int clear);
+static int  set_break(struct tty_struct *tty, int break_state);
+
+static void add_device(SLMP_INFO *info);
+static void device_init(int adapter_num, struct pci_dev *pdev);
+static int  claim_resources(SLMP_INFO *info);
+static void release_resources(SLMP_INFO *info);
+
+static int  startup(SLMP_INFO *info);
+static int  block_til_ready(struct tty_struct *tty, struct file * filp,SLMP_INFO *info);
+static int carrier_raised(struct tty_port *port);
+static void shutdown(SLMP_INFO *info);
+static void program_hw(SLMP_INFO *info);
+static void change_params(SLMP_INFO *info);
+
+static bool init_adapter(SLMP_INFO *info);
+static bool register_test(SLMP_INFO *info);
+static bool irq_test(SLMP_INFO *info);
+static bool loopback_test(SLMP_INFO *info);
+static int  adapter_test(SLMP_INFO *info);
+static bool memory_test(SLMP_INFO *info);
+
+static void reset_adapter(SLMP_INFO *info);
+static void reset_port(SLMP_INFO *info);
+static void async_mode(SLMP_INFO *info);
+static void hdlc_mode(SLMP_INFO *info);
+
+static void rx_stop(SLMP_INFO *info);
+static void rx_start(SLMP_INFO *info);
+static void rx_reset_buffers(SLMP_INFO *info);
+static void rx_free_frame_buffers(SLMP_INFO *info, unsigned int first, unsigned int last);
+static bool rx_get_frame(SLMP_INFO *info);
+
+static void tx_start(SLMP_INFO *info);
+static void tx_stop(SLMP_INFO *info);
+static void tx_load_fifo(SLMP_INFO *info);
+static void tx_set_idle(SLMP_INFO *info);
+static void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int count);
+
+static void get_signals(SLMP_INFO *info);
+static void set_signals(SLMP_INFO *info);
+static void enable_loopback(SLMP_INFO *info, int enable);
+static void set_rate(SLMP_INFO *info, u32 data_rate);
+
+static int  bh_action(SLMP_INFO *info);
+static void bh_handler(struct work_struct *work);
+static void bh_receive(SLMP_INFO *info);
+static void bh_transmit(SLMP_INFO *info);
+static void bh_status(SLMP_INFO *info);
+static void isr_timer(SLMP_INFO *info);
+static void isr_rxint(SLMP_INFO *info);
+static void isr_rxrdy(SLMP_INFO *info);
+static void isr_txint(SLMP_INFO *info);
+static void isr_txrdy(SLMP_INFO *info);
+static void isr_rxdmaok(SLMP_INFO *info);
+static void isr_rxdmaerror(SLMP_INFO *info);
+static void isr_txdmaok(SLMP_INFO *info);
+static void isr_txdmaerror(SLMP_INFO *info);
+static void isr_io_pin(SLMP_INFO *info, u16 status);
+
+static int  alloc_dma_bufs(SLMP_INFO *info);
+static void free_dma_bufs(SLMP_INFO *info);
+static int  alloc_buf_list(SLMP_INFO *info);
+static int  alloc_frame_bufs(SLMP_INFO *info, SCADESC *list, SCADESC_EX *list_ex,int count);
+static int  alloc_tmp_rx_buf(SLMP_INFO *info);
+static void free_tmp_rx_buf(SLMP_INFO *info);
+
+static void load_pci_memory(SLMP_INFO *info, char* dest, const char* src, unsigned short count);
+static void trace_block(SLMP_INFO *info, const char* data, int count, int xmit);
+static void tx_timeout(unsigned long context);
+static void status_timeout(unsigned long context);
+
+static unsigned char read_reg(SLMP_INFO *info, unsigned char addr);
+static void write_reg(SLMP_INFO *info, unsigned char addr, unsigned char val);
+static u16 read_reg16(SLMP_INFO *info, unsigned char addr);
+static void write_reg16(SLMP_INFO *info, unsigned char addr, u16 val);
+static unsigned char read_status_reg(SLMP_INFO * info);
+static void write_control_reg(SLMP_INFO * info);
+
+
+static unsigned char rx_active_fifo_level = 16;        // rx request FIFO activation level in bytes
+static unsigned char tx_active_fifo_level = 16;        // tx request FIFO activation level in bytes
+static unsigned char tx_negate_fifo_level = 32;        // tx request FIFO negation level in bytes
+
+static u32 misc_ctrl_value = 0x007e4040;
+static u32 lcr1_brdr_value = 0x00800028;
+
+static u32 read_ahead_count = 8;
+
+/* DPCR, DMA Priority Control
+ *
+ * 07..05  Not used, must be 0
+ * 04      BRC, bus release condition: 0=all transfers complete
+ *              1=release after 1 xfer on all channels
+ * 03      CCC, channel change condition: 0=every cycle
+ *              1=after each channel completes all xfers
+ * 02..00  PR<2..0>, priority 100=round robin
+ *
+ * 00000100 = 0x00
+ */
+static unsigned char dma_priority = 0x04;
+
+// Number of bytes that can be written to shared RAM
+// in a single write operation
+static u32 sca_pci_load_interval = 64;
+
+/*
+ * 1st function defined in .text section. Calling this function in
+ * init_module() followed by a breakpoint allows a remote debugger
+ * (gdb) to get the .text address for the add-symbol-file command.
+ * This allows remote debugging of dynamically loadable modules.
+ */
+static void* synclinkmp_get_text_ptr(void);
+static void* synclinkmp_get_text_ptr(void) {return synclinkmp_get_text_ptr;}
+
+static inline int sanity_check(SLMP_INFO *info,
+                              char *name, const char *routine)
+{
+#ifdef SANITY_CHECK
+       static const char *badmagic =
+               "Warning: bad magic number for synclinkmp_struct (%s) in %s\n";
+       static const char *badinfo =
+               "Warning: null synclinkmp_struct for (%s) in %s\n";
+
+       if (!info) {
+               printk(badinfo, name, routine);
+               return 1;
+       }
+       if (info->magic != MGSL_MAGIC) {
+               printk(badmagic, name, routine);
+               return 1;
+       }
+#else
+       if (!info)
+               return 1;
+#endif
+       return 0;
+}
+
+/**
+ * line discipline callback wrappers
+ *
+ * The wrappers maintain line discipline references
+ * while calling into the line discipline.
+ *
+ * ldisc_receive_buf  - pass receive data to line discipline
+ */
+
+static void ldisc_receive_buf(struct tty_struct *tty,
+                             const __u8 *data, char *flags, int count)
+{
+       struct tty_ldisc *ld;
+       if (!tty)
+               return;
+       ld = tty_ldisc_ref(tty);
+       if (ld) {
+               if (ld->ops->receive_buf)
+                       ld->ops->receive_buf(tty, data, flags, count);
+               tty_ldisc_deref(ld);
+       }
+}
+
+/* tty callbacks */
+
+/* Called when a port is opened.  Init and enable port.
+ */
+static int open(struct tty_struct *tty, struct file *filp)
+{
+       SLMP_INFO *info;
+       int retval, line;
+       unsigned long flags;
+
+       line = tty->index;
+       if ((line < 0) || (line >= synclinkmp_device_count)) {
+               printk("%s(%d): open with invalid line #%d.\n",
+                       __FILE__,__LINE__,line);
+               return -ENODEV;
+       }
+
+       info = synclinkmp_device_list;
+       while(info && info->line != line)
+               info = info->next_device;
+       if (sanity_check(info, tty->name, "open"))
+               return -ENODEV;
+       if ( info->init_error ) {
+               printk("%s(%d):%s device is not allocated, init error=%d\n",
+                       __FILE__,__LINE__,info->device_name,info->init_error);
+               return -ENODEV;
+       }
+
+       tty->driver_data = info;
+       info->port.tty = tty;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s open(), old ref count = %d\n",
+                        __FILE__,__LINE__,tty->driver->name, info->port.count);
+
+       /* If port is closing, signal caller to try again */
+       if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){
+               if (info->port.flags & ASYNC_CLOSING)
+                       interruptible_sleep_on(&info->port.close_wait);
+               retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS);
+               goto cleanup;
+       }
+
+       info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+       spin_lock_irqsave(&info->netlock, flags);
+       if (info->netcount) {
+               retval = -EBUSY;
+               spin_unlock_irqrestore(&info->netlock, flags);
+               goto cleanup;
+       }
+       info->port.count++;
+       spin_unlock_irqrestore(&info->netlock, flags);
+
+       if (info->port.count == 1) {
+               /* 1st open on this device, init hardware */
+               retval = startup(info);
+               if (retval < 0)
+                       goto cleanup;
+       }
+
+       retval = block_til_ready(tty, filp, info);
+       if (retval) {
+               if (debug_level >= DEBUG_LEVEL_INFO)
+                       printk("%s(%d):%s block_til_ready() returned %d\n",
+                                __FILE__,__LINE__, info->device_name, retval);
+               goto cleanup;
+       }
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s open() success\n",
+                        __FILE__,__LINE__, info->device_name);
+       retval = 0;
+
+cleanup:
+       if (retval) {
+               if (tty->count == 1)
+                       info->port.tty = NULL; /* tty layer will release tty struct */
+               if(info->port.count)
+                       info->port.count--;
+       }
+
+       return retval;
+}
+
+/* Called when port is closed. Wait for remaining data to be
+ * sent. Disable port and free resources.
+ */
+static void close(struct tty_struct *tty, struct file *filp)
+{
+       SLMP_INFO * info = tty->driver_data;
+
+       if (sanity_check(info, tty->name, "close"))
+               return;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s close() entry, count=%d\n",
+                        __FILE__,__LINE__, info->device_name, info->port.count);
+
+       if (tty_port_close_start(&info->port, tty, filp) == 0)
+               goto cleanup;
+
+       mutex_lock(&info->port.mutex);
+       if (info->port.flags & ASYNC_INITIALIZED)
+               wait_until_sent(tty, info->timeout);
+
+       flush_buffer(tty);
+       tty_ldisc_flush(tty);
+       shutdown(info);
+       mutex_unlock(&info->port.mutex);
+
+       tty_port_close_end(&info->port, tty);
+       info->port.tty = NULL;
+cleanup:
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s close() exit, count=%d\n", __FILE__,__LINE__,
+                       tty->driver->name, info->port.count);
+}
+
+/* Called by tty_hangup() when a hangup is signaled.
+ * This is the same as closing all open descriptors for the port.
+ */
+static void hangup(struct tty_struct *tty)
+{
+       SLMP_INFO *info = tty->driver_data;
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s hangup()\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       if (sanity_check(info, tty->name, "hangup"))
+               return;
+
+       mutex_lock(&info->port.mutex);
+       flush_buffer(tty);
+       shutdown(info);
+
+       spin_lock_irqsave(&info->port.lock, flags);
+       info->port.count = 0;
+       info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
+       info->port.tty = NULL;
+       spin_unlock_irqrestore(&info->port.lock, flags);
+       mutex_unlock(&info->port.mutex);
+
+       wake_up_interruptible(&info->port.open_wait);
+}
+
+/* Set new termios settings
+ */
+static void set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+       SLMP_INFO *info = tty->driver_data;
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s set_termios()\n", __FILE__,__LINE__,
+                       tty->driver->name );
+
+       change_params(info);
+
+       /* Handle transition to B0 status */
+       if (old_termios->c_cflag & CBAUD &&
+           !(tty->termios->c_cflag & CBAUD)) {
+               info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+               spin_lock_irqsave(&info->lock,flags);
+               set_signals(info);
+               spin_unlock_irqrestore(&info->lock,flags);
+       }
+
+       /* Handle transition away from B0 status */
+       if (!(old_termios->c_cflag & CBAUD) &&
+           tty->termios->c_cflag & CBAUD) {
+               info->serial_signals |= SerialSignal_DTR;
+               if (!(tty->termios->c_cflag & CRTSCTS) ||
+                   !test_bit(TTY_THROTTLED, &tty->flags)) {
+                       info->serial_signals |= SerialSignal_RTS;
+               }
+               spin_lock_irqsave(&info->lock,flags);
+               set_signals(info);
+               spin_unlock_irqrestore(&info->lock,flags);
+       }
+
+       /* Handle turning off CRTSCTS */
+       if (old_termios->c_cflag & CRTSCTS &&
+           !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               tx_release(tty);
+       }
+}
+
+/* Send a block of data
+ *
+ * Arguments:
+ *
+ *     tty             pointer to tty information structure
+ *     buf             pointer to buffer containing send data
+ *     count           size of send data in bytes
+ *
+ * Return Value:       number of characters written
+ */
+static int write(struct tty_struct *tty,
+                const unsigned char *buf, int count)
+{
+       int     c, ret = 0;
+       SLMP_INFO *info = tty->driver_data;
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s write() count=%d\n",
+                      __FILE__,__LINE__,info->device_name,count);
+
+       if (sanity_check(info, tty->name, "write"))
+               goto cleanup;
+
+       if (!info->tx_buf)
+               goto cleanup;
+
+       if (info->params.mode == MGSL_MODE_HDLC) {
+               if (count > info->max_frame_size) {
+                       ret = -EIO;
+                       goto cleanup;
+               }
+               if (info->tx_active)
+                       goto cleanup;
+               if (info->tx_count) {
+                       /* send accumulated data from send_char() calls */
+                       /* as frame and wait before accepting more data. */
+                       tx_load_dma_buffer(info, info->tx_buf, info->tx_count);
+                       goto start;
+               }
+               ret = info->tx_count = count;
+               tx_load_dma_buffer(info, buf, count);
+               goto start;
+       }
+
+       for (;;) {
+               c = min_t(int, count,
+                       min(info->max_frame_size - info->tx_count - 1,
+                           info->max_frame_size - info->tx_put));
+               if (c <= 0)
+                       break;
+                       
+               memcpy(info->tx_buf + info->tx_put, buf, c);
+
+               spin_lock_irqsave(&info->lock,flags);
+               info->tx_put += c;
+               if (info->tx_put >= info->max_frame_size)
+                       info->tx_put -= info->max_frame_size;
+               info->tx_count += c;
+               spin_unlock_irqrestore(&info->lock,flags);
+
+               buf += c;
+               count -= c;
+               ret += c;
+       }
+
+       if (info->params.mode == MGSL_MODE_HDLC) {
+               if (count) {
+                       ret = info->tx_count = 0;
+                       goto cleanup;
+               }
+               tx_load_dma_buffer(info, info->tx_buf, info->tx_count);
+       }
+start:
+       if (info->tx_count && !tty->stopped && !tty->hw_stopped) {
+               spin_lock_irqsave(&info->lock,flags);
+               if (!info->tx_active)
+                       tx_start(info);
+               spin_unlock_irqrestore(&info->lock,flags);
+       }
+
+cleanup:
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk( "%s(%d):%s write() returning=%d\n",
+                       __FILE__,__LINE__,info->device_name,ret);
+       return ret;
+}
+
+/* Add a character to the transmit buffer.
+ */
+static int put_char(struct tty_struct *tty, unsigned char ch)
+{
+       SLMP_INFO *info = tty->driver_data;
+       unsigned long flags;
+       int ret = 0;
+
+       if ( debug_level >= DEBUG_LEVEL_INFO ) {
+               printk( "%s(%d):%s put_char(%d)\n",
+                       __FILE__,__LINE__,info->device_name,ch);
+       }
+
+       if (sanity_check(info, tty->name, "put_char"))
+               return 0;
+
+       if (!info->tx_buf)
+               return 0;
+
+       spin_lock_irqsave(&info->lock,flags);
+
+       if ( (info->params.mode != MGSL_MODE_HDLC) ||
+            !info->tx_active ) {
+
+               if (info->tx_count < info->max_frame_size - 1) {
+                       info->tx_buf[info->tx_put++] = ch;
+                       if (info->tx_put >= info->max_frame_size)
+                               info->tx_put -= info->max_frame_size;
+                       info->tx_count++;
+                       ret = 1;
+               }
+       }
+
+       spin_unlock_irqrestore(&info->lock,flags);
+       return ret;
+}
+
+/* Send a high-priority XON/XOFF character
+ */
+static void send_xchar(struct tty_struct *tty, char ch)
+{
+       SLMP_INFO *info = tty->driver_data;
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s send_xchar(%d)\n",
+                        __FILE__,__LINE__, info->device_name, ch );
+
+       if (sanity_check(info, tty->name, "send_xchar"))
+               return;
+
+       info->x_char = ch;
+       if (ch) {
+               /* Make sure transmit interrupts are on */
+               spin_lock_irqsave(&info->lock,flags);
+               if (!info->tx_enabled)
+                       tx_start(info);
+               spin_unlock_irqrestore(&info->lock,flags);
+       }
+}
+
+/* Wait until the transmitter is empty.
+ */
+static void wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       SLMP_INFO * info = tty->driver_data;
+       unsigned long orig_jiffies, char_time;
+
+       if (!info )
+               return;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s wait_until_sent() entry\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       if (sanity_check(info, tty->name, "wait_until_sent"))
+               return;
+
+       if (!test_bit(ASYNCB_INITIALIZED, &info->port.flags))
+               goto exit;
+
+       orig_jiffies = jiffies;
+
+       /* Set check interval to 1/5 of estimated time to
+        * send a character, and make it at least 1. The check
+        * interval should also be less than the timeout.
+        * Note: use tight timings here to satisfy the NIST-PCTS.
+        */
+
+       if ( info->params.data_rate ) {
+               char_time = info->timeout/(32 * 5);
+               if (!char_time)
+                       char_time++;
+       } else
+               char_time = 1;
+
+       if (timeout)
+               char_time = min_t(unsigned long, char_time, timeout);
+
+       if ( info->params.mode == MGSL_MODE_HDLC ) {
+               while (info->tx_active) {
+                       msleep_interruptible(jiffies_to_msecs(char_time));
+                       if (signal_pending(current))
+                               break;
+                       if (timeout && time_after(jiffies, orig_jiffies + timeout))
+                               break;
+               }
+       } else {
+               /*
+                * TODO: determine if there is something similar to USC16C32
+                *       TXSTATUS_ALL_SENT status
+                */
+               while ( info->tx_active && info->tx_enabled) {
+                       msleep_interruptible(jiffies_to_msecs(char_time));
+                       if (signal_pending(current))
+                               break;
+                       if (timeout && time_after(jiffies, orig_jiffies + timeout))
+                               break;
+               }
+       }
+
+exit:
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s wait_until_sent() exit\n",
+                        __FILE__,__LINE__, info->device_name );
+}
+
+/* Return the count of free bytes in transmit buffer
+ */
+static int write_room(struct tty_struct *tty)
+{
+       SLMP_INFO *info = tty->driver_data;
+       int ret;
+
+       if (sanity_check(info, tty->name, "write_room"))
+               return 0;
+
+       if (info->params.mode == MGSL_MODE_HDLC) {
+               ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE;
+       } else {
+               ret = info->max_frame_size - info->tx_count - 1;
+               if (ret < 0)
+                       ret = 0;
+       }
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s write_room()=%d\n",
+                      __FILE__, __LINE__, info->device_name, ret);
+
+       return ret;
+}
+
+/* enable transmitter and send remaining buffered characters
+ */
+static void flush_chars(struct tty_struct *tty)
+{
+       SLMP_INFO *info = tty->driver_data;
+       unsigned long flags;
+
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):%s flush_chars() entry tx_count=%d\n",
+                       __FILE__,__LINE__,info->device_name,info->tx_count);
+
+       if (sanity_check(info, tty->name, "flush_chars"))
+               return;
+
+       if (info->tx_count <= 0 || tty->stopped || tty->hw_stopped ||
+           !info->tx_buf)
+               return;
+
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):%s flush_chars() entry, starting transmitter\n",
+                       __FILE__,__LINE__,info->device_name );
+
+       spin_lock_irqsave(&info->lock,flags);
+
+       if (!info->tx_active) {
+               if ( (info->params.mode == MGSL_MODE_HDLC) &&
+                       info->tx_count ) {
+                       /* operating in synchronous (frame oriented) mode */
+                       /* copy data from circular tx_buf to */
+                       /* transmit DMA buffer. */
+                       tx_load_dma_buffer(info,
+                                info->tx_buf,info->tx_count);
+               }
+               tx_start(info);
+       }
+
+       spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* Discard all data in the send buffer
+ */
+static void flush_buffer(struct tty_struct *tty)
+{
+       SLMP_INFO *info = tty->driver_data;
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s flush_buffer() entry\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       if (sanity_check(info, tty->name, "flush_buffer"))
+               return;
+
+       spin_lock_irqsave(&info->lock,flags);
+       info->tx_count = info->tx_put = info->tx_get = 0;
+       del_timer(&info->tx_timer);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       tty_wakeup(tty);
+}
+
+/* throttle (stop) transmitter
+ */
+static void tx_hold(struct tty_struct *tty)
+{
+       SLMP_INFO *info = tty->driver_data;
+       unsigned long flags;
+
+       if (sanity_check(info, tty->name, "tx_hold"))
+               return;
+
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk("%s(%d):%s tx_hold()\n",
+                       __FILE__,__LINE__,info->device_name);
+
+       spin_lock_irqsave(&info->lock,flags);
+       if (info->tx_enabled)
+               tx_stop(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* release (start) transmitter
+ */
+static void tx_release(struct tty_struct *tty)
+{
+       SLMP_INFO *info = tty->driver_data;
+       unsigned long flags;
+
+       if (sanity_check(info, tty->name, "tx_release"))
+               return;
+
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk("%s(%d):%s tx_release()\n",
+                       __FILE__,__LINE__,info->device_name);
+
+       spin_lock_irqsave(&info->lock,flags);
+       if (!info->tx_enabled)
+               tx_start(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* Service an IOCTL request
+ *
+ * Arguments:
+ *
+ *     tty     pointer to tty instance data
+ *     cmd     IOCTL command code
+ *     arg     command argument/context
+ *
+ * Return Value:       0 if success, otherwise error code
+ */
+static int ioctl(struct tty_struct *tty,
+                unsigned int cmd, unsigned long arg)
+{
+       SLMP_INFO *info = tty->driver_data;
+       void __user *argp = (void __user *)arg;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s ioctl() cmd=%08X\n", __FILE__,__LINE__,
+                       info->device_name, cmd );
+
+       if (sanity_check(info, tty->name, "ioctl"))
+               return -ENODEV;
+
+       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+           (cmd != TIOCMIWAIT)) {
+               if (tty->flags & (1 << TTY_IO_ERROR))
+                   return -EIO;
+       }
+
+       switch (cmd) {
+       case MGSL_IOCGPARAMS:
+               return get_params(info, argp);
+       case MGSL_IOCSPARAMS:
+               return set_params(info, argp);
+       case MGSL_IOCGTXIDLE:
+               return get_txidle(info, argp);
+       case MGSL_IOCSTXIDLE:
+               return set_txidle(info, (int)arg);
+       case MGSL_IOCTXENABLE:
+               return tx_enable(info, (int)arg);
+       case MGSL_IOCRXENABLE:
+               return rx_enable(info, (int)arg);
+       case MGSL_IOCTXABORT:
+               return tx_abort(info);
+       case MGSL_IOCGSTATS:
+               return get_stats(info, argp);
+       case MGSL_IOCWAITEVENT:
+               return wait_mgsl_event(info, argp);
+       case MGSL_IOCLOOPTXDONE:
+               return 0; // TODO: Not supported, need to document
+               /* Wait for modem input (DCD,RI,DSR,CTS) change
+                * as specified by mask in arg (TIOCM_RNG/DSR/CD/CTS)
+                */
+       case TIOCMIWAIT:
+               return modem_input_wait(info,(int)arg);
+               
+               /*
+                * 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.
+                */
+       default:
+               return -ENOIOCTLCMD;
+       }
+       return 0;
+}
+
+static int get_icount(struct tty_struct *tty,
+                               struct serial_icounter_struct *icount)
+{
+       SLMP_INFO *info = tty->driver_data;
+       struct mgsl_icount cnow;        /* kernel counter temps */
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->lock,flags);
+       cnow = info->icount;
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       icount->cts = cnow.cts;
+       icount->dsr = cnow.dsr;
+       icount->rng = cnow.rng;
+       icount->dcd = cnow.dcd;
+       icount->rx = cnow.rx;
+       icount->tx = cnow.tx;
+       icount->frame = cnow.frame;
+       icount->overrun = cnow.overrun;
+       icount->parity = cnow.parity;
+       icount->brk = cnow.brk;
+       icount->buf_overrun = cnow.buf_overrun;
+
+       return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static inline void line_info(struct seq_file *m, SLMP_INFO *info)
+{
+       char    stat_buf[30];
+       unsigned long flags;
+
+       seq_printf(m, "%s: SCABase=%08x Mem=%08X StatusControl=%08x LCR=%08X\n"
+                      "\tIRQ=%d MaxFrameSize=%u\n",
+               info->device_name,
+               info->phys_sca_base,
+               info->phys_memory_base,
+               info->phys_statctrl_base,
+               info->phys_lcr_base,
+               info->irq_level,
+               info->max_frame_size );
+
+       /* output current serial signal states */
+       spin_lock_irqsave(&info->lock,flags);
+       get_signals(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       stat_buf[0] = 0;
+       stat_buf[1] = 0;
+       if (info->serial_signals & SerialSignal_RTS)
+               strcat(stat_buf, "|RTS");
+       if (info->serial_signals & SerialSignal_CTS)
+               strcat(stat_buf, "|CTS");
+       if (info->serial_signals & SerialSignal_DTR)
+               strcat(stat_buf, "|DTR");
+       if (info->serial_signals & SerialSignal_DSR)
+               strcat(stat_buf, "|DSR");
+       if (info->serial_signals & SerialSignal_DCD)
+               strcat(stat_buf, "|CD");
+       if (info->serial_signals & SerialSignal_RI)
+               strcat(stat_buf, "|RI");
+
+       if (info->params.mode == MGSL_MODE_HDLC) {
+               seq_printf(m, "\tHDLC txok:%d rxok:%d",
+                             info->icount.txok, info->icount.rxok);
+               if (info->icount.txunder)
+                       seq_printf(m, " txunder:%d", info->icount.txunder);
+               if (info->icount.txabort)
+                       seq_printf(m, " txabort:%d", info->icount.txabort);
+               if (info->icount.rxshort)
+                       seq_printf(m, " rxshort:%d", info->icount.rxshort);
+               if (info->icount.rxlong)
+                       seq_printf(m, " rxlong:%d", info->icount.rxlong);
+               if (info->icount.rxover)
+                       seq_printf(m, " rxover:%d", info->icount.rxover);
+               if (info->icount.rxcrc)
+                       seq_printf(m, " rxlong:%d", info->icount.rxcrc);
+       } else {
+               seq_printf(m, "\tASYNC tx:%d rx:%d",
+                             info->icount.tx, info->icount.rx);
+               if (info->icount.frame)
+                       seq_printf(m, " fe:%d", info->icount.frame);
+               if (info->icount.parity)
+                       seq_printf(m, " pe:%d", info->icount.parity);
+               if (info->icount.brk)
+                       seq_printf(m, " brk:%d", info->icount.brk);
+               if (info->icount.overrun)
+                       seq_printf(m, " oe:%d", info->icount.overrun);
+       }
+
+       /* Append serial signal status to end */
+       seq_printf(m, " %s\n", stat_buf+1);
+
+       seq_printf(m, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
+        info->tx_active,info->bh_requested,info->bh_running,
+        info->pending_bh);
+}
+
+/* Called to print information about devices
+ */
+static int synclinkmp_proc_show(struct seq_file *m, void *v)
+{
+       SLMP_INFO *info;
+
+       seq_printf(m, "synclinkmp driver:%s\n", driver_version);
+
+       info = synclinkmp_device_list;
+       while( info ) {
+               line_info(m, info);
+               info = info->next_device;
+       }
+       return 0;
+}
+
+static int synclinkmp_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, synclinkmp_proc_show, NULL);
+}
+
+static const struct file_operations synclinkmp_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = synclinkmp_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/* Return the count of bytes in transmit buffer
+ */
+static int chars_in_buffer(struct tty_struct *tty)
+{
+       SLMP_INFO *info = tty->driver_data;
+
+       if (sanity_check(info, tty->name, "chars_in_buffer"))
+               return 0;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s chars_in_buffer()=%d\n",
+                      __FILE__, __LINE__, info->device_name, info->tx_count);
+
+       return info->tx_count;
+}
+
+/* Signal remote device to throttle send data (our receive data)
+ */
+static void throttle(struct tty_struct * tty)
+{
+       SLMP_INFO *info = tty->driver_data;
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s throttle() entry\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       if (sanity_check(info, tty->name, "throttle"))
+               return;
+
+       if (I_IXOFF(tty))
+               send_xchar(tty, STOP_CHAR(tty));
+
+       if (tty->termios->c_cflag & CRTSCTS) {
+               spin_lock_irqsave(&info->lock,flags);
+               info->serial_signals &= ~SerialSignal_RTS;
+               set_signals(info);
+               spin_unlock_irqrestore(&info->lock,flags);
+       }
+}
+
+/* Signal remote device to stop throttling send data (our receive data)
+ */
+static void unthrottle(struct tty_struct * tty)
+{
+       SLMP_INFO *info = tty->driver_data;
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s unthrottle() entry\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       if (sanity_check(info, tty->name, "unthrottle"))
+               return;
+
+       if (I_IXOFF(tty)) {
+               if (info->x_char)
+                       info->x_char = 0;
+               else
+                       send_xchar(tty, START_CHAR(tty));
+       }
+
+       if (tty->termios->c_cflag & CRTSCTS) {
+               spin_lock_irqsave(&info->lock,flags);
+               info->serial_signals |= SerialSignal_RTS;
+               set_signals(info);
+               spin_unlock_irqrestore(&info->lock,flags);
+       }
+}
+
+/* set or clear transmit break condition
+ * break_state -1=set break condition, 0=clear
+ */
+static int set_break(struct tty_struct *tty, int break_state)
+{
+       unsigned char RegValue;
+       SLMP_INFO * info = tty->driver_data;
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s set_break(%d)\n",
+                        __FILE__,__LINE__, info->device_name, break_state);
+
+       if (sanity_check(info, tty->name, "set_break"))
+               return -EINVAL;
+
+       spin_lock_irqsave(&info->lock,flags);
+       RegValue = read_reg(info, CTL);
+       if (break_state == -1)
+               RegValue |= BIT3;
+       else
+               RegValue &= ~BIT3;
+       write_reg(info, CTL, RegValue);
+       spin_unlock_irqrestore(&info->lock,flags);
+       return 0;
+}
+
+#if SYNCLINK_GENERIC_HDLC
+
+/**
+ * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.)
+ * set encoding and frame check sequence (FCS) options
+ *
+ * dev       pointer to network device structure
+ * encoding  serial encoding setting
+ * parity    FCS setting
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
+                         unsigned short parity)
+{
+       SLMP_INFO *info = dev_to_port(dev);
+       unsigned char  new_encoding;
+       unsigned short new_crctype;
+
+       /* return error if TTY interface open */
+       if (info->port.count)
+               return -EBUSY;
+
+       switch (encoding)
+       {
+       case ENCODING_NRZ:        new_encoding = HDLC_ENCODING_NRZ; break;
+       case ENCODING_NRZI:       new_encoding = HDLC_ENCODING_NRZI_SPACE; break;
+       case ENCODING_FM_MARK:    new_encoding = HDLC_ENCODING_BIPHASE_MARK; break;
+       case ENCODING_FM_SPACE:   new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break;
+       case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break;
+       default: return -EINVAL;
+       }
+
+       switch (parity)
+       {
+       case PARITY_NONE:            new_crctype = HDLC_CRC_NONE; break;
+       case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break;
+       case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break;
+       default: return -EINVAL;
+       }
+
+       info->params.encoding = new_encoding;
+       info->params.crc_type = new_crctype;
+
+       /* if network interface up, reprogram hardware */
+       if (info->netcount)
+               program_hw(info);
+
+       return 0;
+}
+
+/**
+ * called by generic HDLC layer to send frame
+ *
+ * skb  socket buffer containing HDLC frame
+ * dev  pointer to network device structure
+ */
+static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
+                                     struct net_device *dev)
+{
+       SLMP_INFO *info = dev_to_port(dev);
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk(KERN_INFO "%s:hdlc_xmit(%s)\n",__FILE__,dev->name);
+
+       /* stop sending until this frame completes */
+       netif_stop_queue(dev);
+
+       /* copy data to device buffers */
+       info->tx_count = skb->len;
+       tx_load_dma_buffer(info, skb->data, skb->len);
+
+       /* update network statistics */
+       dev->stats.tx_packets++;
+       dev->stats.tx_bytes += skb->len;
+
+       /* done with socket buffer, so free it */
+       dev_kfree_skb(skb);
+
+       /* save start time for transmit timeout detection */
+       dev->trans_start = jiffies;
+
+       /* start hardware transmitter if necessary */
+       spin_lock_irqsave(&info->lock,flags);
+       if (!info->tx_active)
+               tx_start(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       return NETDEV_TX_OK;
+}
+
+/**
+ * called by network layer when interface enabled
+ * claim resources and initialize hardware
+ *
+ * dev  pointer to network device structure
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_open(struct net_device *dev)
+{
+       SLMP_INFO *info = dev_to_port(dev);
+       int rc;
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s:hdlcdev_open(%s)\n",__FILE__,dev->name);
+
+       /* generic HDLC layer open processing */
+       if ((rc = hdlc_open(dev)))
+               return rc;
+
+       /* arbitrate between network and tty opens */
+       spin_lock_irqsave(&info->netlock, flags);
+       if (info->port.count != 0 || info->netcount != 0) {
+               printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name);
+               spin_unlock_irqrestore(&info->netlock, flags);
+               return -EBUSY;
+       }
+       info->netcount=1;
+       spin_unlock_irqrestore(&info->netlock, flags);
+
+       /* claim resources and init adapter */
+       if ((rc = startup(info)) != 0) {
+               spin_lock_irqsave(&info->netlock, flags);
+               info->netcount=0;
+               spin_unlock_irqrestore(&info->netlock, flags);
+               return rc;
+       }
+
+       /* assert DTR and RTS, apply hardware settings */
+       info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+       program_hw(info);
+
+       /* enable network layer transmit */
+       dev->trans_start = jiffies;
+       netif_start_queue(dev);
+
+       /* inform generic HDLC layer of current DCD status */
+       spin_lock_irqsave(&info->lock, flags);
+       get_signals(info);
+       spin_unlock_irqrestore(&info->lock, flags);
+       if (info->serial_signals & SerialSignal_DCD)
+               netif_carrier_on(dev);
+       else
+               netif_carrier_off(dev);
+       return 0;
+}
+
+/**
+ * called by network layer when interface is disabled
+ * shutdown hardware and release resources
+ *
+ * dev  pointer to network device structure
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_close(struct net_device *dev)
+{
+       SLMP_INFO *info = dev_to_port(dev);
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s:hdlcdev_close(%s)\n",__FILE__,dev->name);
+
+       netif_stop_queue(dev);
+
+       /* shutdown adapter and release resources */
+       shutdown(info);
+
+       hdlc_close(dev);
+
+       spin_lock_irqsave(&info->netlock, flags);
+       info->netcount=0;
+       spin_unlock_irqrestore(&info->netlock, flags);
+
+       return 0;
+}
+
+/**
+ * called by network layer to process IOCTL call to network device
+ *
+ * dev  pointer to network device structure
+ * ifr  pointer to network interface request structure
+ * cmd  IOCTL command code
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       const size_t size = sizeof(sync_serial_settings);
+       sync_serial_settings new_line;
+       sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
+       SLMP_INFO *info = dev_to_port(dev);
+       unsigned int flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s:hdlcdev_ioctl(%s)\n",__FILE__,dev->name);
+
+       /* return error if TTY interface open */
+       if (info->port.count)
+               return -EBUSY;
+
+       if (cmd != SIOCWANDEV)
+               return hdlc_ioctl(dev, ifr, cmd);
+
+       switch(ifr->ifr_settings.type) {
+       case IF_GET_IFACE: /* return current sync_serial_settings */
+
+               ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
+               if (ifr->ifr_settings.size < size) {
+                       ifr->ifr_settings.size = size; /* data size wanted */
+                       return -ENOBUFS;
+               }
+
+               flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
+                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
+                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
+                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
+
+               switch (flags){
+               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break;
+               case (HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_INT; break;
+               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG):    new_line.clock_type = CLOCK_TXINT; break;
+               case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break;
+               default: new_line.clock_type = CLOCK_DEFAULT;
+               }
+
+               new_line.clock_rate = info->params.clock_speed;
+               new_line.loopback   = info->params.loopback ? 1:0;
+
+               if (copy_to_user(line, &new_line, size))
+                       return -EFAULT;
+               return 0;
+
+       case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */
+
+               if(!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               if (copy_from_user(&new_line, line, size))
+                       return -EFAULT;
+
+               switch (new_line.clock_type)
+               {
+               case CLOCK_EXT:      flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break;
+               case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break;
+               case CLOCK_INT:      flags = HDLC_FLAG_RXC_BRG    | HDLC_FLAG_TXC_BRG;    break;
+               case CLOCK_TXINT:    flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG;    break;
+               case CLOCK_DEFAULT:  flags = info->params.flags &
+                                            (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
+                                             HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
+                                             HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
+                                             HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN); break;
+               default: return -EINVAL;
+               }
+
+               if (new_line.loopback != 0 && new_line.loopback != 1)
+                       return -EINVAL;
+
+               info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL |
+                                       HDLC_FLAG_RXC_BRG    | HDLC_FLAG_RXC_TXCPIN |
+                                       HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL |
+                                       HDLC_FLAG_TXC_BRG    | HDLC_FLAG_TXC_RXCPIN);
+               info->params.flags |= flags;
+
+               info->params.loopback = new_line.loopback;
+
+               if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG))
+                       info->params.clock_speed = new_line.clock_rate;
+               else
+                       info->params.clock_speed = 0;
+
+               /* if network interface up, reprogram hardware */
+               if (info->netcount)
+                       program_hw(info);
+               return 0;
+
+       default:
+               return hdlc_ioctl(dev, ifr, cmd);
+       }
+}
+
+/**
+ * called by network layer when transmit timeout is detected
+ *
+ * dev  pointer to network device structure
+ */
+static void hdlcdev_tx_timeout(struct net_device *dev)
+{
+       SLMP_INFO *info = dev_to_port(dev);
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("hdlcdev_tx_timeout(%s)\n",dev->name);
+
+       dev->stats.tx_errors++;
+       dev->stats.tx_aborted_errors++;
+
+       spin_lock_irqsave(&info->lock,flags);
+       tx_stop(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       netif_wake_queue(dev);
+}
+
+/**
+ * called by device driver when transmit completes
+ * reenable network layer transmit if stopped
+ *
+ * info  pointer to device instance information
+ */
+static void hdlcdev_tx_done(SLMP_INFO *info)
+{
+       if (netif_queue_stopped(info->netdev))
+               netif_wake_queue(info->netdev);
+}
+
+/**
+ * called by device driver when frame received
+ * pass frame to network layer
+ *
+ * info  pointer to device instance information
+ * buf   pointer to buffer contianing frame data
+ * size  count of data bytes in buf
+ */
+static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size)
+{
+       struct sk_buff *skb = dev_alloc_skb(size);
+       struct net_device *dev = info->netdev;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("hdlcdev_rx(%s)\n",dev->name);
+
+       if (skb == NULL) {
+               printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n",
+                      dev->name);
+               dev->stats.rx_dropped++;
+               return;
+       }
+
+       memcpy(skb_put(skb, size), buf, size);
+
+       skb->protocol = hdlc_type_trans(skb, dev);
+
+       dev->stats.rx_packets++;
+       dev->stats.rx_bytes += size;
+
+       netif_rx(skb);
+}
+
+static const struct net_device_ops hdlcdev_ops = {
+       .ndo_open       = hdlcdev_open,
+       .ndo_stop       = hdlcdev_close,
+       .ndo_change_mtu = hdlc_change_mtu,
+       .ndo_start_xmit = hdlc_start_xmit,
+       .ndo_do_ioctl   = hdlcdev_ioctl,
+       .ndo_tx_timeout = hdlcdev_tx_timeout,
+};
+
+/**
+ * called by device driver when adding device instance
+ * do generic HDLC initialization
+ *
+ * info  pointer to device instance information
+ *
+ * returns 0 if success, otherwise error code
+ */
+static int hdlcdev_init(SLMP_INFO *info)
+{
+       int rc;
+       struct net_device *dev;
+       hdlc_device *hdlc;
+
+       /* allocate and initialize network and HDLC layer objects */
+
+       if (!(dev = alloc_hdlcdev(info))) {
+               printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__);
+               return -ENOMEM;
+       }
+
+       /* for network layer reporting purposes only */
+       dev->mem_start = info->phys_sca_base;
+       dev->mem_end   = info->phys_sca_base + SCA_BASE_SIZE - 1;
+       dev->irq       = info->irq_level;
+
+       /* network layer callbacks and settings */
+       dev->netdev_ops     = &hdlcdev_ops;
+       dev->watchdog_timeo = 10 * HZ;
+       dev->tx_queue_len   = 50;
+
+       /* generic HDLC layer callbacks and settings */
+       hdlc         = dev_to_hdlc(dev);
+       hdlc->attach = hdlcdev_attach;
+       hdlc->xmit   = hdlcdev_xmit;
+
+       /* register objects with HDLC layer */
+       if ((rc = register_hdlc_device(dev))) {
+               printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__);
+               free_netdev(dev);
+               return rc;
+       }
+
+       info->netdev = dev;
+       return 0;
+}
+
+/**
+ * called by device driver when removing device instance
+ * do generic HDLC cleanup
+ *
+ * info  pointer to device instance information
+ */
+static void hdlcdev_exit(SLMP_INFO *info)
+{
+       unregister_hdlc_device(info->netdev);
+       free_netdev(info->netdev);
+       info->netdev = NULL;
+}
+
+#endif /* CONFIG_HDLC */
+
+
+/* Return next bottom half action to perform.
+ * Return Value:       BH action code or 0 if nothing to do.
+ */
+static int bh_action(SLMP_INFO *info)
+{
+       unsigned long flags;
+       int rc = 0;
+
+       spin_lock_irqsave(&info->lock,flags);
+
+       if (info->pending_bh & BH_RECEIVE) {
+               info->pending_bh &= ~BH_RECEIVE;
+               rc = BH_RECEIVE;
+       } else if (info->pending_bh & BH_TRANSMIT) {
+               info->pending_bh &= ~BH_TRANSMIT;
+               rc = BH_TRANSMIT;
+       } else if (info->pending_bh & BH_STATUS) {
+               info->pending_bh &= ~BH_STATUS;
+               rc = BH_STATUS;
+       }
+
+       if (!rc) {
+               /* Mark BH routine as complete */
+               info->bh_running = false;
+               info->bh_requested = false;
+       }
+
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       return rc;
+}
+
+/* Perform bottom half processing of work items queued by ISR.
+ */
+static void bh_handler(struct work_struct *work)
+{
+       SLMP_INFO *info = container_of(work, SLMP_INFO, task);
+       int action;
+
+       if (!info)
+               return;
+
+       if ( debug_level >= DEBUG_LEVEL_BH )
+               printk( "%s(%d):%s bh_handler() entry\n",
+                       __FILE__,__LINE__,info->device_name);
+
+       info->bh_running = true;
+
+       while((action = bh_action(info)) != 0) {
+
+               /* Process work item */
+               if ( debug_level >= DEBUG_LEVEL_BH )
+                       printk( "%s(%d):%s bh_handler() work item action=%d\n",
+                               __FILE__,__LINE__,info->device_name, action);
+
+               switch (action) {
+
+               case BH_RECEIVE:
+                       bh_receive(info);
+                       break;
+               case BH_TRANSMIT:
+                       bh_transmit(info);
+                       break;
+               case BH_STATUS:
+                       bh_status(info);
+                       break;
+               default:
+                       /* unknown work item ID */
+                       printk("%s(%d):%s Unknown work item ID=%08X!\n",
+                               __FILE__,__LINE__,info->device_name,action);
+                       break;
+               }
+       }
+
+       if ( debug_level >= DEBUG_LEVEL_BH )
+               printk( "%s(%d):%s bh_handler() exit\n",
+                       __FILE__,__LINE__,info->device_name);
+}
+
+static void bh_receive(SLMP_INFO *info)
+{
+       if ( debug_level >= DEBUG_LEVEL_BH )
+               printk( "%s(%d):%s bh_receive()\n",
+                       __FILE__,__LINE__,info->device_name);
+
+       while( rx_get_frame(info) );
+}
+
+static void bh_transmit(SLMP_INFO *info)
+{
+       struct tty_struct *tty = info->port.tty;
+
+       if ( debug_level >= DEBUG_LEVEL_BH )
+               printk( "%s(%d):%s bh_transmit() entry\n",
+                       __FILE__,__LINE__,info->device_name);
+
+       if (tty)
+               tty_wakeup(tty);
+}
+
+static void bh_status(SLMP_INFO *info)
+{
+       if ( debug_level >= DEBUG_LEVEL_BH )
+               printk( "%s(%d):%s bh_status() entry\n",
+                       __FILE__,__LINE__,info->device_name);
+
+       info->ri_chkcount = 0;
+       info->dsr_chkcount = 0;
+       info->dcd_chkcount = 0;
+       info->cts_chkcount = 0;
+}
+
+static void isr_timer(SLMP_INFO * info)
+{
+       unsigned char timer = (info->port_num & 1) ? TIMER2 : TIMER0;
+
+       /* IER2<7..4> = timer<3..0> interrupt enables (0=disabled) */
+       write_reg(info, IER2, 0);
+
+       /* TMCS, Timer Control/Status Register
+        *
+        * 07      CMF, Compare match flag (read only) 1=match
+        * 06      ECMI, CMF Interrupt Enable: 0=disabled
+        * 05      Reserved, must be 0
+        * 04      TME, Timer Enable
+        * 03..00  Reserved, must be 0
+        *
+        * 0000 0000
+        */
+       write_reg(info, (unsigned char)(timer + TMCS), 0);
+
+       info->irq_occurred = true;
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk("%s(%d):%s isr_timer()\n",
+                       __FILE__,__LINE__,info->device_name);
+}
+
+static void isr_rxint(SLMP_INFO * info)
+{
+       struct tty_struct *tty = info->port.tty;
+       struct  mgsl_icount *icount = &info->icount;
+       unsigned char status = read_reg(info, SR1) & info->ie1_value & (FLGD + IDLD + CDCD + BRKD);
+       unsigned char status2 = read_reg(info, SR2) & info->ie2_value & OVRN;
+
+       /* clear status bits */
+       if (status)
+               write_reg(info, SR1, status);
+
+       if (status2)
+               write_reg(info, SR2, status2);
+       
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk("%s(%d):%s isr_rxint status=%02X %02x\n",
+                       __FILE__,__LINE__,info->device_name,status,status2);
+
+       if (info->params.mode == MGSL_MODE_ASYNC) {
+               if (status & BRKD) {
+                       icount->brk++;
+
+                       /* process break detection if tty control
+                        * is not set to ignore it
+                        */
+                       if ( tty ) {
+                               if (!(status & info->ignore_status_mask1)) {
+                                       if (info->read_status_mask1 & BRKD) {
+                                               tty_insert_flip_char(tty, 0, TTY_BREAK);
+                                               if (info->port.flags & ASYNC_SAK)
+                                                       do_SAK(tty);
+                                       }
+                               }
+                       }
+               }
+       }
+       else {
+               if (status & (FLGD|IDLD)) {
+                       if (status & FLGD)
+                               info->icount.exithunt++;
+                       else if (status & IDLD)
+                               info->icount.rxidle++;
+                       wake_up_interruptible(&info->event_wait_q);
+               }
+       }
+
+       if (status & CDCD) {
+               /* simulate a common modem status change interrupt
+                * for our handler
+                */
+               get_signals( info );
+               isr_io_pin(info,
+                       MISCSTATUS_DCD_LATCHED|(info->serial_signals&SerialSignal_DCD));
+       }
+}
+
+/*
+ * handle async rx data interrupts
+ */
+static void isr_rxrdy(SLMP_INFO * info)
+{
+       u16 status;
+       unsigned char DataByte;
+       struct tty_struct *tty = info->port.tty;
+       struct  mgsl_icount *icount = &info->icount;
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk("%s(%d):%s isr_rxrdy\n",
+                       __FILE__,__LINE__,info->device_name);
+
+       while((status = read_reg(info,CST0)) & BIT0)
+       {
+               int flag = 0;
+               bool over = false;
+               DataByte = read_reg(info,TRB);
+
+               icount->rx++;
+
+               if ( status & (PE + FRME + OVRN) ) {
+                       printk("%s(%d):%s rxerr=%04X\n",
+                               __FILE__,__LINE__,info->device_name,status);
+
+                       /* update error statistics */
+                       if (status & PE)
+                               icount->parity++;
+                       else if (status & FRME)
+                               icount->frame++;
+                       else if (status & OVRN)
+                               icount->overrun++;
+
+                       /* discard char if tty control flags say so */
+                       if (status & info->ignore_status_mask2)
+                               continue;
+
+                       status &= info->read_status_mask2;
+
+                       if ( tty ) {
+                               if (status & PE)
+                                       flag = TTY_PARITY;
+                               else if (status & FRME)
+                                       flag = TTY_FRAME;
+                               if (status & OVRN) {
+                                       /* Overrun is special, since it's
+                                        * reported immediately, and doesn't
+                                        * affect the current character
+                                        */
+                                       over = true;
+                               }
+                       }
+               }       /* end of if (error) */
+
+               if ( tty ) {
+                       tty_insert_flip_char(tty, DataByte, flag);
+                       if (over)
+                               tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+               }
+       }
+
+       if ( debug_level >= DEBUG_LEVEL_ISR ) {
+               printk("%s(%d):%s rx=%d brk=%d parity=%d frame=%d overrun=%d\n",
+                       __FILE__,__LINE__,info->device_name,
+                       icount->rx,icount->brk,icount->parity,
+                       icount->frame,icount->overrun);
+       }
+
+       if ( tty )
+               tty_flip_buffer_push(tty);
+}
+
+static void isr_txeom(SLMP_INFO * info, unsigned char status)
+{
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk("%s(%d):%s isr_txeom status=%02x\n",
+                       __FILE__,__LINE__,info->device_name,status);
+
+       write_reg(info, TXDMA + DIR, 0x00); /* disable Tx DMA IRQs */
+       write_reg(info, TXDMA + DSR, 0xc0); /* clear IRQs and disable DMA */
+       write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */
+
+       if (status & UDRN) {
+               write_reg(info, CMD, TXRESET);
+               write_reg(info, CMD, TXENABLE);
+       } else
+               write_reg(info, CMD, TXBUFCLR);
+
+       /* disable and clear tx interrupts */
+       info->ie0_value &= ~TXRDYE;
+       info->ie1_value &= ~(IDLE + UDRN);
+       write_reg16(info, IE0, (unsigned short)((info->ie1_value << 8) + info->ie0_value));
+       write_reg(info, SR1, (unsigned char)(UDRN + IDLE));
+
+       if ( info->tx_active ) {
+               if (info->params.mode != MGSL_MODE_ASYNC) {
+                       if (status & UDRN)
+                               info->icount.txunder++;
+                       else if (status & IDLE)
+                               info->icount.txok++;
+               }
+
+               info->tx_active = false;
+               info->tx_count = info->tx_put = info->tx_get = 0;
+
+               del_timer(&info->tx_timer);
+
+               if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done ) {
+                       info->serial_signals &= ~SerialSignal_RTS;
+                       info->drop_rts_on_tx_done = false;
+                       set_signals(info);
+               }
+
+#if SYNCLINK_GENERIC_HDLC
+               if (info->netcount)
+                       hdlcdev_tx_done(info);
+               else
+#endif
+               {
+                       if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) {
+                               tx_stop(info);
+                               return;
+                       }
+                       info->pending_bh |= BH_TRANSMIT;
+               }
+       }
+}
+
+
+/*
+ * handle tx status interrupts
+ */
+static void isr_txint(SLMP_INFO * info)
+{
+       unsigned char status = read_reg(info, SR1) & info->ie1_value & (UDRN + IDLE + CCTS);
+
+       /* clear status bits */
+       write_reg(info, SR1, status);
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk("%s(%d):%s isr_txint status=%02x\n",
+                       __FILE__,__LINE__,info->device_name,status);
+
+       if (status & (UDRN + IDLE))
+               isr_txeom(info, status);
+
+       if (status & CCTS) {
+               /* simulate a common modem status change interrupt
+                * for our handler
+                */
+               get_signals( info );
+               isr_io_pin(info,
+                       MISCSTATUS_CTS_LATCHED|(info->serial_signals&SerialSignal_CTS));
+
+       }
+}
+
+/*
+ * handle async tx data interrupts
+ */
+static void isr_txrdy(SLMP_INFO * info)
+{
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk("%s(%d):%s isr_txrdy() tx_count=%d\n",
+                       __FILE__,__LINE__,info->device_name,info->tx_count);
+
+       if (info->params.mode != MGSL_MODE_ASYNC) {
+               /* disable TXRDY IRQ, enable IDLE IRQ */
+               info->ie0_value &= ~TXRDYE;
+               info->ie1_value |= IDLE;
+               write_reg16(info, IE0, (unsigned short)((info->ie1_value << 8) + info->ie0_value));
+               return;
+       }
+
+       if (info->port.tty && (info->port.tty->stopped || info->port.tty->hw_stopped)) {
+               tx_stop(info);
+               return;
+       }
+
+       if ( info->tx_count )
+               tx_load_fifo( info );
+       else {
+               info->tx_active = false;
+               info->ie0_value &= ~TXRDYE;
+               write_reg(info, IE0, info->ie0_value);
+       }
+
+       if (info->tx_count < WAKEUP_CHARS)
+               info->pending_bh |= BH_TRANSMIT;
+}
+
+static void isr_rxdmaok(SLMP_INFO * info)
+{
+       /* BIT7 = EOT (end of transfer)
+        * BIT6 = EOM (end of message/frame)
+        */
+       unsigned char status = read_reg(info,RXDMA + DSR) & 0xc0;
+
+       /* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */
+       write_reg(info, RXDMA + DSR, (unsigned char)(status | 1));
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk("%s(%d):%s isr_rxdmaok(), status=%02x\n",
+                       __FILE__,__LINE__,info->device_name,status);
+
+       info->pending_bh |= BH_RECEIVE;
+}
+
+static void isr_rxdmaerror(SLMP_INFO * info)
+{
+       /* BIT5 = BOF (buffer overflow)
+        * BIT4 = COF (counter overflow)
+        */
+       unsigned char status = read_reg(info,RXDMA + DSR) & 0x30;
+
+       /* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */
+       write_reg(info, RXDMA + DSR, (unsigned char)(status | 1));
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk("%s(%d):%s isr_rxdmaerror(), status=%02x\n",
+                       __FILE__,__LINE__,info->device_name,status);
+
+       info->rx_overflow = true;
+       info->pending_bh |= BH_RECEIVE;
+}
+
+static void isr_txdmaok(SLMP_INFO * info)
+{
+       unsigned char status_reg1 = read_reg(info, SR1);
+
+       write_reg(info, TXDMA + DIR, 0x00);     /* disable Tx DMA IRQs */
+       write_reg(info, TXDMA + DSR, 0xc0); /* clear IRQs and disable DMA */
+       write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk("%s(%d):%s isr_txdmaok(), status=%02x\n",
+                       __FILE__,__LINE__,info->device_name,status_reg1);
+
+       /* program TXRDY as FIFO empty flag, enable TXRDY IRQ */
+       write_reg16(info, TRC0, 0);
+       info->ie0_value |= TXRDYE;
+       write_reg(info, IE0, info->ie0_value);
+}
+
+static void isr_txdmaerror(SLMP_INFO * info)
+{
+       /* BIT5 = BOF (buffer overflow)
+        * BIT4 = COF (counter overflow)
+        */
+       unsigned char status = read_reg(info,TXDMA + DSR) & 0x30;
+
+       /* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */
+       write_reg(info, TXDMA + DSR, (unsigned char)(status | 1));
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk("%s(%d):%s isr_txdmaerror(), status=%02x\n",
+                       __FILE__,__LINE__,info->device_name,status);
+}
+
+/* handle input serial signal changes
+ */
+static void isr_io_pin( SLMP_INFO *info, u16 status )
+{
+       struct  mgsl_icount *icount;
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk("%s(%d):isr_io_pin status=%04X\n",
+                       __FILE__,__LINE__,status);
+
+       if (status & (MISCSTATUS_CTS_LATCHED | MISCSTATUS_DCD_LATCHED |
+                     MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) {
+               icount = &info->icount;
+               /* update input line counters */
+               if (status & MISCSTATUS_RI_LATCHED) {
+                       icount->rng++;
+                       if ( status & SerialSignal_RI )
+                               info->input_signal_events.ri_up++;
+                       else
+                               info->input_signal_events.ri_down++;
+               }
+               if (status & MISCSTATUS_DSR_LATCHED) {
+                       icount->dsr++;
+                       if ( status & SerialSignal_DSR )
+                               info->input_signal_events.dsr_up++;
+                       else
+                               info->input_signal_events.dsr_down++;
+               }
+               if (status & MISCSTATUS_DCD_LATCHED) {
+                       if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) {
+                               info->ie1_value &= ~CDCD;
+                               write_reg(info, IE1, info->ie1_value);
+                       }
+                       icount->dcd++;
+                       if (status & SerialSignal_DCD) {
+                               info->input_signal_events.dcd_up++;
+                       } else
+                               info->input_signal_events.dcd_down++;
+#if SYNCLINK_GENERIC_HDLC
+                       if (info->netcount) {
+                               if (status & SerialSignal_DCD)
+                                       netif_carrier_on(info->netdev);
+                               else
+                                       netif_carrier_off(info->netdev);
+                       }
+#endif
+               }
+               if (status & MISCSTATUS_CTS_LATCHED)
+               {
+                       if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) {
+                               info->ie1_value &= ~CCTS;
+                               write_reg(info, IE1, info->ie1_value);
+                       }
+                       icount->cts++;
+                       if ( status & SerialSignal_CTS )
+                               info->input_signal_events.cts_up++;
+                       else
+                               info->input_signal_events.cts_down++;
+               }
+               wake_up_interruptible(&info->status_event_wait_q);
+               wake_up_interruptible(&info->event_wait_q);
+
+               if ( (info->port.flags & ASYNC_CHECK_CD) &&
+                    (status & MISCSTATUS_DCD_LATCHED) ) {
+                       if ( debug_level >= DEBUG_LEVEL_ISR )
+                               printk("%s CD now %s...", info->device_name,
+                                      (status & SerialSignal_DCD) ? "on" : "off");
+                       if (status & SerialSignal_DCD)
+                               wake_up_interruptible(&info->port.open_wait);
+                       else {
+                               if ( debug_level >= DEBUG_LEVEL_ISR )
+                                       printk("doing serial hangup...");
+                               if (info->port.tty)
+                                       tty_hangup(info->port.tty);
+                       }
+               }
+
+               if ( (info->port.flags & ASYNC_CTS_FLOW) &&
+                    (status & MISCSTATUS_CTS_LATCHED) ) {
+                       if ( info->port.tty ) {
+                               if (info->port.tty->hw_stopped) {
+                                       if (status & SerialSignal_CTS) {
+                                               if ( debug_level >= DEBUG_LEVEL_ISR )
+                                                       printk("CTS tx start...");
+                                               info->port.tty->hw_stopped = 0;
+                                               tx_start(info);
+                                               info->pending_bh |= BH_TRANSMIT;
+                                               return;
+                                       }
+                               } else {
+                                       if (!(status & SerialSignal_CTS)) {
+                                               if ( debug_level >= DEBUG_LEVEL_ISR )
+                                                       printk("CTS tx stop...");
+                                               info->port.tty->hw_stopped = 1;
+                                               tx_stop(info);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       info->pending_bh |= BH_STATUS;
+}
+
+/* Interrupt service routine entry point.
+ *
+ * Arguments:
+ *     irq             interrupt number that caused interrupt
+ *     dev_id          device ID supplied during interrupt registration
+ *     regs            interrupted processor context
+ */
+static irqreturn_t synclinkmp_interrupt(int dummy, void *dev_id)
+{
+       SLMP_INFO *info = dev_id;
+       unsigned char status, status0, status1=0;
+       unsigned char dmastatus, dmastatus0, dmastatus1=0;
+       unsigned char timerstatus0, timerstatus1=0;
+       unsigned char shift;
+       unsigned int i;
+       unsigned short tmp;
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk(KERN_DEBUG "%s(%d): synclinkmp_interrupt(%d)entry.\n",
+                       __FILE__, __LINE__, info->irq_level);
+
+       spin_lock(&info->lock);
+
+       for(;;) {
+
+               /* get status for SCA0 (ports 0-1) */
+               tmp = read_reg16(info, ISR0);   /* get ISR0 and ISR1 in one read */
+               status0 = (unsigned char)tmp;
+               dmastatus0 = (unsigned char)(tmp>>8);
+               timerstatus0 = read_reg(info, ISR2);
+
+               if ( debug_level >= DEBUG_LEVEL_ISR )
+                       printk(KERN_DEBUG "%s(%d):%s status0=%02x, dmastatus0=%02x, timerstatus0=%02x\n",
+                               __FILE__, __LINE__, info->device_name,
+                               status0, dmastatus0, timerstatus0);
+
+               if (info->port_count == 4) {
+                       /* get status for SCA1 (ports 2-3) */
+                       tmp = read_reg16(info->port_array[2], ISR0);
+                       status1 = (unsigned char)tmp;
+                       dmastatus1 = (unsigned char)(tmp>>8);
+                       timerstatus1 = read_reg(info->port_array[2], ISR2);
+
+                       if ( debug_level >= DEBUG_LEVEL_ISR )
+                               printk("%s(%d):%s status1=%02x, dmastatus1=%02x, timerstatus1=%02x\n",
+                                       __FILE__,__LINE__,info->device_name,
+                                       status1,dmastatus1,timerstatus1);
+               }
+
+               if (!status0 && !dmastatus0 && !timerstatus0 &&
+                        !status1 && !dmastatus1 && !timerstatus1)
+                       break;
+
+               for(i=0; i < info->port_count ; i++) {
+                       if (info->port_array[i] == NULL)
+                               continue;
+                       if (i < 2) {
+                               status = status0;
+                               dmastatus = dmastatus0;
+                       } else {
+                               status = status1;
+                               dmastatus = dmastatus1;
+                       }
+
+                       shift = i & 1 ? 4 :0;
+
+                       if (status & BIT0 << shift)
+                               isr_rxrdy(info->port_array[i]);
+                       if (status & BIT1 << shift)
+                               isr_txrdy(info->port_array[i]);
+                       if (status & BIT2 << shift)
+                               isr_rxint(info->port_array[i]);
+                       if (status & BIT3 << shift)
+                               isr_txint(info->port_array[i]);
+
+                       if (dmastatus & BIT0 << shift)
+                               isr_rxdmaerror(info->port_array[i]);
+                       if (dmastatus & BIT1 << shift)
+                               isr_rxdmaok(info->port_array[i]);
+                       if (dmastatus & BIT2 << shift)
+                               isr_txdmaerror(info->port_array[i]);
+                       if (dmastatus & BIT3 << shift)
+                               isr_txdmaok(info->port_array[i]);
+               }
+
+               if (timerstatus0 & (BIT5 | BIT4))
+                       isr_timer(info->port_array[0]);
+               if (timerstatus0 & (BIT7 | BIT6))
+                       isr_timer(info->port_array[1]);
+               if (timerstatus1 & (BIT5 | BIT4))
+                       isr_timer(info->port_array[2]);
+               if (timerstatus1 & (BIT7 | BIT6))
+                       isr_timer(info->port_array[3]);
+       }
+
+       for(i=0; i < info->port_count ; i++) {
+               SLMP_INFO * port = info->port_array[i];
+
+               /* Request bottom half processing if there's something
+                * for it to do and the bh is not already running.
+                *
+                * Note: startup adapter diags require interrupts.
+                * do not request bottom half processing if the
+                * device is not open in a normal mode.
+                */
+               if ( port && (port->port.count || port->netcount) &&
+                    port->pending_bh && !port->bh_running &&
+                    !port->bh_requested ) {
+                       if ( debug_level >= DEBUG_LEVEL_ISR )
+                               printk("%s(%d):%s queueing bh task.\n",
+                                       __FILE__,__LINE__,port->device_name);
+                       schedule_work(&port->task);
+                       port->bh_requested = true;
+               }
+       }
+
+       spin_unlock(&info->lock);
+
+       if ( debug_level >= DEBUG_LEVEL_ISR )
+               printk(KERN_DEBUG "%s(%d):synclinkmp_interrupt(%d)exit.\n",
+                       __FILE__, __LINE__, info->irq_level);
+       return IRQ_HANDLED;
+}
+
+/* Initialize and start device.
+ */
+static int startup(SLMP_INFO * info)
+{
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk("%s(%d):%s tx_releaseup()\n",__FILE__,__LINE__,info->device_name);
+
+       if (info->port.flags & ASYNC_INITIALIZED)
+               return 0;
+
+       if (!info->tx_buf) {
+               info->tx_buf = kmalloc(info->max_frame_size, GFP_KERNEL);
+               if (!info->tx_buf) {
+                       printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n",
+                               __FILE__,__LINE__,info->device_name);
+                       return -ENOMEM;
+               }
+       }
+
+       info->pending_bh = 0;
+
+       memset(&info->icount, 0, sizeof(info->icount));
+
+       /* program hardware for current parameters */
+       reset_port(info);
+
+       change_params(info);
+
+       mod_timer(&info->status_timer, jiffies + msecs_to_jiffies(10));
+
+       if (info->port.tty)
+               clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
+
+       info->port.flags |= ASYNC_INITIALIZED;
+
+       return 0;
+}
+
+/* Called by close() and hangup() to shutdown hardware
+ */
+static void shutdown(SLMP_INFO * info)
+{
+       unsigned long flags;
+
+       if (!(info->port.flags & ASYNC_INITIALIZED))
+               return;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s synclinkmp_shutdown()\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       /* clear status wait queue because status changes */
+       /* can't happen after shutting down the hardware */
+       wake_up_interruptible(&info->status_event_wait_q);
+       wake_up_interruptible(&info->event_wait_q);
+
+       del_timer(&info->tx_timer);
+       del_timer(&info->status_timer);
+
+       kfree(info->tx_buf);
+       info->tx_buf = NULL;
+
+       spin_lock_irqsave(&info->lock,flags);
+
+       reset_port(info);
+
+       if (!info->port.tty || info->port.tty->termios->c_cflag & HUPCL) {
+               info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
+               set_signals(info);
+       }
+
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       if (info->port.tty)
+               set_bit(TTY_IO_ERROR, &info->port.tty->flags);
+
+       info->port.flags &= ~ASYNC_INITIALIZED;
+}
+
+static void program_hw(SLMP_INFO *info)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->lock,flags);
+
+       rx_stop(info);
+       tx_stop(info);
+
+       info->tx_count = info->tx_put = info->tx_get = 0;
+
+       if (info->params.mode == MGSL_MODE_HDLC || info->netcount)
+               hdlc_mode(info);
+       else
+               async_mode(info);
+
+       set_signals(info);
+
+       info->dcd_chkcount = 0;
+       info->cts_chkcount = 0;
+       info->ri_chkcount = 0;
+       info->dsr_chkcount = 0;
+
+       info->ie1_value |= (CDCD|CCTS);
+       write_reg(info, IE1, info->ie1_value);
+
+       get_signals(info);
+
+       if (info->netcount || (info->port.tty && info->port.tty->termios->c_cflag & CREAD) )
+               rx_start(info);
+
+       spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* Reconfigure adapter based on new parameters
+ */
+static void change_params(SLMP_INFO *info)
+{
+       unsigned cflag;
+       int bits_per_char;
+
+       if (!info->port.tty || !info->port.tty->termios)
+               return;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s change_params()\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       cflag = info->port.tty->termios->c_cflag;
+
+       /* if B0 rate (hangup) specified then negate DTR and RTS */
+       /* otherwise assert DTR and RTS */
+       if (cflag & CBAUD)
+               info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+       else
+               info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+
+       /* byte size and parity */
+
+       switch (cflag & CSIZE) {
+             case CS5: info->params.data_bits = 5; break;
+             case CS6: info->params.data_bits = 6; break;
+             case CS7: info->params.data_bits = 7; break;
+             case CS8: info->params.data_bits = 8; break;
+             /* Never happens, but GCC is too dumb to figure it out */
+             default:  info->params.data_bits = 7; break;
+             }
+
+       if (cflag & CSTOPB)
+               info->params.stop_bits = 2;
+       else
+               info->params.stop_bits = 1;
+
+       info->params.parity = ASYNC_PARITY_NONE;
+       if (cflag & PARENB) {
+               if (cflag & PARODD)
+                       info->params.parity = ASYNC_PARITY_ODD;
+               else
+                       info->params.parity = ASYNC_PARITY_EVEN;
+#ifdef CMSPAR
+               if (cflag & CMSPAR)
+                       info->params.parity = ASYNC_PARITY_SPACE;
+#endif
+       }
+
+       /* calculate number of jiffies to transmit a full
+        * FIFO (32 bytes) at specified data rate
+        */
+       bits_per_char = info->params.data_bits +
+                       info->params.stop_bits + 1;
+
+       /* if port data rate is set to 460800 or less then
+        * allow tty settings to override, otherwise keep the
+        * current data rate.
+        */
+       if (info->params.data_rate <= 460800) {
+               info->params.data_rate = tty_get_baud_rate(info->port.tty);
+       }
+
+       if ( info->params.data_rate ) {
+               info->timeout = (32*HZ*bits_per_char) /
+                               info->params.data_rate;
+       }
+       info->timeout += HZ/50;         /* Add .02 seconds of slop */
+
+       if (cflag & CRTSCTS)
+               info->port.flags |= ASYNC_CTS_FLOW;
+       else
+               info->port.flags &= ~ASYNC_CTS_FLOW;
+
+       if (cflag & CLOCAL)
+               info->port.flags &= ~ASYNC_CHECK_CD;
+       else
+               info->port.flags |= ASYNC_CHECK_CD;
+
+       /* process tty input control flags */
+
+       info->read_status_mask2 = OVRN;
+       if (I_INPCK(info->port.tty))
+               info->read_status_mask2 |= PE | FRME;
+       if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty))
+               info->read_status_mask1 |= BRKD;
+       if (I_IGNPAR(info->port.tty))
+               info->ignore_status_mask2 |= PE | FRME;
+       if (I_IGNBRK(info->port.tty)) {
+               info->ignore_status_mask1 |= BRKD;
+               /* If ignoring parity and break indicators, ignore
+                * overruns too.  (For real raw support).
+                */
+               if (I_IGNPAR(info->port.tty))
+                       info->ignore_status_mask2 |= OVRN;
+       }
+
+       program_hw(info);
+}
+
+static int get_stats(SLMP_INFO * info, struct mgsl_icount __user *user_icount)
+{
+       int err;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s get_params()\n",
+                        __FILE__,__LINE__, info->device_name);
+
+       if (!user_icount) {
+               memset(&info->icount, 0, sizeof(info->icount));
+       } else {
+               mutex_lock(&info->port.mutex);
+               COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
+               mutex_unlock(&info->port.mutex);
+               if (err)
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int get_params(SLMP_INFO * info, MGSL_PARAMS __user *user_params)
+{
+       int err;
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s get_params()\n",
+                        __FILE__,__LINE__, info->device_name);
+
+       mutex_lock(&info->port.mutex);
+       COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
+       mutex_unlock(&info->port.mutex);
+       if (err) {
+               if ( debug_level >= DEBUG_LEVEL_INFO )
+                       printk( "%s(%d):%s get_params() user buffer copy failed\n",
+                               __FILE__,__LINE__,info->device_name);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int set_params(SLMP_INFO * info, MGSL_PARAMS __user *new_params)
+{
+       unsigned long flags;
+       MGSL_PARAMS tmp_params;
+       int err;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s set_params\n",
+                       __FILE__,__LINE__,info->device_name );
+       COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS));
+       if (err) {
+               if ( debug_level >= DEBUG_LEVEL_INFO )
+                       printk( "%s(%d):%s set_params() user buffer copy failed\n",
+                               __FILE__,__LINE__,info->device_name);
+               return -EFAULT;
+       }
+
+       mutex_lock(&info->port.mutex);
+       spin_lock_irqsave(&info->lock,flags);
+       memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       change_params(info);
+       mutex_unlock(&info->port.mutex);
+
+       return 0;
+}
+
+static int get_txidle(SLMP_INFO * info, int __user *idle_mode)
+{
+       int err;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s get_txidle()=%d\n",
+                        __FILE__,__LINE__, info->device_name, info->idle_mode);
+
+       COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int));
+       if (err) {
+               if ( debug_level >= DEBUG_LEVEL_INFO )
+                       printk( "%s(%d):%s get_txidle() user buffer copy failed\n",
+                               __FILE__,__LINE__,info->device_name);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int set_txidle(SLMP_INFO * info, int idle_mode)
+{
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s set_txidle(%d)\n",
+                       __FILE__,__LINE__,info->device_name, idle_mode );
+
+       spin_lock_irqsave(&info->lock,flags);
+       info->idle_mode = idle_mode;
+       tx_set_idle( info );
+       spin_unlock_irqrestore(&info->lock,flags);
+       return 0;
+}
+
+static int tx_enable(SLMP_INFO * info, int enable)
+{
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s tx_enable(%d)\n",
+                       __FILE__,__LINE__,info->device_name, enable);
+
+       spin_lock_irqsave(&info->lock,flags);
+       if ( enable ) {
+               if ( !info->tx_enabled ) {
+                       tx_start(info);
+               }
+       } else {
+               if ( info->tx_enabled )
+                       tx_stop(info);
+       }
+       spin_unlock_irqrestore(&info->lock,flags);
+       return 0;
+}
+
+/* abort send HDLC frame
+ */
+static int tx_abort(SLMP_INFO * info)
+{
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s tx_abort()\n",
+                       __FILE__,__LINE__,info->device_name);
+
+       spin_lock_irqsave(&info->lock,flags);
+       if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC ) {
+               info->ie1_value &= ~UDRN;
+               info->ie1_value |= IDLE;
+               write_reg(info, IE1, info->ie1_value);  /* disable tx status interrupts */
+               write_reg(info, SR1, (unsigned char)(IDLE + UDRN));     /* clear pending */
+
+               write_reg(info, TXDMA + DSR, 0);                /* disable DMA channel */
+               write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */
+
+               write_reg(info, CMD, TXABORT);
+       }
+       spin_unlock_irqrestore(&info->lock,flags);
+       return 0;
+}
+
+static int rx_enable(SLMP_INFO * info, int enable)
+{
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s rx_enable(%d)\n",
+                       __FILE__,__LINE__,info->device_name,enable);
+
+       spin_lock_irqsave(&info->lock,flags);
+       if ( enable ) {
+               if ( !info->rx_enabled )
+                       rx_start(info);
+       } else {
+               if ( info->rx_enabled )
+                       rx_stop(info);
+       }
+       spin_unlock_irqrestore(&info->lock,flags);
+       return 0;
+}
+
+/* wait for specified event to occur
+ */
+static int wait_mgsl_event(SLMP_INFO * info, int __user *mask_ptr)
+{
+       unsigned long flags;
+       int s;
+       int rc=0;
+       struct mgsl_icount cprev, cnow;
+       int events;
+       int mask;
+       struct  _input_signal_events oldsigs, newsigs;
+       DECLARE_WAITQUEUE(wait, current);
+
+       COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int));
+       if (rc) {
+               return  -EFAULT;
+       }
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s wait_mgsl_event(%d)\n",
+                       __FILE__,__LINE__,info->device_name,mask);
+
+       spin_lock_irqsave(&info->lock,flags);
+
+       /* return immediately if state matches requested events */
+       get_signals(info);
+       s = info->serial_signals;
+
+       events = mask &
+               ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
+                 ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
+                 ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) +
+                 ((s & SerialSignal_RI)  ? MgslEvent_RiActive :MgslEvent_RiInactive) );
+       if (events) {
+               spin_unlock_irqrestore(&info->lock,flags);
+               goto exit;
+       }
+
+       /* save current irq counts */
+       cprev = info->icount;
+       oldsigs = info->input_signal_events;
+
+       /* enable hunt and idle irqs if needed */
+       if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) {
+               unsigned char oldval = info->ie1_value;
+               unsigned char newval = oldval +
+                        (mask & MgslEvent_ExitHuntMode ? FLGD:0) +
+                        (mask & MgslEvent_IdleReceived ? IDLD:0);
+               if ( oldval != newval ) {
+                       info->ie1_value = newval;
+                       write_reg(info, IE1, info->ie1_value);
+               }
+       }
+
+       set_current_state(TASK_INTERRUPTIBLE);
+       add_wait_queue(&info->event_wait_q, &wait);
+
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       for(;;) {
+               schedule();
+               if (signal_pending(current)) {
+                       rc = -ERESTARTSYS;
+                       break;
+               }
+
+               /* get current irq counts */
+               spin_lock_irqsave(&info->lock,flags);
+               cnow = info->icount;
+               newsigs = info->input_signal_events;
+               set_current_state(TASK_INTERRUPTIBLE);
+               spin_unlock_irqrestore(&info->lock,flags);
+
+               /* if no change, wait aborted for some reason */
+               if (newsigs.dsr_up   == oldsigs.dsr_up   &&
+                   newsigs.dsr_down == oldsigs.dsr_down &&
+                   newsigs.dcd_up   == oldsigs.dcd_up   &&
+                   newsigs.dcd_down == oldsigs.dcd_down &&
+                   newsigs.cts_up   == oldsigs.cts_up   &&
+                   newsigs.cts_down == oldsigs.cts_down &&
+                   newsigs.ri_up    == oldsigs.ri_up    &&
+                   newsigs.ri_down  == oldsigs.ri_down  &&
+                   cnow.exithunt    == cprev.exithunt   &&
+                   cnow.rxidle      == cprev.rxidle) {
+                       rc = -EIO;
+                       break;
+               }
+
+               events = mask &
+                       ( (newsigs.dsr_up   != oldsigs.dsr_up   ? MgslEvent_DsrActive:0)   +
+                         (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) +
+                         (newsigs.dcd_up   != oldsigs.dcd_up   ? MgslEvent_DcdActive:0)   +
+                         (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) +
+                         (newsigs.cts_up   != oldsigs.cts_up   ? MgslEvent_CtsActive:0)   +
+                         (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) +
+                         (newsigs.ri_up    != oldsigs.ri_up    ? MgslEvent_RiActive:0)    +
+                         (newsigs.ri_down  != oldsigs.ri_down  ? MgslEvent_RiInactive:0)  +
+                         (cnow.exithunt    != cprev.exithunt   ? MgslEvent_ExitHuntMode:0) +
+                         (cnow.rxidle      != cprev.rxidle     ? MgslEvent_IdleReceived:0) );
+               if (events)
+                       break;
+
+               cprev = cnow;
+               oldsigs = newsigs;
+       }
+
+       remove_wait_queue(&info->event_wait_q, &wait);
+       set_current_state(TASK_RUNNING);
+
+
+       if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
+               spin_lock_irqsave(&info->lock,flags);
+               if (!waitqueue_active(&info->event_wait_q)) {
+                       /* disable enable exit hunt mode/idle rcvd IRQs */
+                       info->ie1_value &= ~(FLGD|IDLD);
+                       write_reg(info, IE1, info->ie1_value);
+               }
+               spin_unlock_irqrestore(&info->lock,flags);
+       }
+exit:
+       if ( rc == 0 )
+               PUT_USER(rc, events, mask_ptr);
+
+       return rc;
+}
+
+static int modem_input_wait(SLMP_INFO *info,int arg)
+{
+       unsigned long flags;
+       int rc;
+       struct mgsl_icount cprev, cnow;
+       DECLARE_WAITQUEUE(wait, current);
+
+       /* save current irq counts */
+       spin_lock_irqsave(&info->lock,flags);
+       cprev = info->icount;
+       add_wait_queue(&info->status_event_wait_q, &wait);
+       set_current_state(TASK_INTERRUPTIBLE);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       for(;;) {
+               schedule();
+               if (signal_pending(current)) {
+                       rc = -ERESTARTSYS;
+                       break;
+               }
+
+               /* get new irq counts */
+               spin_lock_irqsave(&info->lock,flags);
+               cnow = info->icount;
+               set_current_state(TASK_INTERRUPTIBLE);
+               spin_unlock_irqrestore(&info->lock,flags);
+
+               /* if no change, wait aborted for some reason */
+               if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+                   cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
+                       rc = -EIO;
+                       break;
+               }
+
+               /* check for change in caller specified modem input */
+               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)) {
+                       rc = 0;
+                       break;
+               }
+
+               cprev = cnow;
+       }
+       remove_wait_queue(&info->status_event_wait_q, &wait);
+       set_current_state(TASK_RUNNING);
+       return rc;
+}
+
+/* return the state of the serial control and status signals
+ */
+static int tiocmget(struct tty_struct *tty)
+{
+       SLMP_INFO *info = tty->driver_data;
+       unsigned int result;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->lock,flags);
+       get_signals(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) +
+               ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) +
+               ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) +
+               ((info->serial_signals & SerialSignal_RI)  ? TIOCM_RNG:0) +
+               ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) +
+               ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0);
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s tiocmget() value=%08X\n",
+                        __FILE__,__LINE__, info->device_name, result );
+       return result;
+}
+
+/* set modem control signals (DTR/RTS)
+ */
+static int tiocmset(struct tty_struct *tty,
+                                       unsigned int set, unsigned int clear)
+{
+       SLMP_INFO *info = tty->driver_data;
+       unsigned long flags;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s tiocmset(%x,%x)\n",
+                       __FILE__,__LINE__,info->device_name, set, clear);
+
+       if (set & TIOCM_RTS)
+               info->serial_signals |= SerialSignal_RTS;
+       if (set & TIOCM_DTR)
+               info->serial_signals |= SerialSignal_DTR;
+       if (clear & TIOCM_RTS)
+               info->serial_signals &= ~SerialSignal_RTS;
+       if (clear & TIOCM_DTR)
+               info->serial_signals &= ~SerialSignal_DTR;
+
+       spin_lock_irqsave(&info->lock,flags);
+       set_signals(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       return 0;
+}
+
+static int carrier_raised(struct tty_port *port)
+{
+       SLMP_INFO *info = container_of(port, SLMP_INFO, port);
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->lock,flags);
+       get_signals(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       return (info->serial_signals & SerialSignal_DCD) ? 1 : 0;
+}
+
+static void dtr_rts(struct tty_port *port, int on)
+{
+       SLMP_INFO *info = container_of(port, SLMP_INFO, port);
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->lock,flags);
+       if (on)
+               info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+       else
+               info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+       set_signals(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* Block the current process until the specified port is ready to open.
+ */
+static int block_til_ready(struct tty_struct *tty, struct file *filp,
+                          SLMP_INFO *info)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       int             retval;
+       bool            do_clocal = false;
+       bool            extra_count = false;
+       unsigned long   flags;
+       int             cd;
+       struct tty_port *port = &info->port;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s block_til_ready()\n",
+                        __FILE__,__LINE__, tty->driver->name );
+
+       if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){
+               /* nonblock mode is set or port is not enabled */
+               /* just verify that callout device is not active */
+               port->flags |= ASYNC_NORMAL_ACTIVE;
+               return 0;
+       }
+
+       if (tty->termios->c_cflag & CLOCAL)
+               do_clocal = true;
+
+       /* Wait for carrier detect and the line to become
+        * free (i.e., not in use by the callout).  While we are in
+        * this loop, port->count is dropped by one, so that
+        * close() knows when to free things.  We restore it upon
+        * exit, either normal or abnormal.
+        */
+
+       retval = 0;
+       add_wait_queue(&port->open_wait, &wait);
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s block_til_ready() before block, count=%d\n",
+                        __FILE__,__LINE__, tty->driver->name, port->count );
+
+       spin_lock_irqsave(&info->lock, flags);
+       if (!tty_hung_up_p(filp)) {
+               extra_count = true;
+               port->count--;
+       }
+       spin_unlock_irqrestore(&info->lock, flags);
+       port->blocked_open++;
+
+       while (1) {
+               if (tty->termios->c_cflag & CBAUD)
+                       tty_port_raise_dtr_rts(port);
+
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)){
+                       retval = (port->flags & ASYNC_HUP_NOTIFY) ?
+                                       -EAGAIN : -ERESTARTSYS;
+                       break;
+               }
+
+               cd = tty_port_carrier_raised(port);
+
+               if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd))
+                       break;
+
+               if (signal_pending(current)) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+
+               if (debug_level >= DEBUG_LEVEL_INFO)
+                       printk("%s(%d):%s block_til_ready() count=%d\n",
+                                __FILE__,__LINE__, tty->driver->name, port->count );
+
+               tty_unlock();
+               schedule();
+               tty_lock();
+       }
+
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&port->open_wait, &wait);
+
+       if (extra_count)
+               port->count++;
+       port->blocked_open--;
+
+       if (debug_level >= DEBUG_LEVEL_INFO)
+               printk("%s(%d):%s block_til_ready() after, count=%d\n",
+                        __FILE__,__LINE__, tty->driver->name, port->count );
+
+       if (!retval)
+               port->flags |= ASYNC_NORMAL_ACTIVE;
+
+       return retval;
+}
+
+static int alloc_dma_bufs(SLMP_INFO *info)
+{
+       unsigned short BuffersPerFrame;
+       unsigned short BufferCount;
+
+       // Force allocation to start at 64K boundary for each port.
+       // This is necessary because *all* buffer descriptors for a port
+       // *must* be in the same 64K block. All descriptors on a port
+       // share a common 'base' address (upper 8 bits of 24 bits) programmed
+       // into the CBP register.
+       info->port_array[0]->last_mem_alloc = (SCA_MEM_SIZE/4) * info->port_num;
+
+       /* Calculate the number of DMA buffers necessary to hold the */
+       /* largest allowable frame size. Note: If the max frame size is */
+       /* not an even multiple of the DMA buffer size then we need to */
+       /* round the buffer count per frame up one. */
+
+       BuffersPerFrame = (unsigned short)(info->max_frame_size/SCABUFSIZE);
+       if ( info->max_frame_size % SCABUFSIZE )
+               BuffersPerFrame++;
+
+       /* calculate total number of data buffers (SCABUFSIZE) possible
+        * in one ports memory (SCA_MEM_SIZE/4) after allocating memory
+        * for the descriptor list (BUFFERLISTSIZE).
+        */
+       BufferCount = (SCA_MEM_SIZE/4 - BUFFERLISTSIZE)/SCABUFSIZE;
+
+       /* limit number of buffers to maximum amount of descriptors */
+       if (BufferCount > BUFFERLISTSIZE/sizeof(SCADESC))
+               BufferCount = BUFFERLISTSIZE/sizeof(SCADESC);
+
+       /* use enough buffers to transmit one max size frame */
+       info->tx_buf_count = BuffersPerFrame + 1;
+
+       /* never use more than half the available buffers for transmit */
+       if (info->tx_buf_count > (BufferCount/2))
+               info->tx_buf_count = BufferCount/2;
+
+       if (info->tx_buf_count > SCAMAXDESC)
+               info->tx_buf_count = SCAMAXDESC;
+
+       /* use remaining buffers for receive */
+       info->rx_buf_count = BufferCount - info->tx_buf_count;
+
+       if (info->rx_buf_count > SCAMAXDESC)
+               info->rx_buf_count = SCAMAXDESC;
+
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk("%s(%d):%s Allocating %d TX and %d RX DMA buffers.\n",
+                       __FILE__,__LINE__, info->device_name,
+                       info->tx_buf_count,info->rx_buf_count);
+
+       if ( alloc_buf_list( info ) < 0 ||
+               alloc_frame_bufs(info,
+                                       info->rx_buf_list,
+                                       info->rx_buf_list_ex,
+                                       info->rx_buf_count) < 0 ||
+               alloc_frame_bufs(info,
+                                       info->tx_buf_list,
+                                       info->tx_buf_list_ex,
+                                       info->tx_buf_count) < 0 ||
+               alloc_tmp_rx_buf(info) < 0 ) {
+               printk("%s(%d):%s Can't allocate DMA buffer memory\n",
+                       __FILE__,__LINE__, info->device_name);
+               return -ENOMEM;
+       }
+
+       rx_reset_buffers( info );
+
+       return 0;
+}
+
+/* Allocate DMA buffers for the transmit and receive descriptor lists.
+ */
+static int alloc_buf_list(SLMP_INFO *info)
+{
+       unsigned int i;
+
+       /* build list in adapter shared memory */
+       info->buffer_list = info->memory_base + info->port_array[0]->last_mem_alloc;
+       info->buffer_list_phys = info->port_array[0]->last_mem_alloc;
+       info->port_array[0]->last_mem_alloc += BUFFERLISTSIZE;
+
+       memset(info->buffer_list, 0, BUFFERLISTSIZE);
+
+       /* Save virtual address pointers to the receive and */
+       /* transmit buffer lists. (Receive 1st). These pointers will */
+       /* be used by the processor to access the lists. */
+       info->rx_buf_list = (SCADESC *)info->buffer_list;
+
+       info->tx_buf_list = (SCADESC *)info->buffer_list;
+       info->tx_buf_list += info->rx_buf_count;
+
+       /* Build links for circular buffer entry lists (tx and rx)
+        *
+        * Note: links are physical addresses read by the SCA device
+        * to determine the next buffer entry to use.
+        */
+
+       for ( i = 0; i < info->rx_buf_count; i++ ) {
+               /* calculate and store physical address of this buffer entry */
+               info->rx_buf_list_ex[i].phys_entry =
+                       info->buffer_list_phys + (i * sizeof(SCABUFSIZE));
+
+               /* calculate and store physical address of */
+               /* next entry in cirular list of entries */
+               info->rx_buf_list[i].next = info->buffer_list_phys;
+               if ( i < info->rx_buf_count - 1 )
+                       info->rx_buf_list[i].next += (i + 1) * sizeof(SCADESC);
+
+               info->rx_buf_list[i].length = SCABUFSIZE;
+       }
+
+       for ( i = 0; i < info->tx_buf_count; i++ ) {
+               /* calculate and store physical address of this buffer entry */
+               info->tx_buf_list_ex[i].phys_entry = info->buffer_list_phys +
+                       ((info->rx_buf_count + i) * sizeof(SCADESC));
+
+               /* calculate and store physical address of */
+               /* next entry in cirular list of entries */
+
+               info->tx_buf_list[i].next = info->buffer_list_phys +
+                       info->rx_buf_count * sizeof(SCADESC);
+
+               if ( i < info->tx_buf_count - 1 )
+                       info->tx_buf_list[i].next += (i + 1) * sizeof(SCADESC);
+       }
+
+       return 0;
+}
+
+/* Allocate the frame DMA buffers used by the specified buffer list.
+ */
+static int alloc_frame_bufs(SLMP_INFO *info, SCADESC *buf_list,SCADESC_EX *buf_list_ex,int count)
+{
+       int i;
+       unsigned long phys_addr;
+
+       for ( i = 0; i < count; i++ ) {
+               buf_list_ex[i].virt_addr = info->memory_base + info->port_array[0]->last_mem_alloc;
+               phys_addr = info->port_array[0]->last_mem_alloc;
+               info->port_array[0]->last_mem_alloc += SCABUFSIZE;
+
+               buf_list[i].buf_ptr  = (unsigned short)phys_addr;
+               buf_list[i].buf_base = (unsigned char)(phys_addr >> 16);
+       }
+
+       return 0;
+}
+
+static void free_dma_bufs(SLMP_INFO *info)
+{
+       info->buffer_list = NULL;
+       info->rx_buf_list = NULL;
+       info->tx_buf_list = NULL;
+}
+
+/* allocate buffer large enough to hold max_frame_size.
+ * This buffer is used to pass an assembled frame to the line discipline.
+ */
+static int alloc_tmp_rx_buf(SLMP_INFO *info)
+{
+       info->tmp_rx_buf = kmalloc(info->max_frame_size, GFP_KERNEL);
+       if (info->tmp_rx_buf == NULL)
+               return -ENOMEM;
+       return 0;
+}
+
+static void free_tmp_rx_buf(SLMP_INFO *info)
+{
+       kfree(info->tmp_rx_buf);
+       info->tmp_rx_buf = NULL;
+}
+
+static int claim_resources(SLMP_INFO *info)
+{
+       if (request_mem_region(info->phys_memory_base,SCA_MEM_SIZE,"synclinkmp") == NULL) {
+               printk( "%s(%d):%s mem addr conflict, Addr=%08X\n",
+                       __FILE__,__LINE__,info->device_name, info->phys_memory_base);
+               info->init_error = DiagStatus_AddressConflict;
+               goto errout;
+       }
+       else
+               info->shared_mem_requested = true;
+
+       if (request_mem_region(info->phys_lcr_base + info->lcr_offset,128,"synclinkmp") == NULL) {
+               printk( "%s(%d):%s lcr mem addr conflict, Addr=%08X\n",
+                       __FILE__,__LINE__,info->device_name, info->phys_lcr_base);
+               info->init_error = DiagStatus_AddressConflict;
+               goto errout;
+       }
+       else
+               info->lcr_mem_requested = true;
+
+       if (request_mem_region(info->phys_sca_base + info->sca_offset,SCA_BASE_SIZE,"synclinkmp") == NULL) {
+               printk( "%s(%d):%s sca mem addr conflict, Addr=%08X\n",
+                       __FILE__,__LINE__,info->device_name, info->phys_sca_base);
+               info->init_error = DiagStatus_AddressConflict;
+               goto errout;
+       }
+       else
+               info->sca_base_requested = true;
+
+       if (request_mem_region(info->phys_statctrl_base + info->statctrl_offset,SCA_REG_SIZE,"synclinkmp") == NULL) {
+               printk( "%s(%d):%s stat/ctrl mem addr conflict, Addr=%08X\n",
+                       __FILE__,__LINE__,info->device_name, info->phys_statctrl_base);
+               info->init_error = DiagStatus_AddressConflict;
+               goto errout;
+       }
+       else
+               info->sca_statctrl_requested = true;
+
+       info->memory_base = ioremap_nocache(info->phys_memory_base,
+                                                               SCA_MEM_SIZE);
+       if (!info->memory_base) {
+               printk( "%s(%d):%s Cant map shared memory, MemAddr=%08X\n",
+                       __FILE__,__LINE__,info->device_name, info->phys_memory_base );
+               info->init_error = DiagStatus_CantAssignPciResources;
+               goto errout;
+       }
+
+       info->lcr_base = ioremap_nocache(info->phys_lcr_base, PAGE_SIZE);
+       if (!info->lcr_base) {
+               printk( "%s(%d):%s Cant map LCR memory, MemAddr=%08X\n",
+                       __FILE__,__LINE__,info->device_name, info->phys_lcr_base );
+               info->init_error = DiagStatus_CantAssignPciResources;
+               goto errout;
+       }
+       info->lcr_base += info->lcr_offset;
+
+       info->sca_base = ioremap_nocache(info->phys_sca_base, PAGE_SIZE);
+       if (!info->sca_base) {
+               printk( "%s(%d):%s Cant map SCA memory, MemAddr=%08X\n",
+                       __FILE__,__LINE__,info->device_name, info->phys_sca_base );
+               info->init_error = DiagStatus_CantAssignPciResources;
+               goto errout;
+       }
+       info->sca_base += info->sca_offset;
+
+       info->statctrl_base = ioremap_nocache(info->phys_statctrl_base,
+                                                               PAGE_SIZE);
+       if (!info->statctrl_base) {
+               printk( "%s(%d):%s Cant map SCA Status/Control memory, MemAddr=%08X\n",
+                       __FILE__,__LINE__,info->device_name, info->phys_statctrl_base );
+               info->init_error = DiagStatus_CantAssignPciResources;
+               goto errout;
+       }
+       info->statctrl_base += info->statctrl_offset;
+
+       if ( !memory_test(info) ) {
+               printk( "%s(%d):Shared Memory Test failed for device %s MemAddr=%08X\n",
+                       __FILE__,__LINE__,info->device_name, info->phys_memory_base );
+               info->init_error = DiagStatus_MemoryError;
+               goto errout;
+       }
+
+       return 0;
+
+errout:
+       release_resources( info );
+       return -ENODEV;
+}
+
+static void release_resources(SLMP_INFO *info)
+{
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):%s release_resources() entry\n",
+                       __FILE__,__LINE__,info->device_name );
+
+       if ( info->irq_requested ) {
+               free_irq(info->irq_level, info);
+               info->irq_requested = false;
+       }
+
+       if ( info->shared_mem_requested ) {
+               release_mem_region(info->phys_memory_base,SCA_MEM_SIZE);
+               info->shared_mem_requested = false;
+       }
+       if ( info->lcr_mem_requested ) {
+               release_mem_region(info->phys_lcr_base + info->lcr_offset,128);
+               info->lcr_mem_requested = false;
+       }
+       if ( info->sca_base_requested ) {
+               release_mem_region(info->phys_sca_base + info->sca_offset,SCA_BASE_SIZE);
+               info->sca_base_requested = false;
+       }
+       if ( info->sca_statctrl_requested ) {
+               release_mem_region(info->phys_statctrl_base + info->statctrl_offset,SCA_REG_SIZE);
+               info->sca_statctrl_requested = false;
+       }
+
+       if (info->memory_base){
+               iounmap(info->memory_base);
+               info->memory_base = NULL;
+       }
+
+       if (info->sca_base) {
+               iounmap(info->sca_base - info->sca_offset);
+               info->sca_base=NULL;
+       }
+
+       if (info->statctrl_base) {
+               iounmap(info->statctrl_base - info->statctrl_offset);
+               info->statctrl_base=NULL;
+       }
+
+       if (info->lcr_base){
+               iounmap(info->lcr_base - info->lcr_offset);
+               info->lcr_base = NULL;
+       }
+
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):%s release_resources() exit\n",
+                       __FILE__,__LINE__,info->device_name );
+}
+
+/* Add the specified device instance data structure to the
+ * global linked list of devices and increment the device count.
+ */
+static void add_device(SLMP_INFO *info)
+{
+       info->next_device = NULL;
+       info->line = synclinkmp_device_count;
+       sprintf(info->device_name,"ttySLM%dp%d",info->adapter_num,info->port_num);
+
+       if (info->line < MAX_DEVICES) {
+               if (maxframe[info->line])
+                       info->max_frame_size = maxframe[info->line];
+       }
+
+       synclinkmp_device_count++;
+
+       if ( !synclinkmp_device_list )
+               synclinkmp_device_list = info;
+       else {
+               SLMP_INFO *current_dev = synclinkmp_device_list;
+               while( current_dev->next_device )
+                       current_dev = current_dev->next_device;
+               current_dev->next_device = info;
+       }
+
+       if ( info->max_frame_size < 4096 )
+               info->max_frame_size = 4096;
+       else if ( info->max_frame_size > 65535 )
+               info->max_frame_size = 65535;
+
+       printk( "SyncLink MultiPort %s: "
+               "Mem=(%08x %08X %08x %08X) IRQ=%d MaxFrameSize=%u\n",
+               info->device_name,
+               info->phys_sca_base,
+               info->phys_memory_base,
+               info->phys_statctrl_base,
+               info->phys_lcr_base,
+               info->irq_level,
+               info->max_frame_size );
+
+#if SYNCLINK_GENERIC_HDLC
+       hdlcdev_init(info);
+#endif
+}
+
+static const struct tty_port_operations port_ops = {
+       .carrier_raised = carrier_raised,
+       .dtr_rts = dtr_rts,
+};
+
+/* Allocate and initialize a device instance structure
+ *
+ * Return Value:       pointer to SLMP_INFO if success, otherwise NULL
+ */
+static SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev)
+{
+       SLMP_INFO *info;
+
+       info = kzalloc(sizeof(SLMP_INFO),
+                GFP_KERNEL);
+
+       if (!info) {
+               printk("%s(%d) Error can't allocate device instance data for adapter %d, port %d\n",
+                       __FILE__,__LINE__, adapter_num, port_num);
+       } else {
+               tty_port_init(&info->port);
+               info->port.ops = &port_ops;
+               info->magic = MGSL_MAGIC;
+               INIT_WORK(&info->task, bh_handler);
+               info->max_frame_size = 4096;
+               info->port.close_delay = 5*HZ/10;
+               info->port.closing_wait = 30*HZ;
+               init_waitqueue_head(&info->status_event_wait_q);
+               init_waitqueue_head(&info->event_wait_q);
+               spin_lock_init(&info->netlock);
+               memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
+               info->idle_mode = HDLC_TXIDLE_FLAGS;
+               info->adapter_num = adapter_num;
+               info->port_num = port_num;
+
+               /* Copy configuration info to device instance data */
+               info->irq_level = pdev->irq;
+               info->phys_lcr_base = pci_resource_start(pdev,0);
+               info->phys_sca_base = pci_resource_start(pdev,2);
+               info->phys_memory_base = pci_resource_start(pdev,3);
+               info->phys_statctrl_base = pci_resource_start(pdev,4);
+
+               /* Because veremap only works on page boundaries we must map
+                * a larger area than is actually implemented for the LCR
+                * memory range. We map a full page starting at the page boundary.
+                */
+               info->lcr_offset    = info->phys_lcr_base & (PAGE_SIZE-1);
+               info->phys_lcr_base &= ~(PAGE_SIZE-1);
+
+               info->sca_offset    = info->phys_sca_base & (PAGE_SIZE-1);
+               info->phys_sca_base &= ~(PAGE_SIZE-1);
+
+               info->statctrl_offset    = info->phys_statctrl_base & (PAGE_SIZE-1);
+               info->phys_statctrl_base &= ~(PAGE_SIZE-1);
+
+               info->bus_type = MGSL_BUS_TYPE_PCI;
+               info->irq_flags = IRQF_SHARED;
+
+               setup_timer(&info->tx_timer, tx_timeout, (unsigned long)info);
+               setup_timer(&info->status_timer, status_timeout,
+                               (unsigned long)info);
+
+               /* Store the PCI9050 misc control register value because a flaw
+                * in the PCI9050 prevents LCR registers from being read if
+                * BIOS assigns an LCR base address with bit 7 set.
+                *
+                * Only the misc control register is accessed for which only
+                * write access is needed, so set an initial value and change
+                * bits to the device instance data as we write the value
+                * to the actual misc control register.
+                */
+               info->misc_ctrl_value = 0x087e4546;
+
+               /* initial port state is unknown - if startup errors
+                * occur, init_error will be set to indicate the
+                * problem. Once the port is fully initialized,
+                * this value will be set to 0 to indicate the
+                * port is available.
+                */
+               info->init_error = -1;
+       }
+
+       return info;
+}
+
+static void device_init(int adapter_num, struct pci_dev *pdev)
+{
+       SLMP_INFO *port_array[SCA_MAX_PORTS];
+       int port;
+
+       /* allocate device instances for up to SCA_MAX_PORTS devices */
+       for ( port = 0; port < SCA_MAX_PORTS; ++port ) {
+               port_array[port] = alloc_dev(adapter_num,port,pdev);
+               if( port_array[port] == NULL ) {
+                       for ( --port; port >= 0; --port )
+                               kfree(port_array[port]);
+                       return;
+               }
+       }
+
+       /* give copy of port_array to all ports and add to device list  */
+       for ( port = 0; port < SCA_MAX_PORTS; ++port ) {
+               memcpy(port_array[port]->port_array,port_array,sizeof(port_array));
+               add_device( port_array[port] );
+               spin_lock_init(&port_array[port]->lock);
+       }
+
+       /* Allocate and claim adapter resources */
+       if ( !claim_resources(port_array[0]) ) {
+
+               alloc_dma_bufs(port_array[0]);
+
+               /* copy resource information from first port to others */
+               for ( port = 1; port < SCA_MAX_PORTS; ++port ) {
+                       port_array[port]->lock  = port_array[0]->lock;
+                       port_array[port]->irq_level     = port_array[0]->irq_level;
+                       port_array[port]->memory_base   = port_array[0]->memory_base;
+                       port_array[port]->sca_base      = port_array[0]->sca_base;
+                       port_array[port]->statctrl_base = port_array[0]->statctrl_base;
+                       port_array[port]->lcr_base      = port_array[0]->lcr_base;
+                       alloc_dma_bufs(port_array[port]);
+               }
+
+               if ( request_irq(port_array[0]->irq_level,
+                                       synclinkmp_interrupt,
+                                       port_array[0]->irq_flags,
+                                       port_array[0]->device_name,
+                                       port_array[0]) < 0 ) {
+                       printk( "%s(%d):%s Cant request interrupt, IRQ=%d\n",
+                               __FILE__,__LINE__,
+                               port_array[0]->device_name,
+                               port_array[0]->irq_level );
+               }
+               else {
+                       port_array[0]->irq_requested = true;
+                       adapter_test(port_array[0]);
+               }
+       }
+}
+
+static const struct tty_operations ops = {
+       .open = open,
+       .close = close,
+       .write = write,
+       .put_char = put_char,
+       .flush_chars = flush_chars,
+       .write_room = write_room,
+       .chars_in_buffer = chars_in_buffer,
+       .flush_buffer = flush_buffer,
+       .ioctl = ioctl,
+       .throttle = throttle,
+       .unthrottle = unthrottle,
+       .send_xchar = send_xchar,
+       .break_ctl = set_break,
+       .wait_until_sent = wait_until_sent,
+       .set_termios = set_termios,
+       .stop = tx_hold,
+       .start = tx_release,
+       .hangup = hangup,
+       .tiocmget = tiocmget,
+       .tiocmset = tiocmset,
+       .get_icount = get_icount,
+       .proc_fops = &synclinkmp_proc_fops,
+};
+
+
+static void synclinkmp_cleanup(void)
+{
+       int rc;
+       SLMP_INFO *info;
+       SLMP_INFO *tmp;
+
+       printk("Unloading %s %s\n", driver_name, driver_version);
+
+       if (serial_driver) {
+               if ((rc = tty_unregister_driver(serial_driver)))
+                       printk("%s(%d) failed to unregister tty driver err=%d\n",
+                              __FILE__,__LINE__,rc);
+               put_tty_driver(serial_driver);
+       }
+
+       /* reset devices */
+       info = synclinkmp_device_list;
+       while(info) {
+               reset_port(info);
+               info = info->next_device;
+       }
+
+       /* release devices */
+       info = synclinkmp_device_list;
+       while(info) {
+#if SYNCLINK_GENERIC_HDLC
+               hdlcdev_exit(info);
+#endif
+               free_dma_bufs(info);
+               free_tmp_rx_buf(info);
+               if ( info->port_num == 0 ) {
+                       if (info->sca_base)
+                               write_reg(info, LPR, 1); /* set low power mode */
+                       release_resources(info);
+               }
+               tmp = info;
+               info = info->next_device;
+               kfree(tmp);
+       }
+
+       pci_unregister_driver(&synclinkmp_pci_driver);
+}
+
+/* Driver initialization entry point.
+ */
+
+static int __init synclinkmp_init(void)
+{
+       int rc;
+
+       if (break_on_load) {
+               synclinkmp_get_text_ptr();
+               BREAKPOINT();
+       }
+
+       printk("%s %s\n", driver_name, driver_version);
+
+       if ((rc = pci_register_driver(&synclinkmp_pci_driver)) < 0) {
+               printk("%s:failed to register PCI driver, error=%d\n",__FILE__,rc);
+               return rc;
+       }
+
+       serial_driver = alloc_tty_driver(128);
+       if (!serial_driver) {
+               rc = -ENOMEM;
+               goto error;
+       }
+
+       /* Initialize the tty_driver structure */
+
+       serial_driver->owner = THIS_MODULE;
+       serial_driver->driver_name = "synclinkmp";
+       serial_driver->name = "ttySLM";
+       serial_driver->major = ttymajor;
+       serial_driver->minor_start = 64;
+       serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
+       serial_driver->subtype = SERIAL_TYPE_NORMAL;
+       serial_driver->init_termios = tty_std_termios;
+       serial_driver->init_termios.c_cflag =
+               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       serial_driver->init_termios.c_ispeed = 9600;
+       serial_driver->init_termios.c_ospeed = 9600;
+       serial_driver->flags = TTY_DRIVER_REAL_RAW;
+       tty_set_operations(serial_driver, &ops);
+       if ((rc = tty_register_driver(serial_driver)) < 0) {
+               printk("%s(%d):Couldn't register serial driver\n",
+                       __FILE__,__LINE__);
+               put_tty_driver(serial_driver);
+               serial_driver = NULL;
+               goto error;
+       }
+
+       printk("%s %s, tty major#%d\n",
+               driver_name, driver_version,
+               serial_driver->major);
+
+       return 0;
+
+error:
+       synclinkmp_cleanup();
+       return rc;
+}
+
+static void __exit synclinkmp_exit(void)
+{
+       synclinkmp_cleanup();
+}
+
+module_init(synclinkmp_init);
+module_exit(synclinkmp_exit);
+
+/* Set the port for internal loopback mode.
+ * The TxCLK and RxCLK signals are generated from the BRG and
+ * the TxD is looped back to the RxD internally.
+ */
+static void enable_loopback(SLMP_INFO *info, int enable)
+{
+       if (enable) {
+               /* MD2 (Mode Register 2)
+                * 01..00  CNCT<1..0> Channel Connection 11=Local Loopback
+                */
+               write_reg(info, MD2, (unsigned char)(read_reg(info, MD2) | (BIT1 + BIT0)));
+
+               /* degate external TxC clock source */
+               info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2));
+               write_control_reg(info);
+
+               /* RXS/TXS (Rx/Tx clock source)
+                * 07      Reserved, must be 0
+                * 06..04  Clock Source, 100=BRG
+                * 03..00  Clock Divisor, 0000=1
+                */
+               write_reg(info, RXS, 0x40);
+               write_reg(info, TXS, 0x40);
+
+       } else {
+               /* MD2 (Mode Register 2)
+                * 01..00  CNCT<1..0> Channel connection, 0=normal
+                */
+               write_reg(info, MD2, (unsigned char)(read_reg(info, MD2) & ~(BIT1 + BIT0)));
+
+               /* RXS/TXS (Rx/Tx clock source)
+                * 07      Reserved, must be 0
+                * 06..04  Clock Source, 000=RxC/TxC Pin
+                * 03..00  Clock Divisor, 0000=1
+                */
+               write_reg(info, RXS, 0x00);
+               write_reg(info, TXS, 0x00);
+       }
+
+       /* set LinkSpeed if available, otherwise default to 2Mbps */
+       if (info->params.clock_speed)
+               set_rate(info, info->params.clock_speed);
+       else
+               set_rate(info, 3686400);
+}
+
+/* Set the baud rate register to the desired speed
+ *
+ *     data_rate       data rate of clock in bits per second
+ *                     A data rate of 0 disables the AUX clock.
+ */
+static void set_rate( SLMP_INFO *info, u32 data_rate )
+{
+               u32 TMCValue;
+               unsigned char BRValue;
+       u32 Divisor=0;
+
+       /* fBRG = fCLK/(TMC * 2^BR)
+        */
+       if (data_rate != 0) {
+               Divisor = 14745600/data_rate;
+               if (!Divisor)
+                       Divisor = 1;
+
+               TMCValue = Divisor;
+
+               BRValue = 0;
+               if (TMCValue != 1 && TMCValue != 2) {
+                       /* BRValue of 0 provides 50/50 duty cycle *only* when
+                        * TMCValue is 1 or 2. BRValue of 1 to 9 always provides
+                        * 50/50 duty cycle.
+                        */
+                       BRValue = 1;
+                       TMCValue >>= 1;
+               }
+
+               /* while TMCValue is too big for TMC register, divide
+                * by 2 and increment BR exponent.
+                */
+               for(; TMCValue > 256 && BRValue < 10; BRValue++)
+                       TMCValue >>= 1;
+
+               write_reg(info, TXS,
+                       (unsigned char)((read_reg(info, TXS) & 0xf0) | BRValue));
+               write_reg(info, RXS,
+                       (unsigned char)((read_reg(info, RXS) & 0xf0) | BRValue));
+               write_reg(info, TMC, (unsigned char)TMCValue);
+       }
+       else {
+               write_reg(info, TXS,0);
+               write_reg(info, RXS,0);
+               write_reg(info, TMC, 0);
+       }
+}
+
+/* Disable receiver
+ */
+static void rx_stop(SLMP_INFO *info)
+{
+       if (debug_level >= DEBUG_LEVEL_ISR)
+               printk("%s(%d):%s rx_stop()\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       write_reg(info, CMD, RXRESET);
+
+       info->ie0_value &= ~RXRDYE;
+       write_reg(info, IE0, info->ie0_value);  /* disable Rx data interrupts */
+
+       write_reg(info, RXDMA + DSR, 0);        /* disable Rx DMA */
+       write_reg(info, RXDMA + DCMD, SWABORT); /* reset/init Rx DMA */
+       write_reg(info, RXDMA + DIR, 0);        /* disable Rx DMA interrupts */
+
+       info->rx_enabled = false;
+       info->rx_overflow = false;
+}
+
+/* enable the receiver
+ */
+static void rx_start(SLMP_INFO *info)
+{
+       int i;
+
+       if (debug_level >= DEBUG_LEVEL_ISR)
+               printk("%s(%d):%s rx_start()\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       write_reg(info, CMD, RXRESET);
+
+       if ( info->params.mode == MGSL_MODE_HDLC ) {
+               /* HDLC, disabe IRQ on rxdata */
+               info->ie0_value &= ~RXRDYE;
+               write_reg(info, IE0, info->ie0_value);
+
+               /* Reset all Rx DMA buffers and program rx dma */
+               write_reg(info, RXDMA + DSR, 0);                /* disable Rx DMA */
+               write_reg(info, RXDMA + DCMD, SWABORT); /* reset/init Rx DMA */
+
+               for (i = 0; i < info->rx_buf_count; i++) {
+                       info->rx_buf_list[i].status = 0xff;
+
+                       // throttle to 4 shared memory writes at a time to prevent
+                       // hogging local bus (keep latency time for DMA requests low).
+                       if (!(i % 4))
+                               read_status_reg(info);
+               }
+               info->current_rx_buf = 0;
+
+               /* set current/1st descriptor address */
+               write_reg16(info, RXDMA + CDA,
+                       info->rx_buf_list_ex[0].phys_entry);
+
+               /* set new last rx descriptor address */
+               write_reg16(info, RXDMA + EDA,
+                       info->rx_buf_list_ex[info->rx_buf_count - 1].phys_entry);
+
+               /* set buffer length (shared by all rx dma data buffers) */
+               write_reg16(info, RXDMA + BFL, SCABUFSIZE);
+
+               write_reg(info, RXDMA + DIR, 0x60);     /* enable Rx DMA interrupts (EOM/BOF) */
+               write_reg(info, RXDMA + DSR, 0xf2);     /* clear Rx DMA IRQs, enable Rx DMA */
+       } else {
+               /* async, enable IRQ on rxdata */
+               info->ie0_value |= RXRDYE;
+               write_reg(info, IE0, info->ie0_value);
+       }
+
+       write_reg(info, CMD, RXENABLE);
+
+       info->rx_overflow = false;
+       info->rx_enabled = true;
+}
+
+/* Enable the transmitter and send a transmit frame if
+ * one is loaded in the DMA buffers.
+ */
+static void tx_start(SLMP_INFO *info)
+{
+       if (debug_level >= DEBUG_LEVEL_ISR)
+               printk("%s(%d):%s tx_start() tx_count=%d\n",
+                        __FILE__,__LINE__, info->device_name,info->tx_count );
+
+       if (!info->tx_enabled ) {
+               write_reg(info, CMD, TXRESET);
+               write_reg(info, CMD, TXENABLE);
+               info->tx_enabled = true;
+       }
+
+       if ( info->tx_count ) {
+
+               /* If auto RTS enabled and RTS is inactive, then assert */
+               /* RTS and set a flag indicating that the driver should */
+               /* negate RTS when the transmission completes. */
+
+               info->drop_rts_on_tx_done = false;
+
+               if (info->params.mode != MGSL_MODE_ASYNC) {
+
+                       if ( info->params.flags & HDLC_FLAG_AUTO_RTS ) {
+                               get_signals( info );
+                               if ( !(info->serial_signals & SerialSignal_RTS) ) {
+                                       info->serial_signals |= SerialSignal_RTS;
+                                       set_signals( info );
+                                       info->drop_rts_on_tx_done = true;
+                               }
+                       }
+
+                       write_reg16(info, TRC0,
+                               (unsigned short)(((tx_negate_fifo_level-1)<<8) + tx_active_fifo_level));
+
+                       write_reg(info, TXDMA + DSR, 0);                /* disable DMA channel */
+                       write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */
+       
+                       /* set TX CDA (current descriptor address) */
+                       write_reg16(info, TXDMA + CDA,
+                               info->tx_buf_list_ex[0].phys_entry);
+       
+                       /* set TX EDA (last descriptor address) */
+                       write_reg16(info, TXDMA + EDA,
+                               info->tx_buf_list_ex[info->last_tx_buf].phys_entry);
+       
+                       /* enable underrun IRQ */
+                       info->ie1_value &= ~IDLE;
+                       info->ie1_value |= UDRN;
+                       write_reg(info, IE1, info->ie1_value);
+                       write_reg(info, SR1, (unsigned char)(IDLE + UDRN));
+       
+                       write_reg(info, TXDMA + DIR, 0x40);             /* enable Tx DMA interrupts (EOM) */
+                       write_reg(info, TXDMA + DSR, 0xf2);             /* clear Tx DMA IRQs, enable Tx DMA */
+       
+                       mod_timer(&info->tx_timer, jiffies +
+                                       msecs_to_jiffies(5000));
+               }
+               else {
+                       tx_load_fifo(info);
+                       /* async, enable IRQ on txdata */
+                       info->ie0_value |= TXRDYE;
+                       write_reg(info, IE0, info->ie0_value);
+               }
+
+               info->tx_active = true;
+       }
+}
+
+/* stop the transmitter and DMA
+ */
+static void tx_stop( SLMP_INFO *info )
+{
+       if (debug_level >= DEBUG_LEVEL_ISR)
+               printk("%s(%d):%s tx_stop()\n",
+                        __FILE__,__LINE__, info->device_name );
+
+       del_timer(&info->tx_timer);
+
+       write_reg(info, TXDMA + DSR, 0);                /* disable DMA channel */
+       write_reg(info, TXDMA + DCMD, SWABORT); /* reset/init DMA channel */
+
+       write_reg(info, CMD, TXRESET);
+
+       info->ie1_value &= ~(UDRN + IDLE);
+       write_reg(info, IE1, info->ie1_value);  /* disable tx status interrupts */
+       write_reg(info, SR1, (unsigned char)(IDLE + UDRN));     /* clear pending */
+
+       info->ie0_value &= ~TXRDYE;
+       write_reg(info, IE0, info->ie0_value);  /* disable tx data interrupts */
+
+       info->tx_enabled = false;
+       info->tx_active = false;
+}
+
+/* Fill the transmit FIFO until the FIFO is full or
+ * there is no more data to load.
+ */
+static void tx_load_fifo(SLMP_INFO *info)
+{
+       u8 TwoBytes[2];
+
+       /* do nothing is now tx data available and no XON/XOFF pending */
+
+       if ( !info->tx_count && !info->x_char )
+               return;
+
+       /* load the Transmit FIFO until FIFOs full or all data sent */
+
+       while( info->tx_count && (read_reg(info,SR0) & BIT1) ) {
+
+               /* there is more space in the transmit FIFO and */
+               /* there is more data in transmit buffer */
+
+               if ( (info->tx_count > 1) && !info->x_char ) {
+                       /* write 16-bits */
+                       TwoBytes[0] = info->tx_buf[info->tx_get++];
+                       if (info->tx_get >= info->max_frame_size)
+                               info->tx_get -= info->max_frame_size;
+                       TwoBytes[1] = info->tx_buf[info->tx_get++];
+                       if (info->tx_get >= info->max_frame_size)
+                               info->tx_get -= info->max_frame_size;
+
+                       write_reg16(info, TRB, *((u16 *)TwoBytes));
+
+                       info->tx_count -= 2;
+                       info->icount.tx += 2;
+               } else {
+                       /* only 1 byte left to transmit or 1 FIFO slot left */
+
+                       if (info->x_char) {
+                               /* transmit pending high priority char */
+                               write_reg(info, TRB, info->x_char);
+                               info->x_char = 0;
+                       } else {
+                               write_reg(info, TRB, info->tx_buf[info->tx_get++]);
+                               if (info->tx_get >= info->max_frame_size)
+                                       info->tx_get -= info->max_frame_size;
+                               info->tx_count--;
+                       }
+                       info->icount.tx++;
+               }
+       }
+}
+
+/* Reset a port to a known state
+ */
+static void reset_port(SLMP_INFO *info)
+{
+       if (info->sca_base) {
+
+               tx_stop(info);
+               rx_stop(info);
+
+               info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
+               set_signals(info);
+
+               /* disable all port interrupts */
+               info->ie0_value = 0;
+               info->ie1_value = 0;
+               info->ie2_value = 0;
+               write_reg(info, IE0, info->ie0_value);
+               write_reg(info, IE1, info->ie1_value);
+               write_reg(info, IE2, info->ie2_value);
+
+               write_reg(info, CMD, CHRESET);
+       }
+}
+
+/* Reset all the ports to a known state.
+ */
+static void reset_adapter(SLMP_INFO *info)
+{
+       int i;
+
+       for ( i=0; i < SCA_MAX_PORTS; ++i) {
+               if (info->port_array[i])
+                       reset_port(info->port_array[i]);
+       }
+}
+
+/* Program port for asynchronous communications.
+ */
+static void async_mode(SLMP_INFO *info)
+{
+
+       unsigned char RegValue;
+
+       tx_stop(info);
+       rx_stop(info);
+
+       /* MD0, Mode Register 0
+        *
+        * 07..05  PRCTL<2..0>, Protocol Mode, 000=async
+        * 04      AUTO, Auto-enable (RTS/CTS/DCD)
+        * 03      Reserved, must be 0
+        * 02      CRCCC, CRC Calculation, 0=disabled
+        * 01..00  STOP<1..0> Stop bits (00=1,10=2)
+        *
+        * 0000 0000
+        */
+       RegValue = 0x00;
+       if (info->params.stop_bits != 1)
+               RegValue |= BIT1;
+       write_reg(info, MD0, RegValue);
+
+       /* MD1, Mode Register 1
+        *
+        * 07..06  BRATE<1..0>, bit rate, 00=1/1 01=1/16 10=1/32 11=1/64
+        * 05..04  TXCHR<1..0>, tx char size, 00=8 bits,01=7,10=6,11=5
+        * 03..02  RXCHR<1..0>, rx char size
+        * 01..00  PMPM<1..0>, Parity mode, 00=none 10=even 11=odd
+        *
+        * 0100 0000
+        */
+       RegValue = 0x40;
+       switch (info->params.data_bits) {
+       case 7: RegValue |= BIT4 + BIT2; break;
+       case 6: RegValue |= BIT5 + BIT3; break;
+       case 5: RegValue |= BIT5 + BIT4 + BIT3 + BIT2; break;
+       }
+       if (info->params.parity != ASYNC_PARITY_NONE) {
+               RegValue |= BIT1;
+               if (info->params.parity == ASYNC_PARITY_ODD)
+                       RegValue |= BIT0;
+       }
+       write_reg(info, MD1, RegValue);
+
+       /* MD2, Mode Register 2
+        *
+        * 07..02  Reserved, must be 0
+        * 01..00  CNCT<1..0> Channel connection, 00=normal 11=local loopback
+        *
+        * 0000 0000
+        */
+       RegValue = 0x00;
+       if (info->params.loopback)
+               RegValue |= (BIT1 + BIT0);
+       write_reg(info, MD2, RegValue);
+
+       /* RXS, Receive clock source
+        *
+        * 07      Reserved, must be 0
+        * 06..04  RXCS<2..0>, clock source, 000=RxC Pin, 100=BRG, 110=DPLL
+        * 03..00  RXBR<3..0>, rate divisor, 0000=1
+        */
+       RegValue=BIT6;
+       write_reg(info, RXS, RegValue);
+
+       /* TXS, Transmit clock source
+        *
+        * 07      Reserved, must be 0
+        * 06..04  RXCS<2..0>, clock source, 000=TxC Pin, 100=BRG, 110=Receive Clock
+        * 03..00  RXBR<3..0>, rate divisor, 0000=1
+        */
+       RegValue=BIT6;
+       write_reg(info, TXS, RegValue);
+
+       /* Control Register
+        *
+        * 6,4,2,0  CLKSEL<3..0>, 0 = TcCLK in, 1 = Auxclk out
+        */
+       info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2));
+       write_control_reg(info);
+
+       tx_set_idle(info);
+
+       /* RRC Receive Ready Control 0
+        *
+        * 07..05  Reserved, must be 0
+        * 04..00  RRC<4..0> Rx FIFO trigger active 0x00 = 1 byte
+        */
+       write_reg(info, RRC, 0x00);
+
+       /* TRC0 Transmit Ready Control 0
+        *
+        * 07..05  Reserved, must be 0
+        * 04..00  TRC<4..0> Tx FIFO trigger active 0x10 = 16 bytes
+        */
+       write_reg(info, TRC0, 0x10);
+
+       /* TRC1 Transmit Ready Control 1
+        *
+        * 07..05  Reserved, must be 0
+        * 04..00  TRC<4..0> Tx FIFO trigger inactive 0x1e = 31 bytes (full-1)
+        */
+       write_reg(info, TRC1, 0x1e);
+
+       /* CTL, MSCI control register
+        *
+        * 07..06  Reserved, set to 0
+        * 05      UDRNC, underrun control, 0=abort 1=CRC+flag (HDLC/BSC)
+        * 04      IDLC, idle control, 0=mark 1=idle register
+        * 03      BRK, break, 0=off 1 =on (async)
+        * 02      SYNCLD, sync char load enable (BSC) 1=enabled
+        * 01      GOP, go active on poll (LOOP mode) 1=enabled
+        * 00      RTS, RTS output control, 0=active 1=inactive
+        *
+        * 0001 0001
+        */
+       RegValue = 0x10;
+       if (!(info->serial_signals & SerialSignal_RTS))
+               RegValue |= 0x01;
+       write_reg(info, CTL, RegValue);
+
+       /* enable status interrupts */
+       info->ie0_value |= TXINTE + RXINTE;
+       write_reg(info, IE0, info->ie0_value);
+
+       /* enable break detect interrupt */
+       info->ie1_value = BRKD;
+       write_reg(info, IE1, info->ie1_value);
+
+       /* enable rx overrun interrupt */
+       info->ie2_value = OVRN;
+       write_reg(info, IE2, info->ie2_value);
+
+       set_rate( info, info->params.data_rate * 16 );
+}
+
+/* Program the SCA for HDLC communications.
+ */
+static void hdlc_mode(SLMP_INFO *info)
+{
+       unsigned char RegValue;
+       u32 DpllDivisor;
+
+       // Can't use DPLL because SCA outputs recovered clock on RxC when
+       // DPLL mode selected. This causes output contention with RxC receiver.
+       // Use of DPLL would require external hardware to disable RxC receiver
+       // when DPLL mode selected.
+       info->params.flags &= ~(HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL);
+
+       /* disable DMA interrupts */
+       write_reg(info, TXDMA + DIR, 0);
+       write_reg(info, RXDMA + DIR, 0);
+
+       /* MD0, Mode Register 0
+        *
+        * 07..05  PRCTL<2..0>, Protocol Mode, 100=HDLC
+        * 04      AUTO, Auto-enable (RTS/CTS/DCD)
+        * 03      Reserved, must be 0
+        * 02      CRCCC, CRC Calculation, 1=enabled
+        * 01      CRC1, CRC selection, 0=CRC-16,1=CRC-CCITT-16
+        * 00      CRC0, CRC initial value, 1 = all 1s
+        *
+        * 1000 0001
+        */
+       RegValue = 0x81;
+       if (info->params.flags & HDLC_FLAG_AUTO_CTS)
+               RegValue |= BIT4;
+       if (info->params.flags & HDLC_FLAG_AUTO_DCD)
+               RegValue |= BIT4;
+       if (info->params.crc_type == HDLC_CRC_16_CCITT)
+               RegValue |= BIT2 + BIT1;
+       write_reg(info, MD0, RegValue);
+
+       /* MD1, Mode Register 1
+        *
+        * 07..06  ADDRS<1..0>, Address detect, 00=no addr check
+        * 05..04  TXCHR<1..0>, tx char size, 00=8 bits
+        * 03..02  RXCHR<1..0>, rx char size, 00=8 bits
+        * 01..00  PMPM<1..0>, Parity mode, 00=no parity
+        *
+        * 0000 0000
+        */
+       RegValue = 0x00;
+       write_reg(info, MD1, RegValue);
+
+       /* MD2, Mode Register 2
+        *
+        * 07      NRZFM, 0=NRZ, 1=FM
+        * 06..05  CODE<1..0> Encoding, 00=NRZ
+        * 04..03  DRATE<1..0> DPLL Divisor, 00=8
+        * 02      Reserved, must be 0
+        * 01..00  CNCT<1..0> Channel connection, 0=normal
+        *
+        * 0000 0000
+        */
+       RegValue = 0x00;
+       switch(info->params.encoding) {
+       case HDLC_ENCODING_NRZI:          RegValue |= BIT5; break;
+       case HDLC_ENCODING_BIPHASE_MARK:  RegValue |= BIT7 + BIT5; break; /* aka FM1 */
+       case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT7 + BIT6; break; /* aka FM0 */
+       case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT7; break;      /* aka Manchester */
+#if 0
+       case HDLC_ENCODING_NRZB:                                        /* not supported */
+       case HDLC_ENCODING_NRZI_MARK:                                   /* not supported */
+       case HDLC_ENCODING_DIFF_BIPHASE_LEVEL:                          /* not supported */
+#endif
+       }
+       if ( info->params.flags & HDLC_FLAG_DPLL_DIV16 ) {
+               DpllDivisor = 16;
+               RegValue |= BIT3;
+       } else if ( info->params.flags & HDLC_FLAG_DPLL_DIV8 ) {
+               DpllDivisor = 8;
+       } else {
+               DpllDivisor = 32;
+               RegValue |= BIT4;
+       }
+       write_reg(info, MD2, RegValue);
+
+
+       /* RXS, Receive clock source
+        *
+        * 07      Reserved, must be 0
+        * 06..04  RXCS<2..0>, clock source, 000=RxC Pin, 100=BRG, 110=DPLL
+        * 03..00  RXBR<3..0>, rate divisor, 0000=1
+        */
+       RegValue=0;
+       if (info->params.flags & HDLC_FLAG_RXC_BRG)
+               RegValue |= BIT6;
+       if (info->params.flags & HDLC_FLAG_RXC_DPLL)
+               RegValue |= BIT6 + BIT5;
+       write_reg(info, RXS, RegValue);
+
+       /* TXS, Transmit clock source
+        *
+        * 07      Reserved, must be 0
+        * 06..04  RXCS<2..0>, clock source, 000=TxC Pin, 100=BRG, 110=Receive Clock
+        * 03..00  RXBR<3..0>, rate divisor, 0000=1
+        */
+       RegValue=0;
+       if (info->params.flags & HDLC_FLAG_TXC_BRG)
+               RegValue |= BIT6;
+       if (info->params.flags & HDLC_FLAG_TXC_DPLL)
+               RegValue |= BIT6 + BIT5;
+       write_reg(info, TXS, RegValue);
+
+       if (info->params.flags & HDLC_FLAG_RXC_DPLL)
+               set_rate(info, info->params.clock_speed * DpllDivisor);
+       else
+               set_rate(info, info->params.clock_speed);
+
+       /* GPDATA (General Purpose I/O Data Register)
+        *
+        * 6,4,2,0  CLKSEL<3..0>, 0 = TcCLK in, 1 = Auxclk out
+        */
+       if (info->params.flags & HDLC_FLAG_TXC_BRG)
+               info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2));
+       else
+               info->port_array[0]->ctrlreg_value &= ~(BIT0 << (info->port_num * 2));
+       write_control_reg(info);
+
+       /* RRC Receive Ready Control 0
+        *
+        * 07..05  Reserved, must be 0
+        * 04..00  RRC<4..0> Rx FIFO trigger active
+        */
+       write_reg(info, RRC, rx_active_fifo_level);
+
+       /* TRC0 Transmit Ready Control 0
+        *
+        * 07..05  Reserved, must be 0
+        * 04..00  TRC<4..0> Tx FIFO trigger active
+        */
+       write_reg(info, TRC0, tx_active_fifo_level);
+
+       /* TRC1 Transmit Ready Control 1
+        *
+        * 07..05  Reserved, must be 0
+        * 04..00  TRC<4..0> Tx FIFO trigger inactive 0x1f = 32 bytes (full)
+        */
+       write_reg(info, TRC1, (unsigned char)(tx_negate_fifo_level - 1));
+
+       /* DMR, DMA Mode Register
+        *
+        * 07..05  Reserved, must be 0
+        * 04      TMOD, Transfer Mode: 1=chained-block
+        * 03      Reserved, must be 0
+        * 02      NF, Number of Frames: 1=multi-frame
+        * 01      CNTE, Frame End IRQ Counter enable: 0=disabled
+        * 00      Reserved, must be 0
+        *
+        * 0001 0100
+        */
+       write_reg(info, TXDMA + DMR, 0x14);
+       write_reg(info, RXDMA + DMR, 0x14);
+
+       /* Set chain pointer base (upper 8 bits of 24 bit addr) */
+       write_reg(info, RXDMA + CPB,
+               (unsigned char)(info->buffer_list_phys >> 16));
+
+       /* Set chain pointer base (upper 8 bits of 24 bit addr) */
+       write_reg(info, TXDMA + CPB,
+               (unsigned char)(info->buffer_list_phys >> 16));
+
+       /* enable status interrupts. other code enables/disables
+        * the individual sources for these two interrupt classes.
+        */
+       info->ie0_value |= TXINTE + RXINTE;
+       write_reg(info, IE0, info->ie0_value);
+
+       /* CTL, MSCI control register
+        *
+        * 07..06  Reserved, set to 0
+        * 05      UDRNC, underrun control, 0=abort 1=CRC+flag (HDLC/BSC)
+        * 04      IDLC, idle control, 0=mark 1=idle register
+        * 03      BRK, break, 0=off 1 =on (async)
+        * 02      SYNCLD, sync char load enable (BSC) 1=enabled
+        * 01      GOP, go active on poll (LOOP mode) 1=enabled
+        * 00      RTS, RTS output control, 0=active 1=inactive
+        *
+        * 0001 0001
+        */
+       RegValue = 0x10;
+       if (!(info->serial_signals & SerialSignal_RTS))
+               RegValue |= 0x01;
+       write_reg(info, CTL, RegValue);
+
+       /* preamble not supported ! */
+
+       tx_set_idle(info);
+       tx_stop(info);
+       rx_stop(info);
+
+       set_rate(info, info->params.clock_speed);
+
+       if (info->params.loopback)
+               enable_loopback(info,1);
+}
+
+/* Set the transmit HDLC idle mode
+ */
+static void tx_set_idle(SLMP_INFO *info)
+{
+       unsigned char RegValue = 0xff;
+
+       /* Map API idle mode to SCA register bits */
+       switch(info->idle_mode) {
+       case HDLC_TXIDLE_FLAGS:                 RegValue = 0x7e; break;
+       case HDLC_TXIDLE_ALT_ZEROS_ONES:        RegValue = 0xaa; break;
+       case HDLC_TXIDLE_ZEROS:                 RegValue = 0x00; break;
+       case HDLC_TXIDLE_ONES:                  RegValue = 0xff; break;
+       case HDLC_TXIDLE_ALT_MARK_SPACE:        RegValue = 0xaa; break;
+       case HDLC_TXIDLE_SPACE:                 RegValue = 0x00; break;
+       case HDLC_TXIDLE_MARK:                  RegValue = 0xff; break;
+       }
+
+       write_reg(info, IDL, RegValue);
+}
+
+/* Query the adapter for the state of the V24 status (input) signals.
+ */
+static void get_signals(SLMP_INFO *info)
+{
+       u16 status = read_reg(info, SR3);
+       u16 gpstatus = read_status_reg(info);
+       u16 testbit;
+
+       /* clear all serial signals except DTR and RTS */
+       info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS;
+
+       /* set serial signal bits to reflect MISR */
+
+       if (!(status & BIT3))
+               info->serial_signals |= SerialSignal_CTS;
+
+       if ( !(status & BIT2))
+               info->serial_signals |= SerialSignal_DCD;
+
+       testbit = BIT1 << (info->port_num * 2); // Port 0..3 RI is GPDATA<1,3,5,7>
+       if (!(gpstatus & testbit))
+               info->serial_signals |= SerialSignal_RI;
+
+       testbit = BIT0 << (info->port_num * 2); // Port 0..3 DSR is GPDATA<0,2,4,6>
+       if (!(gpstatus & testbit))
+               info->serial_signals |= SerialSignal_DSR;
+}
+
+/* Set the state of DTR and RTS based on contents of
+ * serial_signals member of device context.
+ */
+static void set_signals(SLMP_INFO *info)
+{
+       unsigned char RegValue;
+       u16 EnableBit;
+
+       RegValue = read_reg(info, CTL);
+       if (info->serial_signals & SerialSignal_RTS)
+               RegValue &= ~BIT0;
+       else
+               RegValue |= BIT0;
+       write_reg(info, CTL, RegValue);
+
+       // Port 0..3 DTR is ctrl reg <1,3,5,7>
+       EnableBit = BIT1 << (info->port_num*2);
+       if (info->serial_signals & SerialSignal_DTR)
+               info->port_array[0]->ctrlreg_value &= ~EnableBit;
+       else
+               info->port_array[0]->ctrlreg_value |= EnableBit;
+       write_control_reg(info);
+}
+
+/*******************/
+/* DMA Buffer Code */
+/*******************/
+
+/* Set the count for all receive buffers to SCABUFSIZE
+ * and set the current buffer to the first buffer. This effectively
+ * makes all buffers free and discards any data in buffers.
+ */
+static void rx_reset_buffers(SLMP_INFO *info)
+{
+       rx_free_frame_buffers(info, 0, info->rx_buf_count - 1);
+}
+
+/* Free the buffers used by a received frame
+ *
+ * info   pointer to device instance data
+ * first  index of 1st receive buffer of frame
+ * last   index of last receive buffer of frame
+ */
+static void rx_free_frame_buffers(SLMP_INFO *info, unsigned int first, unsigned int last)
+{
+       bool done = false;
+
+       while(!done) {
+               /* reset current buffer for reuse */
+               info->rx_buf_list[first].status = 0xff;
+
+               if (first == last) {
+                       done = true;
+                       /* set new last rx descriptor address */
+                       write_reg16(info, RXDMA + EDA, info->rx_buf_list_ex[first].phys_entry);
+               }
+
+               first++;
+               if (first == info->rx_buf_count)
+                       first = 0;
+       }
+
+       /* set current buffer to next buffer after last buffer of frame */
+       info->current_rx_buf = first;
+}
+
+/* Return a received frame from the receive DMA buffers.
+ * Only frames received without errors are returned.
+ *
+ * Return Value:       true if frame returned, otherwise false
+ */
+static bool rx_get_frame(SLMP_INFO *info)
+{
+       unsigned int StartIndex, EndIndex;      /* index of 1st and last buffers of Rx frame */
+       unsigned short status;
+       unsigned int framesize = 0;
+       bool ReturnCode = false;
+       unsigned long flags;
+       struct tty_struct *tty = info->port.tty;
+       unsigned char addr_field = 0xff;
+       SCADESC *desc;
+       SCADESC_EX *desc_ex;
+
+CheckAgain:
+       /* assume no frame returned, set zero length */
+       framesize = 0;
+       addr_field = 0xff;
+
+       /*
+        * current_rx_buf points to the 1st buffer of the next available
+        * receive frame. To find the last buffer of the frame look for
+        * a non-zero status field in the buffer entries. (The status
+        * field is set by the 16C32 after completing a receive frame.
+        */
+       StartIndex = EndIndex = info->current_rx_buf;
+
+       for ( ;; ) {
+               desc = &info->rx_buf_list[EndIndex];
+               desc_ex = &info->rx_buf_list_ex[EndIndex];
+
+               if (desc->status == 0xff)
+                       goto Cleanup;   /* current desc still in use, no frames available */
+
+               if (framesize == 0 && info->params.addr_filter != 0xff)
+                       addr_field = desc_ex->virt_addr[0];
+
+               framesize += desc->length;
+
+               /* Status != 0 means last buffer of frame */
+               if (desc->status)
+                       break;
+
+               EndIndex++;
+               if (EndIndex == info->rx_buf_count)
+                       EndIndex = 0;
+
+               if (EndIndex == info->current_rx_buf) {
+                       /* all buffers have been 'used' but none mark      */
+                       /* the end of a frame. Reset buffers and receiver. */
+                       if ( info->rx_enabled ){
+                               spin_lock_irqsave(&info->lock,flags);
+                               rx_start(info);
+                               spin_unlock_irqrestore(&info->lock,flags);
+                       }
+                       goto Cleanup;
+               }
+
+       }
+
+       /* check status of receive frame */
+
+       /* frame status is byte stored after frame data
+        *
+        * 7 EOM (end of msg), 1 = last buffer of frame
+        * 6 Short Frame, 1 = short frame
+        * 5 Abort, 1 = frame aborted
+        * 4 Residue, 1 = last byte is partial
+        * 3 Overrun, 1 = overrun occurred during frame reception
+        * 2 CRC,     1 = CRC error detected
+        *
+        */
+       status = desc->status;
+
+       /* ignore CRC bit if not using CRC (bit is undefined) */
+       /* Note:CRC is not save to data buffer */
+       if (info->params.crc_type == HDLC_CRC_NONE)
+               status &= ~BIT2;
+
+       if (framesize == 0 ||
+                (addr_field != 0xff && addr_field != info->params.addr_filter)) {
+               /* discard 0 byte frames, this seems to occur sometime
+                * when remote is idling flags.
+                */
+               rx_free_frame_buffers(info, StartIndex, EndIndex);
+               goto CheckAgain;
+       }
+
+       if (framesize < 2)
+               status |= BIT6;
+
+       if (status & (BIT6+BIT5+BIT3+BIT2)) {
+               /* received frame has errors,
+                * update counts and mark frame size as 0
+                */
+               if (status & BIT6)
+                       info->icount.rxshort++;
+               else if (status & BIT5)
+                       info->icount.rxabort++;
+               else if (status & BIT3)
+                       info->icount.rxover++;
+               else
+                       info->icount.rxcrc++;
+
+               framesize = 0;
+#if SYNCLINK_GENERIC_HDLC
+               {
+                       info->netdev->stats.rx_errors++;
+                       info->netdev->stats.rx_frame_errors++;
+               }
+#endif
+       }
+
+       if ( debug_level >= DEBUG_LEVEL_BH )
+               printk("%s(%d):%s rx_get_frame() status=%04X size=%d\n",
+                       __FILE__,__LINE__,info->device_name,status,framesize);
+
+       if ( debug_level >= DEBUG_LEVEL_DATA )
+               trace_block(info,info->rx_buf_list_ex[StartIndex].virt_addr,
+                       min_t(int, framesize,SCABUFSIZE),0);
+
+       if (framesize) {
+               if (framesize > info->max_frame_size)
+                       info->icount.rxlong++;
+               else {
+                       /* copy dma buffer(s) to contiguous intermediate buffer */
+                       int copy_count = framesize;
+                       int index = StartIndex;
+                       unsigned char *ptmp = info->tmp_rx_buf;
+                       info->tmp_rx_buf_count = framesize;
+
+                       info->icount.rxok++;
+
+                       while(copy_count) {
+                               int partial_count = min(copy_count,SCABUFSIZE);
+                               memcpy( ptmp,
+                                       info->rx_buf_list_ex[index].virt_addr,
+                                       partial_count );
+                               ptmp += partial_count;
+                               copy_count -= partial_count;
+
+                               if ( ++index == info->rx_buf_count )
+                                       index = 0;
+                       }
+
+#if SYNCLINK_GENERIC_HDLC
+                       if (info->netcount)
+                               hdlcdev_rx(info,info->tmp_rx_buf,framesize);
+                       else
+#endif
+                               ldisc_receive_buf(tty,info->tmp_rx_buf,
+                                                 info->flag_buf, framesize);
+               }
+       }
+       /* Free the buffers used by this frame. */
+       rx_free_frame_buffers( info, StartIndex, EndIndex );
+
+       ReturnCode = true;
+
+Cleanup:
+       if ( info->rx_enabled && info->rx_overflow ) {
+               /* Receiver is enabled, but needs to restarted due to
+                * rx buffer overflow. If buffers are empty, restart receiver.
+                */
+               if (info->rx_buf_list[EndIndex].status == 0xff) {
+                       spin_lock_irqsave(&info->lock,flags);
+                       rx_start(info);
+                       spin_unlock_irqrestore(&info->lock,flags);
+               }
+       }
+
+       return ReturnCode;
+}
+
+/* load the transmit DMA buffer with data
+ */
+static void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int count)
+{
+       unsigned short copy_count;
+       unsigned int i = 0;
+       SCADESC *desc;
+       SCADESC_EX *desc_ex;
+
+       if ( debug_level >= DEBUG_LEVEL_DATA )
+               trace_block(info,buf, min_t(int, count,SCABUFSIZE), 1);
+
+       /* Copy source buffer to one or more DMA buffers, starting with
+        * the first transmit dma buffer.
+        */
+       for(i=0;;)
+       {
+               copy_count = min_t(unsigned short,count,SCABUFSIZE);
+
+               desc = &info->tx_buf_list[i];
+               desc_ex = &info->tx_buf_list_ex[i];
+
+               load_pci_memory(info, desc_ex->virt_addr,buf,copy_count);
+
+               desc->length = copy_count;
+               desc->status = 0;
+
+               buf += copy_count;
+               count -= copy_count;
+
+               if (!count)
+                       break;
+
+               i++;
+               if (i >= info->tx_buf_count)
+                       i = 0;
+       }
+
+       info->tx_buf_list[i].status = 0x81;     /* set EOM and EOT status */
+       info->last_tx_buf = ++i;
+}
+
+static bool register_test(SLMP_INFO *info)
+{
+       static unsigned char testval[] = {0x00, 0xff, 0xaa, 0x55, 0x69, 0x96};
+       static unsigned int count = ARRAY_SIZE(testval);
+       unsigned int i;
+       bool rc = true;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->lock,flags);
+       reset_port(info);
+
+       /* assume failure */
+       info->init_error = DiagStatus_AddressFailure;
+
+       /* Write bit patterns to various registers but do it out of */
+       /* sync, then read back and verify values. */
+
+       for (i = 0 ; i < count ; i++) {
+               write_reg(info, TMC, testval[i]);
+               write_reg(info, IDL, testval[(i+1)%count]);
+               write_reg(info, SA0, testval[(i+2)%count]);
+               write_reg(info, SA1, testval[(i+3)%count]);
+
+               if ( (read_reg(info, TMC) != testval[i]) ||
+                         (read_reg(info, IDL) != testval[(i+1)%count]) ||
+                         (read_reg(info, SA0) != testval[(i+2)%count]) ||
+                         (read_reg(info, SA1) != testval[(i+3)%count]) )
+               {
+                       rc = false;
+                       break;
+               }
+       }
+
+       reset_port(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       return rc;
+}
+
+static bool irq_test(SLMP_INFO *info)
+{
+       unsigned long timeout;
+       unsigned long flags;
+
+       unsigned char timer = (info->port_num & 1) ? TIMER2 : TIMER0;
+
+       spin_lock_irqsave(&info->lock,flags);
+       reset_port(info);
+
+       /* assume failure */
+       info->init_error = DiagStatus_IrqFailure;
+       info->irq_occurred = false;
+
+       /* setup timer0 on SCA0 to interrupt */
+
+       /* IER2<7..4> = timer<3..0> interrupt enables (1=enabled) */
+       write_reg(info, IER2, (unsigned char)((info->port_num & 1) ? BIT6 : BIT4));
+
+       write_reg(info, (unsigned char)(timer + TEPR), 0);      /* timer expand prescale */
+       write_reg16(info, (unsigned char)(timer + TCONR), 1);   /* timer constant */
+
+
+       /* TMCS, Timer Control/Status Register
+        *
+        * 07      CMF, Compare match flag (read only) 1=match
+        * 06      ECMI, CMF Interrupt Enable: 1=enabled
+        * 05      Reserved, must be 0
+        * 04      TME, Timer Enable
+        * 03..00  Reserved, must be 0
+        *
+        * 0101 0000
+        */
+       write_reg(info, (unsigned char)(timer + TMCS), 0x50);
+
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       timeout=100;
+       while( timeout-- && !info->irq_occurred ) {
+               msleep_interruptible(10);
+       }
+
+       spin_lock_irqsave(&info->lock,flags);
+       reset_port(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       return info->irq_occurred;
+}
+
+/* initialize individual SCA device (2 ports)
+ */
+static bool sca_init(SLMP_INFO *info)
+{
+       /* set wait controller to single mem partition (low), no wait states */
+       write_reg(info, PABR0, 0);      /* wait controller addr boundary 0 */
+       write_reg(info, PABR1, 0);      /* wait controller addr boundary 1 */
+       write_reg(info, WCRL, 0);       /* wait controller low range */
+       write_reg(info, WCRM, 0);       /* wait controller mid range */
+       write_reg(info, WCRH, 0);       /* wait controller high range */
+
+       /* DPCR, DMA Priority Control
+        *
+        * 07..05  Not used, must be 0
+        * 04      BRC, bus release condition: 0=all transfers complete
+        * 03      CCC, channel change condition: 0=every cycle
+        * 02..00  PR<2..0>, priority 100=round robin
+        *
+        * 00000100 = 0x04
+        */
+       write_reg(info, DPCR, dma_priority);
+
+       /* DMA Master Enable, BIT7: 1=enable all channels */
+       write_reg(info, DMER, 0x80);
+
+       /* enable all interrupt classes */
+       write_reg(info, IER0, 0xff);    /* TxRDY,RxRDY,TxINT,RxINT (ports 0-1) */
+       write_reg(info, IER1, 0xff);    /* DMIB,DMIA (channels 0-3) */
+       write_reg(info, IER2, 0xf0);    /* TIRQ (timers 0-3) */
+
+       /* ITCR, interrupt control register
+        * 07      IPC, interrupt priority, 0=MSCI->DMA
+        * 06..05  IAK<1..0>, Acknowledge cycle, 00=non-ack cycle
+        * 04      VOS, Vector Output, 0=unmodified vector
+        * 03..00  Reserved, must be 0
+        */
+       write_reg(info, ITCR, 0);
+
+       return true;
+}
+
+/* initialize adapter hardware
+ */
+static bool init_adapter(SLMP_INFO *info)
+{
+       int i;
+
+       /* Set BIT30 of Local Control Reg 0x50 to reset SCA */
+       volatile u32 *MiscCtrl = (u32 *)(info->lcr_base + 0x50);
+       u32 readval;
+
+       info->misc_ctrl_value |= BIT30;
+       *MiscCtrl = info->misc_ctrl_value;
+
+       /*
+        * Force at least 170ns delay before clearing
+        * reset bit. Each read from LCR takes at least
+        * 30ns so 10 times for 300ns to be safe.
+        */
+       for(i=0;i<10;i++)
+               readval = *MiscCtrl;
+
+       info->misc_ctrl_value &= ~BIT30;
+       *MiscCtrl = info->misc_ctrl_value;
+
+       /* init control reg (all DTRs off, all clksel=input) */
+       info->ctrlreg_value = 0xaa;
+       write_control_reg(info);
+
+       {
+               volatile u32 *LCR1BRDR = (u32 *)(info->lcr_base + 0x2c);
+               lcr1_brdr_value &= ~(BIT5 + BIT4 + BIT3);
+
+               switch(read_ahead_count)
+               {
+               case 16:
+                       lcr1_brdr_value |= BIT5 + BIT4 + BIT3;
+                       break;
+               case 8:
+                       lcr1_brdr_value |= BIT5 + BIT4;
+                       break;
+               case 4:
+                       lcr1_brdr_value |= BIT5 + BIT3;
+                       break;
+               case 0:
+                       lcr1_brdr_value |= BIT5;
+                       break;
+               }
+
+               *LCR1BRDR = lcr1_brdr_value;
+               *MiscCtrl = misc_ctrl_value;
+       }
+
+       sca_init(info->port_array[0]);
+       sca_init(info->port_array[2]);
+
+       return true;
+}
+
+/* Loopback an HDLC frame to test the hardware
+ * interrupt and DMA functions.
+ */
+static bool loopback_test(SLMP_INFO *info)
+{
+#define TESTFRAMESIZE 20
+
+       unsigned long timeout;
+       u16 count = TESTFRAMESIZE;
+       unsigned char buf[TESTFRAMESIZE];
+       bool rc = false;
+       unsigned long flags;
+
+       struct tty_struct *oldtty = info->port.tty;
+       u32 speed = info->params.clock_speed;
+
+       info->params.clock_speed = 3686400;
+       info->port.tty = NULL;
+
+       /* assume failure */
+       info->init_error = DiagStatus_DmaFailure;
+
+       /* build and send transmit frame */
+       for (count = 0; count < TESTFRAMESIZE;++count)
+               buf[count] = (unsigned char)count;
+
+       memset(info->tmp_rx_buf,0,TESTFRAMESIZE);
+
+       /* program hardware for HDLC and enabled receiver */
+       spin_lock_irqsave(&info->lock,flags);
+       hdlc_mode(info);
+       enable_loopback(info,1);
+               rx_start(info);
+       info->tx_count = count;
+       tx_load_dma_buffer(info,buf,count);
+       tx_start(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       /* wait for receive complete */
+       /* Set a timeout for waiting for interrupt. */
+       for ( timeout = 100; timeout; --timeout ) {
+               msleep_interruptible(10);
+
+               if (rx_get_frame(info)) {
+                       rc = true;
+                       break;
+               }
+       }
+
+       /* verify received frame length and contents */
+       if (rc &&
+           ( info->tmp_rx_buf_count != count ||
+             memcmp(buf, info->tmp_rx_buf,count))) {
+               rc = false;
+       }
+
+       spin_lock_irqsave(&info->lock,flags);
+       reset_adapter(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       info->params.clock_speed = speed;
+       info->port.tty = oldtty;
+
+       return rc;
+}
+
+/* Perform diagnostics on hardware
+ */
+static int adapter_test( SLMP_INFO *info )
+{
+       unsigned long flags;
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):Testing device %s\n",
+                       __FILE__,__LINE__,info->device_name );
+
+       spin_lock_irqsave(&info->lock,flags);
+       init_adapter(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       info->port_array[0]->port_count = 0;
+
+       if ( register_test(info->port_array[0]) &&
+               register_test(info->port_array[1])) {
+
+               info->port_array[0]->port_count = 2;
+
+               if ( register_test(info->port_array[2]) &&
+                       register_test(info->port_array[3]) )
+                       info->port_array[0]->port_count += 2;
+       }
+       else {
+               printk( "%s(%d):Register test failure for device %s Addr=%08lX\n",
+                       __FILE__,__LINE__,info->device_name, (unsigned long)(info->phys_sca_base));
+               return -ENODEV;
+       }
+
+       if ( !irq_test(info->port_array[0]) ||
+               !irq_test(info->port_array[1]) ||
+                (info->port_count == 4 && !irq_test(info->port_array[2])) ||
+                (info->port_count == 4 && !irq_test(info->port_array[3]))) {
+               printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n",
+                       __FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) );
+               return -ENODEV;
+       }
+
+       if (!loopback_test(info->port_array[0]) ||
+               !loopback_test(info->port_array[1]) ||
+                (info->port_count == 4 && !loopback_test(info->port_array[2])) ||
+                (info->port_count == 4 && !loopback_test(info->port_array[3]))) {
+               printk( "%s(%d):DMA test failure for device %s\n",
+                       __FILE__,__LINE__,info->device_name);
+               return -ENODEV;
+       }
+
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):device %s passed diagnostics\n",
+                       __FILE__,__LINE__,info->device_name );
+
+       info->port_array[0]->init_error = 0;
+       info->port_array[1]->init_error = 0;
+       if ( info->port_count > 2 ) {
+               info->port_array[2]->init_error = 0;
+               info->port_array[3]->init_error = 0;
+       }
+
+       return 0;
+}
+
+/* Test the shared memory on a PCI adapter.
+ */
+static bool memory_test(SLMP_INFO *info)
+{
+       static unsigned long testval[] = { 0x0, 0x55555555, 0xaaaaaaaa,
+               0x66666666, 0x99999999, 0xffffffff, 0x12345678 };
+       unsigned long count = ARRAY_SIZE(testval);
+       unsigned long i;
+       unsigned long limit = SCA_MEM_SIZE/sizeof(unsigned long);
+       unsigned long * addr = (unsigned long *)info->memory_base;
+
+       /* Test data lines with test pattern at one location. */
+
+       for ( i = 0 ; i < count ; i++ ) {
+               *addr = testval[i];
+               if ( *addr != testval[i] )
+                       return false;
+       }
+
+       /* Test address lines with incrementing pattern over */
+       /* entire address range. */
+
+       for ( i = 0 ; i < limit ; i++ ) {
+               *addr = i * 4;
+               addr++;
+       }
+
+       addr = (unsigned long *)info->memory_base;
+
+       for ( i = 0 ; i < limit ; i++ ) {
+               if ( *addr != i * 4 )
+                       return false;
+               addr++;
+       }
+
+       memset( info->memory_base, 0, SCA_MEM_SIZE );
+       return true;
+}
+
+/* Load data into PCI adapter shared memory.
+ *
+ * The PCI9050 releases control of the local bus
+ * after completing the current read or write operation.
+ *
+ * While the PCI9050 write FIFO not empty, the
+ * PCI9050 treats all of the writes as a single transaction
+ * and does not release the bus. This causes DMA latency problems
+ * at high speeds when copying large data blocks to the shared memory.
+ *
+ * This function breaks a write into multiple transations by
+ * interleaving a read which flushes the write FIFO and 'completes'
+ * the write transation. This allows any pending DMA request to gain control
+ * of the local bus in a timely fasion.
+ */
+static void load_pci_memory(SLMP_INFO *info, char* dest, const char* src, unsigned short count)
+{
+       /* A load interval of 16 allows for 4 32-bit writes at */
+       /* 136ns each for a maximum latency of 542ns on the local bus.*/
+
+       unsigned short interval = count / sca_pci_load_interval;
+       unsigned short i;
+
+       for ( i = 0 ; i < interval ; i++ )
+       {
+               memcpy(dest, src, sca_pci_load_interval);
+               read_status_reg(info);
+               dest += sca_pci_load_interval;
+               src += sca_pci_load_interval;
+       }
+
+       memcpy(dest, src, count % sca_pci_load_interval);
+}
+
+static void trace_block(SLMP_INFO *info,const char* data, int count, int xmit)
+{
+       int i;
+       int linecount;
+       if (xmit)
+               printk("%s tx data:\n",info->device_name);
+       else
+               printk("%s rx data:\n",info->device_name);
+
+       while(count) {
+               if (count > 16)
+                       linecount = 16;
+               else
+                       linecount = count;
+
+               for(i=0;i<linecount;i++)
+                       printk("%02X ",(unsigned char)data[i]);
+               for(;i<17;i++)
+                       printk("   ");
+               for(i=0;i<linecount;i++) {
+                       if (data[i]>=040 && data[i]<=0176)
+                               printk("%c",data[i]);
+                       else
+                               printk(".");
+               }
+               printk("\n");
+
+               data  += linecount;
+               count -= linecount;
+       }
+}      /* end of trace_block() */
+
+/* called when HDLC frame times out
+ * update stats and do tx completion processing
+ */
+static void tx_timeout(unsigned long context)
+{
+       SLMP_INFO *info = (SLMP_INFO*)context;
+       unsigned long flags;
+
+       if ( debug_level >= DEBUG_LEVEL_INFO )
+               printk( "%s(%d):%s tx_timeout()\n",
+                       __FILE__,__LINE__,info->device_name);
+       if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) {
+               info->icount.txtimeout++;
+       }
+       spin_lock_irqsave(&info->lock,flags);
+       info->tx_active = false;
+       info->tx_count = info->tx_put = info->tx_get = 0;
+
+       spin_unlock_irqrestore(&info->lock,flags);
+
+#if SYNCLINK_GENERIC_HDLC
+       if (info->netcount)
+               hdlcdev_tx_done(info);
+       else
+#endif
+               bh_transmit(info);
+}
+
+/* called to periodically check the DSR/RI modem signal input status
+ */
+static void status_timeout(unsigned long context)
+{
+       u16 status = 0;
+       SLMP_INFO *info = (SLMP_INFO*)context;
+       unsigned long flags;
+       unsigned char delta;
+
+
+       spin_lock_irqsave(&info->lock,flags);
+       get_signals(info);
+       spin_unlock_irqrestore(&info->lock,flags);
+
+       /* check for DSR/RI state change */
+
+       delta = info->old_signals ^ info->serial_signals;
+       info->old_signals = info->serial_signals;
+
+       if (delta & SerialSignal_DSR)
+               status |= MISCSTATUS_DSR_LATCHED|(info->serial_signals&SerialSignal_DSR);
+
+       if (delta & SerialSignal_RI)
+               status |= MISCSTATUS_RI_LATCHED|(info->serial_signals&SerialSignal_RI);
+
+       if (delta & SerialSignal_DCD)
+               status |= MISCSTATUS_DCD_LATCHED|(info->serial_signals&SerialSignal_DCD);
+
+       if (delta & SerialSignal_CTS)
+               status |= MISCSTATUS_CTS_LATCHED|(info->serial_signals&SerialSignal_CTS);
+
+       if (status)
+               isr_io_pin(info,status);
+
+       mod_timer(&info->status_timer, jiffies + msecs_to_jiffies(10));
+}
+
+
+/* Register Access Routines -
+ * All registers are memory mapped
+ */
+#define CALC_REGADDR() \
+       unsigned char * RegAddr = (unsigned char*)(info->sca_base + Addr); \
+       if (info->port_num > 1) \
+               RegAddr += 256;                 /* port 0-1 SCA0, 2-3 SCA1 */ \
+       if ( info->port_num & 1) { \
+               if (Addr > 0x7f) \
+                       RegAddr += 0x40;        /* DMA access */ \
+               else if (Addr > 0x1f && Addr < 0x60) \
+                       RegAddr += 0x20;        /* MSCI access */ \
+       }
+
+
+static unsigned char read_reg(SLMP_INFO * info, unsigned char Addr)
+{
+       CALC_REGADDR();
+       return *RegAddr;
+}
+static void write_reg(SLMP_INFO * info, unsigned char Addr, unsigned char Value)
+{
+       CALC_REGADDR();
+       *RegAddr = Value;
+}
+
+static u16 read_reg16(SLMP_INFO * info, unsigned char Addr)
+{
+       CALC_REGADDR();
+       return *((u16 *)RegAddr);
+}
+
+static void write_reg16(SLMP_INFO * info, unsigned char Addr, u16 Value)
+{
+       CALC_REGADDR();
+       *((u16 *)RegAddr) = Value;
+}
+
+static unsigned char read_status_reg(SLMP_INFO * info)
+{
+       unsigned char *RegAddr = (unsigned char *)info->statctrl_base;
+       return *RegAddr;
+}
+
+static void write_control_reg(SLMP_INFO * info)
+{
+       unsigned char *RegAddr = (unsigned char *)info->statctrl_base;
+       *RegAddr = info->port_array[0]->ctrlreg_value;
+}
+
+
+static int __devinit synclinkmp_init_one (struct pci_dev *dev,
+                                         const struct pci_device_id *ent)
+{
+       if (pci_enable_device(dev)) {
+               printk("error enabling pci device %p\n", dev);
+               return -EIO;
+       }
+       device_init( ++synclinkmp_adapter_count, dev );
+       return 0;
+}
+
+static void __devexit synclinkmp_remove_one (struct pci_dev *dev)
+{
+}