tty: move obsolete and broken tty drivers to drivers/staging/tty/
authorGreg Kroah-Hartman <gregkh@suse.de>
Wed, 23 Feb 2011 00:57:21 +0000 (16:57 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 23 Feb 2011 00:57:21 +0000 (16:57 -0800)
As planned by Arnd Bergmann, this moves the following drivers to the
drivers/staging/tty/ directory where they will be removed after 2.6.41
if no one steps up to claim them.
epca
epca
ip2
istallion
riscom8
serial167
specialix
stallion

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>
58 files changed:
arch/m68k/Kconfig
drivers/char/Kconfig
drivers/char/Makefile
drivers/char/epca.c [deleted file]
drivers/char/epca.h [deleted file]
drivers/char/epcaconfig.h [deleted file]
drivers/char/ip2/Makefile [deleted file]
drivers/char/ip2/i2cmd.c [deleted file]
drivers/char/ip2/i2cmd.h [deleted file]
drivers/char/ip2/i2ellis.c [deleted file]
drivers/char/ip2/i2ellis.h [deleted file]
drivers/char/ip2/i2hw.h [deleted file]
drivers/char/ip2/i2lib.c [deleted file]
drivers/char/ip2/i2lib.h [deleted file]
drivers/char/ip2/i2pack.h [deleted file]
drivers/char/ip2/ip2.h [deleted file]
drivers/char/ip2/ip2ioctl.h [deleted file]
drivers/char/ip2/ip2main.c [deleted file]
drivers/char/ip2/ip2trace.h [deleted file]
drivers/char/ip2/ip2types.h [deleted file]
drivers/char/istallion.c [deleted file]
drivers/char/riscom8.c [deleted file]
drivers/char/riscom8.h [deleted file]
drivers/char/riscom8_reg.h [deleted file]
drivers/char/serial167.c [deleted file]
drivers/char/specialix.c [deleted file]
drivers/char/specialix_io8.h [deleted file]
drivers/char/stallion.c [deleted file]
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/tty/Kconfig [new file with mode: 0644]
drivers/staging/tty/Makefile [new file with mode: 0644]
drivers/staging/tty/TODO [new file with mode: 0644]
drivers/staging/tty/epca.c [new file with mode: 0644]
drivers/staging/tty/epca.h [new file with mode: 0644]
drivers/staging/tty/epcaconfig.h [new file with mode: 0644]
drivers/staging/tty/ip2/Makefile [new file with mode: 0644]
drivers/staging/tty/ip2/i2cmd.c [new file with mode: 0644]
drivers/staging/tty/ip2/i2cmd.h [new file with mode: 0644]
drivers/staging/tty/ip2/i2ellis.c [new file with mode: 0644]
drivers/staging/tty/ip2/i2ellis.h [new file with mode: 0644]
drivers/staging/tty/ip2/i2hw.h [new file with mode: 0644]
drivers/staging/tty/ip2/i2lib.c [new file with mode: 0644]
drivers/staging/tty/ip2/i2lib.h [new file with mode: 0644]
drivers/staging/tty/ip2/i2pack.h [new file with mode: 0644]
drivers/staging/tty/ip2/ip2.h [new file with mode: 0644]
drivers/staging/tty/ip2/ip2ioctl.h [new file with mode: 0644]
drivers/staging/tty/ip2/ip2main.c [new file with mode: 0644]
drivers/staging/tty/ip2/ip2trace.h [new file with mode: 0644]
drivers/staging/tty/ip2/ip2types.h [new file with mode: 0644]
drivers/staging/tty/istallion.c [new file with mode: 0644]
drivers/staging/tty/riscom8.c [new file with mode: 0644]
drivers/staging/tty/riscom8.h [new file with mode: 0644]
drivers/staging/tty/riscom8_reg.h [new file with mode: 0644]
drivers/staging/tty/serial167.c [new file with mode: 0644]
drivers/staging/tty/specialix.c [new file with mode: 0644]
drivers/staging/tty/specialix_io8.h [new file with mode: 0644]
drivers/staging/tty/stallion.c [new file with mode: 0644]

index bc9271b..a85e251 100644 (file)
@@ -554,14 +554,6 @@ config MVME147_SCC
          This is the driver for the serial ports on the Motorola MVME147
          boards.  Everyone using one of these boards should say Y here.
 
-config SERIAL167
-       bool "CD2401 support for MVME166/7 serial ports"
-       depends on MVME16x
-       help
-         This is the driver for the serial ports on the Motorola MVME166,
-         167, and 172 boards.  Everyone using one of these boards should say
-         Y here.
-
 config MVME162_SCC
        bool "SCC support for MVME162 serial ports"
        depends on MVME16x && BROKEN
index 1adfac6..7b8cf02 100644 (file)
@@ -15,63 +15,6 @@ config DEVKMEM
          kind of kernel debugging operations.
          When in doubt, say "N".
 
-config COMPUTONE
-       tristate "Computone IntelliPort Plus serial support"
-       depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
-       ---help---
-         This driver supports the entire family of Intelliport II/Plus
-         controllers with the exception of the MicroChannel controllers and
-         products previous to the Intelliport II. These are multiport cards,
-         which give you many serial ports. 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. If you have a card like that, say
-         Y here and read <file:Documentation/serial/computone.txt>.
-
-         To compile this driver as module, choose M here: the
-         module will be called ip2.
-
-config DIGIEPCA
-       tristate "Digiboard Intelligent Async Support"
-       depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
-       ---help---
-         This is a driver for Digi International's Xx, Xeve, and Xem series
-         of cards which provide multiple serial ports. 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. This driver
-         supports the original PC (ISA) boards as well as PCI, and EISA. If
-         you have a card like this, say Y here and read the file
-         <file:Documentation/serial/digiepca.txt>.
-
-         To compile this driver as a module, choose M here: the
-         module will be called epca.
-
-config RISCOM8
-       tristate "SDL RISCom/8 card support"
-       depends on SERIAL_NONSTANDARD
-       help
-         This is a driver for the SDL Communications RISCom/8 multiport card,
-         which gives you many serial ports. 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. If you have a card like that,
-         say Y here and read the file <file:Documentation/serial/riscom8.txt>.
-
-         Also it's possible to say M here and compile this driver as kernel
-         loadable module; the module will be called riscom8.
-
-config SPECIALIX
-       tristate "Specialix IO8+ card support"
-       depends on SERIAL_NONSTANDARD
-       help
-         This is a driver for the Specialix IO8+ multiport card (both the
-         ISA and the PCI version) which gives you many serial ports. 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.
-
-         If you have a card like that, say Y here and read the file
-         <file:Documentation/serial/specialix.txt>. Also it's possible to say
-         M here and compile this driver as kernel loadable module which will be
-         called specialix.
-
 config SX
        tristate "Specialix SX (and SI) card support"
        depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) && BROKEN
@@ -112,28 +55,6 @@ config STALDRV
          in this case.  If you have never heard about all this, it's safe to
          say N.
 
-config STALLION
-       tristate "Stallion EasyIO or EC8/32 support"
-       depends on STALDRV && (ISA || EISA || PCI)
-       help
-         If you have an EasyIO or EasyConnection 8/32 multiport Stallion
-         card, then this is for you; say Y.  Make sure to read
-         <file:Documentation/serial/stallion.txt>.
-
-         To compile this driver as a module, choose M here: the
-         module will be called stallion.
-
-config ISTALLION
-       tristate "Stallion EC8/64, ONboard, Brumby support"
-       depends on STALDRV && (ISA || EISA || PCI)
-       help
-         If you have an EasyConnection 8/64, ONboard, Brumby or Stallion
-         serial multiport card, say Y here. Make sure to read
-         <file:Documentation/serial/stallion.txt>.
-
-         To compile this driver as a module, choose M here: the
-         module will be called istallion.
-
 config A2232
        tristate "Commodore A2232 serial support (EXPERIMENTAL)"
        depends on EXPERIMENTAL && ZORRO && BROKEN
index f5dc7c9..48bb8ac 100644 (file)
@@ -8,15 +8,8 @@ obj-y                          += misc.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_SERIAL167)                += serial167.o
-obj-$(CONFIG_STALLION)         += stallion.o
-obj-$(CONFIG_ISTALLION)                += istallion.o
-obj-$(CONFIG_DIGIEPCA)         += epca.o
-obj-$(CONFIG_SPECIALIX)                += specialix.o
 obj-$(CONFIG_A2232)            += ser_a2232.o generic_serial.o
 obj-$(CONFIG_ATARI_DSP56K)     += dsp56k.o
-obj-$(CONFIG_COMPUTONE)                += ip2/
-obj-$(CONFIG_RISCOM8)          += riscom8.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/epca.c b/drivers/char/epca.c
deleted file mode 100644 (file)
index 7ad3638..0000000
+++ /dev/null
@@ -1,2784 +0,0 @@
-/*
-       Copyright (C) 1996  Digi International.
-
-       For technical support please email digiLinux@dgii.com or
-       call Digi tech support at (612) 912-3456
-
-       ** This driver is no longer supported by Digi **
-
-       Much of this design and code came from epca.c which was
-       copyright (C) 1994, 1995 Troy De Jongh, and subsquently
-       modified by David Nugent, Christoph Lameter, Mike McLagan.
-
-       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.
-*/
-/* See README.epca for change history --DAT*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/serial.h>
-#include <linux/delay.h>
-#include <linux/ctype.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
-#include <linux/spinlock.h>
-#include <linux/pci.h>
-#include "digiPCI.h"
-
-
-#include "digi1.h"
-#include "digiFep1.h"
-#include "epca.h"
-#include "epcaconfig.h"
-
-#define VERSION            "1.3.0.1-LK2.6"
-
-/* This major needs to be submitted to Linux to join the majors list */
-#define DIGIINFOMAJOR       35  /* For Digi specific ioctl */
-
-
-#define MAXCARDS 7
-#define epcaassert(x, msg)  if (!(x)) epca_error(__LINE__, msg)
-
-#define PFX "epca: "
-
-static int nbdevs, num_cards, liloconfig;
-static int digi_poller_inhibited = 1 ;
-
-static int setup_error_code;
-static int invalid_lilo_config;
-
-/*
- * The ISA boards do window flipping into the same spaces so its only sane with
- * a single lock. It's still pretty efficient. This lock guards the hardware
- * and the tty_port lock guards the kernel side stuff like use counts. Take
- * this lock inside the port lock if you must take both.
- */
-static DEFINE_SPINLOCK(epca_lock);
-
-/* MAXBOARDS is typically 12, but ISA and EISA cards are restricted
-   to 7 below. */
-static struct board_info boards[MAXBOARDS];
-
-static struct tty_driver *pc_driver;
-static struct tty_driver *pc_info;
-
-/* ------------------ Begin Digi specific structures -------------------- */
-
-/*
- * digi_channels represents an array of structures that keep track of each
- * channel of the Digi product. Information such as transmit and receive
- * pointers, termio data, and signal definitions (DTR, CTS, etc ...) are stored
- * here. This structure is NOT used to overlay the cards physical channel
- * structure.
- */
-static struct channel digi_channels[MAX_ALLOC];
-
-/*
- * card_ptr is an array used to hold the address of the first channel structure
- * of each card. This array will hold the addresses of various channels located
- * in digi_channels.
- */
-static struct channel *card_ptr[MAXCARDS];
-
-static struct timer_list epca_timer;
-
-/*
- * Begin generic memory functions. These functions will be alias (point at)
- * more specific functions dependent on the board being configured.
- */
-static void memwinon(struct board_info *b, unsigned int win);
-static void memwinoff(struct board_info *b, unsigned int win);
-static void globalwinon(struct channel *ch);
-static void rxwinon(struct channel *ch);
-static void txwinon(struct channel *ch);
-static void memoff(struct channel *ch);
-static void assertgwinon(struct channel *ch);
-static void assertmemoff(struct channel *ch);
-
-/* ---- Begin more 'specific' memory functions for cx_like products --- */
-
-static void pcxem_memwinon(struct board_info *b, unsigned int win);
-static void pcxem_memwinoff(struct board_info *b, unsigned int win);
-static void pcxem_globalwinon(struct channel *ch);
-static void pcxem_rxwinon(struct channel *ch);
-static void pcxem_txwinon(struct channel *ch);
-static void pcxem_memoff(struct channel *ch);
-
-/* ------ Begin more 'specific' memory functions for the pcxe ------- */
-
-static void pcxe_memwinon(struct board_info *b, unsigned int win);
-static void pcxe_memwinoff(struct board_info *b, unsigned int win);
-static void pcxe_globalwinon(struct channel *ch);
-static void pcxe_rxwinon(struct channel *ch);
-static void pcxe_txwinon(struct channel *ch);
-static void pcxe_memoff(struct channel *ch);
-
-/* ---- Begin more 'specific' memory functions for the pc64xe and pcxi ---- */
-/* Note : pc64xe and pcxi share the same windowing routines */
-
-static void pcxi_memwinon(struct board_info *b, unsigned int win);
-static void pcxi_memwinoff(struct board_info *b, unsigned int win);
-static void pcxi_globalwinon(struct channel *ch);
-static void pcxi_rxwinon(struct channel *ch);
-static void pcxi_txwinon(struct channel *ch);
-static void pcxi_memoff(struct channel *ch);
-
-/* - Begin 'specific' do nothing memory functions needed for some cards - */
-
-static void dummy_memwinon(struct board_info *b, unsigned int win);
-static void dummy_memwinoff(struct board_info *b, unsigned int win);
-static void dummy_globalwinon(struct channel *ch);
-static void dummy_rxwinon(struct channel *ch);
-static void dummy_txwinon(struct channel *ch);
-static void dummy_memoff(struct channel *ch);
-static void dummy_assertgwinon(struct channel *ch);
-static void dummy_assertmemoff(struct channel *ch);
-
-static struct channel *verifyChannel(struct tty_struct *);
-static void pc_sched_event(struct channel *, int);
-static void epca_error(int, char *);
-static void pc_close(struct tty_struct *, struct file *);
-static void shutdown(struct channel *, struct tty_struct *tty);
-static void pc_hangup(struct tty_struct *);
-static int pc_write_room(struct tty_struct *);
-static int pc_chars_in_buffer(struct tty_struct *);
-static void pc_flush_buffer(struct tty_struct *);
-static void pc_flush_chars(struct tty_struct *);
-static int pc_open(struct tty_struct *, struct file *);
-static void post_fep_init(unsigned int crd);
-static void epcapoll(unsigned long);
-static void doevent(int);
-static void fepcmd(struct channel *, int, int, int, int, int);
-static unsigned termios2digi_h(struct channel *ch, unsigned);
-static unsigned termios2digi_i(struct channel *ch, unsigned);
-static unsigned termios2digi_c(struct channel *ch, unsigned);
-static void epcaparam(struct tty_struct *, struct channel *);
-static void receive_data(struct channel *, struct tty_struct *tty);
-static int pc_ioctl(struct tty_struct *,
-                       unsigned int, unsigned long);
-static int info_ioctl(struct tty_struct *,
-                       unsigned int, unsigned long);
-static void pc_set_termios(struct tty_struct *, struct ktermios *);
-static void do_softint(struct work_struct *work);
-static void pc_stop(struct tty_struct *);
-static void pc_start(struct tty_struct *);
-static void pc_throttle(struct tty_struct *tty);
-static void pc_unthrottle(struct tty_struct *tty);
-static int pc_send_break(struct tty_struct *tty, int msec);
-static void setup_empty_event(struct tty_struct *tty, struct channel *ch);
-
-static int pc_write(struct tty_struct *, const unsigned char *, int);
-static int pc_init(void);
-static int init_PCI(void);
-
-/*
- * Table of functions for each board to handle memory. Mantaining parallelism
- * is a *very* good idea here. The idea is for the runtime code to blindly call
- * these functions, not knowing/caring about the underlying hardware. This
- * stuff should contain no conditionals; if more functionality is needed a
- * different entry should be established. These calls are the interface calls
- * and are the only functions that should be accessed. Anyone caught making
- * direct calls deserves what they get.
- */
-static void memwinon(struct board_info *b, unsigned int win)
-{
-       b->memwinon(b, win);
-}
-
-static void memwinoff(struct board_info *b, unsigned int win)
-{
-       b->memwinoff(b, win);
-}
-
-static void globalwinon(struct channel *ch)
-{
-       ch->board->globalwinon(ch);
-}
-
-static void rxwinon(struct channel *ch)
-{
-       ch->board->rxwinon(ch);
-}
-
-static void txwinon(struct channel *ch)
-{
-       ch->board->txwinon(ch);
-}
-
-static void memoff(struct channel *ch)
-{
-       ch->board->memoff(ch);
-}
-static void assertgwinon(struct channel *ch)
-{
-       ch->board->assertgwinon(ch);
-}
-
-static void assertmemoff(struct channel *ch)
-{
-       ch->board->assertmemoff(ch);
-}
-
-/* PCXEM windowing is the same as that used in the PCXR and CX series cards. */
-static void pcxem_memwinon(struct board_info *b, unsigned int win)
-{
-       outb_p(FEPWIN | win, b->port + 1);
-}
-
-static void pcxem_memwinoff(struct board_info *b, unsigned int win)
-{
-       outb_p(0, b->port + 1);
-}
-
-static void pcxem_globalwinon(struct channel *ch)
-{
-       outb_p(FEPWIN, (int)ch->board->port + 1);
-}
-
-static void pcxem_rxwinon(struct channel *ch)
-{
-       outb_p(ch->rxwin, (int)ch->board->port + 1);
-}
-
-static void pcxem_txwinon(struct channel *ch)
-{
-       outb_p(ch->txwin, (int)ch->board->port + 1);
-}
-
-static void pcxem_memoff(struct channel *ch)
-{
-       outb_p(0, (int)ch->board->port + 1);
-}
-
-/* ----------------- Begin pcxe memory window stuff ------------------ */
-static void pcxe_memwinon(struct board_info *b, unsigned int win)
-{
-       outb_p(FEPWIN | win, b->port + 1);
-}
-
-static void pcxe_memwinoff(struct board_info *b, unsigned int win)
-{
-       outb_p(inb(b->port) & ~FEPMEM, b->port + 1);
-       outb_p(0, b->port + 1);
-}
-
-static void pcxe_globalwinon(struct channel *ch)
-{
-       outb_p(FEPWIN, (int)ch->board->port + 1);
-}
-
-static void pcxe_rxwinon(struct channel *ch)
-{
-       outb_p(ch->rxwin, (int)ch->board->port + 1);
-}
-
-static void pcxe_txwinon(struct channel *ch)
-{
-       outb_p(ch->txwin, (int)ch->board->port + 1);
-}
-
-static void pcxe_memoff(struct channel *ch)
-{
-       outb_p(0, (int)ch->board->port);
-       outb_p(0, (int)ch->board->port + 1);
-}
-
-/* ------------- Begin pc64xe and pcxi memory window stuff -------------- */
-static void pcxi_memwinon(struct board_info *b, unsigned int win)
-{
-       outb_p(inb(b->port) | FEPMEM, b->port);
-}
-
-static void pcxi_memwinoff(struct board_info *b, unsigned int win)
-{
-       outb_p(inb(b->port) & ~FEPMEM, b->port);
-}
-
-static void pcxi_globalwinon(struct channel *ch)
-{
-       outb_p(FEPMEM, ch->board->port);
-}
-
-static void pcxi_rxwinon(struct channel *ch)
-{
-       outb_p(FEPMEM, ch->board->port);
-}
-
-static void pcxi_txwinon(struct channel *ch)
-{
-       outb_p(FEPMEM, ch->board->port);
-}
-
-static void pcxi_memoff(struct channel *ch)
-{
-       outb_p(0, ch->board->port);
-}
-
-static void pcxi_assertgwinon(struct channel *ch)
-{
-       epcaassert(inb(ch->board->port) & FEPMEM, "Global memory off");
-}
-
-static void pcxi_assertmemoff(struct channel *ch)
-{
-       epcaassert(!(inb(ch->board->port) & FEPMEM), "Memory on");
-}
-
-/*
- * Not all of the cards need specific memory windowing routines. Some cards
- * (Such as PCI) needs no windowing routines at all. We provide these do
- * nothing routines so that the same code base can be used. The driver will
- * ALWAYS call a windowing routine if it thinks it needs to; regardless of the
- * card. However, dependent on the card the routine may or may not do anything.
- */
-static void dummy_memwinon(struct board_info *b, unsigned int win)
-{
-}
-
-static void dummy_memwinoff(struct board_info *b, unsigned int win)
-{
-}
-
-static void dummy_globalwinon(struct channel *ch)
-{
-}
-
-static void dummy_rxwinon(struct channel *ch)
-{
-}
-
-static void dummy_txwinon(struct channel *ch)
-{
-}
-
-static void dummy_memoff(struct channel *ch)
-{
-}
-
-static void dummy_assertgwinon(struct channel *ch)
-{
-}
-
-static void dummy_assertmemoff(struct channel *ch)
-{
-}
-
-static struct channel *verifyChannel(struct tty_struct *tty)
-{
-       /*
-        * This routine basically provides a sanity check. It insures that the
-        * channel returned is within the proper range of addresses as well as
-        * properly initialized. If some bogus info gets passed in
-        * through tty->driver_data this should catch it.
-        */
-       if (tty) {
-               struct channel *ch = tty->driver_data;
-               if (ch >= &digi_channels[0] && ch < &digi_channels[nbdevs]) {
-                       if (ch->magic == EPCA_MAGIC)
-                               return ch;
-               }
-       }
-       return NULL;
-}
-
-static void pc_sched_event(struct channel *ch, int event)
-{
-       /*
-        * We call this to schedule interrupt processing on some event. The
-        * kernel sees our request and calls the related routine in OUR driver.
-        */
-       ch->event |= 1 << event;
-       schedule_work(&ch->tqueue);
-}
-
-static void epca_error(int line, char *msg)
-{
-       printk(KERN_ERR "epca_error (Digi): line = %d %s\n", line, msg);
-}
-
-static void pc_close(struct tty_struct *tty, struct file *filp)
-{
-       struct channel *ch;
-       struct tty_port *port;
-       /*
-        * verifyChannel returns the channel from the tty struct if it is
-        * valid. This serves as a sanity check.
-        */
-       ch = verifyChannel(tty);
-       if (ch == NULL)
-               return;
-       port = &ch->port;
-
-       if (tty_port_close_start(port, tty, filp) == 0)
-               return;
-
-       pc_flush_buffer(tty);
-       shutdown(ch, tty);
-
-       tty_port_close_end(port, tty);
-       ch->event = 0;  /* FIXME: review ch->event locking */
-       tty_port_tty_set(port, NULL);
-}
-
-static void shutdown(struct channel *ch, struct tty_struct *tty)
-{
-       unsigned long flags;
-       struct board_chan __iomem *bc;
-       struct tty_port *port = &ch->port;
-
-       if (!(port->flags & ASYNC_INITIALIZED))
-               return;
-
-       spin_lock_irqsave(&epca_lock, flags);
-
-       globalwinon(ch);
-       bc = ch->brdchan;
-
-       /*
-        * In order for an event to be generated on the receipt of data the
-        * idata flag must be set. Since we are shutting down, this is not
-        * necessary clear this flag.
-        */
-       if (bc)
-               writeb(0, &bc->idata);
-
-       /* If we're a modem control device and HUPCL is on, drop RTS & DTR. */
-       if (tty->termios->c_cflag & HUPCL)  {
-               ch->omodem &= ~(ch->m_rts | ch->m_dtr);
-               fepcmd(ch, SETMODEM, 0, ch->m_dtr | ch->m_rts, 10, 1);
-       }
-       memoff(ch);
-
-       /*
-        * The channel has officialy been closed. The next time it is opened it
-        * will have to reinitialized. Set a flag to indicate this.
-        */
-       /* Prevent future Digi programmed interrupts from coming active */
-       port->flags &= ~ASYNC_INITIALIZED;
-       spin_unlock_irqrestore(&epca_lock, flags);
-}
-
-static void pc_hangup(struct tty_struct *tty)
-{
-       struct channel *ch;
-
-       /*
-        * verifyChannel returns the channel from the tty struct if it is
-        * valid. This serves as a sanity check.
-        */
-       ch = verifyChannel(tty);
-       if (ch != NULL) {
-               pc_flush_buffer(tty);
-               tty_ldisc_flush(tty);
-               shutdown(ch, tty);
-
-               ch->event = 0;  /* FIXME: review locking of ch->event */
-               tty_port_hangup(&ch->port);
-       }
-}
-
-static int pc_write(struct tty_struct *tty,
-                       const unsigned char *buf, int bytesAvailable)
-{
-       unsigned int head, tail;
-       int dataLen;
-       int size;
-       int amountCopied;
-       struct channel *ch;
-       unsigned long flags;
-       int remain;
-       struct board_chan __iomem *bc;
-
-       /*
-        * pc_write is primarily called directly by the kernel routine
-        * tty_write (Though it can also be called by put_char) found in
-        * tty_io.c. pc_write is passed a line discipline buffer where the data
-        * to be written out is stored. The line discipline implementation
-        * itself is done at the kernel level and is not brought into the
-        * driver.
-        */
-
-       /*
-        * verifyChannel returns the channel from the tty struct if it is
-        * valid. This serves as a sanity check.
-        */
-       ch = verifyChannel(tty);
-       if (ch == NULL)
-               return 0;
-
-       /* Make a pointer to the channel data structure found on the board. */
-       bc   = ch->brdchan;
-       size = ch->txbufsize;
-       amountCopied = 0;
-
-       spin_lock_irqsave(&epca_lock, flags);
-       globalwinon(ch);
-
-       head = readw(&bc->tin) & (size - 1);
-       tail = readw(&bc->tout);
-
-       if (tail != readw(&bc->tout))
-               tail = readw(&bc->tout);
-       tail &= (size - 1);
-
-       if (head >= tail) {
-               /* head has not wrapped */
-               /*
-                * remain (much like dataLen above) represents the total amount
-                * of space available on the card for data. Here dataLen
-                * represents the space existing between the head pointer and
-                * the end of buffer. This is important because a memcpy cannot
-                * be told to automatically wrap around when it hits the buffer
-                * end.
-                */
-               dataLen = size - head;
-               remain = size - (head - tail) - 1;
-       } else {
-               /* head has wrapped around */
-               remain = tail - head - 1;
-               dataLen = remain;
-       }
-       /*
-        * Check the space on the card. If we have more data than space; reduce
-        * the amount of data to fit the space.
-        */
-       bytesAvailable = min(remain, bytesAvailable);
-       txwinon(ch);
-       while (bytesAvailable > 0) {
-               /* there is data to copy onto card */
-
-               /*
-                * If head is not wrapped, the below will make sure the first
-                * data copy fills to the end of card buffer.
-                */
-               dataLen = min(bytesAvailable, dataLen);
-               memcpy_toio(ch->txptr + head, buf, dataLen);
-               buf += dataLen;
-               head += dataLen;
-               amountCopied += dataLen;
-               bytesAvailable -= dataLen;
-
-               if (head >= size) {
-                       head = 0;
-                       dataLen = tail;
-               }
-       }
-       ch->statusflags |= TXBUSY;
-       globalwinon(ch);
-       writew(head, &bc->tin);
-
-       if ((ch->statusflags & LOWWAIT) == 0)  {
-               ch->statusflags |= LOWWAIT;
-               writeb(1, &bc->ilow);
-       }
-       memoff(ch);
-       spin_unlock_irqrestore(&epca_lock, flags);
-       return amountCopied;
-}
-
-static int pc_write_room(struct tty_struct *tty)
-{
-       int remain = 0;
-       struct channel *ch;
-       unsigned long flags;
-       unsigned int head, tail;
-       struct board_chan __iomem *bc;
-       /*
-        * verifyChannel returns the channel from the tty struct if it is
-        * valid. This serves as a sanity check.
-        */
-       ch = verifyChannel(tty);
-       if (ch != NULL) {
-               spin_lock_irqsave(&epca_lock, flags);
-               globalwinon(ch);
-
-               bc   = ch->brdchan;
-               head = readw(&bc->tin) & (ch->txbufsize - 1);
-               tail = readw(&bc->tout);
-
-               if (tail != readw(&bc->tout))
-                       tail = readw(&bc->tout);
-               /* Wrap tail if necessary */
-               tail &= (ch->txbufsize - 1);
-               remain = tail - head - 1;
-               if (remain < 0)
-                       remain += ch->txbufsize;
-
-               if (remain && (ch->statusflags & LOWWAIT) == 0) {
-                       ch->statusflags |= LOWWAIT;
-                       writeb(1, &bc->ilow);
-               }
-               memoff(ch);
-               spin_unlock_irqrestore(&epca_lock, flags);
-       }
-       /* Return how much room is left on card */
-       return remain;
-}
-
-static int pc_chars_in_buffer(struct tty_struct *tty)
-{
-       int chars;
-       unsigned int ctail, head, tail;
-       int remain;
-       unsigned long flags;
-       struct channel *ch;
-       struct board_chan __iomem *bc;
-       /*
-        * verifyChannel returns the channel from the tty struct if it is
-        * valid. This serves as a sanity check.
-        */
-       ch = verifyChannel(tty);
-       if (ch == NULL)
-               return 0;
-
-       spin_lock_irqsave(&epca_lock, flags);
-       globalwinon(ch);
-
-       bc = ch->brdchan;
-       tail = readw(&bc->tout);
-       head = readw(&bc->tin);
-       ctail = readw(&ch->mailbox->cout);
-
-       if (tail == head && readw(&ch->mailbox->cin) == ctail &&
-                                               readb(&bc->tbusy) == 0)
-               chars = 0;
-       else  { /* Begin if some space on the card has been used */
-               head = readw(&bc->tin) & (ch->txbufsize - 1);
-               tail &= (ch->txbufsize - 1);
-               /*
-                * The logic here is basically opposite of the above
-                * pc_write_room here we are finding the amount of bytes in the
-                * buffer filled. Not the amount of bytes empty.
-                */
-               remain = tail - head - 1;
-               if (remain < 0)
-                       remain += ch->txbufsize;
-               chars = (int)(ch->txbufsize - remain);
-               /*
-                * Make it possible to wakeup anything waiting for output in
-                * tty_ioctl.c, etc.
-                *
-                * If not already set. Setup an event to indicate when the
-                * transmit buffer empties.
-                */
-               if (!(ch->statusflags & EMPTYWAIT))
-                       setup_empty_event(tty, ch);
-       } /* End if some space on the card has been used */
-       memoff(ch);
-       spin_unlock_irqrestore(&epca_lock, flags);
-       /* Return number of characters residing on card. */
-       return chars;
-}
-
-static void pc_flush_buffer(struct tty_struct *tty)
-{
-       unsigned int tail;
-       unsigned long flags;
-       struct channel *ch;
-       struct board_chan __iomem *bc;
-       /*
-        * verifyChannel returns the channel from the tty struct if it is
-        * valid. This serves as a sanity check.
-        */
-       ch = verifyChannel(tty);
-       if (ch == NULL)
-               return;
-
-       spin_lock_irqsave(&epca_lock, flags);
-       globalwinon(ch);
-       bc   = ch->brdchan;
-       tail = readw(&bc->tout);
-       /* Have FEP move tout pointer; effectively flushing transmit buffer */
-       fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0);
-       memoff(ch);
-       spin_unlock_irqrestore(&epca_lock, flags);
-       tty_wakeup(tty);
-}
-
-static void pc_flush_chars(struct tty_struct *tty)
-{
-       struct channel *ch;
-       /*
-        * verifyChannel returns the channel from the tty struct if it is
-        * valid. This serves as a sanity check.
-        */
-       ch = verifyChannel(tty);
-       if (ch != NULL) {
-               unsigned long flags;
-               spin_lock_irqsave(&epca_lock, flags);
-               /*
-                * If not already set and the transmitter is busy setup an
-                * event to indicate when the transmit empties.
-                */
-               if ((ch->statusflags & TXBUSY) &&
-                               !(ch->statusflags & EMPTYWAIT))
-                       setup_empty_event(tty, ch);
-               spin_unlock_irqrestore(&epca_lock, flags);
-       }
-}
-
-static int epca_carrier_raised(struct tty_port *port)
-{
-       struct channel *ch = container_of(port, struct channel, port);
-       if (ch->imodem & ch->dcd)
-               return 1;
-       return 0;
-}
-
-static void epca_dtr_rts(struct tty_port *port, int onoff)
-{
-}
-
-static int pc_open(struct tty_struct *tty, struct file *filp)
-{
-       struct channel *ch;
-       struct tty_port *port;
-       unsigned long flags;
-       int line, retval, boardnum;
-       struct board_chan __iomem *bc;
-       unsigned int head;
-
-       line = tty->index;
-       if (line < 0 || line >= nbdevs)
-               return -ENODEV;
-
-       ch = &digi_channels[line];
-       port = &ch->port;
-       boardnum = ch->boardnum;
-
-       /* Check status of board configured in system.  */
-
-       /*
-        * I check to see if the epca_setup routine detected a user error. It
-        * might be better to put this in pc_init, but for the moment it goes
-        * here.
-        */
-       if (invalid_lilo_config) {
-               if (setup_error_code & INVALID_BOARD_TYPE)
-                       printk(KERN_ERR "epca: pc_open: Invalid board type specified in kernel options.\n");
-               if (setup_error_code & INVALID_NUM_PORTS)
-                       printk(KERN_ERR "epca: pc_open: Invalid number of ports specified in kernel options.\n");
-               if (setup_error_code & INVALID_MEM_BASE)
-                       printk(KERN_ERR "epca: pc_open: Invalid board memory address specified in kernel options.\n");
-               if (setup_error_code & INVALID_PORT_BASE)
-                       printk(KERN_ERR "epca; pc_open: Invalid board port address specified in kernel options.\n");
-               if (setup_error_code & INVALID_BOARD_STATUS)
-                       printk(KERN_ERR "epca: pc_open: Invalid board status specified in kernel options.\n");
-               if (setup_error_code & INVALID_ALTPIN)
-                       printk(KERN_ERR "epca: pc_open: Invalid board altpin specified in kernel options;\n");
-               tty->driver_data = NULL;   /* Mark this device as 'down' */
-               return -ENODEV;
-       }
-       if (boardnum >= num_cards || boards[boardnum].status == DISABLED)  {
-               tty->driver_data = NULL;   /* Mark this device as 'down' */
-               return(-ENODEV);
-       }
-
-       bc = ch->brdchan;
-       if (bc == NULL) {
-               tty->driver_data = NULL;
-               return -ENODEV;
-       }
-
-       spin_lock_irqsave(&port->lock, flags);
-       /*
-        * Every time a channel is opened, increment a counter. This is
-        * necessary because we do not wish to flush and shutdown the channel
-        * until the last app holding the channel open, closes it.
-        */
-       port->count++;
-       /*
-        * Set a kernel structures pointer to our local channel structure. This
-        * way we can get to it when passed only a tty struct.
-        */
-       tty->driver_data = ch;
-       port->tty = tty;
-       /*
-        * If this is the first time the channel has been opened, initialize
-        * the tty->termios struct otherwise let pc_close handle it.
-        */
-       spin_lock(&epca_lock);
-       globalwinon(ch);
-       ch->statusflags = 0;
-
-       /* Save boards current modem status */
-       ch->imodem = readb(&bc->mstat);
-
-       /*
-        * Set receive head and tail ptrs to each other. This indicates no data
-        * available to read.
-        */
-       head = readw(&bc->rin);
-       writew(head, &bc->rout);
-
-       /* Set the channels associated tty structure */
-
-       /*
-        * The below routine generally sets up parity, baud, flow control
-        * issues, etc.... It effect both control flags and input flags.
-        */
-       epcaparam(tty, ch);
-       memoff(ch);
-       spin_unlock(&epca_lock);
-       port->flags |= ASYNC_INITIALIZED;
-       spin_unlock_irqrestore(&port->lock, flags);
-
-       retval = tty_port_block_til_ready(port, tty, filp);
-       if (retval)
-               return retval;
-       /*
-        * Set this again in case a hangup set it to zero while this open() was
-        * waiting for the line...
-        */
-       spin_lock_irqsave(&port->lock, flags);
-       port->tty = tty;
-       spin_lock(&epca_lock);
-       globalwinon(ch);
-       /* Enable Digi Data events */
-       writeb(1, &bc->idata);
-       memoff(ch);
-       spin_unlock(&epca_lock);
-       spin_unlock_irqrestore(&port->lock, flags);
-       return 0;
-}
-
-static int __init epca_module_init(void)
-{
-       return pc_init();
-}
-module_init(epca_module_init);
-
-static struct pci_driver epca_driver;
-
-static void __exit epca_module_exit(void)
-{
-       int               count, crd;
-       struct board_info *bd;
-       struct channel    *ch;
-
-       del_timer_sync(&epca_timer);
-
-       if (tty_unregister_driver(pc_driver) ||
-                               tty_unregister_driver(pc_info)) {
-               printk(KERN_WARNING "epca: cleanup_module failed to un-register tty driver\n");
-               return;
-       }
-       put_tty_driver(pc_driver);
-       put_tty_driver(pc_info);
-
-       for (crd = 0; crd < num_cards; crd++) {
-               bd = &boards[crd];
-               if (!bd) { /* sanity check */
-                       printk(KERN_ERR "<Error> - Digi : cleanup_module failed\n");
-                       return;
-               }
-               ch = card_ptr[crd];
-               for (count = 0; count < bd->numports; count++, ch++) {
-                       struct tty_struct *tty = tty_port_tty_get(&ch->port);
-                       if (tty) {
-                               tty_hangup(tty);
-                               tty_kref_put(tty);
-                       }
-               }
-       }
-       pci_unregister_driver(&epca_driver);
-}
-module_exit(epca_module_exit);
-
-static const struct tty_operations pc_ops = {
-       .open = pc_open,
-       .close = pc_close,
-       .write = pc_write,
-       .write_room = pc_write_room,
-       .flush_buffer = pc_flush_buffer,
-       .chars_in_buffer = pc_chars_in_buffer,
-       .flush_chars = pc_flush_chars,
-       .ioctl = pc_ioctl,
-       .set_termios = pc_set_termios,
-       .stop = pc_stop,
-       .start = pc_start,
-       .throttle = pc_throttle,
-       .unthrottle = pc_unthrottle,
-       .hangup = pc_hangup,
-       .break_ctl = pc_send_break
-};
-
-static const struct tty_port_operations epca_port_ops = {
-       .carrier_raised = epca_carrier_raised,
-       .dtr_rts = epca_dtr_rts,
-};
-
-static int info_open(struct tty_struct *tty, struct file *filp)
-{
-       return 0;
-}
-
-static const struct tty_operations info_ops = {
-       .open = info_open,
-       .ioctl = info_ioctl,
-};
-
-static int __init pc_init(void)
-{
-       int crd;
-       struct board_info *bd;
-       unsigned char board_id = 0;
-       int err = -ENOMEM;
-
-       int pci_boards_found, pci_count;
-
-       pci_count = 0;
-
-       pc_driver = alloc_tty_driver(MAX_ALLOC);
-       if (!pc_driver)
-               goto out1;
-
-       pc_info = alloc_tty_driver(MAX_ALLOC);
-       if (!pc_info)
-               goto out2;
-
-       /*
-        * If epca_setup has not been ran by LILO set num_cards to defaults;
-        * copy board structure defined by digiConfig into drivers board
-        * structure. Note : If LILO has ran epca_setup then epca_setup will
-        * handle defining num_cards as well as copying the data into the board
-        * structure.
-        */
-       if (!liloconfig) {
-               /* driver has been configured via. epcaconfig */
-               nbdevs = NBDEVS;
-               num_cards = NUMCARDS;
-               memcpy(&boards, &static_boards,
-                      sizeof(struct board_info) * NUMCARDS);
-       }
-
-       /*
-        * Note : If lilo was used to configure the driver and the ignore
-        * epcaconfig option was choosen (digiepca=2) then nbdevs and num_cards
-        * will equal 0 at this point. This is okay; PCI cards will still be
-        * picked up if detected.
-        */
-
-       /*
-        * Set up interrupt, we will worry about memory allocation in
-        * post_fep_init.
-        */
-       printk(KERN_INFO "DIGI epca driver version %s loaded.\n", VERSION);
-
-       /*
-        * NOTE : This code assumes that the number of ports found in the
-        * boards array is correct. This could be wrong if the card in question
-        * is PCI (And therefore has no ports entry in the boards structure.)
-        * The rest of the information will be valid for PCI because the
-        * beginning of pc_init scans for PCI and determines i/o and base
-        * memory addresses. I am not sure if it is possible to read the number
-        * of ports supported by the card prior to it being booted (Since that
-        * is the state it is in when pc_init is run). Because it is not
-        * possible to query the number of supported ports until after the card
-        * has booted; we are required to calculate the card_ptrs as the card
-        * is initialized (Inside post_fep_init). The negative thing about this
-        * approach is that digiDload's call to GET_INFO will have a bad port
-        * value. (Since this is called prior to post_fep_init.)
-        */
-       pci_boards_found = 0;
-       if (num_cards < MAXBOARDS)
-               pci_boards_found += init_PCI();
-       num_cards += pci_boards_found;
-
-       pc_driver->owner = THIS_MODULE;
-       pc_driver->name = "ttyD";
-       pc_driver->major = DIGI_MAJOR;
-       pc_driver->minor_start = 0;
-       pc_driver->type = TTY_DRIVER_TYPE_SERIAL;
-       pc_driver->subtype = SERIAL_TYPE_NORMAL;
-       pc_driver->init_termios = tty_std_termios;
-       pc_driver->init_termios.c_iflag = 0;
-       pc_driver->init_termios.c_oflag = 0;
-       pc_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
-       pc_driver->init_termios.c_lflag = 0;
-       pc_driver->init_termios.c_ispeed = 9600;
-       pc_driver->init_termios.c_ospeed = 9600;
-       pc_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_HARDWARE_BREAK;
-       tty_set_operations(pc_driver, &pc_ops);
-
-       pc_info->owner = THIS_MODULE;
-       pc_info->name = "digi_ctl";
-       pc_info->major = DIGIINFOMAJOR;
-       pc_info->minor_start = 0;
-       pc_info->type = TTY_DRIVER_TYPE_SERIAL;
-       pc_info->subtype = SERIAL_TYPE_INFO;
-       pc_info->init_termios = tty_std_termios;
-       pc_info->init_termios.c_iflag = 0;
-       pc_info->init_termios.c_oflag = 0;
-       pc_info->init_termios.c_lflag = 0;
-       pc_info->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
-       pc_info->init_termios.c_ispeed = 9600;
-       pc_info->init_termios.c_ospeed = 9600;
-       pc_info->flags = TTY_DRIVER_REAL_RAW;
-       tty_set_operations(pc_info, &info_ops);
-
-
-       for (crd = 0; crd < num_cards; crd++) {
-               /*
-                * This is where the appropriate memory handlers for the
-                * hardware is set. Everything at runtime blindly jumps through
-                * these vectors.
-                */
-
-               /* defined in epcaconfig.h */
-               bd = &boards[crd];
-
-               switch (bd->type) {
-               case PCXEM:
-               case EISAXEM:
-                       bd->memwinon     = pcxem_memwinon;
-                       bd->memwinoff    = pcxem_memwinoff;
-                       bd->globalwinon  = pcxem_globalwinon;
-                       bd->txwinon      = pcxem_txwinon;
-                       bd->rxwinon      = pcxem_rxwinon;
-                       bd->memoff       = pcxem_memoff;
-                       bd->assertgwinon = dummy_assertgwinon;
-                       bd->assertmemoff = dummy_assertmemoff;
-                       break;
-
-               case PCIXEM:
-               case PCIXRJ:
-               case PCIXR:
-                       bd->memwinon     = dummy_memwinon;
-                       bd->memwinoff    = dummy_memwinoff;
-                       bd->globalwinon  = dummy_globalwinon;
-                       bd->txwinon      = dummy_txwinon;
-                       bd->rxwinon      = dummy_rxwinon;
-                       bd->memoff       = dummy_memoff;
-                       bd->assertgwinon = dummy_assertgwinon;
-                       bd->assertmemoff = dummy_assertmemoff;
-                       break;
-
-               case PCXE:
-               case PCXEVE:
-                       bd->memwinon     = pcxe_memwinon;
-                       bd->memwinoff    = pcxe_memwinoff;
-                       bd->globalwinon  = pcxe_globalwinon;
-                       bd->txwinon      = pcxe_txwinon;
-                       bd->rxwinon      = pcxe_rxwinon;
-                       bd->memoff       = pcxe_memoff;
-                       bd->assertgwinon = dummy_assertgwinon;
-                       bd->assertmemoff = dummy_assertmemoff;
-                       break;
-
-               case PCXI:
-               case PC64XE:
-                       bd->memwinon     = pcxi_memwinon;
-                       bd->memwinoff    = pcxi_memwinoff;
-                       bd->globalwinon  = pcxi_globalwinon;
-                       bd->txwinon      = pcxi_txwinon;
-                       bd->rxwinon      = pcxi_rxwinon;
-                       bd->memoff       = pcxi_memoff;
-                       bd->assertgwinon = pcxi_assertgwinon;
-                       bd->assertmemoff = pcxi_assertmemoff;
-                       break;
-
-               default:
-                       break;
-               }
-
-               /*
-                * Some cards need a memory segment to be defined for use in
-                * transmit and receive windowing operations. These boards are
-                * listed in the below switch. In the case of the XI the amount
-                * of memory on the board is variable so the memory_seg is also
-                * variable. This code determines what they segment should be.
-                */
-               switch (bd->type) {
-               case PCXE:
-               case PCXEVE:
-               case PC64XE:
-                       bd->memory_seg = 0xf000;
-                       break;
-
-               case PCXI:
-                       board_id = inb((int)bd->port);
-                       if ((board_id & 0x1) == 0x1) {
-                               /* it's an XI card */
-                               /* Is it a 64K board */
-                               if ((board_id & 0x30) == 0)
-                                       bd->memory_seg = 0xf000;
-
-                               /* Is it a 128K board */
-                               if ((board_id & 0x30) == 0x10)
-                                       bd->memory_seg = 0xe000;
-
-                               /* Is is a 256K board */
-                               if ((board_id & 0x30) == 0x20)
-                                       bd->memory_seg = 0xc000;
-
-                               /* Is it a 512K board */
-                               if ((board_id & 0x30) == 0x30)
-                                       bd->memory_seg = 0x8000;
-                       } else
-                               printk(KERN_ERR "epca: Board at 0x%x doesn't appear to be an XI\n", (int)bd->port);
-                       break;
-               }
-       }
-
-       err = tty_register_driver(pc_driver);
-       if (err) {
-               printk(KERN_ERR "Couldn't register Digi PC/ driver");
-               goto out3;
-       }
-
-       err = tty_register_driver(pc_info);
-       if (err) {
-               printk(KERN_ERR "Couldn't register Digi PC/ info ");
-               goto out4;
-       }
-
-       /* Start up the poller to check for events on all enabled boards */
-       init_timer(&epca_timer);
-       epca_timer.function = epcapoll;
-       mod_timer(&epca_timer, jiffies + HZ/25);
-       return 0;
-
-out4:
-       tty_unregister_driver(pc_driver);
-out3:
-       put_tty_driver(pc_info);
-out2:
-       put_tty_driver(pc_driver);
-out1:
-       return err;
-}
-
-static void post_fep_init(unsigned int crd)
-{
-       int i;
-       void __iomem *memaddr;
-       struct global_data __iomem *gd;
-       struct board_info *bd;
-       struct board_chan __iomem *bc;
-       struct channel *ch;
-       int shrinkmem = 0, lowwater;
-
-       /*
-        * This call is made by the user via. the ioctl call DIGI_INIT. It is
-        * responsible for setting up all the card specific stuff.
-        */
-       bd = &boards[crd];
-
-       /*
-        * If this is a PCI board, get the port info. Remember PCI cards do not
-        * have entries into the epcaconfig.h file, so we can't get the number
-        * of ports from it. Unfortunetly, this means that anyone doing a
-        * DIGI_GETINFO before the board has booted will get an invalid number
-        * of ports returned (It should return 0). Calls to DIGI_GETINFO after
-        * DIGI_INIT has been called will return the proper values.
-        */
-       if (bd->type >= PCIXEM) { /* Begin get PCI number of ports */
-               /*
-                * Below we use XEMPORTS as a memory offset regardless of which
-                * PCI card it is. This is because all of the supported PCI
-                * cards have the same memory offset for the channel data. This
-                * will have to be changed if we ever develop a PCI/XE card.
-                * NOTE : The FEP manual states that the port offset is 0xC22
-                * as opposed to 0xC02. This is only true for PC/XE, and PC/XI
-                * cards; not for the XEM, or CX series. On the PCI cards the
-                * number of ports is determined by reading a ID PROM located
-                * in the box attached to the card. The card can then determine
-                * the index the id to determine the number of ports available.
-                * (FYI - The id should be located at 0x1ac (And may use up to
-                * 4 bytes if the box in question is a XEM or CX)).
-                */
-               /* PCI cards are already remapped at this point ISA are not */
-               bd->numports = readw(bd->re_map_membase + XEMPORTS);
-               epcaassert(bd->numports <= 64, "PCI returned a invalid number of ports");
-               nbdevs += (bd->numports);
-       } else {
-               /* Fix up the mappings for ISA/EISA etc */
-               /* FIXME: 64K - can we be smarter ? */
-               bd->re_map_membase = ioremap_nocache(bd->membase, 0x10000);
-       }
-
-       if (crd != 0)
-               card_ptr[crd] = card_ptr[crd-1] + boards[crd-1].numports;
-       else
-               card_ptr[crd] = &digi_channels[crd]; /* <- For card 0 only */
-
-       ch = card_ptr[crd];
-       epcaassert(ch <= &digi_channels[nbdevs - 1], "ch out of range");
-
-       memaddr = bd->re_map_membase;
-
-       /*
-        * The below assignment will set bc to point at the BEGINING of the
-        * cards channel structures. For 1 card there will be between 8 and 64
-        * of these structures.
-        */
-       bc = memaddr + CHANSTRUCT;
-
-       /*
-        * The below assignment will set gd to point at the BEGINING of global
-        * memory address 0xc00. The first data in that global memory actually
-        * starts at address 0xc1a. The command in pointer begins at 0xd10.
-        */
-       gd = memaddr + GLOBAL;
-
-       /*
-        * XEPORTS (address 0xc22) points at the number of channels the card
-        * supports. (For 64XE, XI, XEM, and XR use 0xc02)
-        */
-       if ((bd->type == PCXEVE || bd->type == PCXE) &&
-                                       (readw(memaddr + XEPORTS) < 3))
-               shrinkmem = 1;
-       if (bd->type < PCIXEM)
-               if (!request_region((int)bd->port, 4, board_desc[bd->type]))
-                       return;
-       memwinon(bd, 0);
-
-       /*
-        * Remember ch is the main drivers channels structure, while bc is the
-        * cards channel structure.
-        */
-       for (i = 0; i < bd->numports; i++, ch++, bc++) {
-               unsigned long flags;
-               u16 tseg, rseg;
-
-               tty_port_init(&ch->port);
-               ch->port.ops = &epca_port_ops;
-               ch->brdchan = bc;
-               ch->mailbox = gd;
-               INIT_WORK(&ch->tqueue, do_softint);
-               ch->board = &boards[crd];
-
-               spin_lock_irqsave(&epca_lock, flags);
-               switch (bd->type) {
-               /*
-                * Since some of the boards use different bitmaps for
-                * their control signals we cannot hard code these
-                * values and retain portability. We virtualize this
-                * data here.
-                */
-               case EISAXEM:
-               case PCXEM:
-               case PCIXEM:
-               case PCIXRJ:
-               case PCIXR:
-                       ch->m_rts = 0x02;
-                       ch->m_dcd = 0x80;
-                       ch->m_dsr = 0x20;
-                       ch->m_cts = 0x10;
-                       ch->m_ri  = 0x40;
-                       ch->m_dtr = 0x01;
-                       break;
-
-               case PCXE:
-               case PCXEVE:
-               case PCXI:
-               case PC64XE:
-                       ch->m_rts = 0x02;
-                       ch->m_dcd = 0x08;
-                       ch->m_dsr = 0x10;
-                       ch->m_cts = 0x20;
-                       ch->m_ri  = 0x40;
-                       ch->m_dtr = 0x80;
-                       break;
-               }
-
-               if (boards[crd].altpin) {
-                       ch->dsr = ch->m_dcd;
-                       ch->dcd = ch->m_dsr;
-                       ch->digiext.digi_flags |= DIGI_ALTPIN;
-               } else {
-                       ch->dcd = ch->m_dcd;
-                       ch->dsr = ch->m_dsr;
-               }
-
-               ch->boardnum   = crd;
-               ch->channelnum = i;
-               ch->magic      = EPCA_MAGIC;
-               tty_port_tty_set(&ch->port, NULL);
-
-               if (shrinkmem) {
-                       fepcmd(ch, SETBUFFER, 32, 0, 0, 0);
-                       shrinkmem = 0;
-               }
-
-               tseg = readw(&bc->tseg);
-               rseg = readw(&bc->rseg);
-
-               switch (bd->type) {
-               case PCIXEM:
-               case PCIXRJ:
-               case PCIXR:
-                       /* Cover all the 2MEG cards */
-                       ch->txptr = memaddr + ((tseg << 4) & 0x1fffff);
-                       ch->rxptr = memaddr + ((rseg << 4) & 0x1fffff);
-                       ch->txwin = FEPWIN | (tseg >> 11);
-                       ch->rxwin = FEPWIN | (rseg >> 11);
-                       break;
-
-               case PCXEM:
-               case EISAXEM:
-                       /* Cover all the 32K windowed cards */
-                       /* Mask equal to window size - 1 */
-                       ch->txptr = memaddr + ((tseg << 4) & 0x7fff);
-                       ch->rxptr = memaddr + ((rseg << 4) & 0x7fff);
-                       ch->txwin = FEPWIN | (tseg >> 11);
-                       ch->rxwin = FEPWIN | (rseg >> 11);
-                       break;
-
-               case PCXEVE:
-               case PCXE:
-                       ch->txptr = memaddr + (((tseg - bd->memory_seg) << 4)
-                                                               & 0x1fff);
-                       ch->txwin = FEPWIN | ((tseg - bd->memory_seg) >> 9);
-                       ch->rxptr = memaddr + (((rseg - bd->memory_seg) << 4)
-                                                               & 0x1fff);
-                       ch->rxwin = FEPWIN | ((rseg - bd->memory_seg) >> 9);
-                       break;
-
-               case PCXI:
-               case PC64XE:
-                       ch->txptr = memaddr + ((tseg - bd->memory_seg) << 4);
-                       ch->rxptr = memaddr + ((rseg - bd->memory_seg) << 4);
-                       ch->txwin = ch->rxwin = 0;
-                       break;
-               }
-
-               ch->txbufhead = 0;
-               ch->txbufsize = readw(&bc->tmax) + 1;
-
-               ch->rxbufhead = 0;
-               ch->rxbufsize = readw(&bc->rmax) + 1;
-
-               lowwater = ch->txbufsize >= 2000 ? 1024 : (ch->txbufsize / 2);
-
-               /* Set transmitter low water mark */
-               fepcmd(ch, STXLWATER, lowwater, 0, 10, 0);
-
-               /* Set receiver low water mark */
-               fepcmd(ch, SRXLWATER, (ch->rxbufsize / 4), 0, 10, 0);
-
-               /* Set receiver high water mark */
-               fepcmd(ch, SRXHWATER, (3 * ch->rxbufsize / 4), 0, 10, 0);
-
-               writew(100, &bc->edelay);
-               writeb(1, &bc->idata);
-
-               ch->startc  = readb(&bc->startc);
-               ch->stopc   = readb(&bc->stopc);
-               ch->startca = readb(&bc->startca);
-               ch->stopca  = readb(&bc->stopca);
-
-               ch->fepcflag = 0;
-               ch->fepiflag = 0;
-               ch->fepoflag = 0;
-               ch->fepstartc = 0;
-               ch->fepstopc = 0;
-               ch->fepstartca = 0;
-               ch->fepstopca = 0;
-
-               ch->port.close_delay = 50;
-
-               spin_unlock_irqrestore(&epca_lock, flags);
-       }
-
-       printk(KERN_INFO
-       "Digi PC/Xx Driver V%s:  %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n",
-                               VERSION, board_desc[bd->type], (long)bd->port,
-                                       (long)bd->membase, bd->numports);
-       memwinoff(bd, 0);
-}
-
-static void epcapoll(unsigned long ignored)
-{
-       unsigned long flags;
-       int crd;
-       unsigned int head, tail;
-       struct channel *ch;
-       struct board_info *bd;
-
-       /*
-        * This routine is called upon every timer interrupt. Even though the
-        * Digi series cards are capable of generating interrupts this method
-        * of non-looping polling is more efficient. This routine checks for
-        * card generated events (Such as receive data, are transmit buffer
-        * empty) and acts on those events.
-        */
-       for (crd = 0; crd < num_cards; crd++) {
-               bd = &boards[crd];
-               ch = card_ptr[crd];
-
-               if ((bd->status == DISABLED) || digi_poller_inhibited)
-                       continue;
-
-               /*
-                * assertmemoff is not needed here; indeed it is an empty
-                * subroutine. It is being kept because future boards may need
-                * this as well as some legacy boards.
-                */
-               spin_lock_irqsave(&epca_lock, flags);
-
-               assertmemoff(ch);
-
-               globalwinon(ch);
-
-               /*
-                * In this case head and tail actually refer to the event queue
-                * not the transmit or receive queue.
-                */
-               head = readw(&ch->mailbox->ein);
-               tail = readw(&ch->mailbox->eout);
-
-               /* If head isn't equal to tail we have an event */
-               if (head != tail)
-                       doevent(crd);
-               memoff(ch);
-
-               spin_unlock_irqrestore(&epca_lock, flags);
-       } /* End for each card */
-       mod_timer(&epca_timer, jiffies + (HZ / 25));
-}
-
-static void doevent(int crd)
-{
-       void __iomem *eventbuf;
-       struct channel *ch, *chan0;
-       static struct tty_struct *tty;
-       struct board_info *bd;
-       struct board_chan __iomem *bc;
-       unsigned int tail, head;
-       int event, channel;
-       int mstat, lstat;
-
-       /*
-        * This subroutine is called by epcapoll when an event is detected
-        * in the event queue. This routine responds to those events.
-        */
-       bd = &boards[crd];
-
-       chan0 = card_ptr[crd];
-       epcaassert(chan0 <= &digi_channels[nbdevs - 1], "ch out of range");
-       assertgwinon(chan0);
-       while ((tail = readw(&chan0->mailbox->eout)) !=
-                       (head = readw(&chan0->mailbox->ein))) {
-               /* Begin while something in event queue */
-               assertgwinon(chan0);
-               eventbuf = bd->re_map_membase + tail + ISTART;
-               /* Get the channel the event occurred on */
-               channel = readb(eventbuf);
-               /* Get the actual event code that occurred */
-               event = readb(eventbuf + 1);
-               /*
-                * The two assignments below get the current modem status
-                * (mstat) and the previous modem status (lstat). These are
-                * useful becuase an event could signal a change in modem
-                * signals itself.
-                */
-               mstat = readb(eventbuf + 2);
-               lstat = readb(eventbuf + 3);
-
-               ch = chan0 + channel;
-               if ((unsigned)channel >= bd->numports || !ch)  {
-                       if (channel >= bd->numports)
-                               ch = chan0;
-                       bc = ch->brdchan;
-                       goto next;
-               }
-
-               bc = ch->brdchan;
-               if (bc == NULL)
-                       goto next;
-
-               tty = tty_port_tty_get(&ch->port);
-               if (event & DATA_IND)  { /* Begin DATA_IND */
-                       receive_data(ch, tty);
-                       assertgwinon(ch);
-               } /* End DATA_IND */
-               /* else *//* Fix for DCD transition missed bug */
-               if (event & MODEMCHG_IND) {
-                       /* A modem signal change has been indicated */
-                       ch->imodem = mstat;
-                       if (test_bit(ASYNCB_CHECK_CD, &ch->port.flags)) {
-                               /* We are now receiving dcd */
-                               if (mstat & ch->dcd)
-                                       wake_up_interruptible(&ch->port.open_wait);
-                               else    /* No dcd; hangup */
-                                       pc_sched_event(ch, EPCA_EVENT_HANGUP);
-                       }
-               }
-               if (tty) {
-                       if (event & BREAK_IND) {
-                               /* A break has been indicated */
-                               tty_insert_flip_char(tty, 0, TTY_BREAK);
-                               tty_schedule_flip(tty);
-                       } else if (event & LOWTX_IND)  {
-                               if (ch->statusflags & LOWWAIT) {
-                                       ch->statusflags &= ~LOWWAIT;
-                                       tty_wakeup(tty);
-                               }
-                       } else if (event & EMPTYTX_IND) {
-                               /* This event is generated by
-                                  setup_empty_event */
-                               ch->statusflags &= ~TXBUSY;
-                               if (ch->statusflags & EMPTYWAIT) {
-                                       ch->statusflags &= ~EMPTYWAIT;
-                                       tty_wakeup(tty);
-                               }
-                       }
-                       tty_kref_put(tty);
-               }
-next:
-               globalwinon(ch);
-               BUG_ON(!bc);
-               writew(1, &bc->idata);
-               writew((tail + 4) & (IMAX - ISTART - 4), &chan0->mailbox->eout);
-               globalwinon(chan0);
-       } /* End while something in event queue */
-}
-
-static void fepcmd(struct channel *ch, int cmd, int word_or_byte,
-                                       int byte2, int ncmds, int bytecmd)
-{
-       unchar __iomem *memaddr;
-       unsigned int head, cmdTail, cmdStart, cmdMax;
-       long count;
-       int n;
-
-       /* This is the routine in which commands may be passed to the card. */
-
-       if (ch->board->status == DISABLED)
-               return;
-       assertgwinon(ch);
-       /* Remember head (As well as max) is just an offset not a base addr */
-       head = readw(&ch->mailbox->cin);
-       /* cmdStart is a base address */
-       cmdStart = readw(&ch->mailbox->cstart);
-       /*
-        * We do the addition below because we do not want a max pointer
-        * relative to cmdStart. We want a max pointer that points at the
-        * physical end of the command queue.
-        */
-       cmdMax = (cmdStart + 4 + readw(&ch->mailbox->cmax));
-       memaddr = ch->board->re_map_membase;
-
-       if (head >= (cmdMax - cmdStart) || (head & 03))  {
-               printk(KERN_ERR "line %d: Out of range, cmd = %x, head = %x\n",
-                                               __LINE__,  cmd, head);
-               printk(KERN_ERR "line %d: Out of range, cmdMax = %x, cmdStart = %x\n",
-                                               __LINE__,  cmdMax, cmdStart);
-               return;
-       }
-       if (bytecmd)  {
-               writeb(cmd, memaddr + head + cmdStart + 0);
-               writeb(ch->channelnum,  memaddr + head + cmdStart + 1);
-               /* Below word_or_byte is bits to set */
-               writeb(word_or_byte,  memaddr + head + cmdStart + 2);
-               /* Below byte2 is bits to reset */
-               writeb(byte2, memaddr + head + cmdStart + 3);
-       }  else {
-               writeb(cmd, memaddr + head + cmdStart + 0);
-               writeb(ch->channelnum,  memaddr + head + cmdStart + 1);
-               writeb(word_or_byte,  memaddr + head + cmdStart + 2);
-       }
-       head = (head + 4) & (cmdMax - cmdStart - 4);
-       writew(head, &ch->mailbox->cin);
-       count = FEPTIMEOUT;
-
-       for (;;) {
-               count--;
-               if (count == 0)  {
-                       printk(KERN_ERR "<Error> - Fep not responding in fepcmd()\n");
-                       return;
-               }
-               head = readw(&ch->mailbox->cin);
-               cmdTail = readw(&ch->mailbox->cout);
-               n = (head - cmdTail) & (cmdMax - cmdStart - 4);
-               /*
-                * Basically this will break when the FEP acknowledges the
-                * command by incrementing cmdTail (Making it equal to head).
-                */
-               if (n <= ncmds * (sizeof(short) * 4))
-                       break;
-       }
-}
-
-/*
- * Digi products use fields in their channels structures that are very similar
- * to the c_cflag and c_iflag fields typically found in UNIX termios
- * structures. The below three routines allow mappings between these hardware
- * "flags" and their respective Linux flags.
- */
-static unsigned termios2digi_h(struct channel *ch, unsigned cflag)
-{
-       unsigned res = 0;
-
-       if (cflag & CRTSCTS) {
-               ch->digiext.digi_flags |= (RTSPACE | CTSPACE);
-               res |= ((ch->m_cts) | (ch->m_rts));
-       }
-
-       if (ch->digiext.digi_flags & RTSPACE)
-               res |= ch->m_rts;
-
-       if (ch->digiext.digi_flags & DTRPACE)
-               res |= ch->m_dtr;
-
-       if (ch->digiext.digi_flags & CTSPACE)
-               res |= ch->m_cts;
-
-       if (ch->digiext.digi_flags & DSRPACE)
-               res |= ch->dsr;
-
-       if (ch->digiext.digi_flags & DCDPACE)
-               res |= ch->dcd;
-
-       if (res & (ch->m_rts))
-               ch->digiext.digi_flags |= RTSPACE;
-
-       if (res & (ch->m_cts))
-               ch->digiext.digi_flags |= CTSPACE;
-
-       return res;
-}
-
-static unsigned termios2digi_i(struct channel *ch, unsigned iflag)
-{
-       unsigned res = iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK |
-                                       INPCK | ISTRIP | IXON | IXANY | IXOFF);
-       if (ch->digiext.digi_flags & DIGI_AIXON)
-               res |= IAIXON;
-       return res;
-}
-
-static unsigned termios2digi_c(struct channel *ch, unsigned cflag)
-{
-       unsigned res = 0;
-       if (cflag & CBAUDEX) {
-               ch->digiext.digi_flags |= DIGI_FAST;
-               /*
-                * HUPCL bit is used by FEP to indicate fast baud table is to
-                * be used.
-                */
-               res |= FEP_HUPCL;
-       } else
-               ch->digiext.digi_flags &= ~DIGI_FAST;
-       /*
-        * CBAUD has bit position 0x1000 set these days to indicate Linux
-        * baud rate remap. Digi hardware can't handle the bit assignment.
-        * (We use a different bit assignment for high speed.). Clear this
-        * bit out.
-        */
-       res |= cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | CSTOPB | CSIZE);
-       /*
-        * This gets a little confusing. The Digi cards have their own
-        * representation of c_cflags controlling baud rate. For the most part
-        * this is identical to the Linux implementation. However; Digi
-        * supports one rate (76800) that Linux doesn't. This means that the
-        * c_cflag entry that would normally mean 76800 for Digi actually means
-        * 115200 under Linux. Without the below mapping, a stty 115200 would
-        * only drive the board at 76800. Since the rate 230400 is also found
-        * after 76800, the same problem afflicts us when we choose a rate of
-        * 230400. Without the below modificiation stty 230400 would actually
-        * give us 115200.
-        *
-        * There are two additional differences. The Linux value for CLOCAL
-        * (0x800; 0004000) has no meaning to the Digi hardware. Also in later
-        * releases of Linux; the CBAUD define has CBAUDEX (0x1000; 0010000)
-        * ored into it (CBAUD = 0x100f as opposed to 0xf). CBAUDEX should be
-        * checked for a screened out prior to termios2digi_c returning. Since
-        * CLOCAL isn't used by the board this can be ignored as long as the
-        * returned value is used only by Digi hardware.
-        */
-       if (cflag & CBAUDEX) {
-               /*
-                * The below code is trying to guarantee that only baud rates
-                * 115200 and 230400 are remapped. We use exclusive or because
-                * the various baud rates share common bit positions and
-                * therefore can't be tested for easily.
-                */
-               if ((!((cflag & 0x7) ^ (B115200 & ~CBAUDEX))) ||
-                   (!((cflag & 0x7) ^ (B230400 & ~CBAUDEX))))
-                       res += 1;
-       }
-       return res;
-}
-
-/* Caller must hold the locks */
-static void epcaparam(struct tty_struct *tty, struct channel *ch)
-{
-       unsigned int cmdHead;
-       struct ktermios *ts;
-       struct board_chan __iomem *bc;
-       unsigned mval, hflow, cflag, iflag;
-
-       bc = ch->brdchan;
-       epcaassert(bc != NULL, "bc out of range");
-
-       assertgwinon(ch);
-       ts = tty->termios;
-       if ((ts->c_cflag & CBAUD) == 0)  { /* Begin CBAUD detected */
-               cmdHead = readw(&bc->rin);
-               writew(cmdHead, &bc->rout);
-               cmdHead = readw(&bc->tin);
-               /* Changing baud in mid-stream transmission can be wonderful */
-               /*
-                * Flush current transmit buffer by setting cmdTail pointer
-                * (tout) to cmdHead pointer (tin). Hopefully the transmit
-                * buffer is empty.
-                */
-               fepcmd(ch, STOUT, (unsigned) cmdHead, 0, 0, 0);
-               mval = 0;
-       } else { /* Begin CBAUD not detected */
-               /*
-                * c_cflags have changed but that change had nothing to do with
-                * BAUD. Propagate the change to the card.
-                */
-               cflag = termios2digi_c(ch, ts->c_cflag);
-               if (cflag != ch->fepcflag)  {
-                       ch->fepcflag = cflag;
-                       /* Set baud rate, char size, stop bits, parity */
-                       fepcmd(ch, SETCTRLFLAGS, (unsigned) cflag, 0, 0, 0);
-               }
-               /*
-                * If the user has not forced CLOCAL and if the device is not a
-                * CALLOUT device (Which is always CLOCAL) we set flags such
-                * that the driver will wait on carrier detect.
-                */
-               if (ts->c_cflag & CLOCAL)
-                       clear_bit(ASYNCB_CHECK_CD, &ch->port.flags);
-               else
-                       set_bit(ASYNCB_CHECK_CD, &ch->port.flags);
-               mval = ch->m_dtr | ch->m_rts;
-       } /* End CBAUD not detected */
-       iflag = termios2digi_i(ch, ts->c_iflag);
-       /* Check input mode flags */
-       if (iflag != ch->fepiflag)  {
-               ch->fepiflag = iflag;
-               /*
-                * Command sets channels iflag structure on the board. Such
-                * things as input soft flow control, handling of parity
-                * errors, and break handling are all set here.
-                *
-                * break handling, parity handling, input stripping,
-                * flow control chars
-                */
-               fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0);
-       }
-       /*
-        * Set the board mint value for this channel. This will cause hardware
-        * events to be generated each time the DCD signal (Described in mint)
-        * changes.
-        */
-       writeb(ch->dcd, &bc->mint);
-       if ((ts->c_cflag & CLOCAL) || (ch->digiext.digi_flags & DIGI_FORCEDCD))
-               if (ch->digiext.digi_flags & DIGI_FORCEDCD)
-                       writeb(0, &bc->mint);
-       ch->imodem = readb(&bc->mstat);
-       hflow = termios2digi_h(ch, ts->c_cflag);
-       if (hflow != ch->hflow)  {
-               ch->hflow = hflow;
-               /*
-                * Hard flow control has been selected but the board is not
-                * using it. Activate hard flow control now.
-                */
-               fepcmd(ch, SETHFLOW, hflow, 0xff, 0, 1);
-       }
-       mval ^= ch->modemfake & (mval ^ ch->modem);
-
-       if (ch->omodem ^ mval)  {
-               ch->omodem = mval;
-               /*
-                * The below command sets the DTR and RTS mstat structure. If
-                * hard flow control is NOT active these changes will drive the
-                * output of the actual DTR and RTS lines. If hard flow control
-                * is active, the changes will be saved in the mstat structure
-                * and only asserted when hard flow control is turned off.
-                */
-
-               /* First reset DTR & RTS; then set them */
-               fepcmd(ch, SETMODEM, 0, ((ch->m_dtr)|(ch->m_rts)), 0, 1);
-               fepcmd(ch, SETMODEM, mval, 0, 0, 1);
-       }
-       if (ch->startc != ch->fepstartc || ch->stopc != ch->fepstopc)  {
-               ch->fepstartc = ch->startc;
-               ch->fepstopc = ch->stopc;
-               /*
-                * The XON / XOFF characters have changed; propagate these
-                * changes to the card.
-                */
-               fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1);
-       }
-       if (ch->startca != ch->fepstartca || ch->stopca != ch->fepstopca)  {
-               ch->fepstartca = ch->startca;
-               ch->fepstopca = ch->stopca;
-               /*
-                * Similar to the above, this time the auxilarly XON / XOFF
-                * characters have changed; propagate these changes to the card.
-                */
-               fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1);
-       }
-}
-
-/* Caller holds lock */
-static void receive_data(struct channel *ch, struct tty_struct *tty)
-{
-       unchar *rptr;
-       struct ktermios *ts = NULL;
-       struct board_chan __iomem *bc;
-       int dataToRead, wrapgap, bytesAvailable;
-       unsigned int tail, head;
-       unsigned int wrapmask;
-
-       /*
-        * This routine is called by doint when a receive data event has taken
-        * place.
-        */
-       globalwinon(ch);
-       if (ch->statusflags & RXSTOPPED)
-               return;
-       if (tty)
-               ts = tty->termios;
-       bc = ch->brdchan;
-       BUG_ON(!bc);
-       wrapmask = ch->rxbufsize - 1;
-
-       /*
-        * Get the head and tail pointers to the receiver queue. Wrap the head
-        * pointer if it has reached the end of the buffer.
-        */
-       head = readw(&bc->rin);
-       head &= wrapmask;
-       tail = readw(&bc->rout) & wrapmask;
-
-       bytesAvailable = (head - tail) & wrapmask;
-       if (bytesAvailable == 0)
-               return;
-
-       /* If CREAD bit is off or device not open, set TX tail to head */
-       if (!tty || !ts || !(ts->c_cflag & CREAD)) {
-               writew(head, &bc->rout);
-               return;
-       }
-
-       if (tty_buffer_request_room(tty, bytesAvailable + 1) == 0)
-               return;
-
-       if (readb(&bc->orun)) {
-               writeb(0, &bc->orun);
-               printk(KERN_WARNING "epca; overrun! DigiBoard device %s\n",
-                                                               tty->name);
-               tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-       }
-       rxwinon(ch);
-       while (bytesAvailable > 0) {
-               /* Begin while there is data on the card */
-               wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail;
-               /*
-                * Even if head has wrapped around only report the amount of
-                * data to be equal to the size - tail. Remember memcpy can't
-                * automaticly wrap around the receive buffer.
-                */
-               dataToRead = (wrapgap < bytesAvailable) ? wrapgap
-                                                       : bytesAvailable;
-               /* Make sure we don't overflow the buffer */
-               dataToRead = tty_prepare_flip_string(tty, &rptr, dataToRead);
-               if (dataToRead == 0)
-                       break;
-               /*
-                * Move data read from our card into the line disciplines
-                * buffer for translation if necessary.
-                */
-               memcpy_fromio(rptr, ch->rxptr + tail, dataToRead);
-               tail = (tail + dataToRead) & wrapmask;
-               bytesAvailable -= dataToRead;
-       } /* End while there is data on the card */
-       globalwinon(ch);
-       writew(tail, &bc->rout);
-       /* Must be called with global data */
-       tty_schedule_flip(tty);
-}
-
-static int info_ioctl(struct tty_struct *tty,
-                   unsigned int cmd, unsigned long arg)
-{
-       switch (cmd) {
-       case DIGI_GETINFO:
-               {
-                       struct digi_info di;
-                       int brd;
-
-                       if (get_user(brd, (unsigned int __user *)arg))
-                               return -EFAULT;
-                       if (brd < 0 || brd >= num_cards || num_cards == 0)
-                               return -ENODEV;
-
-                       memset(&di, 0, sizeof(di));
-
-                       di.board = brd;
-                       di.status = boards[brd].status;
-                       di.type = boards[brd].type ;
-                       di.numports = boards[brd].numports ;
-                       /* Legacy fixups - just move along nothing to see */
-                       di.port = (unsigned char *)boards[brd].port ;
-                       di.membase = (unsigned char *)boards[brd].membase ;
-
-                       if (copy_to_user((void __user *)arg, &di, sizeof(di)))
-                               return -EFAULT;
-                       break;
-
-               }
-
-       case DIGI_POLLER:
-               {
-                       int brd = arg & 0xff000000 >> 16;
-                       unsigned char state = arg & 0xff;
-
-                       if (brd < 0 || brd >= num_cards) {
-                               printk(KERN_ERR "epca: DIGI POLLER : brd not valid!\n");
-                               return -ENODEV;
-                       }
-                       digi_poller_inhibited = state;
-                       break;
-               }
-
-       case DIGI_INIT:
-               {
-                       /*
-                        * This call is made by the apps to complete the
-                        * initialization of the board(s). This routine is
-                        * responsible for setting the card to its initial
-                        * state and setting the drivers control fields to the
-                        * sutianle settings for the card in question.
-                        */
-                       int crd;
-                       for (crd = 0; crd < num_cards; crd++)
-                               post_fep_init(crd);
-                       break;
-               }
-       default:
-               return -ENOTTY;
-       }
-       return 0;
-}
-
-static int pc_tiocmget(struct tty_struct *tty)
-{
-       struct channel *ch = tty->driver_data;
-       struct board_chan __iomem *bc;
-       unsigned int mstat, mflag = 0;
-       unsigned long flags;
-
-       if (ch)
-               bc = ch->brdchan;
-       else
-               return -EINVAL;
-
-       spin_lock_irqsave(&epca_lock, flags);
-       globalwinon(ch);
-       mstat = readb(&bc->mstat);
-       memoff(ch);
-       spin_unlock_irqrestore(&epca_lock, flags);
-
-       if (mstat & ch->m_dtr)
-               mflag |= TIOCM_DTR;
-       if (mstat & ch->m_rts)
-               mflag |= TIOCM_RTS;
-       if (mstat & ch->m_cts)
-               mflag |= TIOCM_CTS;
-       if (mstat & ch->dsr)
-               mflag |= TIOCM_DSR;
-       if (mstat & ch->m_ri)
-               mflag |= TIOCM_RI;
-       if (mstat & ch->dcd)
-               mflag |= TIOCM_CD;
-       return mflag;
-}
-
-static int pc_tiocmset(struct tty_struct *tty,
-                      unsigned int set, unsigned int clear)
-{
-       struct channel *ch = tty->driver_data;
-       unsigned long flags;
-
-       if (!ch)
-               return -EINVAL;
-
-       spin_lock_irqsave(&epca_lock, flags);
-       /*
-        * I think this modemfake stuff is broken. It doesn't correctly reflect
-        * the behaviour desired by the TIOCM* ioctls. Therefore this is
-        * probably broken.
-        */
-       if (set & TIOCM_RTS) {
-               ch->modemfake |= ch->m_rts;
-               ch->modem |= ch->m_rts;
-       }
-       if (set & TIOCM_DTR) {
-               ch->modemfake |= ch->m_dtr;
-               ch->modem |= ch->m_dtr;
-       }
-       if (clear & TIOCM_RTS) {
-               ch->modemfake |= ch->m_rts;
-               ch->modem &= ~ch->m_rts;
-       }
-       if (clear & TIOCM_DTR) {
-               ch->modemfake |= ch->m_dtr;
-               ch->modem &= ~ch->m_dtr;
-       }
-       globalwinon(ch);
-       /*
-        * The below routine generally sets up parity, baud, flow control
-        * issues, etc.... It effect both control flags and input flags.
-        */
-       epcaparam(tty, ch);
-       memoff(ch);
-       spin_unlock_irqrestore(&epca_lock, flags);
-       return 0;
-}
-
-static int pc_ioctl(struct tty_struct *tty,
-                                       unsigned int cmd, unsigned long arg)
-{
-       digiflow_t dflow;
-       unsigned long flags;
-       unsigned int mflag, mstat;
-       unsigned char startc, stopc;
-       struct board_chan __iomem *bc;
-       struct channel *ch = tty->driver_data;
-       void __user *argp = (void __user *)arg;
-
-       if (ch)
-               bc = ch->brdchan;
-       else
-               return -EINVAL;
-       switch (cmd) {
-       case TIOCMODG:
-               mflag = pc_tiocmget(tty);
-               if (put_user(mflag, (unsigned long __user *)argp))
-                       return -EFAULT;
-               break;
-       case TIOCMODS:
-               if (get_user(mstat, (unsigned __user *)argp))
-                       return -EFAULT;
-               return pc_tiocmset(tty, mstat, ~mstat);
-       case TIOCSDTR:
-               spin_lock_irqsave(&epca_lock, flags);
-               ch->omodem |= ch->m_dtr;
-               globalwinon(ch);
-               fepcmd(ch, SETMODEM, ch->m_dtr, 0, 10, 1);
-               memoff(ch);
-               spin_unlock_irqrestore(&epca_lock, flags);
-               break;
-
-       case TIOCCDTR:
-               spin_lock_irqsave(&epca_lock, flags);
-               ch->omodem &= ~ch->m_dtr;
-               globalwinon(ch);
-               fepcmd(ch, SETMODEM, 0, ch->m_dtr, 10, 1);
-               memoff(ch);
-               spin_unlock_irqrestore(&epca_lock, flags);
-               break;
-       case DIGI_GETA:
-               if (copy_to_user(argp, &ch->digiext, sizeof(digi_t)))
-                       return -EFAULT;
-               break;
-       case DIGI_SETAW:
-       case DIGI_SETAF:
-               if (cmd == DIGI_SETAW) {
-                       /* Setup an event to indicate when the transmit
-                          buffer empties */
-                       spin_lock_irqsave(&epca_lock, flags);
-                       setup_empty_event(tty, ch);
-                       spin_unlock_irqrestore(&epca_lock, flags);
-                       tty_wait_until_sent(tty, 0);
-               } else {
-                       /* ldisc lock already held in ioctl */
-                       if (tty->ldisc->ops->flush_buffer)
-                               tty->ldisc->ops->flush_buffer(tty);
-               }
-               /* Fall Thru */
-       case DIGI_SETA:
-               if (copy_from_user(&ch->digiext, argp, sizeof(digi_t)))
-                       return -EFAULT;
-
-               if (ch->digiext.digi_flags & DIGI_ALTPIN)  {
-                       ch->dcd = ch->m_dsr;
-                       ch->dsr = ch->m_dcd;
-               } else {
-                       ch->dcd = ch->m_dcd;
-                       ch->dsr = ch->m_dsr;
-                       }
-
-               spin_lock_irqsave(&epca_lock, flags);
-               globalwinon(ch);
-
-               /*
-                * The below routine generally sets up parity, baud, flow
-                * control issues, etc.... It effect both control flags and
-                * input flags.
-                */
-               epcaparam(tty, ch);
-               memoff(ch);
-               spin_unlock_irqrestore(&epca_lock, flags);
-               break;
-
-       case DIGI_GETFLOW:
-       case DIGI_GETAFLOW:
-               spin_lock_irqsave(&epca_lock, flags);
-               globalwinon(ch);
-               if (cmd == DIGI_GETFLOW) {
-                       dflow.startc = readb(&bc->startc);
-                       dflow.stopc = readb(&bc->stopc);
-               } else {
-                       dflow.startc = readb(&bc->startca);
-                       dflow.stopc = readb(&bc->stopca);
-               }
-               memoff(ch);
-               spin_unlock_irqrestore(&epca_lock, flags);
-
-               if (copy_to_user(argp, &dflow, sizeof(dflow)))
-                       return -EFAULT;
-               break;
-
-       case DIGI_SETAFLOW:
-       case DIGI_SETFLOW:
-               if (cmd == DIGI_SETFLOW) {
-                       startc = ch->startc;
-                       stopc = ch->stopc;
-               } else {
-                       startc = ch->startca;
-                       stopc = ch->stopca;
-               }
-
-               if (copy_from_user(&dflow, argp, sizeof(dflow)))
-                       return -EFAULT;
-
-               if (dflow.startc != startc || dflow.stopc != stopc) {
-                       /* Begin  if setflow toggled */
-                       spin_lock_irqsave(&epca_lock, flags);
-                       globalwinon(ch);
-
-                       if (cmd == DIGI_SETFLOW) {
-                               ch->fepstartc = ch->startc = dflow.startc;
-                               ch->fepstopc = ch->stopc = dflow.stopc;
-                               fepcmd(ch, SONOFFC, ch->fepstartc,
-                                               ch->fepstopc, 0, 1);
-                       } else {
-                               ch->fepstartca = ch->startca = dflow.startc;
-                               ch->fepstopca  = ch->stopca = dflow.stopc;
-                               fepcmd(ch, SAUXONOFFC, ch->fepstartca,
-                                               ch->fepstopca, 0, 1);
-                       }
-
-                       if (ch->statusflags & TXSTOPPED)
-                               pc_start(tty);
-
-                       memoff(ch);
-                       spin_unlock_irqrestore(&epca_lock, flags);
-               } /* End if setflow toggled */
-               break;
-       default:
-               return -ENOIOCTLCMD;
-       }
-       return 0;
-}
-
-static void pc_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
-{
-       struct channel *ch;
-       unsigned long flags;
-       /*
-        * verifyChannel returns the channel from the tty struct if it is
-        * valid. This serves as a sanity check.
-        */
-       ch = verifyChannel(tty);
-
-       if (ch != NULL)  { /* Begin if channel valid */
-               spin_lock_irqsave(&epca_lock, flags);
-               globalwinon(ch);
-               epcaparam(tty, ch);
-               memoff(ch);
-               spin_unlock_irqrestore(&epca_lock, flags);
-
-               if ((old_termios->c_cflag & CRTSCTS) &&
-                        ((tty->termios->c_cflag & CRTSCTS) == 0))
-                       tty->hw_stopped = 0;
-
-               if (!(old_termios->c_cflag & CLOCAL) &&
-                        (tty->termios->c_cflag & CLOCAL))
-                       wake_up_interruptible(&ch->port.open_wait);
-
-       } /* End if channel valid */
-}
-
-static void do_softint(struct work_struct *work)
-{
-       struct channel *ch = container_of(work, struct channel, tqueue);
-       /* Called in response to a modem change event */
-       if (ch && ch->magic == EPCA_MAGIC) {
-               struct tty_struct *tty = tty_port_tty_get(&ch->port);
-
-               if (tty && tty->driver_data) {
-                       if (test_and_clear_bit(EPCA_EVENT_HANGUP, &ch->event)) {
-                               tty_hangup(tty);
-                               wake_up_interruptible(&ch->port.open_wait);
-                               clear_bit(ASYNCB_NORMAL_ACTIVE,
-                                               &ch->port.flags);
-                       }
-               }
-               tty_kref_put(tty);
-       }
-}
-
-/*
- * pc_stop and pc_start provide software flow control to the routine and the
- * pc_ioctl routine.
- */
-static void pc_stop(struct tty_struct *tty)
-{
-       struct channel *ch;
-       unsigned long flags;
-       /*
-        * verifyChannel returns the channel from the tty struct if it is
-        * valid. This serves as a sanity check.
-        */
-       ch = verifyChannel(tty);
-       if (ch != NULL) {
-               spin_lock_irqsave(&epca_lock, flags);
-               if ((ch->statusflags & TXSTOPPED) == 0) {
-                       /* Begin if transmit stop requested */
-                       globalwinon(ch);
-                       /* STOP transmitting now !! */
-                       fepcmd(ch, PAUSETX, 0, 0, 0, 0);
-                       ch->statusflags |= TXSTOPPED;
-                       memoff(ch);
-               } /* End if transmit stop requested */
-               spin_unlock_irqrestore(&epca_lock, flags);
-       }
-}
-
-static void pc_start(struct tty_struct *tty)
-{
-       struct channel *ch;
-       /*
-        * verifyChannel returns the channel from the tty struct if it is
-        * valid. This serves as a sanity check.
-        */
-       ch = verifyChannel(tty);
-       if (ch != NULL) {
-               unsigned long flags;
-               spin_lock_irqsave(&epca_lock, flags);
-               /* Just in case output was resumed because of a change
-                  in Digi-flow */
-               if (ch->statusflags & TXSTOPPED)  {
-                       /* Begin transmit resume requested */
-                       struct board_chan __iomem *bc;
-                       globalwinon(ch);
-                       bc = ch->brdchan;
-                       if (ch->statusflags & LOWWAIT)
-                               writeb(1, &bc->ilow);
-                       /* Okay, you can start transmitting again... */
-                       fepcmd(ch, RESUMETX, 0, 0, 0, 0);
-                       ch->statusflags &= ~TXSTOPPED;
-                       memoff(ch);
-               } /* End transmit resume requested */
-               spin_unlock_irqrestore(&epca_lock, flags);
-       }
-}
-
-/*
- * The below routines pc_throttle and pc_unthrottle are used to slow (And
- * resume) the receipt of data into the kernels receive buffers. The exact
- * occurrence of this depends on the size of the kernels receive buffer and
- * what the 'watermarks' are set to for that buffer. See the n_ttys.c file for
- * more details.
- */
-static void pc_throttle(struct tty_struct *tty)
-{
-       struct channel *ch;
-       unsigned long flags;
-       /*
-        * verifyChannel returns the channel from the tty struct if it is
-        * valid. This serves as a sanity check.
-        */
-       ch = verifyChannel(tty);
-       if (ch != NULL) {
-               spin_lock_irqsave(&epca_lock, flags);
-               if ((ch->statusflags & RXSTOPPED) == 0) {
-                       globalwinon(ch);
-                       fepcmd(ch, PAUSERX, 0, 0, 0, 0);
-                       ch->statusflags |= RXSTOPPED;
-                       memoff(ch);
-               }
-               spin_unlock_irqrestore(&epca_lock, flags);
-       }
-}
-
-static void pc_unthrottle(struct tty_struct *tty)
-{
-       struct channel *ch;
-       unsigned long flags;
-       /*
-        * verifyChannel returns the channel from the tty struct if it is
-        * valid. This serves as a sanity check.
-        */
-       ch = verifyChannel(tty);
-       if (ch != NULL) {
-               /* Just in case output was resumed because of a change
-                  in Digi-flow */
-               spin_lock_irqsave(&epca_lock, flags);
-               if (ch->statusflags & RXSTOPPED) {
-                       globalwinon(ch);
-                       fepcmd(ch, RESUMERX, 0, 0, 0, 0);
-                       ch->statusflags &= ~RXSTOPPED;
-                       memoff(ch);
-               }
-               spin_unlock_irqrestore(&epca_lock, flags);
-       }
-}
-
-static int pc_send_break(struct tty_struct *tty, int msec)
-{
-       struct channel *ch = tty->driver_data;
-       unsigned long flags;
-
-       if (msec == -1)
-               msec = 0xFFFF;
-       else if (msec > 0xFFFE)
-               msec = 0xFFFE;
-       else if (msec < 1)
-               msec = 1;
-
-       spin_lock_irqsave(&epca_lock, flags);
-       globalwinon(ch);
-       /*
-        * Maybe I should send an infinite break here, schedule() for msec
-        * amount of time, and then stop the break. This way, the user can't
-        * screw up the FEP by causing digi_send_break() to be called (i.e. via
-        * an ioctl()) more than once in msec amount of time.
-        * Try this for now...
-        */
-       fepcmd(ch, SENDBREAK, msec, 0, 10, 0);
-       memoff(ch);
-       spin_unlock_irqrestore(&epca_lock, flags);
-       return 0;
-}
-
-/* Caller MUST hold the lock */
-static void setup_empty_event(struct tty_struct *tty, struct channel *ch)
-{
-       struct board_chan __iomem *bc = ch->brdchan;
-
-       globalwinon(ch);
-       ch->statusflags |= EMPTYWAIT;
-       /*
-        * When set the iempty flag request a event to be generated when the
-        * transmit buffer is empty (If there is no BREAK in progress).
-        */
-       writeb(1, &bc->iempty);
-       memoff(ch);
-}
-
-#ifndef MODULE
-static void __init epca_setup(char *str, int *ints)
-{
-       struct board_info board;
-       int               index, loop, last;
-       char              *temp, *t2;
-       unsigned          len;
-
-       /*
-        * If this routine looks a little strange it is because it is only
-        * called if a LILO append command is given to boot the kernel with
-        * parameters. In this way, we can provide the user a method of
-        * changing his board configuration without rebuilding the kernel.
-        */
-       if (!liloconfig)
-               liloconfig = 1;
-
-       memset(&board, 0, sizeof(board));
-
-       /* Assume the data is int first, later we can change it */
-       /* I think that array position 0 of ints holds the number of args */
-       for (last = 0, index = 1; index <= ints[0]; index++)
-               switch (index) { /* Begin parse switch */
-               case 1:
-                       board.status = ints[index];
-                       /*
-                        * We check for 2 (As opposed to 1; because 2 is a flag
-                        * instructing the driver to ignore epcaconfig.) For
-                        * this reason we check for 2.
-                        */
-                       if (board.status == 2) {
-                       /* Begin ignore epcaconfig as well as lilo cmd line */
-                               nbdevs = 0;
-                               num_cards = 0;
-                               return;
-                       } /* End ignore epcaconfig as well as lilo cmd line */
-
-                       if (board.status > 2) {
-                               printk(KERN_ERR "epca_setup: Invalid board status 0x%x\n",
-                                               board.status);
-                               invalid_lilo_config = 1;
-                               setup_error_code |= INVALID_BOARD_STATUS;
-                               return;
-                       }
-                       last = index;
-                       break;
-               case 2:
-                       board.type = ints[index];
-                       if (board.type >= PCIXEM)  {
-                               printk(KERN_ERR "epca_setup: Invalid board type 0x%x\n", board.type);
-                               invalid_lilo_config = 1;
-                               setup_error_code |= INVALID_BOARD_TYPE;
-                               return;
-                       }
-                       last = index;
-                       break;
-               case 3:
-                       board.altpin = ints[index];
-                       if (board.altpin > 1) {
-                               printk(KERN_ERR "epca_setup: Invalid board altpin 0x%x\n", board.altpin);
-                               invalid_lilo_config = 1;
-                               setup_error_code |= INVALID_ALTPIN;
-                               return;
-                       }
-                       last = index;
-                       break;
-
-               case 4:
-                       board.numports = ints[index];
-                       if (board.numports < 2 || board.numports > 256) {
-                               printk(KERN_ERR "epca_setup: Invalid board numports 0x%x\n", board.numports);
-                               invalid_lilo_config = 1;
-                               setup_error_code |= INVALID_NUM_PORTS;
-                               return;
-                       }
-                       nbdevs += board.numports;
-                       last = index;
-                       break;
-
-               case 5:
-                       board.port = ints[index];
-                       if (ints[index] <= 0) {
-                               printk(KERN_ERR "epca_setup: Invalid io port 0x%x\n", (unsigned int)board.port);
-                               invalid_lilo_config = 1;
-                               setup_error_code |= INVALID_PORT_BASE;
-                               return;
-                       }
-                       last = index;
-                       break;
-
-               case 6:
-                       board.membase = ints[index];
-                       if (ints[index] <= 0) {
-                               printk(KERN_ERR "epca_setup: Invalid memory base 0x%x\n",
-                                       (unsigned int)board.membase);
-                               invalid_lilo_config = 1;
-                               setup_error_code |= INVALID_MEM_BASE;
-                               return;
-                       }
-                       last = index;
-                       break;
-
-               default:
-                       printk(KERN_ERR "<Error> - epca_setup: Too many integer parms\n");
-                       return;
-
-               } /* End parse switch */
-
-       while (str && *str)  { /* Begin while there is a string arg */
-               /* find the next comma or terminator */
-               temp = str;
-               /* While string is not null, and a comma hasn't been found */
-               while (*temp && (*temp != ','))
-                       temp++;
-               if (!*temp)
-                       temp = NULL;
-               else
-                       *temp++ = 0;
-               /* Set index to the number of args + 1 */
-               index = last + 1;
-
-               switch (index) {
-               case 1:
-                       len = strlen(str);
-                       if (strncmp("Disable", str, len) == 0)
-                               board.status = 0;
-                       else if (strncmp("Enable", str, len) == 0)
-                               board.status = 1;
-                       else {
-                               printk(KERN_ERR "epca_setup: Invalid status %s\n", str);
-                               invalid_lilo_config = 1;
-                               setup_error_code |= INVALID_BOARD_STATUS;
-                               return;
-                       }
-                       last = index;
-                       break;
-
-               case 2:
-                       for (loop = 0; loop < EPCA_NUM_TYPES; loop++)
-                               if (strcmp(board_desc[loop], str) == 0)
-                                       break;
-                       /*
-                        * If the index incremented above refers to a
-                        * legitamate board type set it here.
-                        */
-                       if (index < EPCA_NUM_TYPES)
-                               board.type = loop;
-                       else {
-                               printk(KERN_ERR "epca_setup: Invalid board type: %s\n", str);
-                               invalid_lilo_config = 1;
-                               setup_error_code |= INVALID_BOARD_TYPE;
-                               return;
-                       }
-                       last = index;
-                       break;
-
-               case 3:
-                       len = strlen(str);
-                       if (strncmp("Disable", str, len) == 0)
-                               board.altpin = 0;
-                       else if (strncmp("Enable", str, len) == 0)
-                               board.altpin = 1;
-                       else {
-                               printk(KERN_ERR "epca_setup: Invalid altpin %s\n", str);
-                               invalid_lilo_config = 1;
-                               setup_error_code |= INVALID_ALTPIN;
-                               return;
-                       }
-                       last = index;
-                       break;
-
-               case 4:
-                       t2 = str;
-                       while (isdigit(*t2))
-                               t2++;
-
-                       if (*t2) {
-                               printk(KERN_ERR "epca_setup: Invalid port count %s\n", str);
-                               invalid_lilo_config = 1;
-                               setup_error_code |= INVALID_NUM_PORTS;
-                               return;
-                       }
-
-                       /*
-                        * There is not a man page for simple_strtoul but the
-                        * code can be found in vsprintf.c. The first argument
-                        * is the string to translate (To an unsigned long
-                        * obviously), the second argument can be the address
-                        * of any character variable or a NULL. If a variable
-                        * is given, the end pointer of the string will be
-                        * stored in that variable; if a NULL is given the end
-                        * pointer will not be returned. The last argument is
-                        * the base to use. If a 0 is indicated, the routine
-                        * will attempt to determine the proper base by looking
-                        * at the values prefix (A '0' for octal, a 'x' for
-                        * hex, etc ... If a value is given it will use that
-                        * value as the base.
-                        */
-                       board.numports = simple_strtoul(str, NULL, 0);
-                       nbdevs += board.numports;
-                       last = index;
-                       break;
-
-               case 5:
-                       t2 = str;
-                       while (isxdigit(*t2))
-                               t2++;
-
-                       if (*t2) {
-                               printk(KERN_ERR "epca_setup: Invalid i/o address %s\n", str);
-                               invalid_lilo_config = 1;
-                               setup_error_code |= INVALID_PORT_BASE;
-                               return;
-                       }
-
-                       board.port = simple_strtoul(str, NULL, 16);
-                       last = index;
-                       break;
-
-               case 6:
-                       t2 = str;
-                       while (isxdigit(*t2))
-                               t2++;
-
-                       if (*t2) {
-                               printk(KERN_ERR "epca_setup: Invalid memory base %s\n", str);
-                               invalid_lilo_config = 1;
-                               setup_error_code |= INVALID_MEM_BASE;
-                               return;
-                       }
-                       board.membase = simple_strtoul(str, NULL, 16);
-                       last = index;
-                       break;
-               default:
-                       printk(KERN_ERR "epca: Too many string parms\n");
-                       return;
-               }
-               str = temp;
-       } /* End while there is a string arg */
-
-       if (last < 6) {
-               printk(KERN_ERR "epca: Insufficient parms specified\n");
-               return;
-       }
-
-       /* I should REALLY validate the stuff here */
-       /* Copies our local copy of board into boards */
-       memcpy((void *)&boards[num_cards], (void *)&board, sizeof(board));
-       /* Does this get called once per lilo arg are what ? */
-       printk(KERN_INFO "PC/Xx: Added board %i, %s %i ports at 0x%4.4X base 0x%6.6X\n",
-               num_cards, board_desc[board.type],
-               board.numports, (int)board.port, (unsigned int) board.membase);
-       num_cards++;
-}
-
-static int __init epca_real_setup(char *str)
-{
-       int ints[11];
-
-       epca_setup(get_options(str, 11, ints), ints);
-       return 1;
-}
-
-__setup("digiepca", epca_real_setup);
-#endif
-
-enum epic_board_types {
-       brd_xr = 0,
-       brd_xem,
-       brd_cx,
-       brd_xrj,
-};
-
-/* indexed directly by epic_board_types enum */
-static struct {
-       unsigned char board_type;
-       unsigned bar_idx;               /* PCI base address region */
-} epca_info_tbl[] = {
-       { PCIXR, 0, },
-       { PCIXEM, 0, },
-       { PCICX, 0, },
-       { PCIXRJ, 2, },
-};
-
-static int __devinit epca_init_one(struct pci_dev *pdev,
-                                const struct pci_device_id *ent)
-{
-       static int board_num = -1;
-       int board_idx, info_idx = ent->driver_data;
-       unsigned long addr;
-
-       if (pci_enable_device(pdev))
-               return -EIO;
-
-       board_num++;
-       board_idx = board_num + num_cards;
-       if (board_idx >= MAXBOARDS)
-               goto err_out;
-
-       addr = pci_resource_start(pdev, epca_info_tbl[info_idx].bar_idx);
-       if (!addr) {
-               printk(KERN_ERR PFX "PCI region #%d not available (size 0)\n",
-                       epca_info_tbl[info_idx].bar_idx);
-               goto err_out;
-       }
-
-       boards[board_idx].status = ENABLED;
-       boards[board_idx].type = epca_info_tbl[info_idx].board_type;
-       boards[board_idx].numports = 0x0;
-       boards[board_idx].port = addr + PCI_IO_OFFSET;
-       boards[board_idx].membase = addr;
-
-       if (!request_mem_region(addr + PCI_IO_OFFSET, 0x200000, "epca")) {
-               printk(KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n",
-                       0x200000, addr + PCI_IO_OFFSET);
-               goto err_out;
-       }
-
-       boards[board_idx].re_map_port = ioremap_nocache(addr + PCI_IO_OFFSET,
-                                                               0x200000);
-       if (!boards[board_idx].re_map_port) {
-               printk(KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n",
-                       0x200000, addr + PCI_IO_OFFSET);
-               goto err_out_free_pciio;
-       }
-
-       if (!request_mem_region(addr, 0x200000, "epca")) {
-               printk(KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n",
-                       0x200000, addr);
-               goto err_out_free_iounmap;
-       }
-
-       boards[board_idx].re_map_membase = ioremap_nocache(addr, 0x200000);
-       if (!boards[board_idx].re_map_membase) {
-               printk(KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n",
-                       0x200000, addr + PCI_IO_OFFSET);
-               goto err_out_free_memregion;
-       }
-
-       /*
-        * I don't know what the below does, but the hardware guys say its
-        * required on everything except PLX (In this case XRJ).
-        */
-       if (info_idx != brd_xrj) {
-               pci_write_config_byte(pdev, 0x40, 0);
-               pci_write_config_byte(pdev, 0x46, 0);
-       }
-
-       return 0;
-
-err_out_free_memregion:
-       release_mem_region(addr, 0x200000);
-err_out_free_iounmap:
-       iounmap(boards[board_idx].re_map_port);
-err_out_free_pciio:
-       release_mem_region(addr + PCI_IO_OFFSET, 0x200000);
-err_out:
-       return -ENODEV;
-}
-
-
-static struct pci_device_id epca_pci_tbl[] = {
-       { PCI_VENDOR_DIGI, PCI_DEVICE_XR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xr },
-       { PCI_VENDOR_DIGI, PCI_DEVICE_XEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xem },
-       { PCI_VENDOR_DIGI, PCI_DEVICE_CX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_cx },
-       { PCI_VENDOR_DIGI, PCI_DEVICE_XRJ, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xrj },
-       { 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, epca_pci_tbl);
-
-static int __init init_PCI(void)
-{
-       memset(&epca_driver, 0, sizeof(epca_driver));
-       epca_driver.name = "epca";
-       epca_driver.id_table = epca_pci_tbl;
-       epca_driver.probe = epca_init_one;
-
-       return pci_register_driver(&epca_driver);
-}
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/epca.h b/drivers/char/epca.h
deleted file mode 100644 (file)
index d414bf2..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-#define XEMPORTS    0xC02
-#define XEPORTS     0xC22
-
-#define MAX_ALLOC   0x100
-
-#define MAXBOARDS   12
-#define FEPCODESEG  0x0200L
-#define FEPCODE     0x2000L
-#define BIOSCODE    0xf800L
-
-#define MISCGLOBAL  0x0C00L
-#define NPORT       0x0C22L
-#define MBOX        0x0C40L
-#define PORTBASE    0x0C90L
-
-/* Begin code defines used for epca_setup */
-
-#define INVALID_BOARD_TYPE   0x1
-#define INVALID_NUM_PORTS    0x2
-#define INVALID_MEM_BASE     0x4
-#define INVALID_PORT_BASE    0x8
-#define INVALID_BOARD_STATUS 0x10
-#define INVALID_ALTPIN       0x20
-
-/* End code defines used for epca_setup */
-
-
-#define FEPCLR      0x00
-#define FEPMEM      0x02
-#define FEPRST      0x04
-#define FEPINT      0x08
-#define        FEPMASK     0x0e
-#define        FEPWIN      0x80
-
-#define PCXE    0
-#define PCXEVE  1
-#define PCXEM   2   
-#define EISAXEM 3
-#define PC64XE  4
-#define PCXI    5
-#define PCIXEM  7
-#define PCICX   8
-#define PCIXR   9
-#define PCIXRJ  10
-#define EPCA_NUM_TYPES 6
-
-
-static char *board_desc[] = 
-{
-       "PC/Xe",
-       "PC/Xeve",
-       "PC/Xem",
-       "EISA/Xem",
-       "PC/64Xe",
-       "PC/Xi",
-       "unknown",
-       "PCI/Xem",
-       "PCI/CX",
-       "PCI/Xr",
-       "PCI/Xrj",
-};
-
-#define STARTC      021
-#define STOPC       023
-#define IAIXON      0x2000
-
-
-#define TXSTOPPED  0x1
-#define LOWWAIT    0x2
-#define EMPTYWAIT  0x4
-#define RXSTOPPED  0x8
-#define TXBUSY     0x10
-
-#define DISABLED   0
-#define ENABLED    1
-#define OFF        0
-#define ON         1
-
-#define FEPTIMEOUT 200000  
-#define SERIAL_TYPE_INFO    3
-#define EPCA_EVENT_HANGUP   1
-#define EPCA_MAGIC          0x5c6df104L
-
-struct channel 
-{
-       long   magic;
-       struct tty_port port;
-       unsigned char boardnum;
-       unsigned char channelnum;
-       unsigned char omodem;         /* FEP output modem status     */
-       unsigned char imodem;         /* FEP input modem status      */
-       unsigned char modemfake;      /* Modem values to be forced   */
-       unsigned char modem;          /* Force values                */
-       unsigned char hflow;
-       unsigned char dsr;
-       unsigned char dcd;
-       unsigned char m_rts ;           /* The bits used in whatever FEP */
-       unsigned char m_dcd ;           /* is indiginous to this board to */
-       unsigned char m_dsr ;           /* represent each of the physical */
-       unsigned char m_cts ;           /* handshake lines */
-       unsigned char m_ri ;
-       unsigned char m_dtr ;
-       unsigned char stopc;
-       unsigned char startc;
-       unsigned char stopca;
-       unsigned char startca;
-       unsigned char fepstopc;
-       unsigned char fepstartc;
-       unsigned char fepstopca;
-       unsigned char fepstartca;
-       unsigned char txwin;
-       unsigned char rxwin;
-       unsigned short fepiflag;
-       unsigned short fepcflag;
-       unsigned short fepoflag;
-       unsigned short txbufhead;
-       unsigned short txbufsize;
-       unsigned short rxbufhead;
-       unsigned short rxbufsize;
-       int    close_delay;
-       unsigned long  event;
-       uint   dev;
-       unsigned long  statusflags;
-       unsigned long  c_iflag;
-       unsigned long  c_cflag;
-       unsigned long  c_lflag;
-       unsigned long  c_oflag;
-       unsigned char __iomem *txptr;
-       unsigned char __iomem *rxptr;
-       struct board_info           *board;
-       struct board_chan           __iomem *brdchan;
-       struct digi_struct          digiext;
-       struct work_struct          tqueue;
-       struct global_data          __iomem *mailbox;
-};
-
-struct board_info      
-{
-       unsigned char status;
-       unsigned char type;
-       unsigned char altpin;
-       unsigned short numports;
-       unsigned long port;
-       unsigned long membase;
-       void __iomem *re_map_port;
-       void __iomem *re_map_membase;
-       unsigned long  memory_seg;
-       void ( * memwinon )     (struct board_info *, unsigned int) ;
-       void ( * memwinoff )    (struct board_info *, unsigned int) ;
-       void ( * globalwinon )  (struct channel *) ;
-       void ( * txwinon )      (struct channel *) ;
-       void ( * rxwinon )      (struct channel *) ;
-       void ( * memoff )       (struct channel *) ;
-       void ( * assertgwinon ) (struct channel *) ;
-       void ( * assertmemoff ) (struct channel *) ;
-       unsigned char poller_inhibited ;
-};
-
diff --git a/drivers/char/epcaconfig.h b/drivers/char/epcaconfig.h
deleted file mode 100644 (file)
index 55dec06..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#define NUMCARDS 0
-#define NBDEVS 0
-
-struct board_info static_boards[NUMCARDS]={
-};
-
-/* DO NOT HAND EDIT THIS FILE! */
diff --git a/drivers/char/ip2/Makefile b/drivers/char/ip2/Makefile
deleted file mode 100644 (file)
index 7b78e0d..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Makefile for the Computone IntelliPort Plus Driver
-#
-
-obj-$(CONFIG_COMPUTONE)         += ip2.o
-
-ip2-y                  := ip2main.o
-
diff --git a/drivers/char/ip2/i2cmd.c b/drivers/char/ip2/i2cmd.c
deleted file mode 100644 (file)
index e7af647..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-/*******************************************************************************
-*
-*   (c) 1998 by Computone Corporation
-*
-********************************************************************************
-*
-*
-*   PACKAGE:     Linux tty Device Driver for IntelliPort family of multiport
-*                serial I/O controllers.
-*
-*   DESCRIPTION: Definition table for In-line and Bypass commands. Applicable
-*                only when the standard loadware is active. (This is included
-*                source code, not a separate compilation module.)
-*
-*******************************************************************************/
-
-//------------------------------------------------------------------------------
-//
-// Revision History:
-//
-// 10 October 1991   MAG First Draft
-//  7 November 1991  MAG Reflects additional commands.
-// 24 February 1992  MAG Additional commands for 1.4.x loadware
-// 11 March 1992     MAG Additional commands
-// 30 March 1992     MAG Additional command: CMD_DSS_NOW
-// 18 May 1992       MAG Discovered commands 39 & 40 must be at the end of a
-//                       packet: affects implementation.
-//------------------------------------------------------------------------------
-
-//************
-//* Includes *
-//************
-
-#include "i2cmd.h"   /* To get some bit-defines */
-
-//------------------------------------------------------------------------------
-// Here is the table of global arrays which represent each type of command
-// supported in the IntelliPort standard loadware. See also i2cmd.h
-// for a more complete explanation of what is going on.
-//------------------------------------------------------------------------------
-
-// Here are the various globals: note that the names are not used except through
-// the macros defined in i2cmd.h. Also note that although they are character
-// arrays here (for extendability) they are cast to structure pointers in the
-// i2cmd.h macros. See i2cmd.h for flags definitions.
-
-//                     Length Flags Command
-static UCHAR ct02[] = { 1, BTH,     0x02                     }; // DTR UP
-static UCHAR ct03[] = { 1, BTH,     0x03                     }; // DTR DN
-static UCHAR ct04[] = { 1, BTH,     0x04                     }; // RTS UP
-static UCHAR ct05[] = { 1, BTH,     0x05                     }; // RTS DN
-static UCHAR ct06[] = { 1, BYP,     0x06                     }; // START FL
-static UCHAR ct07[] = { 2, BTH,     0x07,0                   }; // BAUD
-static UCHAR ct08[] = { 2, BTH,     0x08,0                   }; // BITS
-static UCHAR ct09[] = { 2, BTH,     0x09,0                   }; // STOP
-static UCHAR ct10[] = { 2, BTH,     0x0A,0                   }; // PARITY
-static UCHAR ct11[] = { 2, BTH,     0x0B,0                   }; // XON
-static UCHAR ct12[] = { 2, BTH,     0x0C,0                   }; // XOFF
-static UCHAR ct13[] = { 1, BTH,     0x0D                     }; // STOP FL
-static UCHAR ct14[] = { 1, BYP|VIP, 0x0E                     }; // ACK HOTK
-//static UCHAR ct15[]={ 2, BTH|VIP, 0x0F,0                   }; // IRQ SET
-static UCHAR ct16[] = { 2, INL,     0x10,0                   }; // IXONOPTS
-static UCHAR ct17[] = { 2, INL,     0x11,0                   }; // OXONOPTS
-static UCHAR ct18[] = { 1, INL,     0x12                     }; // CTSENAB
-static UCHAR ct19[] = { 1, BTH,     0x13                     }; // CTSDSAB
-static UCHAR ct20[] = { 1, INL,     0x14                     }; // DCDENAB
-static UCHAR ct21[] = { 1, BTH,     0x15                     }; // DCDDSAB
-static UCHAR ct22[] = { 1, BTH,     0x16                     }; // DSRENAB
-static UCHAR ct23[] = { 1, BTH,     0x17                     }; // DSRDSAB
-static UCHAR ct24[] = { 1, BTH,     0x18                     }; // RIENAB
-static UCHAR ct25[] = { 1, BTH,     0x19                     }; // RIDSAB
-static UCHAR ct26[] = { 2, BTH,     0x1A,0                   }; // BRKENAB
-static UCHAR ct27[] = { 1, BTH,     0x1B                     }; // BRKDSAB
-//static UCHAR ct28[]={ 2, BTH,     0x1C,0                   }; // MAXBLOKSIZE
-//static UCHAR ct29[]={ 2, 0,       0x1D,0                   }; // reserved
-static UCHAR ct30[] = { 1, INL,     0x1E                     }; // CTSFLOWENAB
-static UCHAR ct31[] = { 1, INL,     0x1F                     }; // CTSFLOWDSAB
-static UCHAR ct32[] = { 1, INL,     0x20                     }; // RTSFLOWENAB
-static UCHAR ct33[] = { 1, INL,     0x21                     }; // RTSFLOWDSAB
-static UCHAR ct34[] = { 2, BTH,     0x22,0                   }; // ISTRIPMODE
-static UCHAR ct35[] = { 2, BTH|END, 0x23,0                   }; // SENDBREAK
-static UCHAR ct36[] = { 2, BTH,     0x24,0                   }; // SETERRMODE
-//static UCHAR ct36a[]={ 3, INL,    0x24,0,0                 }; // SET_REPLACE
-
-// The following is listed for completeness, but should never be sent directly
-// by user-level code. It is sent only by library routines in response to data
-// movement.
-//static UCHAR ct37[]={ 5, BYP|VIP, 0x25,0,0,0,0             }; // FLOW PACKET
-
-// Back to normal
-//static UCHAR ct38[] = {11, BTH|VAR, 0x26,0,0,0,0,0,0,0,0,0,0 }; // DEF KEY SEQ
-//static UCHAR ct39[]={ 3, BTH|END, 0x27,0,0                 }; // OPOSTON
-//static UCHAR ct40[]={ 1, BTH|END, 0x28                     }; // OPOSTOFF
-static UCHAR ct41[] = { 1, BYP,     0x29                     }; // RESUME
-//static UCHAR ct42[]={ 2, BTH,     0x2A,0                   }; // TXBAUD
-//static UCHAR ct43[]={ 2, BTH,     0x2B,0                   }; // RXBAUD
-//static UCHAR ct44[]={ 2, BTH,     0x2C,0                   }; // MS PING
-//static UCHAR ct45[]={ 1, BTH,     0x2D                     }; // HOTENAB
-//static UCHAR ct46[]={ 1, BTH,     0x2E                     }; // HOTDSAB
-//static UCHAR ct47[]={ 7, BTH,     0x2F,0,0,0,0,0,0         }; // UNIX FLAGS
-//static UCHAR ct48[]={ 1, BTH,     0x30                     }; // DSRFLOWENAB
-//static UCHAR ct49[]={ 1, BTH,     0x31                     }; // DSRFLOWDSAB
-//static UCHAR ct50[]={ 1, BTH,     0x32                     }; // DTRFLOWENAB
-//static UCHAR ct51[]={ 1, BTH,     0x33                     }; // DTRFLOWDSAB
-//static UCHAR ct52[]={ 1, BTH,     0x34                     }; // BAUDTABRESET
-//static UCHAR ct53[] = { 3, BTH,     0x35,0,0                 }; // BAUDREMAP
-static UCHAR ct54[] = { 3, BTH,     0x36,0,0                 }; // CUSTOMBAUD1
-static UCHAR ct55[] = { 3, BTH,     0x37,0,0                 }; // CUSTOMBAUD2
-static UCHAR ct56[] = { 2, BTH|END, 0x38,0                   }; // PAUSE
-static UCHAR ct57[] = { 1, BYP,     0x39                     }; // SUSPEND
-static UCHAR ct58[] = { 1, BYP,     0x3A                     }; // UNSUSPEND
-static UCHAR ct59[] = { 2, BTH,     0x3B,0                   }; // PARITYCHK
-static UCHAR ct60[] = { 1, INL|VIP, 0x3C                     }; // BOOKMARKREQ
-//static UCHAR ct61[]={ 2, BTH,     0x3D,0                   }; // INTERNALLOOP
-//static UCHAR ct62[]={ 2, BTH,     0x3E,0                   }; // HOTKTIMEOUT
-static UCHAR ct63[] = { 2, INL,     0x3F,0                   }; // SETTXON
-static UCHAR ct64[] = { 2, INL,     0x40,0                   }; // SETTXOFF
-//static UCHAR ct65[]={ 2, BTH,     0x41,0                   }; // SETAUTORTS
-//static UCHAR ct66[]={ 2, BTH,     0x42,0                   }; // SETHIGHWAT
-//static UCHAR ct67[]={ 2, BYP,     0x43,0                   }; // STARTSELFL
-//static UCHAR ct68[]={ 2, INL,     0x44,0                   }; // ENDSELFL
-//static UCHAR ct69[]={ 1, BYP,     0x45                     }; // HWFLOW_OFF
-//static UCHAR ct70[]={ 1, BTH,     0x46                     }; // ODSRFL_ENAB
-//static UCHAR ct71[]={ 1, BTH,     0x47                     }; // ODSRFL_DSAB
-//static UCHAR ct72[]={ 1, BTH,     0x48                     }; // ODCDFL_ENAB
-//static UCHAR ct73[]={ 1, BTH,     0x49                     }; // ODCDFL_DSAB
-//static UCHAR ct74[]={ 2, BTH,     0x4A,0                   }; // LOADLEVEL
-//static UCHAR ct75[]={ 2, BTH,     0x4B,0                   }; // STATDATA
-//static UCHAR ct76[]={ 1, BYP,     0x4C                     }; // BREAK_ON
-//static UCHAR ct77[]={ 1, BYP,     0x4D                     }; // BREAK_OFF
-//static UCHAR ct78[]={ 1, BYP,     0x4E                     }; // GETFC
-static UCHAR ct79[] = { 2, BYP,     0x4F,0                   }; // XMIT_NOW
-//static UCHAR ct80[]={ 4, BTH,     0x50,0,0,0               }; // DIVISOR_LATCH
-//static UCHAR ct81[]={ 1, BYP,     0x51                     }; // GET_STATUS
-//static UCHAR ct82[]={ 1, BYP,     0x52                     }; // GET_TXCNT
-//static UCHAR ct83[]={ 1, BYP,     0x53                     }; // GET_RXCNT
-//static UCHAR ct84[]={ 1, BYP,     0x54                     }; // GET_BOXIDS
-//static UCHAR ct85[]={10, BYP,     0x55,0,0,0,0,0,0,0,0,0   }; // ENAB_MULT
-//static UCHAR ct86[]={ 2, BTH,     0x56,0                   }; // RCV_ENABLE
-static UCHAR ct87[] = { 1, BYP,     0x57                     }; // HW_TEST
-//static UCHAR ct88[]={ 3, BTH,     0x58,0,0                 }; // RCV_THRESHOLD
-//static UCHAR ct90[]={ 3, BYP,     0x5A,0,0                 }; // Set SILO
-//static UCHAR ct91[]={ 2, BYP,     0x5B,0                   }; // timed break
-
-// Some composite commands as well
-//static UCHAR cc01[]={ 2, BTH,     0x02,0x04                }; // DTR & RTS UP
-//static UCHAR cc02[]={ 2, BTH,     0x03,0x05                }; // DTR & RTS DN
-
-//********
-//* Code *
-//********
-
-//******************************************************************************
-// Function:   i2cmdUnixFlags(iflag, cflag, lflag)
-// Parameters: Unix tty flags
-//
-// Returns:    Pointer to command structure
-//
-// Description:
-//
-// This routine sets the parameters of command 47 and returns a pointer to the
-// appropriate structure.
-//******************************************************************************
-#if 0
-cmdSyntaxPtr
-i2cmdUnixFlags(unsigned short iflag,unsigned short cflag,unsigned short lflag)
-{
-       cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct47;
-
-       pCM->cmd[1] = (unsigned char)  iflag;
-       pCM->cmd[2] = (unsigned char) (iflag >> 8);
-       pCM->cmd[3] = (unsigned char)  cflag;
-       pCM->cmd[4] = (unsigned char) (cflag >> 8);
-       pCM->cmd[5] = (unsigned char)  lflag;
-       pCM->cmd[6] = (unsigned char) (lflag >> 8);
-       return pCM;
-}
-#endif  /*  0  */
-
-//******************************************************************************
-// Function:   i2cmdBaudDef(which, rate)
-// Parameters: ?
-//
-// Returns:    Pointer to command structure
-//
-// Description:
-//
-// This routine sets the parameters of commands 54 or 55 (according to the
-// argument which), and returns a pointer to the appropriate structure.
-//******************************************************************************
-static cmdSyntaxPtr
-i2cmdBaudDef(int which, unsigned short rate)
-{
-       cmdSyntaxPtr pCM;
-
-       switch(which)
-       {
-       case 1:
-               pCM = (cmdSyntaxPtr) ct54;
-               break;
-       default:
-       case 2:
-               pCM = (cmdSyntaxPtr) ct55;
-               break;
-       }
-       pCM->cmd[1] = (unsigned char) rate;
-       pCM->cmd[2] = (unsigned char) (rate >> 8);
-       return pCM;
-}
-
diff --git a/drivers/char/ip2/i2cmd.h b/drivers/char/ip2/i2cmd.h
deleted file mode 100644 (file)
index 29277ec..0000000
+++ /dev/null
@@ -1,630 +0,0 @@
-/*******************************************************************************
-*
-*   (c) 1999 by Computone Corporation
-*
-********************************************************************************
-*
-*
-*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
-*                serial I/O controllers.
-*
-*   DESCRIPTION: Definitions and support for In-line and Bypass commands.
-*                Applicable only when the standard loadware is active.
-*
-*******************************************************************************/
-//------------------------------------------------------------------------------
-// Revision History:
-//
-// 10 October 1991   MAG First Draft
-//  7 November 1991  MAG Reflects some new commands
-// 20 February 1992  MAG CMD_HOTACK corrected: no argument.
-// 24 February 1992  MAG Support added for new commands for 1.4.x loadware.
-// 11 March 1992     MAG Additional commands.
-// 16 March 1992     MAG Additional commands.
-// 30 March 1992     MAG Additional command: CMD_DSS_NOW
-// 18 May   1992     MAG Changed CMD_OPOST
-//
-//------------------------------------------------------------------------------
-#ifndef I2CMD_H      // To prevent multiple includes
-#define I2CMD_H   1
-
-#include "ip2types.h"
-
-// This module is designed to provide a uniform method of sending commands to
-// the board through command packets. The difficulty is, some commands take
-// parameters, others do not. Furthermore, it is often useful to send several
-// commands to the same channel as part of the same packet. (See also i2pack.h.)
-//
-// This module is designed so that the caller should not be responsible for
-// remembering the exact syntax of each command, or at least so that the
-// compiler could check things somewhat. I'll explain as we go...
-//
-// First, a structure which can embody the syntax of each type of command.
-//
-typedef struct _cmdSyntax
-{
-       UCHAR length;   // Number of bytes in the command
-       UCHAR flags;    // Information about the command (see below)
-
-       // The command and its parameters, which may be of arbitrary length. Don't
-       // worry yet how the parameters will be initialized; macros later take care
-       // of it. Also, don't worry about the arbitrary length issue; this structure
-       // is never used to allocate space (see i2cmd.c).
-       UCHAR cmd[2];
-} cmdSyntax, *cmdSyntaxPtr;
-
-// Bit assignments for flags
-
-#define INL 1           // Set if suitable for inline commands
-#define BYP 2           // Set if suitable for bypass commands
-#define BTH (INL|BYP)   // suitable for either!
-#define END 4           // Set if this must be the last command in a block
-#define VIP 8           // Set if this command is special in some way and really
-                                               // should only be sent from the library-level and not
-                                               // directly from user-level
-#define VAR 0x10        // This command is of variable length!
-
-// Declarations for the global arrays used to bear the commands and their
-// arguments.
-//
-// Note: Since these are globals and the arguments might change, it is important
-// that the library routine COPY these into buffers from whence they would be
-// sent, rather than merely storing the pointers. In multi-threaded
-// environments, important that the copy should obtain before any context switch
-// is allowed. Also, for parameterized commands, DO NOT ISSUE THE SAME COMMAND
-// MORE THAN ONCE WITH THE SAME PARAMETERS in the same call.
-//
-static UCHAR ct02[];
-static UCHAR ct03[];
-static UCHAR ct04[];
-static UCHAR ct05[];
-static UCHAR ct06[];
-static UCHAR ct07[];
-static UCHAR ct08[];
-static UCHAR ct09[];
-static UCHAR ct10[];
-static UCHAR ct11[];
-static UCHAR ct12[];
-static UCHAR ct13[];
-static UCHAR ct14[];
-static UCHAR ct15[];
-static UCHAR ct16[];
-static UCHAR ct17[];
-static UCHAR ct18[];
-static UCHAR ct19[];
-static UCHAR ct20[];
-static UCHAR ct21[];
-static UCHAR ct22[];
-static UCHAR ct23[];
-static UCHAR ct24[];
-static UCHAR ct25[];
-static UCHAR ct26[];
-static UCHAR ct27[];
-static UCHAR ct28[];
-static UCHAR ct29[];
-static UCHAR ct30[];
-static UCHAR ct31[];
-static UCHAR ct32[];
-static UCHAR ct33[];
-static UCHAR ct34[];
-static UCHAR ct35[];
-static UCHAR ct36[];
-static UCHAR ct36a[];
-static UCHAR ct41[];
-static UCHAR ct42[];
-static UCHAR ct43[];
-static UCHAR ct44[];
-static UCHAR ct45[];
-static UCHAR ct46[];
-static UCHAR ct48[];
-static UCHAR ct49[];
-static UCHAR ct50[];
-static UCHAR ct51[];
-static UCHAR ct52[];
-static UCHAR ct56[];
-static UCHAR ct57[];
-static UCHAR ct58[];
-static UCHAR ct59[];
-static UCHAR ct60[];
-static UCHAR ct61[];
-static UCHAR ct62[];
-static UCHAR ct63[];
-static UCHAR ct64[];
-static UCHAR ct65[];
-static UCHAR ct66[];
-static UCHAR ct67[];
-static UCHAR ct68[];
-static UCHAR ct69[];
-static UCHAR ct70[];
-static UCHAR ct71[];
-static UCHAR ct72[];
-static UCHAR ct73[];
-static UCHAR ct74[];
-static UCHAR ct75[];
-static UCHAR ct76[];
-static UCHAR ct77[];
-static UCHAR ct78[];
-static UCHAR ct79[];
-static UCHAR ct80[];
-static UCHAR ct81[];
-static UCHAR ct82[];
-static UCHAR ct83[];
-static UCHAR ct84[];
-static UCHAR ct85[];
-static UCHAR ct86[];
-static UCHAR ct87[];
-static UCHAR ct88[];
-static UCHAR ct89[];
-static UCHAR ct90[];
-static UCHAR ct91[];
-static UCHAR cc01[];
-static UCHAR cc02[];
-
-// Now, refer to i2cmd.c, and see the character arrays defined there. They are
-// cast here to cmdSyntaxPtr.
-//
-// There are library functions for issuing bypass or inline commands. These
-// functions take one or more arguments of the type cmdSyntaxPtr. The routine
-// then can figure out how long each command is supposed to be and easily add it
-// to the list.
-//
-// For ease of use, we define manifests which return pointers to appropriate
-// cmdSyntaxPtr things. But some commands also take arguments. If a single
-// argument is used, we define a macro which performs the single assignment and
-// (through the expedient of a comma expression) references the appropriate
-// pointer. For commands requiring several arguments, we actually define a
-// function to perform the assignments.
-
-#define CMD_DTRUP      (cmdSyntaxPtr)(ct02)    // Raise DTR
-#define CMD_DTRDN      (cmdSyntaxPtr)(ct03)    // Lower DTR
-#define CMD_RTSUP      (cmdSyntaxPtr)(ct04)    // Raise RTS
-#define CMD_RTSDN      (cmdSyntaxPtr)(ct05)    // Lower RTS
-#define CMD_STARTFL    (cmdSyntaxPtr)(ct06)    // Start Flushing Data
-
-#define CMD_DTRRTS_UP (cmdSyntaxPtr)(cc01)     // Raise DTR and RTS
-#define CMD_DTRRTS_DN (cmdSyntaxPtr)(cc02)     // Lower DTR and RTS
-
-// Set Baud Rate for transmit and receive
-#define CMD_SETBAUD(arg) \
-       (((cmdSyntaxPtr)(ct07))->cmd[1] = (arg),(cmdSyntaxPtr)(ct07))
-
-#define CBR_50       1
-#define CBR_75       2
-#define CBR_110      3
-#define CBR_134      4
-#define CBR_150      5
-#define CBR_200      6
-#define CBR_300      7
-#define CBR_600      8
-#define CBR_1200     9
-#define CBR_1800     10
-#define CBR_2400     11
-#define CBR_4800     12
-#define CBR_9600     13
-#define CBR_19200    14
-#define CBR_38400    15
-#define CBR_2000     16
-#define CBR_3600     17
-#define CBR_7200     18
-#define CBR_56000    19
-#define CBR_57600    20
-#define CBR_64000    21
-#define CBR_76800    22
-#define CBR_115200   23
-#define CBR_C1       24    // Custom baud rate 1
-#define CBR_C2       25    // Custom baud rate 2
-#define CBR_153600   26
-#define CBR_230400   27
-#define CBR_307200   28
-#define CBR_460800   29
-#define CBR_921600   30
-
-// Set Character size
-//
-#define CMD_SETBITS(arg) \
-       (((cmdSyntaxPtr)(ct08))->cmd[1] = (arg),(cmdSyntaxPtr)(ct08))
-
-#define CSZ_5  0
-#define CSZ_6  1
-#define CSZ_7  2
-#define CSZ_8  3
-
-// Set number of stop bits
-//
-#define CMD_SETSTOP(arg) \
-       (((cmdSyntaxPtr)(ct09))->cmd[1] = (arg),(cmdSyntaxPtr)(ct09))
-
-#define CST_1  0
-#define CST_15 1  // 1.5 stop bits
-#define CST_2  2
-
-// Set parity option
-//
-#define CMD_SETPAR(arg) \
-       (((cmdSyntaxPtr)(ct10))->cmd[1] = (arg),(cmdSyntaxPtr)(ct10))
-
-#define CSP_NP 0  // no parity
-#define CSP_OD 1  // odd parity
-#define CSP_EV 2  // Even parity
-#define CSP_SP 3  // Space parity
-#define CSP_MK 4  // Mark parity
-
-// Define xon char for transmitter flow control
-//
-#define CMD_DEF_IXON(arg) \
-       (((cmdSyntaxPtr)(ct11))->cmd[1] = (arg),(cmdSyntaxPtr)(ct11))
-
-// Define xoff char for transmitter flow control
-//
-#define CMD_DEF_IXOFF(arg) \
-       (((cmdSyntaxPtr)(ct12))->cmd[1] = (arg),(cmdSyntaxPtr)(ct12))
-
-#define CMD_STOPFL   (cmdSyntaxPtr)(ct13) // Stop Flushing data
-
-// Acknowledge receipt of hotkey signal
-//
-#define CMD_HOTACK   (cmdSyntaxPtr)(ct14)
-
-// Define irq level to use. Should actually be sent by library-level code, not
-// directly from user...
-//
-#define CMDVALUE_IRQ 15 // For library use at initialization. Until this command
-                                               // is sent, board processing doesn't really start.
-#define CMD_SET_IRQ(arg) \
-       (((cmdSyntaxPtr)(ct15))->cmd[1] = (arg),(cmdSyntaxPtr)(ct15))
-
-#define CIR_POLL  0  // No IRQ - Poll
-#define CIR_3     3  // IRQ 3
-#define CIR_4     4  // IRQ 4
-#define CIR_5     5  // IRQ 5
-#define CIR_7     7  // IRQ 7
-#define CIR_10    10 // IRQ 10
-#define CIR_11    11 // IRQ 11
-#define CIR_12    12 // IRQ 12
-#define CIR_15    15 // IRQ 15
-
-// Select transmit flow xon/xoff options
-//
-#define CMD_IXON_OPT(arg) \
-       (((cmdSyntaxPtr)(ct16))->cmd[1] = (arg),(cmdSyntaxPtr)(ct16))
-
-#define CIX_NONE  0  // Incoming Xon/Xoff characters not special
-#define CIX_XON   1  // Xoff disable, Xon enable
-#define CIX_XANY  2  // Xoff disable, any key enable
-
-// Select receive flow xon/xoff options
-//
-#define CMD_OXON_OPT(arg) \
-       (((cmdSyntaxPtr)(ct17))->cmd[1] = (arg),(cmdSyntaxPtr)(ct17))
-
-#define COX_NONE  0  // Don't send Xon/Xoff
-#define COX_XON   1  // Send xon/xoff to start/stop incoming data
-
-
-#define CMD_CTS_REP  (cmdSyntaxPtr)(ct18) // Enable  CTS reporting
-#define CMD_CTS_NREP (cmdSyntaxPtr)(ct19) // Disable CTS reporting
-
-#define CMD_DCD_REP  (cmdSyntaxPtr)(ct20) // Enable  DCD reporting
-#define CMD_DCD_NREP (cmdSyntaxPtr)(ct21) // Disable DCD reporting
-
-#define CMD_DSR_REP  (cmdSyntaxPtr)(ct22) // Enable  DSR reporting
-#define CMD_DSR_NREP (cmdSyntaxPtr)(ct23) // Disable DSR reporting
-
-#define CMD_RI_REP   (cmdSyntaxPtr)(ct24) // Enable  RI  reporting
-#define CMD_RI_NREP  (cmdSyntaxPtr)(ct25) // Disable RI  reporting
-
-// Enable break reporting and select style
-//
-#define CMD_BRK_REP(arg) \
-       (((cmdSyntaxPtr)(ct26))->cmd[1] = (arg),(cmdSyntaxPtr)(ct26))
-
-#define CBK_STAT     0x00  // Report breaks as a status (exception,irq)
-#define CBK_NULL     0x01  // Report breaks as a good null
-#define CBK_STAT_SEQ 0x02  // Report breaks as a status AND as in-band character
-                           //  sequence FFh, 01h, 10h
-#define CBK_SEQ      0x03  // Report breaks as the in-band 
-                                                  //sequence FFh, 01h, 10h ONLY.
-#define CBK_FLSH     0x04  // if this bit set also flush input data
-#define CBK_POSIX    0x08  // if this bit set report as FF,0,0 sequence
-#define CBK_SINGLE   0x10  // if this bit set with CBK_SEQ or CBK_STAT_SEQ
-                                                  //then reports single null instead of triple
-
-#define CMD_BRK_NREP (cmdSyntaxPtr)(ct27) // Disable break reporting
-
-// Specify maximum block size for received data
-//
-#define CMD_MAX_BLOCK(arg) \
-       (((cmdSyntaxPtr)(ct28))->cmd[1] = (arg),(cmdSyntaxPtr)(ct28))
-
-// -- COMMAND 29 is reserved --
-
-#define CMD_CTSFL_ENAB  (cmdSyntaxPtr)(ct30) // Enable  CTS flow control
-#define CMD_CTSFL_DSAB  (cmdSyntaxPtr)(ct31) // Disable CTS flow control
-#define CMD_RTSFL_ENAB  (cmdSyntaxPtr)(ct32) // Enable  RTS flow control
-#define CMD_RTSFL_DSAB  (cmdSyntaxPtr)(ct33) // Disable RTS flow control
-
-// Specify istrip option
-//
-#define CMD_ISTRIP_OPT(arg) \
-       (((cmdSyntaxPtr)(ct34))->cmd[1] = (arg),(cmdSyntaxPtr)(ct34))
-
-#define CIS_NOSTRIP  0  // Strip characters to character size
-#define CIS_STRIP    1  // Strip any 8-bit characters to 7 bits
-
-// Send a break of arg milliseconds
-//
-#define CMD_SEND_BRK(arg) \
-       (((cmdSyntaxPtr)(ct35))->cmd[1] = (arg),(cmdSyntaxPtr)(ct35))
-
-// Set error reporting mode
-//
-#define CMD_SET_ERROR(arg) \
-       (((cmdSyntaxPtr)(ct36))->cmd[1] = (arg),(cmdSyntaxPtr)(ct36))
-
-#define CSE_ESTAT 0  // Report error in a status packet
-#define CSE_NOREP 1  // Treat character as though it were good
-#define CSE_DROP  2  // Discard the character
-#define CSE_NULL  3  // Replace with a null
-#define CSE_MARK  4  // Replace with a 3-character sequence (as Unix)
-
-#define CSE_REPLACE  0x8       // Replace the errored character with the
-                                                       // replacement character defined here
-
-#define CSE_STAT_REPLACE   0x18        // Replace the errored character with the
-                                                               // replacement character defined here AND
-                                                               // report the error as a status packet (as in
-                                                               // CSE_ESTAT).
-
-
-// COMMAND 37, to send flow control packets, is handled only by low-level
-// library code in response to data movement and shouldn't ever be sent by the
-// user code. See i2pack.h and the body of i2lib.c for details.
-
-// Enable on-board post-processing, using options given in oflag argument.
-// Formerly, this command was automatically preceded by a CMD_OPOST_OFF command
-// because the loadware does not permit sending back-to-back CMD_OPOST_ON
-// commands without an intervening CMD_OPOST_OFF. BUT, WE LEARN 18 MAY 92, that
-// CMD_OPOST_ON and CMD_OPOST_OFF must each be at the end of a packet (or in a
-// solo packet). This means the caller must specify separately CMD_OPOST_OFF,
-// CMD_OPOST_ON(parm) when he calls i2QueueCommands(). That function will ensure
-// each gets a separate packet. Extra CMD_OPOST_OFF's are always ok.
-//
-#define CMD_OPOST_ON(oflag)   \
-       (*(USHORT *)(((cmdSyntaxPtr)(ct39))->cmd[1]) = (oflag), \
-               (cmdSyntaxPtr)(ct39))
-
-#define CMD_OPOST_OFF   (cmdSyntaxPtr)(ct40) // Disable on-board post-proc
-
-#define CMD_RESUME   (cmdSyntaxPtr)(ct41)      // Resume: behave as though an XON
-                                                                                       // were received;
-
-// Set Transmit baud rate (see command 7 for arguments)
-//
-#define CMD_SETBAUD_TX(arg) \
-       (((cmdSyntaxPtr)(ct42))->cmd[1] = (arg),(cmdSyntaxPtr)(ct42))
-
-// Set Receive baud rate (see command 7 for arguments)
-//
-#define CMD_SETBAUD_RX(arg) \
-       (((cmdSyntaxPtr)(ct43))->cmd[1] = (arg),(cmdSyntaxPtr)(ct43))
-
-// Request interrupt from board each arg milliseconds. Interrupt will specify
-// "received data", even though there may be no data present. If arg == 0,
-// disables any such interrupts.
-//
-#define CMD_PING_REQ(arg) \
-       (((cmdSyntaxPtr)(ct44))->cmd[1] = (arg),(cmdSyntaxPtr)(ct44))
-
-#define CMD_HOT_ENAB (cmdSyntaxPtr)(ct45) // Enable Hot-key checking
-#define CMD_HOT_DSAB (cmdSyntaxPtr)(ct46) // Disable Hot-key checking
-
-#if 0
-// COMMAND 47: Send Protocol info via Unix flags:
-// iflag = Unix tty t_iflag
-// cflag = Unix tty t_cflag
-// lflag = Unix tty t_lflag
-// See System V Unix/Xenix documentation for the meanings of the bit fields
-// within these flags
-//
-#define CMD_UNIX_FLAGS(iflag,cflag,lflag) i2cmdUnixFlags(iflag,cflag,lflag)
-#endif  /*  0  */
-
-#define CMD_DSRFL_ENAB  (cmdSyntaxPtr)(ct48) // Enable  DSR receiver ctrl
-#define CMD_DSRFL_DSAB  (cmdSyntaxPtr)(ct49) // Disable DSR receiver ctrl
-#define CMD_DTRFL_ENAB  (cmdSyntaxPtr)(ct50) // Enable  DTR flow control
-#define CMD_DTRFL_DSAB  (cmdSyntaxPtr)(ct51) // Disable DTR flow control
-#define CMD_BAUD_RESET  (cmdSyntaxPtr)(ct52) // Reset baudrate table
-
-// COMMAND 54: Define custom rate #1
-// rate = (short) 1/10 of the desired baud rate
-//
-#define CMD_BAUD_DEF1(rate) i2cmdBaudDef(1,rate)
-
-// COMMAND 55: Define custom rate #2
-// rate = (short) 1/10 of the desired baud rate
-//
-#define CMD_BAUD_DEF2(rate) i2cmdBaudDef(2,rate)
-
-// Pause arg hundredths of seconds. (Note, this is NOT milliseconds.)
-//
-#define CMD_PAUSE(arg) \
-       (((cmdSyntaxPtr)(ct56))->cmd[1] = (arg),(cmdSyntaxPtr)(ct56))
-
-#define CMD_SUSPEND     (cmdSyntaxPtr)(ct57) // Suspend output
-#define CMD_UNSUSPEND   (cmdSyntaxPtr)(ct58) // Un-Suspend output
-
-// Set parity-checking options
-//
-#define CMD_PARCHK(arg) \
-       (((cmdSyntaxPtr)(ct59))->cmd[1] = (arg),(cmdSyntaxPtr)(ct59))
-
-#define CPK_ENAB  0     // Enable parity checking on input
-#define CPK_DSAB  1     // Disable parity checking on input
-
-#define CMD_BMARK_REQ   (cmdSyntaxPtr)(ct60) // Bookmark request
-
-
-// Enable/Disable internal loopback mode
-//
-#define CMD_INLOOP(arg) \
-       (((cmdSyntaxPtr)(ct61))->cmd[1] = (arg),(cmdSyntaxPtr)(ct61))
-
-#define CIN_DISABLE  0  // Normal operation (default)
-#define CIN_ENABLE   1  // Internal (local) loopback
-#define CIN_REMOTE   2  // Remote loopback
-
-// Specify timeout for hotkeys: Delay will be (arg x 10) milliseconds, arg == 0
-// --> no timeout: wait forever.
-//
-#define CMD_HOT_TIME(arg) \
-       (((cmdSyntaxPtr)(ct62))->cmd[1] = (arg),(cmdSyntaxPtr)(ct62))
-
-
-// Define (outgoing) xon for receive flow control
-//
-#define CMD_DEF_OXON(arg) \
-       (((cmdSyntaxPtr)(ct63))->cmd[1] = (arg),(cmdSyntaxPtr)(ct63))
-
-// Define (outgoing) xoff for receiver flow control
-//
-#define CMD_DEF_OXOFF(arg) \
-       (((cmdSyntaxPtr)(ct64))->cmd[1] = (arg),(cmdSyntaxPtr)(ct64))
-
-// Enable/Disable RTS on transmit (1/2 duplex-style)
-//
-#define CMD_RTS_XMIT(arg) \
-       (((cmdSyntaxPtr)(ct65))->cmd[1] = (arg),(cmdSyntaxPtr)(ct65))
-
-#define CHD_DISABLE  0
-#define CHD_ENABLE   1
-
-// Set high-water-mark level (debugging use only)
-//
-#define CMD_SETHIGHWAT(arg) \
-       (((cmdSyntaxPtr)(ct66))->cmd[1] = (arg),(cmdSyntaxPtr)(ct66))
-
-// Start flushing tagged data (tag = 0-14)
-//
-#define CMD_START_SELFL(tag) \
-       (((cmdSyntaxPtr)(ct67))->cmd[1] = (tag),(cmdSyntaxPtr)(ct67))
-
-// End flushing tagged data (tag = 0-14)
-//
-#define CMD_END_SELFL(tag) \
-       (((cmdSyntaxPtr)(ct68))->cmd[1] = (tag),(cmdSyntaxPtr)(ct68))
-
-#define CMD_HWFLOW_OFF  (cmdSyntaxPtr)(ct69) // Disable HW TX flow control
-#define CMD_ODSRFL_ENAB (cmdSyntaxPtr)(ct70) // Enable DSR output f/c
-#define CMD_ODSRFL_DSAB (cmdSyntaxPtr)(ct71) // Disable DSR output f/c
-#define CMD_ODCDFL_ENAB (cmdSyntaxPtr)(ct72) // Enable DCD output f/c
-#define CMD_ODCDFL_DSAB (cmdSyntaxPtr)(ct73) // Disable DCD output f/c
-
-// Set transmit interrupt load level. Count should be an even value 2-12
-//
-#define CMD_LOADLEVEL(count) \
-       (((cmdSyntaxPtr)(ct74))->cmd[1] = (count),(cmdSyntaxPtr)(ct74))
-
-// If reporting DSS changes, map to character sequence FFh, 2, MSR
-//
-#define CMD_STATDATA(arg) \
-       (((cmdSyntaxPtr)(ct75))->cmd[1] = (arg),(cmdSyntaxPtr)(ct75))
-
-#define CSTD_DISABLE// Report DSS changes as status packets only (default)
-#define CSTD_ENABLE    // Report DSS changes as in-band data sequence as well as
-                                       // by status packet.
-
-#define CMD_BREAK_ON    (cmdSyntaxPtr)(ct76)// Set break and stop xmit
-#define CMD_BREAK_OFF   (cmdSyntaxPtr)(ct77)// End break and restart xmit
-#define CMD_GETFC       (cmdSyntaxPtr)(ct78)// Request for flow control packet
-                                                                                       // from board.
-
-// Transmit this character immediately
-//
-#define CMD_XMIT_NOW(ch) \
-       (((cmdSyntaxPtr)(ct79))->cmd[1] = (ch),(cmdSyntaxPtr)(ct79))
-
-// Set baud rate via "divisor latch"
-//
-#define CMD_DIVISOR_LATCH(which,value) \
-                       (((cmdSyntaxPtr)(ct80))->cmd[1] = (which), \
-                       *(USHORT *)(((cmdSyntaxPtr)(ct80))->cmd[2]) = (value), \
-                       (cmdSyntaxPtr)(ct80))
-
-#define CDL_RX 1       // Set receiver rate
-#define CDL_TX 2       // Set transmit rate
-                                       // (CDL_TX | CDL_RX) Set both rates
-
-// Request for special diagnostic status pkt from the board.
-//
-#define CMD_GET_STATUS (cmdSyntaxPtr)(ct81)
-
-// Request time-stamped transmit character count packet.
-//
-#define CMD_GET_TXCNT  (cmdSyntaxPtr)(ct82)
-
-// Request time-stamped receive character count packet.
-//
-#define CMD_GET_RXCNT  (cmdSyntaxPtr)(ct83)
-
-// Request for box/board I.D. packet.
-#define CMD_GET_BOXIDS (cmdSyntaxPtr)(ct84)
-
-// Enable or disable multiple channels according to bit-mapped ushorts box 1-4
-//
-#define CMD_ENAB_MULT(enable, box1, box2, box3, box4)    \
-                       (((cmdSytaxPtr)(ct85))->cmd[1] = (enable),            \
-                       *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[2]) = (box1), \
-                       *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[4]) = (box2), \
-                       *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[6]) = (box3), \
-                       *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[8]) = (box4), \
-                       (cmdSyntaxPtr)(ct85))
-
-#define CEM_DISABLE  0
-#define CEM_ENABLE   1
-
-// Enable or disable receiver or receiver interrupts (default both enabled)
-//
-#define CMD_RCV_ENABLE(ch) \
-       (((cmdSyntaxPtr)(ct86))->cmd[1] = (ch),(cmdSyntaxPtr)(ct86))
-
-#define CRE_OFF      0  // Disable the receiver
-#define CRE_ON       1  // Enable the receiver
-#define CRE_INTOFF   2  // Disable receiver interrupts (to loadware)
-#define CRE_INTON    3  // Enable receiver interrupts (to loadware)
-
-// Starts up a hardware test process, which runs transparently, and sends a
-// STAT_HWFAIL packet in case a hardware failure is detected.
-//
-#define CMD_HW_TEST  (cmdSyntaxPtr)(ct87)
-
-// Change receiver threshold and timeout value:
-// Defaults: timeout = 20mS
-// threshold count = 8 when DTRflow not in use,
-// threshold count = 5 when DTRflow in use.
-//
-#define CMD_RCV_THRESHOLD(count,ms) \
-                       (((cmdSyntaxPtr)(ct88))->cmd[1] = (count), \
-                       ((cmdSyntaxPtr)(ct88))->cmd[2] = (ms), \
-                       (cmdSyntaxPtr)(ct88))
-
-// Makes the loadware report DSS signals for this channel immediately.
-//
-#define CMD_DSS_NOW (cmdSyntaxPtr)(ct89)
-       
-// Set the receive silo parameters 
-//     timeout is ms idle wait until delivery       (~VTIME)
-//     threshold is max characters cause interrupt  (~VMIN)
-//
-#define CMD_SET_SILO(timeout,threshold) \
-                       (((cmdSyntaxPtr)(ct90))->cmd[1] = (timeout), \
-                       ((cmdSyntaxPtr)(ct90))->cmd[2]  = (threshold), \
-                       (cmdSyntaxPtr)(ct90))
-
-// Set timed break in decisecond (1/10s)
-//
-#define CMD_LBREAK(ds) \
-       (((cmdSyntaxPtr)(ct91))->cmd[1] = (ds),(cmdSyntaxPtr)(ct66))
-
-
-
-#endif // I2CMD_H
diff --git a/drivers/char/ip2/i2ellis.c b/drivers/char/ip2/i2ellis.c
deleted file mode 100644 (file)
index 29db44d..0000000
+++ /dev/null
@@ -1,1403 +0,0 @@
-/*******************************************************************************
-*
-*   (c) 1998 by Computone Corporation
-*
-********************************************************************************
-*
-*
-*   PACKAGE:     Linux tty Device Driver for IntelliPort family of multiport
-*                serial I/O controllers.
-*
-*   DESCRIPTION: Low-level interface code for the device driver
-*                (This is included source code, not a separate compilation
-*                module.)
-*
-*******************************************************************************/
-//---------------------------------------------
-// Function declarations private to this module
-//---------------------------------------------
-// Functions called only indirectly through i2eBordStr entries.
-
-static int iiWriteBuf16(i2eBordStrPtr, unsigned char *, int);
-static int iiWriteBuf8(i2eBordStrPtr, unsigned char *, int);
-static int iiReadBuf16(i2eBordStrPtr, unsigned char *, int);
-static int iiReadBuf8(i2eBordStrPtr, unsigned char *, int);
-
-static unsigned short iiReadWord16(i2eBordStrPtr);
-static unsigned short iiReadWord8(i2eBordStrPtr);
-static void iiWriteWord16(i2eBordStrPtr, unsigned short);
-static void iiWriteWord8(i2eBordStrPtr, unsigned short);
-
-static int iiWaitForTxEmptyII(i2eBordStrPtr, int);
-static int iiWaitForTxEmptyIIEX(i2eBordStrPtr, int);
-static int iiTxMailEmptyII(i2eBordStrPtr);
-static int iiTxMailEmptyIIEX(i2eBordStrPtr);
-static int iiTrySendMailII(i2eBordStrPtr, unsigned char);
-static int iiTrySendMailIIEX(i2eBordStrPtr, unsigned char);
-
-static unsigned short iiGetMailII(i2eBordStrPtr);
-static unsigned short iiGetMailIIEX(i2eBordStrPtr);
-
-static void iiEnableMailIrqII(i2eBordStrPtr);
-static void iiEnableMailIrqIIEX(i2eBordStrPtr);
-static void iiWriteMaskII(i2eBordStrPtr, unsigned char);
-static void iiWriteMaskIIEX(i2eBordStrPtr, unsigned char);
-
-static void ii2Nop(void);
-
-//***************
-//* Static Data *
-//***************
-
-static int ii2Safe;         // Safe I/O address for delay routine
-
-static int iiDelayed;  // Set when the iiResetDelay function is
-                                                       // called. Cleared when ANY board is reset.
-static DEFINE_RWLOCK(Dl_spinlock);
-
-//********
-//* Code *
-//********
-
-//=======================================================
-// Initialization Routines
-//
-// iiSetAddress
-// iiReset
-// iiResetDelay
-// iiInitialize
-//=======================================================
-
-//******************************************************************************
-// Function:   iiSetAddress(pB, address, delay)
-// Parameters: pB      - pointer to the board structure
-//             address - the purported I/O address of the board
-//             delay   - pointer to the 1-ms delay function to use
-//                       in this and any future operations to this board
-//
-// Returns:    True if everything appears copacetic.
-//             False if there is any error: the pB->i2eError field has the error
-//
-// Description:
-//
-// This routine (roughly) checks for address validity, sets the i2eValid OK and
-// sets the state to II_STATE_COLD which means that we haven't even sent a reset
-// yet.
-//
-//******************************************************************************
-static int
-iiSetAddress( i2eBordStrPtr pB, int address, delayFunc_t delay )
-{
-       // Should any failure occur before init is finished...
-       pB->i2eValid = I2E_INCOMPLETE;
-
-       // Cannot check upper limit except extremely: Might be microchannel
-       // Address must be on an 8-byte boundary
-
-       if ((unsigned int)address <= 0x100
-               || (unsigned int)address >= 0xfff8
-               || (address & 0x7)
-               )
-       {
-               I2_COMPLETE(pB, I2EE_BADADDR);
-       }
-
-       // Initialize accelerators
-       pB->i2eBase    = address;
-       pB->i2eData    = address + FIFO_DATA;
-       pB->i2eStatus  = address + FIFO_STATUS;
-       pB->i2ePointer = address + FIFO_PTR;
-       pB->i2eXMail   = address + FIFO_MAIL;
-       pB->i2eXMask   = address + FIFO_MASK;
-
-       // Initialize i/o address for ii2DelayIO
-       ii2Safe = address + FIFO_NOP;
-
-       // Initialize the delay routine
-       pB->i2eDelay = ((delay != (delayFunc_t)NULL) ? delay : (delayFunc_t)ii2Nop);
-
-       pB->i2eValid = I2E_MAGIC;
-       pB->i2eState = II_STATE_COLD;
-
-       I2_COMPLETE(pB, I2EE_GOOD);
-}
-
-//******************************************************************************
-// Function:   iiReset(pB)
-// Parameters: pB - pointer to the board structure
-//
-// Returns:    True if everything appears copacetic.
-//             False if there is any error: the pB->i2eError field has the error
-//
-// Description:
-//
-// Attempts to reset the board (see also i2hw.h). Normally, we would use this to
-// reset a board immediately after iiSetAddress(), but it is valid to reset a
-// board from any state, say, in order to change or re-load loadware. (Under
-// such circumstances, no reason to re-run iiSetAddress(), which is why it is a
-// separate routine and not included in this routine.
-//
-//******************************************************************************
-static int
-iiReset(i2eBordStrPtr pB)
-{
-       // Magic number should be set, else even the address is suspect
-       if (pB->i2eValid != I2E_MAGIC)
-       {
-               I2_COMPLETE(pB, I2EE_BADMAGIC);
-       }
-
-       outb(0, pB->i2eBase + FIFO_RESET);  /* Any data will do */
-       iiDelay(pB, 50);                    // Pause between resets
-       outb(0, pB->i2eBase + FIFO_RESET);  /* Second reset */
-
-       // We must wait before even attempting to read anything from the FIFO: the
-       // board's P.O.S.T may actually attempt to read and write its end of the
-       // FIFO in order to check flags, loop back (where supported), etc. On
-       // completion of this testing it would reset the FIFO, and on completion
-       // of all // P.O.S.T., write the message. We must not mistake data which
-       // might have been sent for testing as part of the reset message. To
-       // better utilize time, say, when resetting several boards, we allow the
-       // delay to be performed externally; in this way the caller can reset 
-       // several boards, delay a single time, then call the initialization
-       // routine for all.
-
-       pB->i2eState = II_STATE_RESET;
-
-       iiDelayed = 0;  // i.e., the delay routine hasn't been called since the most
-                                       // recent reset.
-
-       // Ensure anything which would have been of use to standard loadware is
-       // blanked out, since board has now forgotten everything!.
-
-       pB->i2eUsingIrq = I2_IRQ_UNDEFINED; /* to not use an interrupt so far */
-       pB->i2eWaitingForEmptyFifo = 0;
-       pB->i2eOutMailWaiting = 0;
-       pB->i2eChannelPtr = NULL;
-       pB->i2eChannelCnt = 0;
-
-       pB->i2eLeadoffWord[0] = 0;
-       pB->i2eFifoInInts = 0;
-       pB->i2eFifoOutInts = 0;
-       pB->i2eFatalTrap = NULL;
-       pB->i2eFatal = 0;
-
-       I2_COMPLETE(pB, I2EE_GOOD);
-}
-
-//******************************************************************************
-// Function:   iiResetDelay(pB)
-// Parameters: pB - pointer to the board structure
-//
-// Returns:    True if everything appears copacetic.
-//             False if there is any error: the pB->i2eError field has the error
-//
-// Description:
-//
-// Using the delay defined in board structure, waits two seconds (for board to
-// reset).
-//
-//******************************************************************************
-static int
-iiResetDelay(i2eBordStrPtr pB)
-{
-       if (pB->i2eValid != I2E_MAGIC) {
-               I2_COMPLETE(pB, I2EE_BADMAGIC);
-       }
-       if (pB->i2eState != II_STATE_RESET) {
-               I2_COMPLETE(pB, I2EE_BADSTATE);
-       }
-       iiDelay(pB,2000);       /* Now we wait for two seconds. */
-       iiDelayed = 1;          /* Delay has been called: ok to initialize */
-       I2_COMPLETE(pB, I2EE_GOOD);
-}
-
-//******************************************************************************
-// Function:   iiInitialize(pB)
-// Parameters: pB - pointer to the board structure
-//
-// Returns:    True if everything appears copacetic.
-//             False if there is any error: the pB->i2eError field has the error
-//
-// Description:
-//
-// Attempts to read the Power-on reset message. Initializes any remaining fields
-// in the pB structure.
-//
-// This should be called as the third step of a process beginning with
-// iiReset(), then iiResetDelay(). This routine checks to see that the structure
-// is "valid" and in the reset state, also confirms that the delay routine has
-// been called since the latest reset (to any board! overly strong!).
-//
-//******************************************************************************
-static int
-iiInitialize(i2eBordStrPtr pB)
-{
-       int itemp;
-       unsigned char c;
-       unsigned short utemp;
-       unsigned int ilimit;
-
-       if (pB->i2eValid != I2E_MAGIC)
-       {
-               I2_COMPLETE(pB, I2EE_BADMAGIC);
-       }
-
-       if (pB->i2eState != II_STATE_RESET || !iiDelayed)
-       {
-               I2_COMPLETE(pB, I2EE_BADSTATE);
-       }
-
-       // In case there is a failure short of our completely reading the power-up
-       // message.
-       pB->i2eValid = I2E_INCOMPLETE;
-
-
-       // Now attempt to read the message.
-
-       for (itemp = 0; itemp < sizeof(porStr); itemp++)
-       {
-               // We expect the entire message is ready.
-               if (!I2_HAS_INPUT(pB)) {
-                       pB->i2ePomSize = itemp;
-                       I2_COMPLETE(pB, I2EE_PORM_SHORT);
-               }
-
-               pB->i2ePom.c[itemp] = c = inb(pB->i2eData);
-
-               // We check the magic numbers as soon as they are supposed to be read
-               // (rather than after) to minimize effect of reading something we
-               // already suspect can't be "us".
-               if (  (itemp == POR_1_INDEX && c != POR_MAGIC_1) ||
-                               (itemp == POR_2_INDEX && c != POR_MAGIC_2))
-               {
-                       pB->i2ePomSize = itemp+1;
-                       I2_COMPLETE(pB, I2EE_BADMAGIC);
-               }
-       }
-
-       pB->i2ePomSize = itemp;
-
-       // Ensure that this was all the data...
-       if (I2_HAS_INPUT(pB))
-               I2_COMPLETE(pB, I2EE_PORM_LONG);
-
-       // For now, we'll fail to initialize if P.O.S.T reports bad chip mapper:
-       // Implying we will not be able to download any code either:  That's ok: the
-       // condition is pretty explicit.
-       if (pB->i2ePom.e.porDiag1 & POR_BAD_MAPPER)
-       {
-               I2_COMPLETE(pB, I2EE_POSTERR);
-       }
-
-       // Determine anything which must be done differently depending on the family
-       // of boards!
-       switch (pB->i2ePom.e.porID & POR_ID_FAMILY)
-       {
-       case POR_ID_FII:  // IntelliPort-II
-
-               pB->i2eFifoStyle   = FIFO_II;
-               pB->i2eFifoSize    = 512;     // 512 bytes, always
-               pB->i2eDataWidth16 = false;
-
-               pB->i2eMaxIrq = 15;     // Because board cannot tell us it is in an 8-bit
-                                                       // slot, we do allow it to be done (documentation!)
-
-               pB->i2eGoodMap[1] =
-               pB->i2eGoodMap[2] =
-               pB->i2eGoodMap[3] =
-               pB->i2eChannelMap[1] =
-               pB->i2eChannelMap[2] =
-               pB->i2eChannelMap[3] = 0;
-
-               switch (pB->i2ePom.e.porID & POR_ID_SIZE)
-               {
-               case POR_ID_II_4:
-                       pB->i2eGoodMap[0] =
-                       pB->i2eChannelMap[0] = 0x0f;  // four-port
-
-                       // Since porPorts1 is based on the Hardware ID register, the numbers
-                       // should always be consistent for IntelliPort-II.  Ditto below...
-                       if (pB->i2ePom.e.porPorts1 != 4)
-                       {
-                               I2_COMPLETE(pB, I2EE_INCONSIST);
-                       }
-                       break;
-
-               case POR_ID_II_8:
-               case POR_ID_II_8R:
-                       pB->i2eGoodMap[0] =
-                       pB->i2eChannelMap[0] = 0xff;  // Eight port
-                       if (pB->i2ePom.e.porPorts1 != 8)
-                       {
-                               I2_COMPLETE(pB, I2EE_INCONSIST);
-                       }
-                       break;
-
-               case POR_ID_II_6:
-                       pB->i2eGoodMap[0] =
-                       pB->i2eChannelMap[0] = 0x3f;  // Six Port
-                       if (pB->i2ePom.e.porPorts1 != 6)
-                       {
-                               I2_COMPLETE(pB, I2EE_INCONSIST);
-                       }
-                       break;
-               }
-
-               // Fix up the "good channel list based on any errors reported.
-               if (pB->i2ePom.e.porDiag1 & POR_BAD_UART1)
-               {
-                       pB->i2eGoodMap[0] &= ~0x0f;
-               }
-
-               if (pB->i2ePom.e.porDiag1 & POR_BAD_UART2)
-               {
-                       pB->i2eGoodMap[0] &= ~0xf0;
-               }
-
-               break;   // POR_ID_FII case
-
-       case POR_ID_FIIEX:   // IntelliPort-IIEX
-
-               pB->i2eFifoStyle = FIFO_IIEX;
-
-               itemp = pB->i2ePom.e.porFifoSize;
-
-               // Implicit assumption that fifo would not grow beyond 32k, 
-               // nor would ever be less than 256.
-
-               if (itemp < 8 || itemp > 15)
-               {
-                       I2_COMPLETE(pB, I2EE_INCONSIST);
-               }
-               pB->i2eFifoSize = (1 << itemp);
-
-               // These are based on what P.O.S.T thinks should be there, based on
-               // box ID registers
-               ilimit = pB->i2ePom.e.porNumBoxes;
-               if (ilimit > ABS_MAX_BOXES)
-               {
-                       ilimit = ABS_MAX_BOXES;
-               }
-
-               // For as many boxes as EXIST, gives the type of box.
-               // Added 8/6/93: check for the ISA-4 (asic) which looks like an
-               // expandable but for whom "8 or 16?" is not the right question.
-
-               utemp = pB->i2ePom.e.porFlags;
-               if (utemp & POR_CEX4)
-               {
-                       pB->i2eChannelMap[0] = 0x000f;
-               } else {
-                       utemp &= POR_BOXES;
-                       for (itemp = 0; itemp < ilimit; itemp++)
-                       {
-                               pB->i2eChannelMap[itemp] = 
-                                       ((utemp & POR_BOX_16) ? 0xffff : 0x00ff);
-                               utemp >>= 1;
-                       }
-               }
-
-               // These are based on what P.O.S.T actually found.
-
-               utemp = (pB->i2ePom.e.porPorts2 << 8) + pB->i2ePom.e.porPorts1;
-
-               for (itemp = 0; itemp < ilimit; itemp++)
-               {
-                       pB->i2eGoodMap[itemp] = 0;
-                       if (utemp & 1) pB->i2eGoodMap[itemp] |= 0x000f;
-                       if (utemp & 2) pB->i2eGoodMap[itemp] |= 0x00f0;
-                       if (utemp & 4) pB->i2eGoodMap[itemp] |= 0x0f00;
-                       if (utemp & 8) pB->i2eGoodMap[itemp] |= 0xf000;
-                       utemp >>= 4;
-               }
-
-               // Now determine whether we should transfer in 8 or 16-bit mode.
-               switch (pB->i2ePom.e.porBus & (POR_BUS_SLOT16 | POR_BUS_DIP16) )
-               {
-               case POR_BUS_SLOT16 | POR_BUS_DIP16:
-                       pB->i2eDataWidth16 = true;
-                       pB->i2eMaxIrq = 15;
-                       break;
-
-               case POR_BUS_SLOT16:
-                       pB->i2eDataWidth16 = false;
-                       pB->i2eMaxIrq = 15;
-                       break;
-
-               case 0:
-               case POR_BUS_DIP16:     // In an 8-bit slot, DIP switch don't care.
-               default:
-                       pB->i2eDataWidth16 = false;
-                       pB->i2eMaxIrq = 7;
-                       break;
-               }
-               break;   // POR_ID_FIIEX case
-
-       default:    // Unknown type of board
-               I2_COMPLETE(pB, I2EE_BAD_FAMILY);
-               break;
-       }  // End the switch based on family
-
-       // Temporarily, claim there is no room in the outbound fifo. 
-       // We will maintain this whenever we check for an empty outbound FIFO.
-       pB->i2eFifoRemains = 0;
-
-       // Now, based on the bus type, should we expect to be able to re-configure
-       // interrupts (say, for testing purposes).
-       switch (pB->i2ePom.e.porBus & POR_BUS_TYPE)
-       {
-       case POR_BUS_T_ISA:
-       case POR_BUS_T_UNK:  // If the type of bus is undeclared, assume ok.
-       case POR_BUS_T_MCA:
-       case POR_BUS_T_EISA:
-               break;
-       default:
-               I2_COMPLETE(pB, I2EE_BADBUS);
-       }
-
-       if (pB->i2eDataWidth16)
-       {
-               pB->i2eWriteBuf  = iiWriteBuf16;
-               pB->i2eReadBuf   = iiReadBuf16;
-               pB->i2eWriteWord = iiWriteWord16;
-               pB->i2eReadWord  = iiReadWord16;
-       } else {
-               pB->i2eWriteBuf  = iiWriteBuf8;
-               pB->i2eReadBuf   = iiReadBuf8;
-               pB->i2eWriteWord = iiWriteWord8;
-               pB->i2eReadWord  = iiReadWord8;
-       }
-
-       switch(pB->i2eFifoStyle)
-       {
-       case FIFO_II:
-               pB->i2eWaitForTxEmpty = iiWaitForTxEmptyII;
-               pB->i2eTxMailEmpty    = iiTxMailEmptyII;
-               pB->i2eTrySendMail    = iiTrySendMailII;
-               pB->i2eGetMail        = iiGetMailII;
-               pB->i2eEnableMailIrq  = iiEnableMailIrqII;
-               pB->i2eWriteMask      = iiWriteMaskII;
-
-               break;
-
-       case FIFO_IIEX:
-               pB->i2eWaitForTxEmpty = iiWaitForTxEmptyIIEX;
-               pB->i2eTxMailEmpty    = iiTxMailEmptyIIEX;
-               pB->i2eTrySendMail    = iiTrySendMailIIEX;
-               pB->i2eGetMail        = iiGetMailIIEX;
-               pB->i2eEnableMailIrq  = iiEnableMailIrqIIEX;
-               pB->i2eWriteMask      = iiWriteMaskIIEX;
-
-               break;
-
-       default:
-               I2_COMPLETE(pB, I2EE_INCONSIST);
-       }
-
-       // Initialize state information.
-       pB->i2eState = II_STATE_READY;   // Ready to load loadware.
-
-       // Some Final cleanup:
-       // For some boards, the bootstrap firmware may perform some sort of test
-       // resulting in a stray character pending in the incoming mailbox. If one is
-       // there, it should be read and discarded, especially since for the standard
-       // firmware, it's the mailbox that interrupts the host.
-
-       pB->i2eStartMail = iiGetMail(pB);
-
-       // Throw it away and clear the mailbox structure element
-       pB->i2eStartMail = NO_MAIL_HERE;
-
-       // Everything is ok now, return with good status/
-
-       pB->i2eValid = I2E_MAGIC;
-       I2_COMPLETE(pB, I2EE_GOOD);
-}
-
-//******************************************************************************
-// Function:   ii2DelayTimer(mseconds)
-// Parameters: mseconds - number of milliseconds to delay
-//
-// Returns:    Nothing
-//
-// Description:
-//
-// This routine delays for approximately mseconds milliseconds and is intended
-// to be called indirectly through i2Delay field in i2eBordStr. It uses the
-// Linux timer_list mechanism.
-//
-// The Linux timers use a unit called "jiffies" which are 10mS in the Intel
-// architecture. This function rounds the delay period up to the next "jiffy".
-// In the Alpha architecture the "jiffy" is 1mS, but this driver is not intended
-// for Alpha platforms at this time.
-//
-//******************************************************************************
-static void
-ii2DelayTimer(unsigned int mseconds)
-{
-       msleep_interruptible(mseconds);
-}
-
-#if 0
-//static void ii2DelayIO(unsigned int);
-//******************************************************************************
-// !!! Not Used, this is DOS crap, some of you young folks may be interested in
-//     in how things were done in the stone age of caculating machines       !!!
-// Function:   ii2DelayIO(mseconds)
-// Parameters: mseconds - number of milliseconds to delay
-//
-// Returns:    Nothing
-//
-// Description:
-//
-// This routine delays for approximately mseconds milliseconds and is intended
-// to be called indirectly through i2Delay field in i2eBordStr. It is intended
-// for use where a clock-based function is impossible: for example, DOS drivers.
-//
-// This function uses the IN instruction to place bounds on the timing and
-// assumes that ii2Safe has been set. This is because I/O instructions are not
-// subject to caching and will therefore take a certain minimum time. To ensure
-// the delay is at least long enough on fast machines, it is based on some
-// fastest-case calculations.  On slower machines this may cause VERY long
-// delays. (3 x fastest case). In the fastest case, everything is cached except
-// the I/O instruction itself.
-//
-// Timing calculations:
-// The fastest bus speed for I/O operations is likely to be 10 MHz. The I/O
-// operation in question is a byte operation to an odd address. For 8-bit
-// operations, the architecture generally enforces two wait states. At 10 MHz, a
-// single cycle time is 100nS. A read operation at two wait states takes 6
-// cycles for a total time of 600nS. Therefore approximately 1666 iterations
-// would be required to generate a single millisecond delay. The worst
-// (reasonable) case would be an 8MHz system with no cacheing. In this case, the
-// I/O instruction would take 125nS x 6 cyles = 750 nS. More importantly, code
-// fetch of other instructions in the loop would take time (zero wait states,
-// however) and would be hard to estimate. This is minimized by using in-line
-// assembler for the in inner loop of IN instructions. This consists of just a
-// few bytes. So we'll guess about four code fetches per loop. Each code fetch
-// should take four cycles, so we have 125nS * 8 = 1000nS. Worst case then is
-// that what should have taken 1 mS takes instead 1666 * (1750) = 2.9 mS.
-//
-// So much for theoretical timings: results using 1666 value on some actual
-// machines:
-// IBM      286      6MHz     3.15 mS
-// Zenith   386      33MHz    2.45 mS
-// (brandX) 386      33MHz    1.90 mS  (has cache)
-// (brandY) 486      33MHz    2.35 mS
-// NCR      486      ??       1.65 mS (microchannel)
-//
-// For most machines, it is probably safe to scale this number back (remember,
-// for robust operation use an actual timed delay if possible), so we are using
-// a value of 1190. This yields 1.17 mS for the fastest machine in our sample,
-// 1.75 mS for typical 386 machines, and 2.25 mS the absolute slowest machine.
-//
-// 1/29/93:
-// The above timings are too slow. Actual cycle times might be faster. ISA cycle
-// times could approach 500 nS, and ...
-// The IBM model 77 being microchannel has no wait states for 8-bit reads and
-// seems to be accessing the I/O at 440 nS per access (from start of one to
-// start of next). This would imply we need 1000/.440 = 2272 iterations to
-// guarantee we are fast enough. In actual testing, we see that 2 * 1190 are in
-// fact enough. For diagnostics, we keep the level at 1190, but developers note
-// this needs tuning.
-//
-// Safe assumption:  2270 i/o reads = 1 millisecond
-//
-//******************************************************************************
-
-
-static int ii2DelValue = 1190;  // See timing calculations below
-                                               // 1666 for fastest theoretical machine
-                                               // 1190 safe for most fast 386 machines
-                                               // 1000 for fastest machine tested here
-                                               //  540 (sic) for AT286/6Mhz
-static void
-ii2DelayIO(unsigned int mseconds)
-{
-       if (!ii2Safe) 
-               return;   /* Do nothing if this variable uninitialized */
-
-       while(mseconds--) {
-               int i = ii2DelValue;
-               while ( i-- ) {
-                       inb(ii2Safe);
-               }
-       }
-}
-#endif 
-
-//******************************************************************************
-// Function:   ii2Nop()
-// Parameters: None
-//
-// Returns:    Nothing
-//
-// Description:
-//
-// iiInitialize will set i2eDelay to this if the delay parameter is NULL. This
-// saves checking for a NULL pointer at every call.
-//******************************************************************************
-static void
-ii2Nop(void)
-{
-       return; // no mystery here
-}
-
-//=======================================================
-// Routines which are available in 8/16-bit versions, or
-// in different fifo styles. These are ALL called
-// indirectly through the board structure.
-//=======================================================
-
-//******************************************************************************
-// Function:   iiWriteBuf16(pB, address, count)
-// Parameters: pB      - pointer to board structure
-//             address - address of data to write
-//             count   - number of data bytes to write
-//
-// Returns:    True if everything appears copacetic.
-//             False if there is any error: the pB->i2eError field has the error
-//
-// Description:
-//
-// Writes 'count' bytes from 'address' to the data fifo specified by the board
-// structure pointer pB. Should count happen to be odd, an extra pad byte is
-// sent (identity unknown...). Uses 16-bit (word) operations. Is called
-// indirectly through pB->i2eWriteBuf.
-//
-//******************************************************************************
-static int
-iiWriteBuf16(i2eBordStrPtr pB, unsigned char *address, int count)
-{
-       // Rudimentary sanity checking here.
-       if (pB->i2eValid != I2E_MAGIC)
-               I2_COMPLETE(pB, I2EE_INVALID);
-
-       I2_OUTSW(pB->i2eData, address, count);
-
-       I2_COMPLETE(pB, I2EE_GOOD);
-}
-
-//******************************************************************************
-// Function:   iiWriteBuf8(pB, address, count)
-// Parameters: pB      - pointer to board structure
-//             address - address of data to write
-//             count   - number of data bytes to write
-//
-// Returns:    True if everything appears copacetic.
-//             False if there is any error: the pB->i2eError field has the error
-//
-// Description:
-//
-// Writes 'count' bytes from 'address' to the data fifo specified by the board
-// structure pointer pB. Should count happen to be odd, an extra pad byte is
-// sent (identity unknown...). This is to be consistent with the 16-bit version.
-// Uses 8-bit (byte) operations. Is called indirectly through pB->i2eWriteBuf.
-//
-//******************************************************************************
-static int
-iiWriteBuf8(i2eBordStrPtr pB, unsigned char *address, int count)
-{
-       /* Rudimentary sanity checking here */
-       if (pB->i2eValid != I2E_MAGIC)
-               I2_COMPLETE(pB, I2EE_INVALID);
-
-       I2_OUTSB(pB->i2eData, address, count);
-
-       I2_COMPLETE(pB, I2EE_GOOD);
-}
-
-//******************************************************************************
-// Function:   iiReadBuf16(pB, address, count)
-// Parameters: pB      - pointer to board structure
-//             address - address to put data read
-//             count   - number of data bytes to read
-//
-// Returns:    True if everything appears copacetic.
-//             False if there is any error: the pB->i2eError field has the error
-//
-// Description:
-//
-// Reads 'count' bytes into 'address' from the data fifo specified by the board
-// structure pointer pB. Should count happen to be odd, an extra pad byte is
-// received (identity unknown...). Uses 16-bit (word) operations. Is called
-// indirectly through pB->i2eReadBuf.
-//
-//******************************************************************************
-static int
-iiReadBuf16(i2eBordStrPtr pB, unsigned char *address, int count)
-{
-       // Rudimentary sanity checking here.
-       if (pB->i2eValid != I2E_MAGIC)
-               I2_COMPLETE(pB, I2EE_INVALID);
-
-       I2_INSW(pB->i2eData, address, count);
-
-       I2_COMPLETE(pB, I2EE_GOOD);
-}
-
-//******************************************************************************
-// Function:   iiReadBuf8(pB, address, count)
-// Parameters: pB      - pointer to board structure
-//             address - address to put data read
-//             count   - number of data bytes to read
-//
-// Returns:    True if everything appears copacetic.
-//             False if there is any error: the pB->i2eError field has the error
-//
-// Description:
-//
-// Reads 'count' bytes into 'address' from the data fifo specified by the board
-// structure pointer pB. Should count happen to be odd, an extra pad byte is
-// received (identity unknown...). This to match the 16-bit behaviour. Uses
-// 8-bit (byte) operations. Is called indirectly through pB->i2eReadBuf.
-//
-//******************************************************************************
-static int
-iiReadBuf8(i2eBordStrPtr pB, unsigned char *address, int count)
-{
-       // Rudimentary sanity checking here.
-       if (pB->i2eValid != I2E_MAGIC)
-               I2_COMPLETE(pB, I2EE_INVALID);
-
-       I2_INSB(pB->i2eData, address, count);
-
-       I2_COMPLETE(pB, I2EE_GOOD);
-}
-
-//******************************************************************************
-// Function:   iiReadWord16(pB)
-// Parameters: pB      - pointer to board structure
-//
-// Returns:    True if everything appears copacetic.
-//             False if there is any error: the pB->i2eError field has the error
-//
-// Description:
-//
-// Returns the word read from the data fifo specified by the board-structure
-// pointer pB. Uses a 16-bit operation. Is called indirectly through
-// pB->i2eReadWord.
-//
-//******************************************************************************
-static unsigned short
-iiReadWord16(i2eBordStrPtr pB)
-{
-       return inw(pB->i2eData);
-}
-
-//******************************************************************************
-// Function:   iiReadWord8(pB)
-// Parameters: pB      - pointer to board structure
-//
-// Returns:    True if everything appears copacetic.
-//             False if there is any error: the pB->i2eError field has the error
-//
-// Description:
-//
-// Returns the word read from the data fifo specified by the board-structure
-// pointer pB. Uses two 8-bit operations. Bytes are assumed to be LSB first. Is
-// called indirectly through pB->i2eReadWord.
-//
-//******************************************************************************
-static unsigned short
-iiReadWord8(i2eBordStrPtr pB)
-{
-       unsigned short urs;
-
-       urs = inb(pB->i2eData);
-
-       return (inb(pB->i2eData) << 8) | urs;
-}
-
-//******************************************************************************
-// Function:   iiWriteWord16(pB, value)
-// Parameters: pB    - pointer to board structure
-//             value - data to write
-//
-// Returns:    True if everything appears copacetic.
-//             False if there is any error: the pB->i2eError field has the error
-//
-// Description:
-//
-// Writes the word 'value' to the data fifo specified by the board-structure
-// pointer pB. Uses 16-bit operation. Is called indirectly through
-// pB->i2eWriteWord.
-//
-//******************************************************************************
-static void
-iiWriteWord16(i2eBordStrPtr pB, unsigned short value)
-{
-       outw((int)value, pB->i2eData);
-}
-
-//******************************************************************************
-// Function:   iiWriteWord8(pB, value)
-// Parameters: pB    - pointer to board structure
-//             value - data to write
-//
-// Returns:    True if everything appears copacetic.
-//             False if there is any error: the pB->i2eError field has the error
-//
-// Description:
-//
-// Writes the word 'value' to the data fifo specified by the board-structure
-// pointer pB. Uses two 8-bit operations (writes LSB first). Is called
-// indirectly through pB->i2eWriteWord.
-//
-//******************************************************************************
-static void
-iiWriteWord8(i2eBordStrPtr pB, unsigned short value)
-{
-       outb((char)value, pB->i2eData);
-       outb((char)(value >> 8), pB->i2eData);
-}
-
-//******************************************************************************
-// Function:   iiWaitForTxEmptyII(pB, mSdelay)
-// Parameters: pB      - pointer to board structure
-//             mSdelay - period to wait before returning
-//
-// Returns:    True if the FIFO is empty.
-//             False if it not empty in the required time: the pB->i2eError
-//             field has the error.
-//
-// Description:
-//
-// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if
-// not empty by the required time, returns false and error in pB->i2eError,
-// otherwise returns true.
-//
-// mSdelay == 0 is taken to mean must be empty on the first test.
-//
-// This version operates on IntelliPort-II - style FIFO's
-//
-// Note this routine is organized so that if status is ok there is no delay at
-// all called either before or after the test.  Is called indirectly through
-// pB->i2eWaitForTxEmpty.
-//
-//******************************************************************************
-static int
-iiWaitForTxEmptyII(i2eBordStrPtr pB, int mSdelay)
-{
-       unsigned long   flags;
-       int itemp;
-
-       for (;;)
-       {
-               // This routine hinges on being able to see the "other" status register
-               // (as seen by the local processor).  His incoming fifo is our outgoing
-               // FIFO.
-               //
-               // By the nature of this routine, you would be using this as part of a
-               // larger atomic context: i.e., you would use this routine to ensure the
-               // fifo empty, then act on this information. Between these two halves, 
-               // you will generally not want to service interrupts or in any way 
-               // disrupt the assumptions implicit in the larger context.
-               //
-               // Even worse, however, this routine "shifts" the status register to 
-               // point to the local status register which is not the usual situation.
-               // Therefore for extra safety, we force the critical section to be
-               // completely atomic, and pick up after ourselves before allowing any
-               // interrupts of any kind.
-
-
-               write_lock_irqsave(&Dl_spinlock, flags);
-               outb(SEL_COMMAND, pB->i2ePointer);
-               outb(SEL_CMD_SH, pB->i2ePointer);
-
-               itemp = inb(pB->i2eStatus);
-
-               outb(SEL_COMMAND, pB->i2ePointer);
-               outb(SEL_CMD_UNSH, pB->i2ePointer);
-
-               if (itemp & ST_IN_EMPTY)
-               {
-                       I2_UPDATE_FIFO_ROOM(pB);
-                       write_unlock_irqrestore(&Dl_spinlock, flags);
-                       I2_COMPLETE(pB, I2EE_GOOD);
-               }
-
-               write_unlock_irqrestore(&Dl_spinlock, flags);
-
-               if (mSdelay-- == 0)
-                       break;
-
-               iiDelay(pB, 1);      /* 1 mS granularity on checking condition */
-       }
-       I2_COMPLETE(pB, I2EE_TXE_TIME);
-}
-
-//******************************************************************************
-// Function:   iiWaitForTxEmptyIIEX(pB, mSdelay)
-// Parameters: pB      - pointer to board structure
-//             mSdelay - period to wait before returning
-//
-// Returns:    True if the FIFO is empty.
-//             False if it not empty in the required time: the pB->i2eError
-//             field has the error.
-//
-// Description:
-//
-// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if
-// not empty by the required time, returns false and error in pB->i2eError,
-// otherwise returns true.
-//
-// mSdelay == 0 is taken to mean must be empty on the first test.
-//
-// This version operates on IntelliPort-IIEX - style FIFO's
-//
-// Note this routine is organized so that if status is ok there is no delay at
-// all called either before or after the test.  Is called indirectly through
-// pB->i2eWaitForTxEmpty.
-//
-//******************************************************************************
-static int
-iiWaitForTxEmptyIIEX(i2eBordStrPtr pB, int mSdelay)
-{
-       unsigned long   flags;
-
-       for (;;)
-       {
-               // By the nature of this routine, you would be using this as part of a
-               // larger atomic context: i.e., you would use this routine to ensure the
-               // fifo empty, then act on this information. Between these two halves,
-               // you will generally not want to service interrupts or in any way
-               // disrupt the assumptions implicit in the larger context.
-
-               write_lock_irqsave(&Dl_spinlock, flags);
-
-               if (inb(pB->i2eStatus) & STE_OUT_MT) {
-                       I2_UPDATE_FIFO_ROOM(pB);
-                       write_unlock_irqrestore(&Dl_spinlock, flags);
-                       I2_COMPLETE(pB, I2EE_GOOD);
-               }
-               write_unlock_irqrestore(&Dl_spinlock, flags);
-
-               if (mSdelay-- == 0)
-                       break;
-
-               iiDelay(pB, 1);      // 1 mS granularity on checking condition
-       }
-       I2_COMPLETE(pB, I2EE_TXE_TIME);
-}
-
-//******************************************************************************
-// Function:   iiTxMailEmptyII(pB)
-// Parameters: pB      - pointer to board structure
-//
-// Returns:    True if the transmit mailbox is empty.
-//             False if it not empty.
-//
-// Description:
-//
-// Returns true or false according to whether the transmit mailbox is empty (and
-// therefore able to accept more mail)
-//
-// This version operates on IntelliPort-II - style FIFO's
-//
-//******************************************************************************
-static int
-iiTxMailEmptyII(i2eBordStrPtr pB)
-{
-       int port = pB->i2ePointer;
-       outb(SEL_OUTMAIL, port);
-       return inb(port) == 0;
-}
-
-//******************************************************************************
-// Function:   iiTxMailEmptyIIEX(pB)
-// Parameters: pB      - pointer to board structure
-//
-// Returns:    True if the transmit mailbox is empty.
-//             False if it not empty.
-//
-// Description:
-//
-// Returns true or false according to whether the transmit mailbox is empty (and
-// therefore able to accept more mail)
-//
-// This version operates on IntelliPort-IIEX - style FIFO's
-//
-//******************************************************************************
-static int
-iiTxMailEmptyIIEX(i2eBordStrPtr pB)
-{
-       return !(inb(pB->i2eStatus) & STE_OUT_MAIL);
-}
-
-//******************************************************************************
-// Function:   iiTrySendMailII(pB,mail)
-// Parameters: pB   - pointer to board structure
-//             mail - value to write to mailbox
-//
-// Returns:    True if the transmit mailbox is empty, and mail is sent.
-//             False if it not empty.
-//
-// Description:
-//
-// If outgoing mailbox is empty, sends mail and returns true. If outgoing
-// mailbox is not empty, returns false.
-//
-// This version operates on IntelliPort-II - style FIFO's
-//
-//******************************************************************************
-static int
-iiTrySendMailII(i2eBordStrPtr pB, unsigned char mail)
-{
-       int port = pB->i2ePointer;
-
-       outb(SEL_OUTMAIL, port);
-       if (inb(port) == 0) {
-               outb(SEL_OUTMAIL, port);
-               outb(mail, port);
-               return 1;
-       }
-       return 0;
-}
-
-//******************************************************************************
-// Function:   iiTrySendMailIIEX(pB,mail)
-// Parameters: pB   - pointer to board structure
-//             mail - value to write to mailbox
-//
-// Returns:    True if the transmit mailbox is empty, and mail is sent.
-//             False if it not empty.
-//
-// Description:
-//
-// If outgoing mailbox is empty, sends mail and returns true. If outgoing
-// mailbox is not empty, returns false.
-//
-// This version operates on IntelliPort-IIEX - style FIFO's
-//
-//******************************************************************************
-static int
-iiTrySendMailIIEX(i2eBordStrPtr pB, unsigned char mail)
-{
-       if (inb(pB->i2eStatus) & STE_OUT_MAIL)
-               return 0;
-       outb(mail, pB->i2eXMail);
-       return 1;
-}
-
-//******************************************************************************
-// Function:   iiGetMailII(pB,mail)
-// Parameters: pB   - pointer to board structure
-//
-// Returns:    Mailbox data or NO_MAIL_HERE.
-//
-// Description:
-//
-// If no mail available, returns NO_MAIL_HERE otherwise returns the data from
-// the mailbox, which is guaranteed != NO_MAIL_HERE.
-//
-// This version operates on IntelliPort-II - style FIFO's
-//
-//******************************************************************************
-static unsigned short
-iiGetMailII(i2eBordStrPtr pB)
-{
-       if (I2_HAS_MAIL(pB)) {
-               outb(SEL_INMAIL, pB->i2ePointer);
-               return inb(pB->i2ePointer);
-       } else {
-               return NO_MAIL_HERE;
-       }
-}
-
-//******************************************************************************
-// Function:   iiGetMailIIEX(pB,mail)
-// Parameters: pB   - pointer to board structure
-//
-// Returns:    Mailbox data or NO_MAIL_HERE.
-//
-// Description:
-//
-// If no mail available, returns NO_MAIL_HERE otherwise returns the data from
-// the mailbox, which is guaranteed != NO_MAIL_HERE.
-//
-// This version operates on IntelliPort-IIEX - style FIFO's
-//
-//******************************************************************************
-static unsigned short
-iiGetMailIIEX(i2eBordStrPtr pB)
-{
-       if (I2_HAS_MAIL(pB))
-               return inb(pB->i2eXMail);
-       else
-               return NO_MAIL_HERE;
-}
-
-//******************************************************************************
-// Function:   iiEnableMailIrqII(pB)
-// Parameters: pB - pointer to board structure
-//
-// Returns:    Nothing
-//
-// Description:
-//
-// Enables board to interrupt host (only) by writing to host's in-bound mailbox.
-//
-// This version operates on IntelliPort-II - style FIFO's
-//
-//******************************************************************************
-static void
-iiEnableMailIrqII(i2eBordStrPtr pB)
-{
-       outb(SEL_MASK, pB->i2ePointer);
-       outb(ST_IN_MAIL, pB->i2ePointer);
-}
-
-//******************************************************************************
-// Function:   iiEnableMailIrqIIEX(pB)
-// Parameters: pB - pointer to board structure
-//
-// Returns:    Nothing
-//
-// Description:
-//
-// Enables board to interrupt host (only) by writing to host's in-bound mailbox.
-//
-// This version operates on IntelliPort-IIEX - style FIFO's
-//
-//******************************************************************************
-static void
-iiEnableMailIrqIIEX(i2eBordStrPtr pB)
-{
-       outb(MX_IN_MAIL, pB->i2eXMask);
-}
-
-//******************************************************************************
-// Function:   iiWriteMaskII(pB)
-// Parameters: pB - pointer to board structure
-//
-// Returns:    Nothing
-//
-// Description:
-//
-// Writes arbitrary value to the mask register.
-//
-// This version operates on IntelliPort-II - style FIFO's
-//
-//******************************************************************************
-static void
-iiWriteMaskII(i2eBordStrPtr pB, unsigned char value)
-{
-       outb(SEL_MASK, pB->i2ePointer);
-       outb(value, pB->i2ePointer);
-}
-
-//******************************************************************************
-// Function:   iiWriteMaskIIEX(pB)
-// Parameters: pB - pointer to board structure
-//
-// Returns:    Nothing
-//
-// Description:
-//
-// Writes arbitrary value to the mask register.
-//
-// This version operates on IntelliPort-IIEX - style FIFO's
-//
-//******************************************************************************
-static void
-iiWriteMaskIIEX(i2eBordStrPtr pB, unsigned char value)
-{
-       outb(value, pB->i2eXMask);
-}
-
-//******************************************************************************
-// Function:   iiDownloadBlock(pB, pSource, isStandard)
-// Parameters: pB         - pointer to board structure
-//             pSource    - loadware block to download
-//             isStandard - True if "standard" loadware, else false.
-//
-// Returns:    Success or Failure
-//
-// Description:
-//
-// Downloads a single block (at pSource)to the board referenced by pB. Caller
-// sets isStandard to true/false according to whether the "standard" loadware is
-// what's being loaded. The normal process, then, is to perform an iiInitialize
-// to the board, then perform some number of iiDownloadBlocks using the returned
-// state to determine when download is complete.
-//
-// Possible return values: (see I2ELLIS.H)
-// II_DOWN_BADVALID
-// II_DOWN_BADFILE
-// II_DOWN_CONTINUING
-// II_DOWN_GOOD
-// II_DOWN_BAD
-// II_DOWN_BADSTATE
-// II_DOWN_TIMEOUT
-//
-// Uses the i2eState and i2eToLoad fields (initialized at iiInitialize) to
-// determine whether this is the first block, whether to check for magic
-// numbers, how many blocks there are to go...
-//
-//******************************************************************************
-static int
-iiDownloadBlock ( i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard)
-{
-       int itemp;
-       int loadedFirst;
-
-       if (pB->i2eValid != I2E_MAGIC) return II_DOWN_BADVALID;
-
-       switch(pB->i2eState)
-       {
-       case II_STATE_READY:
-
-               // Loading the first block after reset. Must check the magic number of the
-               // loadfile, store the number of blocks we expect to load.
-               if (pSource->e.loadMagic != MAGIC_LOADFILE)
-               {
-                       return II_DOWN_BADFILE;
-               }
-
-               // Next we store the total number of blocks to load, including this one.
-               pB->i2eToLoad = 1 + pSource->e.loadBlocksMore;
-
-               // Set the state, store the version numbers. ('Cause this may have come
-               // from a file - we might want to report these versions and revisions in
-               // case of an error!
-               pB->i2eState = II_STATE_LOADING;
-               pB->i2eLVersion = pSource->e.loadVersion;
-               pB->i2eLRevision = pSource->e.loadRevision;
-               pB->i2eLSub = pSource->e.loadSubRevision;
-
-               // The time and date of compilation is also available but don't bother
-               // storing it for normal purposes.
-               loadedFirst = 1;
-               break;
-
-       case II_STATE_LOADING:
-               loadedFirst = 0;
-               break;
-
-       default:
-               return II_DOWN_BADSTATE;
-       }
-
-       // Now we must be in the II_STATE_LOADING state, and we assume i2eToLoad
-       // must be positive still, because otherwise we would have cleaned up last
-       // time and set the state to II_STATE_LOADED.
-       if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) {
-               return II_DOWN_TIMEOUT;
-       }
-
-       if (!iiWriteBuf(pB, pSource->c, LOADWARE_BLOCK_SIZE)) {
-               return II_DOWN_BADVALID;
-       }
-
-       // If we just loaded the first block, wait for the fifo to empty an extra
-       // long time to allow for any special startup code in the firmware, like
-       // sending status messages to the LCD's.
-
-       if (loadedFirst) {
-               if (!iiWaitForTxEmpty(pB, MAX_DLOAD_START_TIME)) {
-                       return II_DOWN_TIMEOUT;
-               }
-       }
-
-       // Determine whether this was our last block!
-       if (--(pB->i2eToLoad)) {
-               return II_DOWN_CONTINUING;    // more to come...
-       }
-
-       // It WAS our last block: Clean up operations...
-       // ...Wait for last buffer to drain from the board...
-       if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) {
-               return II_DOWN_TIMEOUT;
-       }
-       // If there were only a single block written, this would come back
-       // immediately and be harmless, though not strictly necessary.
-       itemp = MAX_DLOAD_ACK_TIME/10;
-       while (--itemp) {
-               if (I2_HAS_INPUT(pB)) {
-                       switch (inb(pB->i2eData)) {
-                       case LOADWARE_OK:
-                               pB->i2eState =
-                                       isStandard ? II_STATE_STDLOADED :II_STATE_LOADED;
-
-                               // Some revisions of the bootstrap firmware (e.g. ISA-8 1.0.2)
-                               // will, // if there is a debug port attached, require some
-                               // time to send information to the debug port now. It will do
-                               // this before // executing any of the code we just downloaded.
-                               // It may take up to 700 milliseconds.
-                               if (pB->i2ePom.e.porDiag2 & POR_DEBUG_PORT) {
-                                       iiDelay(pB, 700);
-                               }
-
-                               return II_DOWN_GOOD;
-
-                       case LOADWARE_BAD:
-                       default:
-                               return II_DOWN_BAD;
-                       }
-               }
-
-               iiDelay(pB, 10);      // 10 mS granularity on checking condition
-       }
-
-       // Drop-through --> timed out waiting for firmware confirmation
-
-       pB->i2eState = II_STATE_BADLOAD;
-       return II_DOWN_TIMEOUT;
-}
-
-//******************************************************************************
-// Function:   iiDownloadAll(pB, pSource, isStandard, size)
-// Parameters: pB         - pointer to board structure
-//             pSource    - loadware block to download
-//             isStandard - True if "standard" loadware, else false.
-//             size       - size of data to download (in bytes)
-//
-// Returns:    Success or Failure
-//
-// Description:
-//
-// Given a pointer to a board structure, a pointer to the beginning of some
-// loadware, whether it is considered the "standard loadware", and the size of
-// the array in bytes loads the entire array to the board as loadware.
-//
-// Assumes the board has been freshly reset and the power-up reset message read.
-// (i.e., in II_STATE_READY). Complains if state is bad, or if there seems to be
-// too much or too little data to load, or if iiDownloadBlock complains.
-//******************************************************************************
-static int
-iiDownloadAll(i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard, int size)
-{
-       int status;
-
-       // We know (from context) board should be ready for the first block of
-       // download.  Complain if not.
-       if (pB->i2eState != II_STATE_READY) return II_DOWN_BADSTATE;
-
-       while (size > 0) {
-               size -= LOADWARE_BLOCK_SIZE;    // How much data should there be left to
-                                                                               // load after the following operation ?
-
-               // Note we just bump pSource by "one", because its size is actually that
-               // of an entire block, same as LOADWARE_BLOCK_SIZE.
-               status = iiDownloadBlock(pB, pSource++, isStandard);
-
-               switch(status)
-               {
-               case II_DOWN_GOOD:
-                       return ( (size > 0) ? II_DOWN_OVER : II_DOWN_GOOD);
-
-               case II_DOWN_CONTINUING:
-                       break;
-
-               default:
-                       return status;
-               }
-       }
-
-       // We shouldn't drop out: it means "while" caught us with nothing left to
-       // download, yet the previous DownloadBlock did not return complete. Ergo,
-       // not enough data to match the size byte in the header.
-       return II_DOWN_UNDER;
-}
diff --git a/drivers/char/ip2/i2ellis.h b/drivers/char/ip2/i2ellis.h
deleted file mode 100644 (file)
index fb6df24..0000000
+++ /dev/null
@@ -1,566 +0,0 @@
-/*******************************************************************************
-*
-*   (c) 1999 by Computone Corporation
-*
-********************************************************************************
-*
-*
-*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
-*                serial I/O controllers.
-*
-*   DESCRIPTION: Mainline code for the device driver
-*
-*******************************************************************************/
-//------------------------------------------------------------------------------
-// i2ellis.h
-//
-// IntelliPort-II and IntelliPort-IIEX
-//
-// Extremely
-// Low
-// Level
-// Interface
-// Services
-//
-// Structure Definitions and declarations for "ELLIS" service routines found in
-// i2ellis.c
-//
-// These routines are based on properties of the IntelliPort-II and -IIEX
-// hardware and bootstrap firmware, and are not sensitive to particular
-// conventions of any particular loadware.
-//
-// Unlike i2hw.h, which provides IRONCLAD hardware definitions, the material
-// here and in i2ellis.c is intended to provice a useful, but not required,
-// layer of insulation from the hardware specifics.
-//------------------------------------------------------------------------------
-#ifndef  I2ELLIS_H   /* To prevent multiple includes */
-#define  I2ELLIS_H   1
-//------------------------------------------------
-// Revision History:
-//
-// 30 September 1991 MAG First Draft Started
-// 12 October   1991 ...continued...
-//
-// 20 December  1996 AKM Linux version
-//-------------------------------------------------
-
-//----------------------
-// Mandatory Includes:
-//----------------------
-#include "ip2types.h"
-#include "i2hw.h"       // The hardware definitions
-
-//------------------------------------------
-// STAT_BOXIDS packets
-//------------------------------------------
-#define MAX_BOX                4
-
-typedef struct _bidStat
-{
-       unsigned char bid_value[MAX_BOX];
-} bidStat, *bidStatPtr;
-
-// This packet is sent in response to a CMD_GET_BOXIDS bypass command. For -IIEX
-// boards, reports the hardware-specific "asynchronous resource register" on
-// each expansion box. Boxes not present report 0xff. For -II boards, the first
-// element contains 0x80 for 8-port, 0x40 for 4-port boards.
-
-// Box IDs aka ARR or Async Resource Register (more than you want to know)
-//   7   6   5   4   3   2   1   0
-//   F   F   N   N   L   S   S   S
-//   =============================
-//   F   F   -  Product Family Designator
-//   =====+++++++++++++++++++++++++++++++
-//   0   0   -  Intelliport II EX / ISA-8
-//   1   0   -  IntelliServer
-//   0   1   -  SAC - Port Device (Intelliport III ??? )
-//           =====+++++++++++++++++++++++++++++++++++++++
-//           N   N   -  Number of Ports
-//           0   0   -  8  (eight)
-//           0   1   -  4  (four)
-//           1   0   -  12 (twelve)
-//           1   1   -  16 (sixteen)
-//                   =++++++++++++++++++++++++++++++++++
-//                   L  -   LCD Display Module Present
-//                   0  -   No
-//                   1  -   LCD module present
-//                   =========+++++++++++++++++++++++++++++++++++++
-//                      S   S   S - Async Signals Supported Designator
-//                      0   0   0 - 8dss, Mod DCE DB25 Female
-//                      0   0   1 - 6dss, RJ-45
-//                      0   1   0 - RS-232/422 dss, DB25 Female
-//                      0   1   1 - RS-232/422 dss, separate 232/422 DB25 Female
-//                      1   0   0 - 6dss, 921.6 I/F with ST654's
-//                      1   0   1 - RS-423/232 8dss, RJ-45 10Pin
-//                      1   1   0 - 6dss, Mod DCE DB25 Female
-//                      1   1   1 - NO BOX PRESENT
-
-#define FF(c)  ((c & 0xC0) >> 6)
-#define NN(c)  ((c & 0x30) >> 4)
-#define L(c)   ((c & 0x08) >> 3)
-#define SSS(c)  (c & 0x07)
-
-#define BID_HAS_654(x) (SSS(x) == 0x04)
-#define BID_NO_BOX     0xff /* no box */
-#define BID_8PORT      0x80 /* IP2-8 port */
-#define BID_4PORT      0x81 /* IP2-4 port */
-#define BID_EXP_MASK           0x30 /* IP2-EX  */
-#define BID_EXP_8PORT  0x00 /*     8, */
-#define BID_EXP_4PORT  0x10 /*     4, */
-#define BID_EXP_UNDEF  0x20 /*     UNDEF, */
-#define BID_EXP_16PORT 0x30 /*    16, */
-#define BID_LCD_CTRL           0x08 /* LCD Controller */
-#define BID_LCD_NONE   0x00 /* - no controller present */
-#define BID_LCD_PRES           0x08 /* - controller present */
-#define BID_CON_MASK   0x07 /* - connector pinouts */
-#define BID_CON_DB25   0x00 /* - DB-25 F */
-#define BID_CON_RJ45   0x01 /* - rj45 */
-
-//------------------------------------------------------------------------------
-// i2eBordStr
-//
-// This structure contains all the information the ELLIS routines require in
-// dealing with a particular board.
-//------------------------------------------------------------------------------
-// There are some queues here which are guaranteed to never contain the entry
-// for a single channel twice. So they must be slightly larger to allow
-// unambiguous full/empty management
-//
-#define CH_QUEUE_SIZE ABS_MOST_PORTS+2
-
-typedef struct _i2eBordStr
-{
-       porStr         i2ePom;  // Structure containing the power-on message.
-
-       unsigned short i2ePomSize;
-                                               // The number of bytes actually read if
-                                               // different from sizeof i2ePom, indicates
-                                               // there is an error!
-
-       unsigned short i2eStartMail;
-                                               // Contains whatever inbound mailbox data
-                                               // present at startup. NO_MAIL_HERE indicates
-                                               // nothing was present. No special
-                                               // significance as of this writing, but may be
-                                               // useful for diagnostic reasons.
-
-       unsigned short i2eValid;
-                                               // Indicates validity of the structure; if
-                                               // i2eValid == I2E_MAGIC, then we can trust
-                                               // the other fields. Some (especially
-                                               // initialization) functions are good about
-                                               // checking for validity.  Many functions do
-                                               // not, it being assumed that the larger
-                                               // context assures we are using a valid
-                                               // i2eBordStrPtr.
-
-       unsigned short i2eError;
-                                               // Used for returning an error condition from
-                                               // several functions which use i2eBordStrPtr
-                                               // as an argument.
-
-       // Accelerators to characterize separate features of a board, derived from a
-       // number of sources.
-
-       unsigned short i2eFifoSize;
-                                               // Always, the size of the FIFO. For
-                                               // IntelliPort-II, always the same, for -IIEX
-                                               // taken from the Power-On reset message.
-
-       volatile 
-       unsigned short i2eFifoRemains;
-                                               // Used during normal operation to indicate a
-                                               // lower bound on the amount of data which
-                                               // might be in the outbound fifo.
-
-       unsigned char  i2eFifoStyle;
-                                               // Accelerator which tells which style (-II or
-                                               // -IIEX) FIFO we are using.
-
-       unsigned char  i2eDataWidth16;
-                                               // Accelerator which tells whether we should
-                                               // do 8 or 16-bit data transfers.
-
-       unsigned char  i2eMaxIrq;
-                                               // The highest allowable IRQ, based on the
-                                               // slot size.
-
-       // Accelerators for various addresses on the board
-       int            i2eBase;        // I/O Address of the Board
-       int            i2eData;        // From here data transfers happen
-       int            i2eStatus;      // From here status reads happen
-       int            i2ePointer;     // (IntelliPort-II: pointer/commands)
-       int            i2eXMail;       // (IntelliPOrt-IIEX: mailboxes
-       int            i2eXMask;       // (IntelliPort-IIEX: mask write
-
-       //-------------------------------------------------------
-       // Information presented in a common format across boards
-       // For each box, bit map of the channels present.  Box closest to 
-       // the host is box 0. LSB is channel 0. IntelliPort-II (non-expandable)
-       // is taken to be box 0. These are derived from product i.d. registers.
-
-       unsigned short i2eChannelMap[ABS_MAX_BOXES];
-
-       // Same as above, except each is derived from firmware attempting to detect
-       // the uart presence (by reading a valid GFRCR register). If bits are set in
-       // i2eChannelMap and not in i2eGoodMap, there is a potential problem.
-
-       unsigned short i2eGoodMap[ABS_MAX_BOXES];
-
-       // ---------------------------
-       // For indirect function calls
-
-       // Routine to cause an N-millisecond delay: Patched by the ii2Initialize
-       // function.
-
-       void  (*i2eDelay)(unsigned int);
-
-       // Routine to write N bytes to the board through the FIFO. Returns true if
-       // all copacetic, otherwise returns false and error is in i2eError field.
-       // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER.
-
-       int   (*i2eWriteBuf)(struct _i2eBordStr *, unsigned char *, int);
-
-       // Routine to read N bytes from the board through the FIFO. Returns true if
-       // copacetic, otherwise returns false and error in i2eError.
-       // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER.
-
-       int   (*i2eReadBuf)(struct _i2eBordStr *, unsigned char *, int);
-
-       // Returns a word from FIFO. Will use 2 byte operations if needed.
-
-       unsigned short (*i2eReadWord)(struct _i2eBordStr *);
-
-       // Writes a word to FIFO. Will use 2 byte operations if needed.
-
-       void  (*i2eWriteWord)(struct _i2eBordStr *, unsigned short);
-
-       // Waits specified time for the Transmit FIFO to go empty. Returns true if
-       //  ok, otherwise returns false and error in i2eError.
-
-       int   (*i2eWaitForTxEmpty)(struct _i2eBordStr *, int);
-
-       // Returns true or false according to whether the outgoing mailbox is empty.
-
-       int   (*i2eTxMailEmpty)(struct _i2eBordStr *);
-
-       // Checks whether outgoing mailbox is empty.  If so, sends mail and returns
-       // true.  Otherwise returns false.
-
-       int   (*i2eTrySendMail)(struct _i2eBordStr *, unsigned char);
-
-       // If no mail available, returns NO_MAIL_HERE, else returns the value in the
-       // mailbox (guaranteed can't be NO_MAIL_HERE).
-
-       unsigned short (*i2eGetMail)(struct _i2eBordStr *);
-
-       // Enables the board to interrupt the host when it writes to the mailbox.
-       // Irqs will not occur, however, until the loadware separately enables
-       // interrupt generation to the host.  The standard loadware does this in
-       // response to a command packet sent by the host. (Also, disables
-       // any other potential interrupt sources from the board -- other than the
-       // inbound mailbox).
-
-       void  (*i2eEnableMailIrq)(struct _i2eBordStr *);
-
-       // Writes an arbitrary value to the mask register.
-
-       void  (*i2eWriteMask)(struct _i2eBordStr *, unsigned char);
-
-
-       // State information
-
-       // During downloading, indicates the number of blocks remaining to download
-       // to the board.
-
-       short i2eToLoad;
-
-       // State of board (see manifests below) (e.g., whether in reset condition,
-       // whether standard loadware is installed, etc.
-
-       unsigned char  i2eState;
-
-       // These three fields are only valid when there is loadware running on the
-       // board. (i2eState == II_STATE_LOADED or i2eState == II_STATE_STDLOADED )
-
-       unsigned char  i2eLVersion;  // Loadware version
-       unsigned char  i2eLRevision; // Loadware revision
-       unsigned char  i2eLSub;      // Loadware subrevision
-
-       // Flags which only have meaning in the context of the standard loadware.
-       // Somewhat violates the layering concept, but there is so little additional
-       // needed at the board level (while much additional at the channel level),
-       // that this beats maintaining two different per-board structures.
-
-       // Indicates which IRQ the board has been initialized (from software) to use
-       // For MicroChannel boards, any value different from IRQ_UNDEFINED means
-       // that the software command has been sent to enable interrupts (or specify
-       // they are disabled). Special value: IRQ_UNDEFINED indicates that the
-       // software command to select the interrupt has not yet been sent, therefore
-       // (since the standard loadware insists that it be sent before any other
-       // packets are sent) no other packets should be sent yet.
-
-       unsigned short i2eUsingIrq;
-
-       // This is set when we hit the MB_OUT_STUFFED mailbox, which prevents us
-       // putting more in the mailbox until an appropriate mailbox message is
-       // received.
-
-       unsigned char  i2eWaitingForEmptyFifo;
-
-       // Any mailbox bits waiting to be sent to the board are OR'ed in here.
-
-       unsigned char  i2eOutMailWaiting;
-
-       // The head of any incoming packet is read into here, is then examined and 
-       // we dispatch accordingly.
-
-       unsigned short i2eLeadoffWord[1];
-
-       // Running counter of interrupts where the mailbox indicated incoming data.
-
-       unsigned short i2eFifoInInts;
-
-       // Running counter of interrupts where the mailbox indicated outgoing data
-       // had been stripped.
-
-       unsigned short i2eFifoOutInts;
-
-       // If not void, gives the address of a routine to call if fatal board error
-       // is found (only applies to standard l/w).
-
-       void  (*i2eFatalTrap)(struct _i2eBordStr *);
-
-       // Will point to an array of some sort of channel structures (whose format
-       // is unknown at this level, being a function of what loadware is
-       // installed and the code configuration (max sizes of buffers, etc.)).
-
-       void  *i2eChannelPtr;
-
-       // Set indicates that the board has gone fatal.
-
-       unsigned short i2eFatal;
-
-       // The number of elements pointed to by i2eChannelPtr.
-
-       unsigned short i2eChannelCnt;
-
-       // Ring-buffers of channel structures whose channels have particular needs.
-
-       rwlock_t        Fbuf_spinlock;
-       volatile
-       unsigned short i2Fbuf_strip;    // Strip index
-       volatile 
-       unsigned short i2Fbuf_stuff;    // Stuff index
-       void  *i2Fbuf[CH_QUEUE_SIZE];   // An array of channel pointers
-                                                                       // of channels who need to send
-                                                                       // flow control packets.
-       rwlock_t        Dbuf_spinlock;
-       volatile
-       unsigned short i2Dbuf_strip;    // Strip index
-       volatile
-       unsigned short i2Dbuf_stuff;    // Stuff index
-       void  *i2Dbuf[CH_QUEUE_SIZE];   // An array of channel pointers
-                                                                       // of channels who need to send
-                                                                       // data or in-line command packets.
-       rwlock_t        Bbuf_spinlock;
-       volatile
-       unsigned short i2Bbuf_strip;    // Strip index
-       volatile
-       unsigned short i2Bbuf_stuff;    // Stuff index
-       void  *i2Bbuf[CH_QUEUE_SIZE];   // An array of channel pointers
-                                                                       // of channels who need to send
-                                                                       // bypass command packets.
-
-       /*
-        * A set of flags to indicate that certain events have occurred on at least
-        * one of the ports on this board. We use this to decide whether to spin
-        * through the channels looking for breaks, etc.
-        */
-       int             got_input;
-       int             status_change;
-       bidStat channelBtypes;
-
-       /*
-        * Debugging counters, etc.
-        */
-       unsigned long debugFlowQueued;
-       unsigned long debugInlineQueued;
-       unsigned long debugDataQueued;
-       unsigned long debugBypassQueued;
-       unsigned long debugFlowCount;
-       unsigned long debugInlineCount;
-       unsigned long debugBypassCount;
-       
-       rwlock_t        read_fifo_spinlock;
-       rwlock_t        write_fifo_spinlock;
-
-//     For queuing interrupt bottom half handlers.     /\/\|=mhw=|\/\/
-       struct work_struct      tqueue_interrupt;
-
-       struct timer_list  SendPendingTimer;   // Used by iiSendPending
-       unsigned int    SendPendingRetry;
-} i2eBordStr, *i2eBordStrPtr;
-
-//-------------------------------------------------------------------
-// Macro Definitions for the indirect calls defined in the i2eBordStr
-//-------------------------------------------------------------------
-//
-#define iiDelay(a,b)          (*(a)->i2eDelay)(b)
-#define iiWriteBuf(a,b,c)     (*(a)->i2eWriteBuf)(a,b,c)
-#define iiReadBuf(a,b,c)      (*(a)->i2eReadBuf)(a,b,c)
-
-#define iiWriteWord(a,b)      (*(a)->i2eWriteWord)(a,b)
-#define iiReadWord(a)         (*(a)->i2eReadWord)(a)
-
-#define iiWaitForTxEmpty(a,b) (*(a)->i2eWaitForTxEmpty)(a,b)
-
-#define iiTxMailEmpty(a)      (*(a)->i2eTxMailEmpty)(a)
-#define iiTrySendMail(a,b)    (*(a)->i2eTrySendMail)(a,b)
-
-#define iiGetMail(a)          (*(a)->i2eGetMail)(a)
-#define iiEnableMailIrq(a)    (*(a)->i2eEnableMailIrq)(a)
-#define iiDisableMailIrq(a)   (*(a)->i2eWriteMask)(a,0)
-#define iiWriteMask(a,b)      (*(a)->i2eWriteMask)(a,b)
-
-//-------------------------------------------
-// Manifests for i2eBordStr:
-//-------------------------------------------
-
-typedef void (*delayFunc_t)(unsigned int);
-
-// i2eValid
-//
-#define I2E_MAGIC       0x4251   // Structure is valid.
-#define I2E_INCOMPLETE  0x1122   // Structure failed during init.
-
-
-// i2eError
-//
-#define I2EE_GOOD       0      // Operation successful
-#define I2EE_BADADDR    1      // Address out of range
-#define I2EE_BADSTATE   2      // Attempt to perform a function when the board
-                                                       // structure was in the incorrect state
-#define I2EE_BADMAGIC   3      // Bad magic number from Power On test (i2ePomSize
-                                                       // reflects what was read
-#define I2EE_PORM_SHORT 4      // Power On message too short
-#define I2EE_PORM_LONG  5      // Power On message too long
-#define I2EE_BAD_FAMILY 6      // Un-supported board family type
-#define I2EE_INCONSIST  7      // Firmware reports something impossible,
-                                                       // e.g. unexpected number of ports... Almost no
-                                                       // excuse other than bad FIFO...
-#define I2EE_POSTERR    8      // Power-On self test reported a bad error
-#define I2EE_BADBUS     9      // Unknown Bus type declared in message
-#define I2EE_TXE_TIME   10     // Timed out waiting for TX Fifo to empty
-#define I2EE_INVALID    11     // i2eValid field does not indicate a valid and
-                                                       // complete board structure (for functions which
-                                                       // require this be so.)
-#define I2EE_BAD_PORT   12     // Discrepancy between channels actually found and
-                                                       // what the product is supposed to have. Check
-                                                       // i2eGoodMap vs i2eChannelMap for details.
-#define I2EE_BAD_IRQ    13     // Someone specified an unsupported IRQ
-#define I2EE_NOCHANNELS 14     // No channel structures have been defined (for
-                                                       // functions requiring this).
-
-// i2eFifoStyle
-//
-#define FIFO_II   0  /* IntelliPort-II style: see also i2hw.h */
-#define FIFO_IIEX 1  /* IntelliPort-IIEX style */
-
-// i2eGetMail
-//
-#define NO_MAIL_HERE    0x1111 // Since mail is unsigned char, cannot possibly
-                                                               // promote to 0x1111.
-// i2eState
-//
-#define II_STATE_COLD      0  // Addresses have been defined, but board not even
-                                                         // reset yet.
-#define II_STATE_RESET     1  // Board,if it exists, has just been reset
-#define II_STATE_READY     2  // Board ready for its first block
-#define II_STATE_LOADING   3  // Board continuing load
-#define II_STATE_LOADED    4  // Board has finished load: status ok
-#define II_STATE_BADLOAD   5  // Board has finished load: failed!
-#define II_STATE_STDLOADED 6  // Board has finished load: standard firmware
-
-// i2eUsingIrq
-//
-#define I2_IRQ_UNDEFINED       0x1352  /* No valid irq (or polling = 0) can
-                                        * ever promote to this! */
-//------------------------------------------
-// Handy Macros for i2ellis.c and others
-// Note these are common to -II and -IIEX
-//------------------------------------------
-
-// Given a pointer to the board structure, does the input FIFO have any data or
-// not?
-//
-#define I2_HAS_INPUT(pB)       !(inb(pB->i2eStatus) & ST_IN_EMPTY)
-
-// Given a pointer to the board structure, is there anything in the incoming
-// mailbox?
-//
-#define I2_HAS_MAIL(pB)                (inb(pB->i2eStatus) & ST_IN_MAIL)
-
-#define I2_UPDATE_FIFO_ROOM(pB)        ((pB)->i2eFifoRemains = (pB)->i2eFifoSize)
-
-//------------------------------------------
-// Function Declarations for i2ellis.c
-//------------------------------------------
-//
-// Functions called directly
-//
-// Initialization of a board & structure is in four (five!) parts:
-//
-// 1) iiSetAddress() - Define the board address & delay function for a board.
-// 2) iiReset()      - Reset the board   (provided it exists)
-//       -- Note you may do this to several boards --
-// 3) iiResetDelay() - Delay for 2 seconds (once for all boards)
-// 4) iiInitialize() - Attempt to read Power-up message; further initialize
-//                     accelerators
-//
-// Then you may use iiDownloadAll() or iiDownloadFile() (in i2file.c) to write
-// loadware.  To change loadware, you must begin again with step 2, resetting
-// the board again (step 1 not needed).
-
-static int iiSetAddress(i2eBordStrPtr, int, delayFunc_t );
-static int iiReset(i2eBordStrPtr);
-static int iiResetDelay(i2eBordStrPtr);
-static int iiInitialize(i2eBordStrPtr);
-
-// Routine to validate that all channels expected are there.
-//
-extern int iiValidateChannels(i2eBordStrPtr);
-
-// Routine used to download a block of loadware.
-//
-static int iiDownloadBlock(i2eBordStrPtr, loadHdrStrPtr, int);
-
-// Return values given by iiDownloadBlock, iiDownloadAll, iiDownloadFile:
-//
-#define II_DOWN_BADVALID   0   // board structure is invalid
-#define II_DOWN_CONTINUING 1   // So far, so good, firmware expects more
-#define II_DOWN_GOOD       2   // Download complete, CRC good
-#define II_DOWN_BAD        3   // Download complete, but CRC bad
-#define II_DOWN_BADFILE    4   // Bad magic number in loadware file
-#define II_DOWN_BADSTATE   5   // Board is in an inappropriate state for
-                                                               // downloading loadware. (see i2eState)
-#define II_DOWN_TIMEOUT    6   // Timeout waiting for firmware
-#define II_DOWN_OVER       7   // Too much data
-#define II_DOWN_UNDER      8   // Not enough data
-#define II_DOWN_NOFILE     9   // Loadware file not found
-
-// Routine to download an entire loadware module: Return values are a subset of
-// iiDownloadBlock's, excluding, of course, II_DOWN_CONTINUING
-//
-static int iiDownloadAll(i2eBordStrPtr, loadHdrStrPtr, int, int);
-
-// Many functions defined here return True if good, False otherwise, with an
-// error code in i2eError field. Here is a handy macro for setting the error
-// code and returning.
-//
-#define I2_COMPLETE(pB,code) do { \
-                pB->i2eError = code; \
-                return (code == I2EE_GOOD);\
-       } while (0)
-
-#endif   // I2ELLIS_H
diff --git a/drivers/char/ip2/i2hw.h b/drivers/char/ip2/i2hw.h
deleted file mode 100644 (file)
index c0ba6c0..0000000
+++ /dev/null
@@ -1,652 +0,0 @@
-/*******************************************************************************
-*
-*   (c) 1999 by Computone Corporation
-*
-********************************************************************************
-*
-*
-*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
-*                serial I/O controllers.
-*
-*   DESCRIPTION: Definitions limited to properties of the hardware or the
-*                bootstrap firmware. As such, they are applicable regardless of
-*                operating system or loadware (standard or diagnostic).
-*
-*******************************************************************************/
-#ifndef I2HW_H
-#define I2HW_H 1
-//------------------------------------------------------------------------------
-// Revision History:
-//
-// 23 September 1991 MAG   First Draft Started...through...
-// 11 October 1991   ...   Continuing development...
-//  6 August 1993          Added support for ISA-4 (asic) which is architected
-//                         as an ISA-CEX with a single 4-port box.
-//
-// 20 December 1996  AKM   Version for Linux
-//
-//------------------------------------------------------------------------------
-/*------------------------------------------------------------------------------
-
-HARDWARE DESCRIPTION:
-
-Introduction:
-
-The IntelliPort-II and IntelliPort-IIEX products occupy a block of eight (8)
-addresses in the host's I/O space.
-
-Some addresses are used to transfer data to/from the board, some to transfer
-so-called "mailbox" messages, and some to read bit-mapped status information.
-While all the products in the line are functionally similar, some use a 16-bit
-data path to transfer data while others use an 8-bit path. Also, the use of
-command /status/mailbox registers differs slightly between the II and IIEX
-branches of the family.
-
-The host determines what type of board it is dealing with by reading a string of
-sixteen characters from the board. These characters are always placed in the
-fifo by the board's local processor whenever the board is reset (either from
-power-on or under software control) and are known as the "Power-on Reset
-Message." In order that this message can be read from either type of board, the
-hardware registers used in reading this message are the same. Once this message
-has been read by the host, then it has the information required to operate.
-
-General Differences between boards:
-
-The greatest structural difference is between the -II and -IIEX families of
-product. The -II boards use the Am4701 dual 512x8 bidirectional fifo to support
-the data path, mailbox registers, and status registers. This chip contains some
-features which are not used in the IntelliPort-II products; a description of
-these is omitted here. Because of these many features, it contains many
-registers, too many to access directly within a small address space. They are
-accessed by first writing a value to a "pointer" register. This value selects
-the register to be accessed.  The next read or write to that address accesses
-the selected register rather than the pointer register.
-
-The -IIEX boards use a proprietary design similar to the Am4701 in function. But
-because of a simpler, more streamlined design it doesn't require so many
-registers. This means they can be accessed directly in single operations rather
-than through a pointer register.
-
-Besides these differences, there are differences in whether 8-bit or 16-bit
-transfers are used to move data to the board.
-
-The -II boards are capable only of 8-bit data transfers, while the -IIEX boards
-may be configured for either 8-bit or 16-bit data transfers. If the on-board DIP
-switch #8 is ON, and the card has been installed in a 16-bit slot, 16-bit
-transfers are supported (and will be expected by the standard loadware). The
-on-board firmware can determine the position of the switch, and whether the
-board is installed in a 16-bit slot; it supplies this information to the host as
-part of the power-up reset message.
-
-The configuration switch (#8) and slot selection do not directly configure the
-hardware. It is up to the on-board loadware and host-based drivers to act
-according to the selected options. That is, loadware and drivers could be
-written to perform 8-bit transfers regardless of the state of the DIP switch or
-slot (and in a diagnostic environment might well do so). Likewise, 16-bit
-transfers could be performed as long as the card is in a 16-bit slot.
-
-Note the slot selection and DIP switch selection are provided separately: a
-board running in 8-bit mode in a 16-bit slot has a greater range of possible
-interrupts to choose from; information of potential use to the host.
-
-All 8-bit data transfers are done in the same way, regardless of whether on a
--II board or a -IIEX board.
-
-The host must consider two things then: 1) whether a -II or -IIEX product is
-being used, and 2) whether an 8-bit or 16-bit data path is used.
-
-A further difference is that -II boards always have a 512-byte fifo operating in
-each direction. -IIEX boards may use fifos of varying size; this size is
-reported as part of the power-up message.
-
-I/O Map Of IntelliPort-II and IntelliPort-IIEX boards:
-(Relative to the chosen base address)
-
-Addr  R/W      IntelliPort-II    IntelliPort-IIEX
-----  ---      --------------    ----------------
-0     R/W      Data Port (byte)  Data Port (byte or word)
-1     R/W      (Not used)        (MSB of word-wide data written to Data Port)
-2     R        Status Register   Status Register
-2     W        Pointer Register  Interrupt Mask Register
-3     R/W      (Not used)        Mailbox Registers (6 bits: 11111100)
-4,5   --       Reserved for future products
-6     --       Reserved for future products
-7     R        Guaranteed to have no effect
-7     W        Hardware reset of board.
-
-
-Rules:
-All data transfers are performed using the even i/o address. If byte-wide data
-transfers are being used, do INB/OUTB operations on the data port. If word-wide
-transfers are used, do INW/OUTW operations. In some circumstances (such as
-reading the power-up message) you will do INB from the data port, but in this
-case the MSB of each word read is lost. When accessing all other unreserved
-registers, use byte operations only.
-------------------------------------------------------------------------------*/
-
-//------------------------------------------------
-// Mandatory Includes:
-//------------------------------------------------
-//
-#include "ip2types.h"
-
-//-------------------------------------------------------------------------
-// Manifests for the I/O map:
-//-------------------------------------------------------------------------
-// R/W: Data port (byte) for IntelliPort-II,
-// R/W: Data port (byte or word) for IntelliPort-IIEX
-// Incoming or outgoing data passes through a FIFO, the status of which is
-// available in some of the bits in FIFO_STATUS. This (bidirectional) FIFO is
-// the primary means of transferring data, commands, flow-control, and status
-// information between the host and board.
-//
-#define FIFO_DATA 0
-
-// Another way of passing information between the board and the host is
-// through "mailboxes". Unlike a FIFO, a mailbox holds only a single byte of
-// data.  Writing data to the mailbox causes a status bit to be set, and
-// potentially interrupting the intended receiver. The sender has some way to
-// determine whether the data has been read yet; as soon as it has, it may send
-// more. The mailboxes are handled differently on -II and -IIEX products, as
-// suggested below.
-//------------------------------------------------------------------------------
-// Read: Status Register for IntelliPort-II or -IIEX
-// The presence of any bit set here will cause an interrupt to the host,
-// provided the corresponding bit has been unmasked in the interrupt mask
-// register. Furthermore, interrupts to the host are disabled globally until the
-// loadware selects the irq line to use. With the exception of STN_MR, the bits
-// remain set so long as the associated condition is true.
-//
-#define FIFO_STATUS 2
-
-// Bit map of status bits which are identical for -II and -IIEX
-//
-#define ST_OUT_FULL  0x40  // Outbound FIFO full
-#define ST_IN_EMPTY  0x20  // Inbound FIFO empty
-#define ST_IN_MAIL   0x04  // Inbound Mailbox full
-
-// The following exists only on the Intelliport-IIEX, and indicates that the
-// board has not read the last outgoing mailbox data yet. In the IntelliPort-II,
-// the outgoing mailbox may be read back: a zero indicates the board has read
-// the data.
-//
-#define STE_OUT_MAIL 0x80  // Outbound mailbox full (!)
-
-// The following bits are defined differently for -II and -IIEX boards. Code
-// which relies on these bits will need to be functionally different for the two
-// types of boards and should be generally avoided because of the additional
-// complexity this creates:
-
-// Bit map of status bits only on -II
-
-// Fifo has been RESET (cleared when the status register is read). Note that
-// this condition cannot be masked and would always interrupt the host, except
-// that the hardware reset also disables interrupts globally from the board
-// until re-enabled by loadware. This could also arise from the
-// Am4701-supported command to reset the chip, but this command is generally not
-// used here.
-//
-#define STN_MR       0x80
-
-// See the AMD Am4701 data sheet for details on the following four bits. They
-// are not presently used by Computone drivers.
-//
-#define STN_OUT_AF  0x10  // Outbound FIFO almost full (programmable)
-#define STN_IN_AE   0x08  // Inbound FIFO almost empty (programmable)
-#define STN_BD      0x02  // Inbound byte detected
-#define STN_PE      0x01  // Parity/Framing condition detected
-
-// Bit-map of status bits only on -IIEX
-//
-#define STE_OUT_HF  0x10  // Outbound FIFO half full
-#define STE_IN_HF   0x08  // Inbound FIFO half full
-#define STE_IN_FULL 0x02  // Inbound FIFO full
-#define STE_OUT_MT  0x01  // Outbound FIFO empty
-
-//------------------------------------------------------------------------------
-
-// Intelliport-II -- Write Only: the pointer register.
-// Values are written to this register to select the Am4701 internal register to
-// be accessed on the next operation.
-//
-#define FIFO_PTR    0x02
-
-// Values for the pointer register
-//
-#define SEL_COMMAND 0x1    // Selects the Am4701 command register
-
-// Some possible commands:
-//
-#define SEL_CMD_MR  0x80       // Am4701 command to reset the chip
-#define SEL_CMD_SH  0x40       // Am4701 command to map the "other" port into the
-                                                       // status register.
-#define SEL_CMD_UNSH   0       // Am4701 command to "unshift": port maps into its
-                                                       // own status register.
-#define SEL_MASK     0x2       // Selects the Am4701 interrupt mask register. The
-                                                       // interrupt mask register is bit-mapped to match 
-                                                       // the status register (FIFO_STATUS) except for
-                                                       // STN_MR. (See above.)
-#define SEL_BYTE_DET 0x3       // Selects the Am4701 byte-detect register. (Not
-                                                       // normally used except in diagnostics.)
-#define SEL_OUTMAIL  0x4       // Selects the outbound mailbox (R/W). Reading back
-                                                       // a value of zero indicates that the mailbox has
-                                                       // been read by the board and is available for more
-                                                       // data./ Writing to the mailbox optionally
-                                                       // interrupts the board, depending on the loadware's
-                                                       // setting of its interrupt mask register.
-#define SEL_AEAF     0x5       // Selects AE/AF threshold register.
-#define SEL_INMAIL   0x6       // Selects the inbound mailbox (Read)
-
-//------------------------------------------------------------------------------
-// IntelliPort-IIEX --  Write Only: interrupt mask (and misc flags) register:
-// Unlike IntelliPort-II, bit assignments do NOT match those of the status
-// register.
-//
-#define FIFO_MASK    0x2
-
-// Mailbox readback select:
-// If set, reads to FIFO_MAIL will read the OUTBOUND mailbox (host to board). If
-// clear (default on reset) reads to FIFO_MAIL will read the INBOUND mailbox.
-// This is the normal situation. The clearing of a mailbox is determined on
-// -IIEX boards by waiting for the STE_OUT_MAIL bit to clear. Readback
-// capability is provided for diagnostic purposes only.
-//
-#define  MX_OUTMAIL_RSEL   0x80
-
-#define  MX_IN_MAIL  0x40      // Enables interrupts when incoming mailbox goes
-                                                       // full (ST_IN_MAIL set).
-#define  MX_IN_FULL  0x20      // Enables interrupts when incoming FIFO goes full
-                                                       // (STE_IN_FULL).
-#define  MX_IN_MT    0x08      // Enables interrupts when incoming FIFO goes empty
-                                                       // (ST_IN_MT).
-#define  MX_OUT_FULL 0x04      // Enables interrupts when outgoing FIFO goes full
-                                                       // (ST_OUT_FULL).
-#define  MX_OUT_MT   0x01      // Enables interrupts when outgoing FIFO goes empty
-                                                       // (STE_OUT_MT).
-
-// Any remaining bits are reserved, and should be written to ZERO for
-// compatibility with future Computone products.
-
-//------------------------------------------------------------------------------
-// IntelliPort-IIEX: -- These are only 6-bit mailboxes !!! -- 11111100 (low two
-// bits always read back 0).
-// Read:  One of the mailboxes, usually Inbound.
-//        Inbound Mailbox (MX_OUTMAIL_RSEL = 0)
-//        Outbound Mailbox (MX_OUTMAIL_RSEL = 1)
-// Write: Outbound Mailbox
-// For the IntelliPort-II boards, the outbound mailbox is read back to determine
-// whether the board has read the data (0 --> data has been read). For the
-// IntelliPort-IIEX, this is done by reading a status register. To determine
-// whether mailbox is available for more outbound data, use the STE_OUT_MAIL bit
-// in FIFO_STATUS. Moreover, although the Outbound Mailbox can be read back by
-// setting MX_OUTMAIL_RSEL, it is NOT cleared when the board reads it, as is the
-// case with the -II boards. For this reason, FIFO_MAIL is normally used to read
-// the inbound FIFO, and MX_OUTMAIL_RSEL kept clear. (See above for
-// MX_OUTMAIL_RSEL description.)
-//
-#define  FIFO_MAIL   0x3
-
-//------------------------------------------------------------------------------
-// WRITE ONLY:  Resets the board. (Data doesn't matter).
-//
-#define  FIFO_RESET  0x7
-
-//------------------------------------------------------------------------------
-// READ ONLY:  Will have no effect. (Data is undefined.)
-// Actually, there will be an effect, in that the operation is sure to generate
-// a bus cycle: viz., an I/O byte Read. This fact can be used to enforce short
-// delays when no comparable time constant is available.
-//
-#define  FIFO_NOP    0x7
-
-//------------------------------------------------------------------------------
-// RESET & POWER-ON RESET MESSAGE
-/*------------------------------------------------------------------------------
-RESET:
-
-The IntelliPort-II and -IIEX boards are reset in three ways: Power-up, channel
-reset, and via a write to the reset register described above. For products using
-the ISA bus, these three sources of reset are equvalent. For MCA and EISA buses,
-the Power-up and channel reset sources cause additional hardware initialization
-which should only occur at system startup time.
-
-The third type of reset, called a "command reset", is done by writing any data
-to the FIFO_RESET address described above. This resets the on-board processor,
-FIFO, UARTS, and associated hardware.
-
-This passes control of the board to the bootstrap firmware, which performs a
-Power-On Self Test and which detects its current configuration. For example,
--IIEX products determine the size of FIFO which has been installed, and the
-number and type of expansion boxes attached.
-
-This and other information is then written to the FIFO in a 16-byte data block
-to be read by the host. This block is guaranteed to be present within two (2)
-seconds of having received the command reset. The firmware is now ready to
-receive loadware from the host.
-
-It is good practice to perform a command reset to the board explicitly as part
-of your software initialization.  This allows your code to properly restart from
-a soft boot. (Many systems do not issue channel reset on soft boot).
-
-Because of a hardware reset problem on some of the Cirrus Logic 1400's which are
-used on the product, it is recommended that you reset the board twice, separated
-by an approximately 50 milliseconds delay. (VERY approximately: probably ok to
-be off by a factor of five. The important point is that the first command reset
-in fact generates a reset pulse on the board. This pulse is guaranteed to last
-less than 10 milliseconds. The additional delay ensures the 1400 has had the
-chance to respond sufficiently to the first reset. Why not a longer delay? Much
-more than 50 milliseconds gets to be noticable, but the board would still work.
-
-Once all 16 bytes of the Power-on Reset Message have been read, the bootstrap
-firmware is ready to receive loadware.
-
-Note on Power-on Reset Message format:
-The various fields have been designed with future expansion in view.
-Combinations of bitfields and values have been defined which define products
-which may not currently exist. This has been done to allow drivers to anticipate
-the possible introduction of products in a systematic fashion. This is not
-intended to suggest that each potential product is actually under consideration.
-------------------------------------------------------------------------------*/
-
-//----------------------------------------
-// Format of Power-on Reset Message
-//----------------------------------------
-
-typedef union _porStr          // "por" stands for Power On Reset
-{
-       unsigned char  c[16];   // array used when considering the message as a
-                                                       // string of undifferentiated characters
-
-       struct                                  // Elements used when considering values
-       {
-               // The first two bytes out of the FIFO are two magic numbers. These are
-               // intended to establish that there is indeed a member of the
-               // IntelliPort-II(EX) family present. The remaining bytes may be 
-               // expected // to be valid. When reading the Power-on Reset message, 
-               // if the magic numbers do not match it is probably best to stop
-               // reading immediately. You are certainly not reading our board (unless
-               // hardware is faulty), and may in fact be reading some other piece of
-               // hardware.
-
-               unsigned char porMagic1;   // magic number: first byte == POR_MAGIC_1 
-               unsigned char porMagic2;   // magic number: second byte == POR_MAGIC_2 
-
-               // The Version, Revision, and Subrevision are stored as absolute numbers
-               // and would normally be displayed in the format V.R.S (e.g. 1.0.2)
-
-               unsigned char porVersion;  // Bootstrap firmware version number
-               unsigned char porRevision; // Bootstrap firmware revision number
-               unsigned char porSubRev;   // Bootstrap firmware sub-revision number
-
-               unsigned char porID;    // Product ID:  Bit-mapped according to
-                                                               // conventions described below. Among other
-                                                               // things, this allows us to distinguish
-                                                               // IntelliPort-II boards from IntelliPort-IIEX
-                                                               // boards.
-
-               unsigned char porBus;   // IntelliPort-II: Unused
-                                                               // IntelliPort-IIEX: Bus Information:
-                                                               // Bit-mapped below
-
-               unsigned char porMemory;        // On-board DRAM size: in 32k blocks
-
-               // porPorts1 (and porPorts2) are used to determine the ports which are
-               // available to the board. For non-expandable product, a single number 
-               // is sufficient. For expandable product, the board may be connected
-               // to as many as four boxes. Each box may be (so far) either a 16-port
-               // or an 8-port size. Whenever an 8-port box is used, the remaining 8
-               // ports leave gaps between existing channels. For that reason,
-               // expandable products must report a MAP of available channels. Since 
-               // each UART supports four ports, we represent each UART found by a
-               // single bit. Using two bytes to supply the mapping information we
-               // report the presense or absense of up to 16 UARTS, or 64 ports in
-               // steps of 4 ports. For -IIEX products, the ports are numbered
-               // starting at the box closest to the controller in the "chain".
-
-               // Interpreted Differently for IntelliPort-II and -IIEX.
-               // -II:   Number of ports (Derived actually from product ID). See
-               // Diag1&2 to indicate if uart was actually detected.
-               // -IIEX: Bit-map of UARTS found, LSB (see below for MSB of this). This
-               //        bitmap is based on detecting the uarts themselves; 
-               //        see porFlags for information from the box i.d's.
-               unsigned char  porPorts1;
-
-               unsigned char  porDiag1;        // Results of on-board P.O.S.T, 1st byte
-               unsigned char  porDiag2;        // Results of on-board P.O.S.T, 2nd byte
-               unsigned char  porSpeed;        // Speed of local CPU: given as MHz x10
-                                                                       // e.g., 16.0 MHz CPU is reported as 160
-               unsigned char  porFlags;        // Misc information (see manifests below)
-                                                                       // Bit-mapped: CPU type, UART's present
-
-               unsigned char  porPorts2;       // -II:  Undefined
-                                                                       // -IIEX: Bit-map of UARTS found, MSB (see
-                                                                       //        above for LSB)
-
-               // IntelliPort-II: undefined
-               // IntelliPort-IIEX: 1 << porFifoSize gives the size, in bytes, of the
-               // host interface FIFO, in each direction. When running the -IIEX in
-               // 8-bit mode, fifo capacity is halved. The bootstrap firmware will
-               // have already accounted for this fact in generating this number.
-               unsigned char  porFifoSize;
-
-               // IntelliPort-II: undefined
-               // IntelliPort-IIEX: The number of boxes connected. (Presently 1-4)
-               unsigned char  porNumBoxes;
-       } e;
-} porStr, *porStrPtr;
-
-//--------------------------
-// Values for porStr fields
-//--------------------------
-
-//---------------------
-// porMagic1, porMagic2
-//----------------------
-//
-#define  POR_MAGIC_1    0x96  // The only valid value for porMagic1
-#define  POR_MAGIC_2    0x35  // The only valid value for porMagic2
-#define  POR_1_INDEX    0     // Byte position of POR_MAGIC_1
-#define  POR_2_INDEX    1     // Ditto for POR_MAGIC_2
-
-//----------------------
-// porID
-//----------------------
-//
-#define  POR_ID_FAMILY  0xc0   // These bits indicate the general family of
-                                                               // product.
-#define  POR_ID_FII     0x00   // Family is "IntelliPort-II"
-#define  POR_ID_FIIEX   0x40   // Family is "IntelliPort-IIEX"
-
-// These bits are reserved, presently zero. May be used at a later date to
-// convey other product information.
-//
-#define POR_ID_RESERVED 0x3c
-
-#define POR_ID_SIZE     0x03   // Remaining bits indicate number of ports &
-                                                               // Connector information.
-#define POR_ID_II_8     0x00   // For IntelliPort-II, indicates 8-port using
-                                                               // standard brick.
-#define POR_ID_II_8R    0x01   // For IntelliPort-II, indicates 8-port using
-                                                               // RJ11's (no CTS)
-#define POR_ID_II_6     0x02   // For IntelliPort-II, indicates 6-port using
-                                                               // RJ45's
-#define POR_ID_II_4     0x03   // For IntelliPort-II, indicates 4-port using
-                                                               // 4xRJ45 connectors
-#define POR_ID_EX       0x00   // For IntelliPort-IIEX, indicates standard
-                                                               // expandable controller (other values reserved)
-
-//----------------------
-// porBus
-//----------------------
-
-// IntelliPort-IIEX only: Board is installed in a 16-bit slot
-//
-#define POR_BUS_SLOT16  0x20
-
-// IntelliPort-IIEX only: DIP switch #8 is on, selecting 16-bit host interface
-// operation.
-// 
-#define POR_BUS_DIP16   0x10
-
-// Bits 0-2 indicate type of bus: This information is stored in the bootstrap
-// loadware, different loadware being used on different products for different
-// buses. For most situations, the drivers do not need this information; but it
-// is handy in a diagnostic environment. For example, on microchannel boards,
-// you would not want to try to test several interrupts, only the one for which
-// you were configured.
-//
-#define  POR_BUS_TYPE   0x07
-
-// Unknown:  this product doesn't know what bus it is running in. (e.g. if same
-// bootstrap firmware were wanted for two different buses.)
-//
-#define  POR_BUS_T_UNK  0
-
-// Note: existing firmware for ISA-8 and MC-8 currently report the POR_BUS_T_UNK
-// state, since the same bootstrap firmware is used for each.
-
-#define  POR_BUS_T_MCA  1  // MCA BUS */
-#define  POR_BUS_T_EISA 2  // EISA BUS */
-#define  POR_BUS_T_ISA  3  // ISA BUS */
-
-// Values 4-7 Reserved
-
-// Remaining bits are reserved
-
-//----------------------
-// porDiag1
-//----------------------
-
-#define  POR_BAD_MAPPER 0x80   // HW failure on P.O.S.T: Chip mapper failed
-
-// These two bits valid only for the IntelliPort-II
-//
-#define  POR_BAD_UART1  0x01   // First  1400 bad
-#define  POR_BAD_UART2  0x02   // Second 1400 bad
-
-//----------------------
-// porDiag2
-//----------------------
-
-#define  POR_DEBUG_PORT 0x80   // debug port was detected by the P.O.S.T
-#define  POR_DIAG_OK    0x00   // Indicates passage: Failure codes not yet
-                                                               // available.
-                                                               // Other bits undefined.
-//----------------------
-// porFlags
-//----------------------
-
-#define  POR_CPU     0x03      // These bits indicate supposed CPU type
-#define  POR_CPU_8   0x01      // Board uses an 80188 (no such thing yet)
-#define  POR_CPU_6   0x02      // Board uses an 80186 (all existing products)
-#define  POR_CEX4    0x04      // If set, this is an ISA-CEX/4: An ISA-4 (asic)
-                                                       // which is architected like an ISA-CEX connected
-                                                       // to a (hitherto impossible) 4-port box.
-#define POR_BOXES    0xf0      // Valid for IntelliPort-IIEX only: Map of Box
-                                                       // sizes based on box I.D.
-#define POR_BOX_16   0x10      // Set indicates 16-port, clear 8-port
-
-//-------------------------------------
-// LOADWARE and DOWNLOADING CODE
-//-------------------------------------
-
-/*
-Loadware may be sent to the board in two ways:
-1) It may be read from a (binary image) data file block by block as each block
-       is sent to the board. This is only possible when the initialization is
-       performed by code which can access your file system. This is most suitable
-       for diagnostics and appications which use the interface library directly.
-
-2) It may be hard-coded into your source by including a .h file (typically
-       supplied by Computone), which declares a data array and initializes every
-       element. This achieves the same result as if an entire loadware file had 
-       been read into the array.
-
-       This requires more data space in your program, but access to the file system
-       is not required. This method is more suited to driver code, which typically
-       is running at a level too low to access the file system directly.
-
-At present, loadware can only be generated at Computone.
-
-All Loadware begins with a header area which has a particular format. This
-includes a magic number which identifies the file as being (purportedly)
-loadware, CRC (for the loader), and version information.
-*/
-
-
-//-----------------------------------------------------------------------------
-// Format of loadware block
-//
-// This is defined as a union so we can pass a pointer to one of these items
-// and (if it is the first block) pick out the version information, etc.
-//
-// Otherwise, to deal with this as a simple character array
-//------------------------------------------------------------------------------
-
-#define LOADWARE_BLOCK_SIZE   512   // Number of bytes in each block of loadware
-
-typedef union _loadHdrStr
-{
-       unsigned char c[LOADWARE_BLOCK_SIZE];  // Valid for every block
-
-       struct  // These fields are valid for only the first block of loadware.
-       {
-               unsigned char loadMagic;                // Magic number: see below
-               unsigned char loadBlocksMore;   // How many more blocks?
-               unsigned char loadCRC[2];               // Two CRC bytes: used by loader
-               unsigned char loadVersion;              // Version number
-               unsigned char loadRevision;             // Revision number
-               unsigned char loadSubRevision;  // Sub-revision number
-               unsigned char loadSpares[9];    // Presently unused
-               unsigned char loadDates[32];    // Null-terminated string which can give
-                                                                               // date and time of compilation
-       } e;
-} loadHdrStr, *loadHdrStrPtr;
-
-//------------------------------------
-// Defines for downloading code:
-//------------------------------------
-
-// The loadMagic field in the first block of the loadfile must be this, else the
-// file is not valid.
-//
-#define  MAGIC_LOADFILE 0x3c
-
-// How do we know the load was successful? On completion of the load, the
-// bootstrap firmware returns a code to indicate whether it thought the download
-// was valid and intends to execute it. These are the only possible valid codes:
-//
-#define  LOADWARE_OK    0xc3        // Download was ok
-#define  LOADWARE_BAD   0x5a        // Download was bad (CRC error)
-
-// Constants applicable to writing blocks of loadware:
-// The first block of loadware might take 600 mS to load, in extreme cases.
-// (Expandable board: worst case for sending startup messages to the LCD's).
-// The 600mS figure is not really a calculation, but a conservative
-// guess/guarantee. Usually this will be within 100 mS, like subsequent blocks.
-//
-#define  MAX_DLOAD_START_TIME 1000  // 1000 mS
-#define  MAX_DLOAD_READ_TIME  100   // 100 mS
-
-// Firmware should respond with status (see above) within this long of host
-// having sent the final block.
-//
-#define  MAX_DLOAD_ACK_TIME   100   // 100 mS, again!
-
-//------------------------------------------------------
-// MAXIMUM NUMBER OF PORTS PER BOARD:
-// This is fixed for now (with the expandable), but may
-// be expanding according to even newer products.
-//------------------------------------------------------
-//
-#define ABS_MAX_BOXES   4     // Absolute most boxes per board
-#define ABS_BIGGEST_BOX 16    // Absolute the most ports per box
-#define ABS_MOST_PORTS  (ABS_MAX_BOXES * ABS_BIGGEST_BOX)
-
-#define I2_OUTSW(port, addr, count)    outsw((port), (addr), (((count)+1)/2))
-#define I2_OUTSB(port, addr, count)    outsb((port), (addr), (((count)+1))&-2)
-#define I2_INSW(port, addr, count)     insw((port), (addr), (((count)+1)/2))
-#define I2_INSB(port, addr, count)     insb((port), (addr), (((count)+1))&-2)
-
-#endif   // I2HW_H
-
diff --git a/drivers/char/ip2/i2lib.c b/drivers/char/ip2/i2lib.c
deleted file mode 100644 (file)
index 0d10b89..0000000
+++ /dev/null
@@ -1,2214 +0,0 @@
-/*******************************************************************************
-*
-*   (c) 1999 by Computone Corporation
-*
-********************************************************************************
-*
-*
-*   PACKAGE:     Linux tty Device Driver for IntelliPort family of multiport
-*                serial I/O controllers.
-*
-*   DESCRIPTION: High-level interface code for the device driver. Uses the
-*                Extremely Low Level Interface Support (i2ellis.c). Provides an
-*                interface to the standard loadware, to support drivers or
-*                application code. (This is included source code, not a separate
-*                compilation module.)
-*
-*******************************************************************************/
-//------------------------------------------------------------------------------
-// Note on Strategy:
-// Once the board has been initialized, it will interrupt us when:
-// 1) It has something in the fifo for us to read (incoming data, flow control
-// packets, or whatever).
-// 2) It has stripped whatever we have sent last time in the FIFO (and
-// consequently is ready for more).
-//
-// Note also that the buffer sizes declared in i2lib.h are VERY SMALL. This
-// worsens performance considerably, but is done so that a great many channels
-// might use only a little memory.
-//------------------------------------------------------------------------------
-
-//------------------------------------------------------------------------------
-// Revision History:
-//
-// 0.00 -  4/16/91 --- First Draft
-// 0.01 -  4/29/91 --- 1st beta release
-// 0.02 -  6/14/91 --- Changes to allow small model compilation
-// 0.03 -  6/17/91 MAG Break reporting protected from interrupts routines with
-//                     in-line asm added for moving data to/from ring buffers,
-//                     replacing a variety of methods used previously.
-// 0.04 -  6/21/91 MAG Initial flow-control packets not queued until
-//                     i2_enable_interrupts time. Former versions would enqueue
-//                     them at i2_init_channel time, before we knew how many
-//                     channels were supposed to exist!
-// 0.05 - 10/12/91 MAG Major changes: works through the ellis.c routines now;
-//                     supports new 16-bit protocol and expandable boards.
-//      - 10/24/91 MAG Most changes in place and stable.
-// 0.06 -  2/20/92 MAG Format of CMD_HOTACK corrected: the command takes no
-//                     argument.
-// 0.07 -- 3/11/92 MAG Support added to store special packet types at interrupt
-//                     level (mostly responses to specific commands.)
-// 0.08 -- 3/30/92 MAG Support added for STAT_MODEM packet
-// 0.09 -- 6/24/93 MAG i2Link... needed to update number of boards BEFORE
-//                     turning on the interrupt.
-// 0.10 -- 6/25/93 MAG To avoid gruesome death from a bad board, we sanity check
-//                     some incoming.
-//
-// 1.1  - 12/25/96 AKM Linux version.
-//      - 10/09/98 DMC Revised Linux version.
-//------------------------------------------------------------------------------
-
-//************
-//* Includes *
-//************
-
-#include <linux/sched.h>
-#include "i2lib.h"
-
-
-//***********************
-//* Function Prototypes *
-//***********************
-static void i2QueueNeeds(i2eBordStrPtr, i2ChanStrPtr, int);
-static i2ChanStrPtr i2DeQueueNeeds(i2eBordStrPtr, int );
-static void i2StripFifo(i2eBordStrPtr);
-static void i2StuffFifoBypass(i2eBordStrPtr);
-static void i2StuffFifoFlow(i2eBordStrPtr);
-static void i2StuffFifoInline(i2eBordStrPtr);
-static int i2RetryFlushOutput(i2ChanStrPtr);
-
-// Not a documented part of the library routines (careful...) but the Diagnostic
-// i2diag.c finds them useful to help the throughput in certain limited
-// single-threaded operations.
-static void iiSendPendingMail(i2eBordStrPtr);
-static void serviceOutgoingFifo(i2eBordStrPtr);
-
-// Functions defined in ip2.c as part of interrupt handling
-static void do_input(struct work_struct *);
-static void do_status(struct work_struct *);
-
-//***************
-//* Debug  Data *
-//***************
-#ifdef DEBUG_FIFO
-
-unsigned char DBGBuf[0x4000];
-unsigned short I = 0;
-
-static void
-WriteDBGBuf(char *s, unsigned char *src, unsigned short n ) 
-{
-       char *p = src;
-
-       // XXX: We need a spin lock here if we ever use this again
-
-       while (*s) {    // copy label
-               DBGBuf[I] = *s++;
-               I = I++ & 0x3fff;
-       }
-       while (n--) {   // copy data
-               DBGBuf[I] = *p++;
-               I = I++ & 0x3fff;
-       }
-}
-
-static void
-fatality(i2eBordStrPtr pB )
-{
-       int i;
-
-       for (i=0;i<sizeof(DBGBuf);i++) {
-               if ((i%16) == 0)
-                       printk("\n%4x:",i);
-               printk("%02x ",DBGBuf[i]);
-       }
-       printk("\n");
-       for (i=0;i<sizeof(DBGBuf);i++) {
-               if ((i%16) == 0)
-                       printk("\n%4x:",i);
-               if (DBGBuf[i] >= ' ' && DBGBuf[i] <= '~') {
-                       printk(" %c ",DBGBuf[i]);
-               } else {
-                       printk(" . ");
-               }
-       }
-       printk("\n");
-       printk("Last index %x\n",I);
-}
-#endif /* DEBUG_FIFO */
-
-//********
-//* Code *
-//********
-
-static inline int
-i2Validate ( i2ChanStrPtr pCh )
-{
-       //ip2trace(pCh->port_index, ITRC_VERIFY,ITRC_ENTER,2,pCh->validity,
-       //      (CHANNEL_MAGIC | CHANNEL_SUPPORT));
-       return ((pCh->validity & (CHANNEL_MAGIC_BITS | CHANNEL_SUPPORT)) 
-                         == (CHANNEL_MAGIC | CHANNEL_SUPPORT));
-}
-
-static void iiSendPendingMail_t(unsigned long data)
-{
-       i2eBordStrPtr pB = (i2eBordStrPtr)data;
-
-       iiSendPendingMail(pB);
-}
-
-//******************************************************************************
-// Function:   iiSendPendingMail(pB)
-// Parameters: Pointer to a board structure
-// Returns:    Nothing
-//
-// Description:
-// If any outgoing mail bits are set and there is outgoing mailbox is empty,
-// send the mail and clear the bits.
-//******************************************************************************
-static void
-iiSendPendingMail(i2eBordStrPtr pB)
-{
-       if (pB->i2eOutMailWaiting && (!pB->i2eWaitingForEmptyFifo) )
-       {
-               if (iiTrySendMail(pB, pB->i2eOutMailWaiting))
-               {
-                       /* If we were already waiting for fifo to empty,
-                        * or just sent MB_OUT_STUFFED, then we are
-                        * still waiting for it to empty, until we should
-                        * receive an MB_IN_STRIPPED from the board.
-                        */
-                       pB->i2eWaitingForEmptyFifo |=
-                               (pB->i2eOutMailWaiting & MB_OUT_STUFFED);
-                       pB->i2eOutMailWaiting = 0;
-                       pB->SendPendingRetry = 0;
-               } else {
-/*             The only time we hit this area is when "iiTrySendMail" has
-               failed.  That only occurs when the outbound mailbox is
-               still busy with the last message.  We take a short breather
-               to let the board catch up with itself and then try again.
-               16 Retries is the limit - then we got a borked board.
-                       /\/\|=mhw=|\/\/                         */
-
-                       if( ++pB->SendPendingRetry < 16 ) {
-                               setup_timer(&pB->SendPendingTimer,
-                                       iiSendPendingMail_t, (unsigned long)pB);
-                               mod_timer(&pB->SendPendingTimer, jiffies + 1);
-                       } else {
-                               printk( KERN_ERR "IP2: iiSendPendingMail unable to queue outbound mail\n" );
-                       }
-               }
-       }
-}
-
-//******************************************************************************
-// Function:   i2InitChannels(pB, nChannels, pCh)
-// Parameters: Pointer to Ellis Board structure
-//             Number of channels to initialize
-//             Pointer to first element in an array of channel structures
-// Returns:    Success or failure
-//
-// Description:
-//
-// This function patches pointers, back-pointers, and initializes all the
-// elements in the channel structure array.
-//
-// This should be run after the board structure is initialized, through having
-// loaded the standard loadware (otherwise it complains).
-//
-// In any case, it must be done before any serious work begins initializing the
-// irq's or sending commands...
-//
-//******************************************************************************
-static int
-i2InitChannels ( i2eBordStrPtr pB, int nChannels, i2ChanStrPtr pCh)
-{
-       int index, stuffIndex;
-       i2ChanStrPtr *ppCh;
-       
-       if (pB->i2eValid != I2E_MAGIC) {
-               I2_COMPLETE(pB, I2EE_BADMAGIC);
-       }
-       if (pB->i2eState != II_STATE_STDLOADED) {
-               I2_COMPLETE(pB, I2EE_BADSTATE);
-       }
-
-       rwlock_init(&pB->read_fifo_spinlock);
-       rwlock_init(&pB->write_fifo_spinlock);
-       rwlock_init(&pB->Dbuf_spinlock);
-       rwlock_init(&pB->Bbuf_spinlock);
-       rwlock_init(&pB->Fbuf_spinlock);
-       
-       // NO LOCK needed yet - this is init
-
-       pB->i2eChannelPtr = pCh;
-       pB->i2eChannelCnt = nChannels;
-
-       pB->i2Fbuf_strip = pB->i2Fbuf_stuff = 0;
-       pB->i2Dbuf_strip = pB->i2Dbuf_stuff = 0;
-       pB->i2Bbuf_strip = pB->i2Bbuf_stuff = 0;
-
-       pB->SendPendingRetry = 0;
-
-       memset ( pCh, 0, sizeof (i2ChanStr) * nChannels );
-
-       for (index = stuffIndex = 0, ppCh = (i2ChanStrPtr *)(pB->i2Fbuf);
-                 nChannels && index < ABS_MOST_PORTS;
-                 index++)
-       {
-               if ( !(pB->i2eChannelMap[index >> 4] & (1 << (index & 0xf)) ) ) {
-                       continue;
-               }
-               rwlock_init(&pCh->Ibuf_spinlock);
-               rwlock_init(&pCh->Obuf_spinlock);
-               rwlock_init(&pCh->Cbuf_spinlock);
-               rwlock_init(&pCh->Pbuf_spinlock);
-               // NO LOCK needed yet - this is init
-               // Set up validity flag according to support level
-               if (pB->i2eGoodMap[index >> 4] & (1 << (index & 0xf)) ) {
-                       pCh->validity = CHANNEL_MAGIC | CHANNEL_SUPPORT;
-               } else {
-                       pCh->validity = CHANNEL_MAGIC;
-               }
-               pCh->pMyBord = pB;      /* Back-pointer */
-
-               // Prepare an outgoing flow-control packet to send as soon as the chance
-               // occurs.
-               if ( pCh->validity & CHANNEL_SUPPORT ) {
-                       pCh->infl.hd.i2sChannel = index;
-                       pCh->infl.hd.i2sCount = 5;
-                       pCh->infl.hd.i2sType = PTYPE_BYPASS;
-                       pCh->infl.fcmd = 37;
-                       pCh->infl.asof = 0;
-                       pCh->infl.room = IBUF_SIZE - 1;
-
-                       pCh->whenSendFlow = (IBUF_SIZE/5)*4; // when 80% full
-
-               // The following is similar to calling i2QueueNeeds, except that this
-               // is done in longhand, since we are setting up initial conditions on
-               // many channels at once.
-                       pCh->channelNeeds = NEED_FLOW;  // Since starting from scratch
-                       pCh->sinceLastFlow = 0;         // No bytes received since last flow
-                                                                                       // control packet was queued
-                       stuffIndex++;
-                       *ppCh++ = pCh;      // List this channel as needing
-                                                               // initial flow control packet sent
-               }
-
-               // Don't allow anything to be sent until the status packets come in from
-               // the board.
-
-               pCh->outfl.asof = 0;
-               pCh->outfl.room = 0;
-
-               // Initialize all the ring buffers
-
-               pCh->Ibuf_stuff = pCh->Ibuf_strip = 0;
-               pCh->Obuf_stuff = pCh->Obuf_strip = 0;
-               pCh->Cbuf_stuff = pCh->Cbuf_strip = 0;
-
-               memset( &pCh->icount, 0, sizeof (struct async_icount) );
-               pCh->hotKeyIn       = HOT_CLEAR;
-               pCh->channelOptions = 0;
-               pCh->bookMarks      = 0;
-               init_waitqueue_head(&pCh->pBookmarkWait);
-
-               init_waitqueue_head(&pCh->open_wait);
-               init_waitqueue_head(&pCh->close_wait);
-               init_waitqueue_head(&pCh->delta_msr_wait);
-
-               // Set base and divisor so default custom rate is 9600
-               pCh->BaudBase    = 921600;      // MAX for ST654, changed after we get
-               pCh->BaudDivisor = 96;          // the boxids (UART types) later
-
-               pCh->dataSetIn   = 0;
-               pCh->dataSetOut  = 0;
-
-               pCh->wopen       = 0;
-               pCh->throttled   = 0;
-
-               pCh->speed       = CBR_9600;
-
-               pCh->flags    = 0;
-
-               pCh->ClosingDelay     = 5*HZ/10;
-               pCh->ClosingWaitTime  = 30*HZ;
-
-               // Initialize task queue objects
-               INIT_WORK(&pCh->tqueue_input, do_input);
-               INIT_WORK(&pCh->tqueue_status, do_status);
-
-#ifdef IP2DEBUG_TRACE
-               pCh->trace = ip2trace;
-#endif
-
-               ++pCh;
-       --nChannels;
-       }
-       // No need to check for wrap here; this is initialization.
-       pB->i2Fbuf_stuff = stuffIndex;
-       I2_COMPLETE(pB, I2EE_GOOD);
-
-}
-
-//******************************************************************************
-// Function:   i2DeQueueNeeds(pB, type)
-// Parameters: Pointer to a board structure
-//             type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW
-// Returns:   
-//             Pointer to a channel structure
-//
-// Description: Returns pointer struct of next channel that needs service of
-//  the type specified. Otherwise returns a NULL reference.
-//
-//******************************************************************************
-static i2ChanStrPtr 
-i2DeQueueNeeds(i2eBordStrPtr pB, int type)
-{
-       unsigned short queueIndex;
-       unsigned long flags;
-
-       i2ChanStrPtr pCh = NULL;
-
-       switch(type) {
-
-       case  NEED_INLINE:
-
-               write_lock_irqsave(&pB->Dbuf_spinlock, flags);
-               if ( pB->i2Dbuf_stuff != pB->i2Dbuf_strip)
-               {
-                       queueIndex = pB->i2Dbuf_strip;
-                       pCh = pB->i2Dbuf[queueIndex];
-                       queueIndex++;
-                       if (queueIndex >= CH_QUEUE_SIZE) {
-                               queueIndex = 0;
-                       }
-                       pB->i2Dbuf_strip = queueIndex;
-                       pCh->channelNeeds &= ~NEED_INLINE;
-               }
-               write_unlock_irqrestore(&pB->Dbuf_spinlock, flags);
-               break;
-
-       case NEED_BYPASS:
-
-               write_lock_irqsave(&pB->Bbuf_spinlock, flags);
-               if (pB->i2Bbuf_stuff != pB->i2Bbuf_strip)
-               {
-                       queueIndex = pB->i2Bbuf_strip;
-                       pCh = pB->i2Bbuf[queueIndex];
-                       queueIndex++;
-                       if (queueIndex >= CH_QUEUE_SIZE) {
-                               queueIndex = 0;
-                       }
-                       pB->i2Bbuf_strip = queueIndex;
-                       pCh->channelNeeds &= ~NEED_BYPASS;
-               }
-               write_unlock_irqrestore(&pB->Bbuf_spinlock, flags);
-               break;
-       
-       case NEED_FLOW:
-
-               write_lock_irqsave(&pB->Fbuf_spinlock, flags);
-               if (pB->i2Fbuf_stuff != pB->i2Fbuf_strip)
-               {
-                       queueIndex = pB->i2Fbuf_strip;
-                       pCh = pB->i2Fbuf[queueIndex];
-                       queueIndex++;
-                       if (queueIndex >= CH_QUEUE_SIZE) {
-                               queueIndex = 0;
-                       }
-                       pB->i2Fbuf_strip = queueIndex;
-                       pCh->channelNeeds &= ~NEED_FLOW;
-               }
-               write_unlock_irqrestore(&pB->Fbuf_spinlock, flags);
-               break;
-       default:
-               printk(KERN_ERR "i2DeQueueNeeds called with bad type:%x\n",type);
-               break;
-       }
-       return pCh;
-}
-
-//******************************************************************************
-// Function:   i2QueueNeeds(pB, pCh, type)
-// Parameters: Pointer to a board structure
-//             Pointer to a channel structure
-//             type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW
-// Returns:    Nothing
-//
-// Description:
-// For each type of need selected, if the given channel is not already in the
-// queue, adds it, and sets the flag indicating it is in the queue.
-//******************************************************************************
-static void
-i2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type)
-{
-       unsigned short queueIndex;
-       unsigned long flags;
-
-       // We turn off all the interrupts during this brief process, since the
-       // interrupt-level code might want to put things on the queue as well.
-
-       switch (type) {
-
-       case NEED_INLINE:
-
-               write_lock_irqsave(&pB->Dbuf_spinlock, flags);
-               if ( !(pCh->channelNeeds & NEED_INLINE) )
-               {
-                       pCh->channelNeeds |= NEED_INLINE;
-                       queueIndex = pB->i2Dbuf_stuff;
-                       pB->i2Dbuf[queueIndex++] = pCh;
-                       if (queueIndex >= CH_QUEUE_SIZE)
-                               queueIndex = 0;
-                       pB->i2Dbuf_stuff = queueIndex;
-               }
-               write_unlock_irqrestore(&pB->Dbuf_spinlock, flags);
-               break;
-
-       case NEED_BYPASS:
-
-               write_lock_irqsave(&pB->Bbuf_spinlock, flags);
-               if ((type & NEED_BYPASS) && !(pCh->channelNeeds & NEED_BYPASS))
-               {
-                       pCh->channelNeeds |= NEED_BYPASS;
-                       queueIndex = pB->i2Bbuf_stuff;
-                       pB->i2Bbuf[queueIndex++] = pCh;
-                       if (queueIndex >= CH_QUEUE_SIZE)
-                               queueIndex = 0;
-                       pB->i2Bbuf_stuff = queueIndex;
-               } 
-               write_unlock_irqrestore(&pB->Bbuf_spinlock, flags);
-               break;
-
-       case NEED_FLOW:
-
-               write_lock_irqsave(&pB->Fbuf_spinlock, flags);
-               if ((type & NEED_FLOW) && !(pCh->channelNeeds & NEED_FLOW))
-               {
-                       pCh->channelNeeds |= NEED_FLOW;
-                       queueIndex = pB->i2Fbuf_stuff;
-                       pB->i2Fbuf[queueIndex++] = pCh;
-                       if (queueIndex >= CH_QUEUE_SIZE)
-                               queueIndex = 0;
-                       pB->i2Fbuf_stuff = queueIndex;
-               }
-               write_unlock_irqrestore(&pB->Fbuf_spinlock, flags);
-               break;
-
-       case NEED_CREDIT:
-               pCh->channelNeeds |= NEED_CREDIT;
-               break;
-       default:
-               printk(KERN_ERR "i2QueueNeeds called with bad type:%x\n",type);
-               break;
-       }
-       return;
-}
-
-//******************************************************************************
-// Function:   i2QueueCommands(type, pCh, timeout, nCommands, pCs,...)
-// Parameters: type - PTYPE_BYPASS or PTYPE_INLINE
-//             pointer to the channel structure
-//             maximum period to wait
-//             number of commands (n)
-//             n commands
-// Returns:    Number of commands sent, or -1 for error
-//
-// get board lock before calling
-//
-// Description:
-// Queues up some commands to be sent to a channel. To send possibly several
-// bypass or inline commands to the given channel. The timeout parameter
-// indicates how many HUNDREDTHS OF SECONDS to wait until there is room:
-// 0 = return immediately if no room, -ive  = wait forever, +ive = number of
-// 1/100 seconds to wait. Return values:
-// -1 Some kind of nasty error: bad channel structure or invalid arguments.
-//  0 No room to send all the commands
-// (+)   Number of commands sent
-//******************************************************************************
-static int
-i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands,
-                                        cmdSyntaxPtr pCs0,...)
-{
-       int totalsize = 0;
-       int blocksize;
-       int lastended;
-       cmdSyntaxPtr *ppCs;
-       cmdSyntaxPtr pCs;
-       int count;
-       int flag;
-       i2eBordStrPtr pB;
-
-       unsigned short maxBlock;
-       unsigned short maxBuff;
-       short bufroom;
-       unsigned short stuffIndex;
-       unsigned char *pBuf;
-       unsigned char *pInsert;
-       unsigned char *pDest, *pSource;
-       unsigned short channel;
-       int cnt;
-       unsigned long flags = 0;
-       rwlock_t *lock_var_p = NULL;
-
-       // Make sure the channel exists, otherwise do nothing
-       if ( !i2Validate ( pCh ) ) {
-               return -1;
-       }
-
-       ip2trace (CHANN, ITRC_QUEUE, ITRC_ENTER, 0 );
-
-       pB = pCh->pMyBord;
-
-       // Board must also exist, and THE INTERRUPT COMMAND ALREADY SENT
-       if (pB->i2eValid != I2E_MAGIC || pB->i2eUsingIrq == I2_IRQ_UNDEFINED)
-               return -2;
-       // If the board has gone fatal, return bad, and also hit the trap routine if
-       // it exists.
-       if (pB->i2eFatal) {
-               if ( pB->i2eFatalTrap ) {
-                       (*(pB)->i2eFatalTrap)(pB);
-               }
-               return -3;
-       }
-       // Set up some variables, Which buffers are we using?  How big are they?
-       switch(type)
-       {
-       case PTYPE_INLINE:
-               flag = INL;
-               maxBlock = MAX_OBUF_BLOCK;
-               maxBuff = OBUF_SIZE;
-               pBuf = pCh->Obuf;
-               break;
-       case PTYPE_BYPASS:
-               flag = BYP;
-               maxBlock = MAX_CBUF_BLOCK;
-               maxBuff = CBUF_SIZE;
-               pBuf = pCh->Cbuf;
-               break;
-       default:
-               return -4;
-       }
-       // Determine the total size required for all the commands
-       totalsize = blocksize = sizeof(i2CmdHeader);
-       lastended = 0;
-       ppCs = &pCs0;
-       for ( count = nCommands; count; count--, ppCs++)
-       {
-               pCs = *ppCs;
-               cnt = pCs->length;
-               // Will a new block be needed for this one? 
-               // Two possible reasons: too
-               // big or previous command has to be at the end of a packet.
-               if ((blocksize + cnt > maxBlock) || lastended) {
-                       blocksize = sizeof(i2CmdHeader);
-                       totalsize += sizeof(i2CmdHeader);
-               }
-               totalsize += cnt;
-               blocksize += cnt;
-
-               // If this command had to end a block, then we will make sure to
-               // account for it should there be any more blocks.
-               lastended = pCs->flags & END;
-       }
-       for (;;) {
-               // Make sure any pending flush commands go out before we add more data.
-               if ( !( pCh->flush_flags && i2RetryFlushOutput( pCh ) ) ) {
-                       // How much room (this time through) ?
-                       switch(type) {
-                       case PTYPE_INLINE:
-                               lock_var_p = &pCh->Obuf_spinlock;
-                               write_lock_irqsave(lock_var_p, flags);
-                               stuffIndex = pCh->Obuf_stuff;
-                               bufroom = pCh->Obuf_strip - stuffIndex;
-                               break;
-                       case PTYPE_BYPASS:
-                               lock_var_p = &pCh->Cbuf_spinlock;
-                               write_lock_irqsave(lock_var_p, flags);
-                               stuffIndex = pCh->Cbuf_stuff;
-                               bufroom = pCh->Cbuf_strip - stuffIndex;
-                               break;
-                       default:
-                               return -5;
-                       }
-                       if (--bufroom < 0) {
-                               bufroom += maxBuff;
-                       }
-
-                       ip2trace (CHANN, ITRC_QUEUE, 2, 1, bufroom );
-
-                       // Check for overflow
-                       if (totalsize <= bufroom) {
-                               // Normal Expected path - We still hold LOCK
-                               break; /* from for()- Enough room: goto proceed */
-                       }
-                       ip2trace(CHANN, ITRC_QUEUE, 3, 1, totalsize);
-                       write_unlock_irqrestore(lock_var_p, flags);
-               } else
-                       ip2trace(CHANN, ITRC_QUEUE, 3, 1, totalsize);
-
-               /* Prepare to wait for buffers to empty */
-               serviceOutgoingFifo(pB);        // Dump what we got
-
-               if (timeout == 0) {
-                       return 0;   // Tired of waiting
-               }
-               if (timeout > 0)
-                       timeout--;   // So negative values == forever
-               
-               if (!in_interrupt()) {
-                       schedule_timeout_interruptible(1);      // short nap
-               } else {
-                       // we cannot sched/sleep in interrupt silly
-                       return 0;   
-               }
-               if (signal_pending(current)) {
-                       return 0;   // Wake up! Time to die!!!
-               }
-
-               ip2trace (CHANN, ITRC_QUEUE, 4, 0 );
-
-       }       // end of for(;;)
-
-       // At this point we have room and the lock - stick them in.
-       channel = pCh->infl.hd.i2sChannel;
-       pInsert = &pBuf[stuffIndex];     // Pointer to start of packet
-       pDest = CMD_OF(pInsert);         // Pointer to start of command
-
-       // When we start counting, the block is the size of the header
-       for (blocksize = sizeof(i2CmdHeader), count = nCommands,
-                       lastended = 0, ppCs = &pCs0;
-               count;
-               count--, ppCs++)
-       {
-               pCs = *ppCs;         // Points to command protocol structure
-
-               // If this is a bookmark request command, post the fact that a bookmark
-               // request is pending. NOTE THIS TRICK ONLY WORKS BECAUSE CMD_BMARK_REQ
-               // has no parameters!  The more general solution would be to reference
-               // pCs->cmd[0].
-               if (pCs == CMD_BMARK_REQ) {
-                       pCh->bookMarks++;
-
-                       ip2trace (CHANN, ITRC_DRAIN, 30, 1, pCh->bookMarks );
-
-               }
-               cnt = pCs->length;
-
-               // If this command would put us over the maximum block size or 
-               // if the last command had to be at the end of a block, we end
-               // the existing block here and start a new one.
-               if ((blocksize + cnt > maxBlock) || lastended) {
-
-                       ip2trace (CHANN, ITRC_QUEUE, 5, 0 );
-
-                       PTYPE_OF(pInsert) = type;
-                       CHANNEL_OF(pInsert) = channel;
-                       // count here does not include the header
-                       CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader);
-                       stuffIndex += blocksize;
-                       if(stuffIndex >= maxBuff) {
-                               stuffIndex = 0;
-                               pInsert = pBuf;
-                       }
-                       pInsert = &pBuf[stuffIndex];  // Pointer to start of next pkt
-                       pDest = CMD_OF(pInsert);
-                       blocksize = sizeof(i2CmdHeader);
-               }
-               // Now we know there is room for this one in the current block
-
-               blocksize += cnt;       // Total bytes in this command
-               pSource = pCs->cmd;     // Copy the command into the buffer
-               while (cnt--) {
-                       *pDest++ = *pSource++;
-               }
-               // If this command had to end a block, then we will make sure to account
-               // for it should there be any more blocks.
-               lastended = pCs->flags & END;
-       }       // end for
-       // Clean up the final block by writing header, etc
-
-       PTYPE_OF(pInsert) = type;
-       CHANNEL_OF(pInsert) = channel;
-       // count here does not include the header
-       CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader);
-       stuffIndex += blocksize;
-       if(stuffIndex >= maxBuff) {
-               stuffIndex = 0;
-               pInsert = pBuf;
-       }
-       // Updates the index, and post the need for service. When adding these to
-       // the queue of channels, we turn off the interrupt while doing so,
-       // because at interrupt level we might want to push a channel back to the
-       // end of the queue.
-       switch(type)
-       {
-       case PTYPE_INLINE:
-               pCh->Obuf_stuff = stuffIndex;  // Store buffer pointer
-               write_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
-
-               pB->debugInlineQueued++;
-               // Add the channel pointer to list of channels needing service (first
-               // come...), if it's not already there.
-               i2QueueNeeds(pB, pCh, NEED_INLINE);
-               break;
-
-       case PTYPE_BYPASS:
-               pCh->Cbuf_stuff = stuffIndex;  // Store buffer pointer
-               write_unlock_irqrestore(&pCh->Cbuf_spinlock, flags);
-
-               pB->debugBypassQueued++;
-               // Add the channel pointer to list of channels needing service (first
-               // come...), if it's not already there.
-               i2QueueNeeds(pB, pCh, NEED_BYPASS);
-               break;
-       }
-
-       ip2trace (CHANN, ITRC_QUEUE, ITRC_RETURN, 1, nCommands );
-
-       return nCommands; // Good status: number of commands sent
-}
-
-//******************************************************************************
-// Function:   i2GetStatus(pCh,resetBits)
-// Parameters: Pointer to a channel structure
-//             Bit map of status bits to clear
-// Returns:    Bit map of current status bits
-//
-// Description:
-// Returns the state of data set signals, and whether a break has been received,
-// (see i2lib.h for bit-mapped result). resetBits is a bit-map of any status
-// bits to be cleared: I2_BRK, I2_PAR, I2_FRA, I2_OVR,... These are cleared
-// AFTER the condition is passed. If pCh does not point to a valid channel,
-// returns -1 (which would be impossible otherwise.
-//******************************************************************************
-static int
-i2GetStatus(i2ChanStrPtr pCh, int resetBits)
-{
-       unsigned short status;
-       i2eBordStrPtr pB;
-
-       ip2trace (CHANN, ITRC_STATUS, ITRC_ENTER, 2, pCh->dataSetIn, resetBits );
-
-       // Make sure the channel exists, otherwise do nothing */
-       if ( !i2Validate ( pCh ) )
-               return -1;
-
-       pB = pCh->pMyBord;
-
-       status = pCh->dataSetIn;
-
-       // Clear any specified error bits: but note that only actual error bits can
-       // be cleared, regardless of the value passed.
-       if (resetBits)
-       {
-               pCh->dataSetIn &= ~(resetBits & (I2_BRK | I2_PAR | I2_FRA | I2_OVR));
-               pCh->dataSetIn &= ~(I2_DDCD | I2_DCTS | I2_DDSR | I2_DRI);
-       }
-
-       ip2trace (CHANN, ITRC_STATUS, ITRC_RETURN, 1, pCh->dataSetIn );
-
-       return status;
-}
-
-//******************************************************************************
-// Function:   i2Input(pChpDest,count)
-// Parameters: Pointer to a channel structure
-//             Pointer to data buffer
-//             Number of bytes to read
-// Returns:    Number of bytes read, or -1 for error
-//
-// Description:
-// Strips data from the input buffer and writes it to pDest. If there is a
-// collosal blunder, (invalid structure pointers or the like), returns -1.
-// Otherwise, returns the number of bytes read.
-//******************************************************************************
-static int
-i2Input(i2ChanStrPtr pCh)
-{
-       int amountToMove;
-       unsigned short stripIndex;
-       int count;
-       unsigned long flags = 0;
-
-       ip2trace (CHANN, ITRC_INPUT, ITRC_ENTER, 0);
-
-       // Ensure channel structure seems real
-       if ( !i2Validate( pCh ) ) {
-               count = -1;
-               goto i2Input_exit;
-       }
-       write_lock_irqsave(&pCh->Ibuf_spinlock, flags);
-
-       // initialize some accelerators and private copies
-       stripIndex = pCh->Ibuf_strip;
-
-       count = pCh->Ibuf_stuff - stripIndex;
-
-       // If buffer is empty or requested data count was 0, (trivial case) return
-       // without any further thought.
-       if ( count == 0 ) {
-               write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
-               goto i2Input_exit;
-       }
-       // Adjust for buffer wrap
-       if ( count < 0 ) {
-               count += IBUF_SIZE;
-       }
-       // Don't give more than can be taken by the line discipline
-       amountToMove = pCh->pTTY->receive_room;
-       if (count > amountToMove) {
-               count = amountToMove;
-       }
-       // How much could we copy without a wrap?
-       amountToMove = IBUF_SIZE - stripIndex;
-
-       if (amountToMove > count) {
-               amountToMove = count;
-       }
-       // Move the first block
-       pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY,
-                &(pCh->Ibuf[stripIndex]), NULL, amountToMove );
-       // If we needed to wrap, do the second data move
-       if (count > amountToMove) {
-               pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY,
-                pCh->Ibuf, NULL, count - amountToMove );
-       }
-       // Bump and wrap the stripIndex all at once by the amount of data read. This
-       // method is good regardless of whether the data was in one or two pieces.
-       stripIndex += count;
-       if (stripIndex >= IBUF_SIZE) {
-               stripIndex -= IBUF_SIZE;
-       }
-       pCh->Ibuf_strip = stripIndex;
-
-       // Update our flow control information and possibly queue ourselves to send
-       // it, depending on how much data has been stripped since the last time a
-       // packet was sent.
-       pCh->infl.asof += count;
-
-       if ((pCh->sinceLastFlow += count) >= pCh->whenSendFlow) {
-               pCh->sinceLastFlow -= pCh->whenSendFlow;
-               write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
-               i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW);
-       } else {
-               write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
-       }
-
-i2Input_exit:
-
-       ip2trace (CHANN, ITRC_INPUT, ITRC_RETURN, 1, count);
-
-       return count;
-}
-
-//******************************************************************************
-// Function:   i2InputFlush(pCh)
-// Parameters: Pointer to a channel structure
-// Returns:    Number of bytes stripped, or -1 for error
-//
-// Description:
-// Strips any data from the input buffer. If there is a collosal blunder,
-// (invalid structure pointers or the like), returns -1. Otherwise, returns the
-// number of bytes stripped.
-//******************************************************************************
-static int
-i2InputFlush(i2ChanStrPtr pCh)
-{
-       int count;
-       unsigned long flags;
-
-       // Ensure channel structure seems real
-       if ( !i2Validate ( pCh ) )
-               return -1;
-
-       ip2trace (CHANN, ITRC_INPUT, 10, 0);
-
-       write_lock_irqsave(&pCh->Ibuf_spinlock, flags);
-       count = pCh->Ibuf_stuff - pCh->Ibuf_strip;
-
-       // Adjust for buffer wrap
-       if (count < 0) {
-               count += IBUF_SIZE;
-       }
-
-       // Expedient way to zero out the buffer
-       pCh->Ibuf_strip = pCh->Ibuf_stuff;
-
-
-       // Update our flow control information and possibly queue ourselves to send
-       // it, depending on how much data has been stripped since the last time a
-       // packet was sent.
-
-       pCh->infl.asof += count;
-
-       if ( (pCh->sinceLastFlow += count) >= pCh->whenSendFlow )
-       {
-               pCh->sinceLastFlow -= pCh->whenSendFlow;
-               write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
-               i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW);
-       } else {
-               write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
-       }
-
-       ip2trace (CHANN, ITRC_INPUT, 19, 1, count);
-
-       return count;
-}
-
-//******************************************************************************
-// Function:   i2InputAvailable(pCh)
-// Parameters: Pointer to a channel structure
-// Returns:    Number of bytes available, or -1 for error
-//
-// Description:
-// If there is a collosal blunder, (invalid structure pointers or the like),
-// returns -1. Otherwise, returns the number of bytes stripped. Otherwise,
-// returns the number of bytes available in the buffer.
-//******************************************************************************
-#if 0
-static int
-i2InputAvailable(i2ChanStrPtr pCh)
-{
-       int count;
-
-       // Ensure channel structure seems real
-       if ( !i2Validate ( pCh ) ) return -1;
-
-
-       // initialize some accelerators and private copies
-       read_lock_irqsave(&pCh->Ibuf_spinlock, flags);
-       count = pCh->Ibuf_stuff - pCh->Ibuf_strip;
-       read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
-
-       // Adjust for buffer wrap
-       if (count < 0)
-       {
-               count += IBUF_SIZE;
-       }
-
-       return count;
-}
-#endif 
-
-//******************************************************************************
-// Function:   i2Output(pCh, pSource, count)
-// Parameters: Pointer to channel structure
-//             Pointer to source data
-//             Number of bytes to send
-// Returns:    Number of bytes sent, or -1 for error
-//
-// Description:
-// Queues the data at pSource to be sent as data packets to the board. If there
-// is a collosal blunder, (invalid structure pointers or the like), returns -1.
-// Otherwise, returns the number of bytes written. What if there is not enough
-// room for all the data? If pCh->channelOptions & CO_NBLOCK_WRITE is set, then
-// we transfer as many characters as we can now, then return. If this bit is
-// clear (default), routine will spin along until all the data is buffered.
-// Should this occur, the 1-ms delay routine is called while waiting to avoid
-// applications that one cannot break out of.
-//******************************************************************************
-static int
-i2Output(i2ChanStrPtr pCh, const char *pSource, int count)
-{
-       i2eBordStrPtr pB;
-       unsigned char *pInsert;
-       int amountToMove;
-       int countOriginal = count;
-       unsigned short channel;
-       unsigned short stuffIndex;
-       unsigned long flags;
-
-       int bailout = 10;
-
-       ip2trace (CHANN, ITRC_OUTPUT, ITRC_ENTER, 2, count, 0 );
-
-       // Ensure channel structure seems real
-       if ( !i2Validate ( pCh ) ) 
-               return -1;
-
-       // initialize some accelerators and private copies
-       pB = pCh->pMyBord;
-       channel = pCh->infl.hd.i2sChannel;
-
-       // If the board has gone fatal, return bad, and also hit the trap routine if
-       // it exists.
-       if (pB->i2eFatal) {
-               if (pB->i2eFatalTrap) {
-                       (*(pB)->i2eFatalTrap)(pB);
-               }
-               return -1;
-       }
-       // Proceed as though we would do everything
-       while ( count > 0 ) {
-
-               // How much room in output buffer is there?
-               read_lock_irqsave(&pCh->Obuf_spinlock, flags);
-               amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1;
-               read_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
-               if (amountToMove < 0) {
-                       amountToMove += OBUF_SIZE;
-               }
-               // Subtract off the headers size and see how much room there is for real
-               // data. If this is negative, we will discover later.
-               amountToMove -= sizeof (i2DataHeader);
-
-               // Don't move more (now) than can go in a single packet
-               if ( amountToMove > (int)(MAX_OBUF_BLOCK - sizeof(i2DataHeader)) ) {
-                       amountToMove = MAX_OBUF_BLOCK - sizeof(i2DataHeader);
-               }
-               // Don't move more than the count we were given
-               if (amountToMove > count) {
-                       amountToMove = count;
-               }
-               // Now we know how much we must move: NB because the ring buffers have
-               // an overflow area at the end, we needn't worry about wrapping in the
-               // middle of a packet.
-
-// Small WINDOW here with no LOCK but I can't call Flush with LOCK
-// We would be flushing (or ending flush) anyway
-
-               ip2trace (CHANN, ITRC_OUTPUT, 10, 1, amountToMove );
-
-               if ( !(pCh->flush_flags && i2RetryFlushOutput(pCh) ) 
-                               && amountToMove > 0 )
-               {
-                       write_lock_irqsave(&pCh->Obuf_spinlock, flags);
-                       stuffIndex = pCh->Obuf_stuff;
-      
-                       // Had room to move some data: don't know whether the block size,
-                       // buffer space, or what was the limiting factor...
-                       pInsert = &(pCh->Obuf[stuffIndex]);
-
-                       // Set up the header
-                       CHANNEL_OF(pInsert)     = channel;
-                       PTYPE_OF(pInsert)       = PTYPE_DATA;
-                       TAG_OF(pInsert)         = 0;
-                       ID_OF(pInsert)          = ID_ORDINARY_DATA;
-                       DATA_COUNT_OF(pInsert)  = amountToMove;
-
-                       // Move the data
-                       memcpy( (char*)(DATA_OF(pInsert)), pSource, amountToMove );
-                       // Adjust pointers and indices
-                       pSource                                 += amountToMove;
-                       pCh->Obuf_char_count    += amountToMove;
-                       stuffIndex                              += amountToMove + sizeof(i2DataHeader);
-                       count                                   -= amountToMove;
-
-                       if (stuffIndex >= OBUF_SIZE) {
-                               stuffIndex = 0;
-                       }
-                       pCh->Obuf_stuff = stuffIndex;
-
-                       write_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
-
-                       ip2trace (CHANN, ITRC_OUTPUT, 13, 1, stuffIndex );
-
-               } else {
-
-                       // Cannot move data
-                       // becuz we need to stuff a flush 
-                       // or amount to move is <= 0
-
-                       ip2trace(CHANN, ITRC_OUTPUT, 14, 3,
-                               amountToMove,  pB->i2eFifoRemains,
-                               pB->i2eWaitingForEmptyFifo );
-
-                       // Put this channel back on queue
-                       // this ultimatly gets more data or wakes write output
-                       i2QueueNeeds(pB, pCh, NEED_INLINE);
-
-                       if ( pB->i2eWaitingForEmptyFifo ) {
-
-                               ip2trace (CHANN, ITRC_OUTPUT, 16, 0 );
-
-                               // or schedule
-                               if (!in_interrupt()) {
-
-                                       ip2trace (CHANN, ITRC_OUTPUT, 61, 0 );
-
-                                       schedule_timeout_interruptible(2);
-                                       if (signal_pending(current)) {
-                                               break;
-                                       }
-                                       continue;
-                               } else {
-
-                                       ip2trace (CHANN, ITRC_OUTPUT, 62, 0 );
-
-                                       // let interrupt in = WAS restore_flags()
-                                       // We hold no lock nor is irq off anymore???
-                                       
-                                       break;
-                               }
-                               break;   // from while(count)
-                       }
-                       else if ( pB->i2eFifoRemains < 32 && !pB->i2eTxMailEmpty ( pB ) )
-                       {
-                               ip2trace (CHANN, ITRC_OUTPUT, 19, 2,
-                                       pB->i2eFifoRemains,
-                                       pB->i2eTxMailEmpty );
-
-                               break;   // from while(count)
-                       } else if ( pCh->channelNeeds & NEED_CREDIT ) {
-
-                               ip2trace (CHANN, ITRC_OUTPUT, 22, 0 );
-
-                               break;   // from while(count)
-                       } else if ( --bailout) {
-
-                               // Try to throw more things (maybe not us) in the fifo if we're
-                               // not already waiting for it.
-       
-                               ip2trace (CHANN, ITRC_OUTPUT, 20, 0 );
-
-                               serviceOutgoingFifo(pB);
-                               //break;  CONTINUE;
-                       } else {
-                               ip2trace (CHANN, ITRC_OUTPUT, 21, 3,
-                                       pB->i2eFifoRemains,
-                                       pB->i2eOutMailWaiting,
-                                       pB->i2eWaitingForEmptyFifo );
-
-                               break;   // from while(count)
-                       }
-               }
-       } // End of while(count)
-
-       i2QueueNeeds(pB, pCh, NEED_INLINE);
-
-       // We drop through either when the count expires, or when there is some
-       // count left, but there was a non-blocking write.
-       if (countOriginal > count) {
-
-               ip2trace (CHANN, ITRC_OUTPUT, 17, 2, countOriginal, count );
-
-               serviceOutgoingFifo( pB );
-       }
-
-       ip2trace (CHANN, ITRC_OUTPUT, ITRC_RETURN, 2, countOriginal, count );
-
-       return countOriginal - count;
-}
-
-//******************************************************************************
-// Function:   i2FlushOutput(pCh)
-// Parameters: Pointer to a channel structure
-// Returns:    Nothing
-//
-// Description:
-// Sends bypass command to start flushing (waiting possibly forever until there
-// is room), then sends inline command to stop flushing output, (again waiting
-// possibly forever).
-//******************************************************************************
-static inline void
-i2FlushOutput(i2ChanStrPtr pCh)
-{
-
-       ip2trace (CHANN, ITRC_FLUSH, 1, 1, pCh->flush_flags );
-
-       if (pCh->flush_flags)
-               return;
-
-       if ( 1 != i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) {
-               pCh->flush_flags = STARTFL_FLAG;                // Failed - flag for later
-
-               ip2trace (CHANN, ITRC_FLUSH, 2, 0 );
-
-       } else if ( 1 != i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL) ) {
-               pCh->flush_flags = STOPFL_FLAG;         // Failed - flag for later
-
-               ip2trace (CHANN, ITRC_FLUSH, 3, 0 );
-       }
-}
-
-static int 
-i2RetryFlushOutput(i2ChanStrPtr pCh)
-{
-       int old_flags = pCh->flush_flags;
-
-       ip2trace (CHANN, ITRC_FLUSH, 14, 1, old_flags );
-
-       pCh->flush_flags = 0;   // Clear flag so we can avoid recursion
-                                                                       // and queue the commands
-
-       if ( old_flags & STARTFL_FLAG ) {
-               if ( 1 == i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) {
-                       old_flags = STOPFL_FLAG;        //Success - send stop flush
-               } else {
-                       old_flags = STARTFL_FLAG;       //Failure - Flag for retry later
-               }
-
-               ip2trace (CHANN, ITRC_FLUSH, 15, 1, old_flags );
-
-       }
-       if ( old_flags & STOPFL_FLAG ) {
-               if (1 == i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL)) {
-                       old_flags = 0;  // Success - clear flags
-               }
-
-               ip2trace (CHANN, ITRC_FLUSH, 16, 1, old_flags );
-       }
-       pCh->flush_flags = old_flags;
-
-       ip2trace (CHANN, ITRC_FLUSH, 17, 1, old_flags );
-
-       return old_flags;
-}
-
-//******************************************************************************
-// Function:   i2DrainOutput(pCh,timeout)
-// Parameters: Pointer to a channel structure
-//             Maximum period to wait
-// Returns:    ?
-//
-// Description:
-// Uses the bookmark request command to ask the board to send a bookmark back as
-// soon as all the data is completely sent.
-//******************************************************************************
-static void
-i2DrainWakeup(unsigned long d)
-{
-       i2ChanStrPtr pCh = (i2ChanStrPtr)d;
-
-       ip2trace (CHANN, ITRC_DRAIN, 10, 1, pCh->BookmarkTimer.expires );
-
-       pCh->BookmarkTimer.expires = 0;
-       wake_up_interruptible( &pCh->pBookmarkWait );
-}
-
-static void
-i2DrainOutput(i2ChanStrPtr pCh, int timeout)
-{
-       wait_queue_t wait;
-       i2eBordStrPtr pB;
-
-       ip2trace (CHANN, ITRC_DRAIN, ITRC_ENTER, 1, pCh->BookmarkTimer.expires);
-
-       pB = pCh->pMyBord;
-       // If the board has gone fatal, return bad, 
-       // and also hit the trap routine if it exists.
-       if (pB->i2eFatal) {
-               if (pB->i2eFatalTrap) {
-                       (*(pB)->i2eFatalTrap)(pB);
-               }
-               return;
-       }
-       if ((timeout > 0) && (pCh->BookmarkTimer.expires == 0 )) {
-               // One per customer (channel)
-               setup_timer(&pCh->BookmarkTimer, i2DrainWakeup,
-                               (unsigned long)pCh);
-
-               ip2trace (CHANN, ITRC_DRAIN, 1, 1, pCh->BookmarkTimer.expires );
-
-               mod_timer(&pCh->BookmarkTimer, jiffies + timeout);
-       }
-       
-       i2QueueCommands( PTYPE_INLINE, pCh, -1, 1, CMD_BMARK_REQ );
-
-       init_waitqueue_entry(&wait, current);
-       add_wait_queue(&(pCh->pBookmarkWait), &wait);
-       set_current_state( TASK_INTERRUPTIBLE );
-
-       serviceOutgoingFifo( pB );
-       
-       schedule();     // Now we take our interruptible sleep on
-
-       // Clean up the queue
-       set_current_state( TASK_RUNNING );
-       remove_wait_queue(&(pCh->pBookmarkWait), &wait);
-
-       // if expires == 0 then timer poped, then do not need to del_timer
-       if ((timeout > 0) && pCh->BookmarkTimer.expires && 
-                            time_before(jiffies, pCh->BookmarkTimer.expires)) {
-               del_timer( &(pCh->BookmarkTimer) );
-               pCh->BookmarkTimer.expires = 0;
-
-               ip2trace (CHANN, ITRC_DRAIN, 3, 1, pCh->BookmarkTimer.expires );
-
-       }
-       ip2trace (CHANN, ITRC_DRAIN, ITRC_RETURN, 1, pCh->BookmarkTimer.expires );
-       return;
-}
-
-//******************************************************************************
-// Function:   i2OutputFree(pCh)
-// Parameters: Pointer to a channel structure
-// Returns:    Space in output buffer
-//
-// Description:
-// Returns -1 if very gross error. Otherwise returns the amount of bytes still
-// free in the output buffer.
-//******************************************************************************
-static int
-i2OutputFree(i2ChanStrPtr pCh)
-{
-       int amountToMove;
-       unsigned long flags;
-
-       // Ensure channel structure seems real
-       if ( !i2Validate ( pCh ) ) {
-               return -1;
-       }
-       read_lock_irqsave(&pCh->Obuf_spinlock, flags);
-       amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1;
-       read_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
-
-       if (amountToMove < 0) {
-               amountToMove += OBUF_SIZE;
-       }
-       // If this is negative, we will discover later
-       amountToMove -= sizeof(i2DataHeader);
-
-       return (amountToMove < 0) ? 0 : amountToMove;
-}
-static void
-
-ip2_owake( PTTY tp)
-{
-       i2ChanStrPtr  pCh;
-
-       if (tp == NULL) return;
-
-       pCh = tp->driver_data;
-
-       ip2trace (CHANN, ITRC_SICMD, 10, 2, tp->flags,
-                       (1 << TTY_DO_WRITE_WAKEUP) );
-
-       tty_wakeup(tp);
-}
-
-static inline void
-set_baud_params(i2eBordStrPtr pB) 
-{
-       int i,j;
-       i2ChanStrPtr  *pCh;
-
-       pCh = (i2ChanStrPtr *) pB->i2eChannelPtr;
-
-       for (i = 0; i < ABS_MAX_BOXES; i++) {
-               if (pB->channelBtypes.bid_value[i]) {
-                       if (BID_HAS_654(pB->channelBtypes.bid_value[i])) {
-                               for (j = 0; j < ABS_BIGGEST_BOX; j++) {
-                                       if (pCh[i*16+j] == NULL)
-                                               break;
-                                       (pCh[i*16+j])->BaudBase    = 921600;    // MAX for ST654
-                                       (pCh[i*16+j])->BaudDivisor = 96;
-                               }
-                       } else {        // has cirrus cd1400
-                               for (j = 0; j < ABS_BIGGEST_BOX; j++) {
-                                       if (pCh[i*16+j] == NULL)
-                                               break;
-                                       (pCh[i*16+j])->BaudBase    = 115200;    // MAX for CD1400
-                                       (pCh[i*16+j])->BaudDivisor = 12;
-                               }
-                       }
-               }
-       }
-}
-
-//******************************************************************************
-// Function:   i2StripFifo(pB)
-// Parameters: Pointer to a board structure
-// Returns:    ?
-//
-// Description:
-// Strips all the available data from the incoming FIFO, identifies the type of
-// packet, and either buffers the data or does what needs to be done.
-//
-// Note there is no overflow checking here: if the board sends more data than it
-// ought to, we will not detect it here, but blindly overflow...
-//******************************************************************************
-
-// A buffer for reading in blocks for unknown channels
-static unsigned char junkBuffer[IBUF_SIZE];
-
-// A buffer to read in a status packet. Because of the size of the count field
-// for these things, the maximum packet size must be less than MAX_CMD_PACK_SIZE
-static unsigned char cmdBuffer[MAX_CMD_PACK_SIZE + 4];
-
-// This table changes the bit order from MSR order given by STAT_MODEM packet to
-// status bits used in our library.
-static char xlatDss[16] = {
-0      | 0     | 0      | 0      ,
-0      | 0     | 0      | I2_CTS ,
-0      | 0     | I2_DSR | 0      ,
-0      | 0     | I2_DSR | I2_CTS ,
-0      | I2_RI | 0      | 0      ,
-0      | I2_RI | 0      | I2_CTS ,
-0      | I2_RI | I2_DSR | 0      ,
-0      | I2_RI | I2_DSR | I2_CTS ,
-I2_DCD | 0     | 0      | 0      ,
-I2_DCD | 0     | 0      | I2_CTS ,
-I2_DCD | 0     | I2_DSR | 0      ,
-I2_DCD | 0     | I2_DSR | I2_CTS ,
-I2_DCD | I2_RI | 0      | 0      ,
-I2_DCD | I2_RI | 0      | I2_CTS ,
-I2_DCD | I2_RI | I2_DSR | 0      ,
-I2_DCD | I2_RI | I2_DSR | I2_CTS };
-
-static inline void
-i2StripFifo(i2eBordStrPtr pB)
-{
-       i2ChanStrPtr pCh;
-       int channel;
-       int count;
-       unsigned short stuffIndex;
-       int amountToRead;
-       unsigned char *pc, *pcLimit;
-       unsigned char uc;
-       unsigned char dss_change;
-       unsigned long bflags,cflags;
-
-//     ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_ENTER, 0 );
-
-       while (I2_HAS_INPUT(pB)) {
-//             ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 2, 0 );
-
-               // Process packet from fifo a one atomic unit
-               write_lock_irqsave(&pB->read_fifo_spinlock, bflags);
-   
-               // The first word (or two bytes) will have channel number and type of
-               // packet, possibly other information
-               pB->i2eLeadoffWord[0] = iiReadWord(pB);
-
-               switch(PTYPE_OF(pB->i2eLeadoffWord))
-               {
-               case PTYPE_DATA:
-                       pB->got_input = 1;
-
-//                     ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 3, 0 );
-
-                       channel = CHANNEL_OF(pB->i2eLeadoffWord); /* Store channel */
-                       count = iiReadWord(pB);          /* Count is in the next word */
-
-// NEW: Check the count for sanity! Should the hardware fail, our death
-// is more pleasant. While an oversize channel is acceptable (just more
-// than the driver supports), an over-length count clearly means we are
-// sick!
-                       if ( ((unsigned int)count) > IBUF_SIZE ) {
-                               pB->i2eFatal = 2;
-                               write_unlock_irqrestore(&pB->read_fifo_spinlock,
-                                               bflags);
-                               return;     /* Bail out ASAP */
-                       }
-                       // Channel is illegally big ?
-                       if ((channel >= pB->i2eChannelCnt) ||
-                               (NULL==(pCh = ((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])))
-                       {
-                               iiReadBuf(pB, junkBuffer, count);
-                               write_unlock_irqrestore(&pB->read_fifo_spinlock,
-                                               bflags);
-                               break;         /* From switch: ready for next packet */
-                       }
-
-                       // Channel should be valid, then
-
-                       // If this is a hot-key, merely post its receipt for now. These are
-                       // always supposed to be 1-byte packets, so we won't even check the
-                       // count. Also we will post an acknowledgement to the board so that
-                       // more data can be forthcoming. Note that we are not trying to use
-                       // these sequences in this driver, merely to robustly ignore them.
-                       if(ID_OF(pB->i2eLeadoffWord) == ID_HOT_KEY)
-                       {
-                               pCh->hotKeyIn = iiReadWord(pB) & 0xff;
-                               write_unlock_irqrestore(&pB->read_fifo_spinlock,
-                                               bflags);
-                               i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_HOTACK);
-                               break;   /* From the switch: ready for next packet */
-                       }
-
-                       // Normal data! We crudely assume there is room for the data in our
-                       // buffer because the board wouldn't have exceeded his credit limit.
-                       write_lock_irqsave(&pCh->Ibuf_spinlock, cflags);
-                                                                                                       // We have 2 locks now
-                       stuffIndex = pCh->Ibuf_stuff;
-                       amountToRead = IBUF_SIZE - stuffIndex;
-                       if (amountToRead > count)
-                               amountToRead = count;
-
-                       // stuffIndex would have been already adjusted so there would 
-                       // always be room for at least one, and count is always at least
-                       // one.
-
-                       iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead);
-                       pCh->icount.rx += amountToRead;
-
-                       // Update the stuffIndex by the amount of data moved. Note we could
-                       // never ask for more data than would just fit. However, we might
-                       // have read in one more byte than we wanted because the read
-                       // rounds up to even bytes. If this byte is on the end of the
-                       // packet, and is padding, we ignore it. If the byte is part of
-                       // the actual data, we need to move it.
-
-                       stuffIndex += amountToRead;
-
-                       if (stuffIndex >= IBUF_SIZE) {
-                               if ((amountToRead & 1) && (count > amountToRead)) {
-                                       pCh->Ibuf[0] = pCh->Ibuf[IBUF_SIZE];
-                                       amountToRead++;
-                                       stuffIndex = 1;
-                               } else {
-                                       stuffIndex = 0;
-                               }
-                       }
-
-                       // If there is anything left over, read it as well
-                       if (count > amountToRead) {
-                               amountToRead = count - amountToRead;
-                               iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead);
-                               pCh->icount.rx += amountToRead;
-                               stuffIndex += amountToRead;
-                       }
-
-                       // Update stuff index
-                       pCh->Ibuf_stuff = stuffIndex;
-                       write_unlock_irqrestore(&pCh->Ibuf_spinlock, cflags);
-                       write_unlock_irqrestore(&pB->read_fifo_spinlock,
-                                       bflags);
-
-#ifdef USE_IQ
-                       schedule_work(&pCh->tqueue_input);
-#else
-                       do_input(&pCh->tqueue_input);
-#endif
-
-                       // Note we do not need to maintain any flow-control credits at this
-                       // time:  if we were to increment .asof and decrement .room, there
-                       // would be no net effect. Instead, when we strip data, we will
-                       // increment .asof and leave .room unchanged.
-
-                       break;   // From switch: ready for next packet
-
-               case PTYPE_STATUS:
-                       ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 4, 0 );
-      
-                       count = CMD_COUNT_OF(pB->i2eLeadoffWord);
-
-                       iiReadBuf(pB, cmdBuffer, count);
-                       // We can release early with buffer grab
-                       write_unlock_irqrestore(&pB->read_fifo_spinlock,
-                                       bflags);
-
-                       pc = cmdBuffer;
-                       pcLimit = &(cmdBuffer[count]);
-
-                       while (pc < pcLimit) {
-                               channel = *pc++;
-
-                               ip2trace (channel, ITRC_SFIFO, 7, 2, channel, *pc );
-
-                               /* check for valid channel */
-                               if (channel < pB->i2eChannelCnt
-                                        && 
-                                        (pCh = (((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])) != NULL
-                                       )
-                               {
-                                       dss_change = 0;
-
-                                       switch (uc = *pc++)
-                                       {
-                                       /* Breaks and modem signals are easy: just update status */
-                                       case STAT_CTS_UP:
-                                               if ( !(pCh->dataSetIn & I2_CTS) )
-                                               {
-                                                       pCh->dataSetIn |= I2_DCTS;
-                                                       pCh->icount.cts++;
-                                                       dss_change = 1;
-                                               }
-                                               pCh->dataSetIn |= I2_CTS;
-                                               break;
-
-                                       case STAT_CTS_DN:
-                                               if ( pCh->dataSetIn & I2_CTS )
-                                               {
-                                                       pCh->dataSetIn |= I2_DCTS;
-                                                       pCh->icount.cts++;
-                                                       dss_change = 1;
-                                               }
-                                               pCh->dataSetIn &= ~I2_CTS;
-                                               break;
-
-                                       case STAT_DCD_UP:
-                                               ip2trace (channel, ITRC_MODEM, 1, 1, pCh->dataSetIn );
-
-                                               if ( !(pCh->dataSetIn & I2_DCD) )
-                                               {
-                                                       ip2trace (CHANN, ITRC_MODEM, 2, 0 );
-                                                       pCh->dataSetIn |= I2_DDCD;
-                                                       pCh->icount.dcd++;
-                                                       dss_change = 1;
-                                               }
-                                               pCh->dataSetIn |= I2_DCD;
-
-                                               ip2trace (channel, ITRC_MODEM, 3, 1, pCh->dataSetIn );
-                                               break;
-
-                                       case STAT_DCD_DN:
-                                               ip2trace (channel, ITRC_MODEM, 4, 1, pCh->dataSetIn );
-                                               if ( pCh->dataSetIn & I2_DCD )
-                                               {
-                                                       ip2trace (channel, ITRC_MODEM, 5, 0 );
-                                                       pCh->dataSetIn |= I2_DDCD;
-                                                       pCh->icount.dcd++;
-                                                       dss_change = 1;
-                                               }
-                                               pCh->dataSetIn &= ~I2_DCD;
-
-                                               ip2trace (channel, ITRC_MODEM, 6, 1, pCh->dataSetIn );
-                                               break;
-
-                                       case STAT_DSR_UP:
-                                               if ( !(pCh->dataSetIn & I2_DSR) )
-                                               {
-                                                       pCh->dataSetIn |= I2_DDSR;
-                                                       pCh->icount.dsr++;
-                                                       dss_change = 1;
-                                               }
-                                               pCh->dataSetIn |= I2_DSR;
-                                               break;
-
-                                       case STAT_DSR_DN:
-                                               if ( pCh->dataSetIn & I2_DSR )
-                                               {
-                                                       pCh->dataSetIn |= I2_DDSR;
-                                                       pCh->icount.dsr++;
-                                                       dss_change = 1;
-                                               }
-                                               pCh->dataSetIn &= ~I2_DSR;
-                                               break;
-
-                                       case STAT_RI_UP:
-                                               if ( !(pCh->dataSetIn & I2_RI) )
-                                               {
-                                                       pCh->dataSetIn |= I2_DRI;
-                                                       pCh->icount.rng++;
-                                                       dss_change = 1;
-                                               }
-                                               pCh->dataSetIn |= I2_RI ;
-                                               break;
-
-                                       case STAT_RI_DN:
-                                               // to be compat with serial.c
-                                               //if ( pCh->dataSetIn & I2_RI )
-                                               //{
-                                               //      pCh->dataSetIn |= I2_DRI;
-                                               //      pCh->icount.rng++; 
-                                               //      dss_change = 1;
-                                               //}
-                                               pCh->dataSetIn &= ~I2_RI ;
-                                               break;
-
-                                       case STAT_BRK_DET:
-                                               pCh->dataSetIn |= I2_BRK;
-                                               pCh->icount.brk++;
-                                               dss_change = 1;
-                                               break;
-
-                                       // Bookmarks? one less request we're waiting for
-                                       case STAT_BMARK:
-                                               pCh->bookMarks--;
-                                               if (pCh->bookMarks <= 0 ) {
-                                                       pCh->bookMarks = 0;
-                                                       wake_up_interruptible( &pCh->pBookmarkWait );
-
-                                               ip2trace (channel, ITRC_DRAIN, 20, 1, pCh->BookmarkTimer.expires );
-                                               }
-                                               break;
-
-                                       // Flow control packets? Update the new credits, and if
-                                       // someone was waiting for output, queue him up again.
-                                       case STAT_FLOW:
-                                               pCh->outfl.room =
-                                                       ((flowStatPtr)pc)->room -
-                                                       (pCh->outfl.asof - ((flowStatPtr)pc)->asof);
-
-                                               ip2trace (channel, ITRC_STFLW, 1, 1, pCh->outfl.room );
-
-                                               if (pCh->channelNeeds & NEED_CREDIT)
-                                               {
-                                                       ip2trace (channel, ITRC_STFLW, 2, 1, pCh->channelNeeds);
-
-                                                       pCh->channelNeeds &= ~NEED_CREDIT;
-                                                       i2QueueNeeds(pB, pCh, NEED_INLINE);
-                                                       if ( pCh->pTTY )
-                                                               ip2_owake(pCh->pTTY);
-                                               }
-
-                                               ip2trace (channel, ITRC_STFLW, 3, 1, pCh->channelNeeds);
-
-                                               pc += sizeof(flowStat);
-                                               break;
-
-                                       /* Special packets: */
-                                       /* Just copy the information into the channel structure */
-
-                                       case STAT_STATUS:
-
-                                               pCh->channelStatus = *((debugStatPtr)pc);
-                                               pc += sizeof(debugStat);
-                                               break;
-
-                                       case STAT_TXCNT:
-
-                                               pCh->channelTcount = *((cntStatPtr)pc);
-                                               pc += sizeof(cntStat);
-                                               break;
-
-                                       case STAT_RXCNT:
-
-                                               pCh->channelRcount = *((cntStatPtr)pc);
-                                               pc += sizeof(cntStat);
-                                               break;
-
-                                       case STAT_BOXIDS:
-                                               pB->channelBtypes = *((bidStatPtr)pc);
-                                               pc += sizeof(bidStat);
-                                               set_baud_params(pB);
-                                               break;
-
-                                       case STAT_HWFAIL:
-                                               i2QueueCommands (PTYPE_INLINE, pCh, 0, 1, CMD_HW_TEST);
-                                               pCh->channelFail = *((failStatPtr)pc);
-                                               pc += sizeof(failStat);
-                                               break;
-
-                                       /* No explicit match? then
-                                        * Might be an error packet...
-                                        */
-                                       default:
-                                               switch (uc & STAT_MOD_ERROR)
-                                               {
-                                               case STAT_ERROR:
-                                                       if (uc & STAT_E_PARITY) {
-                                                               pCh->dataSetIn |= I2_PAR;
-                                                               pCh->icount.parity++;
-                                                       }
-                                                       if (uc & STAT_E_FRAMING){
-                                                               pCh->dataSetIn |= I2_FRA;
-                                                               pCh->icount.frame++;
-                                                       }
-                                                       if (uc & STAT_E_OVERRUN){
-                                                               pCh->dataSetIn |= I2_OVR;
-                                                               pCh->icount.overrun++;
-                                                       }
-                                                       break;
-
-                                               case STAT_MODEM:
-                                                       // the answer to DSS_NOW request (not change)
-                                                       pCh->dataSetIn = (pCh->dataSetIn
-                                                               & ~(I2_RI | I2_CTS | I2_DCD | I2_DSR) )
-                                                               | xlatDss[uc & 0xf];
-                                                       wake_up_interruptible ( &pCh->dss_now_wait );
-                                               default:
-                                                       break;
-                                               }
-                                       }  /* End of switch on status type */
-                                       if (dss_change) {
-#ifdef USE_IQ
-                                               schedule_work(&pCh->tqueue_status);
-#else
-                                               do_status(&pCh->tqueue_status);
-#endif
-                                       }
-                               }
-                               else  /* Or else, channel is invalid */
-                               {
-                                       // Even though the channel is invalid, we must test the
-                                       // status to see how much additional data it has (to be
-                                       // skipped)
-                                       switch (*pc++)
-                                       {
-                                       case STAT_FLOW:
-                                               pc += 4;    /* Skip the data */
-                                               break;
-
-                                       default:
-                                               break;
-                                       }
-                               }
-                       }  // End of while (there is still some status packet left)
-                       break;
-
-               default: // Neither packet? should be impossible
-                       ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 5, 1,
-                               PTYPE_OF(pB->i2eLeadoffWord) );
-                       write_unlock_irqrestore(&pB->read_fifo_spinlock,
-                                       bflags);
-
-                       break;
-               }  // End of switch on type of packets
-       }       /*while(board I2_HAS_INPUT)*/
-
-       ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_RETURN, 0 );
-
-       // Send acknowledgement to the board even if there was no data!
-       pB->i2eOutMailWaiting |= MB_IN_STRIPPED;
-       return;
-}
-
-//******************************************************************************
-// Function:   i2Write2Fifo(pB,address,count)
-// Parameters: Pointer to a board structure, source address, byte count
-// Returns:    bytes written
-//
-// Description:
-//  Writes count bytes to board io address(implied) from source
-//  Adjusts count, leaves reserve for next time around bypass cmds
-//******************************************************************************
-static int
-i2Write2Fifo(i2eBordStrPtr pB, unsigned char *source, int count,int reserve)
-{
-       int rc = 0;
-       unsigned long flags;
-       write_lock_irqsave(&pB->write_fifo_spinlock, flags);
-       if (!pB->i2eWaitingForEmptyFifo) {
-               if (pB->i2eFifoRemains > (count+reserve)) {
-                       pB->i2eFifoRemains -= count;
-                       iiWriteBuf(pB, source, count);
-                       pB->i2eOutMailWaiting |= MB_OUT_STUFFED;
-                       rc =  count;
-               }
-       }
-       write_unlock_irqrestore(&pB->write_fifo_spinlock, flags);
-       return rc;
-}
-//******************************************************************************
-// Function:   i2StuffFifoBypass(pB)
-// Parameters: Pointer to a board structure
-// Returns:    Nothing
-//
-// Description:
-// Stuffs as many bypass commands into the fifo as possible. This is simpler
-// than stuffing data or inline commands to fifo, since we do not have
-// flow-control to deal with.
-//******************************************************************************
-static inline void
-i2StuffFifoBypass(i2eBordStrPtr pB)
-{
-       i2ChanStrPtr pCh;
-       unsigned char *pRemove;
-       unsigned short stripIndex;
-       unsigned short packetSize;
-       unsigned short paddedSize;
-       unsigned short notClogged = 1;
-       unsigned long flags;
-
-       int bailout = 1000;
-
-       // Continue processing so long as there are entries, or there is room in the
-       // fifo. Each entry represents a channel with something to do.
-       while ( --bailout && notClogged && 
-                       (NULL != (pCh = i2DeQueueNeeds(pB,NEED_BYPASS))))
-       {
-               write_lock_irqsave(&pCh->Cbuf_spinlock, flags);
-               stripIndex = pCh->Cbuf_strip;
-
-               // as long as there are packets for this channel...
-
-               while (stripIndex != pCh->Cbuf_stuff) {
-                       pRemove = &(pCh->Cbuf[stripIndex]);
-                       packetSize = CMD_COUNT_OF(pRemove) + sizeof(i2CmdHeader);
-                       paddedSize = roundup(packetSize, 2);
-
-                       if (paddedSize > 0) {
-                               if ( 0 == i2Write2Fifo(pB, pRemove, paddedSize,0)) {
-                                       notClogged = 0; /* fifo full */
-                                       i2QueueNeeds(pB, pCh, NEED_BYPASS);     // Put back on queue
-                                       break;   // Break from the channel
-                               } 
-                       }
-#ifdef DEBUG_FIFO
-WriteDBGBuf("BYPS", pRemove, paddedSize);
-#endif /* DEBUG_FIFO */
-                       pB->debugBypassCount++;
-
-                       pRemove += packetSize;
-                       stripIndex += packetSize;
-                       if (stripIndex >= CBUF_SIZE) {
-                               stripIndex = 0;
-                               pRemove = pCh->Cbuf;
-                       }
-               }
-               // Done with this channel. Move to next, removing this one from 
-               // the queue of channels if we cleaned it out (i.e., didn't get clogged.
-               pCh->Cbuf_strip = stripIndex;
-               write_unlock_irqrestore(&pCh->Cbuf_spinlock, flags);
-       }  // Either clogged or finished all the work
-
-#ifdef IP2DEBUG_TRACE
-       if ( !bailout ) {
-               ip2trace (ITRC_NO_PORT, ITRC_ERROR, 1, 0 );
-       }
-#endif
-}
-
-//******************************************************************************
-// Function:   i2StuffFifoFlow(pB)
-// Parameters: Pointer to a board structure
-// Returns:    Nothing
-//
-// Description:
-// Stuffs as many flow control packets into the fifo as possible. This is easier
-// even than doing normal bypass commands, because there is always at most one
-// packet, already assembled, for each channel.
-//******************************************************************************
-static inline void
-i2StuffFifoFlow(i2eBordStrPtr pB)
-{
-       i2ChanStrPtr pCh;
-       unsigned short paddedSize = roundup(sizeof(flowIn), 2);
-
-       ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_ENTER, 2,
-               pB->i2eFifoRemains, paddedSize );
-
-       // Continue processing so long as there are entries, or there is room in the
-       // fifo. Each entry represents a channel with something to do.
-       while ( (NULL != (pCh = i2DeQueueNeeds(pB,NEED_FLOW)))) {
-               pB->debugFlowCount++;
-
-               // NO Chan LOCK needed ???
-               if ( 0 == i2Write2Fifo(pB,(unsigned char *)&(pCh->infl),paddedSize,0)) {
-                       break;
-               }
-#ifdef DEBUG_FIFO
-               WriteDBGBuf("FLOW",(unsigned char *) &(pCh->infl), paddedSize);
-#endif /* DEBUG_FIFO */
-
-       }  // Either clogged or finished all the work
-
-       ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_RETURN, 0 );
-}
-
-//******************************************************************************
-// Function:   i2StuffFifoInline(pB)
-// Parameters: Pointer to a board structure
-// Returns:    Nothing
-//
-// Description:
-// Stuffs as much data and inline commands into the fifo as possible. This is
-// the most complex fifo-stuffing operation, since there if now the channel
-// flow-control issue to deal with.
-//******************************************************************************
-static inline void
-i2StuffFifoInline(i2eBordStrPtr pB)
-{
-       i2ChanStrPtr pCh;
-       unsigned char *pRemove;
-       unsigned short stripIndex;
-       unsigned short packetSize;
-       unsigned short paddedSize;
-       unsigned short notClogged = 1;
-       unsigned short flowsize;
-       unsigned long flags;
-
-       int bailout  = 1000;
-       int bailout2;
-
-       ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_ENTER, 3, pB->i2eFifoRemains, 
-                       pB->i2Dbuf_strip, pB->i2Dbuf_stuff );
-
-       // Continue processing so long as there are entries, or there is room in the
-       // fifo. Each entry represents a channel with something to do.
-       while ( --bailout && notClogged && 
-                       (NULL != (pCh = i2DeQueueNeeds(pB,NEED_INLINE))) )
-       {
-               write_lock_irqsave(&pCh->Obuf_spinlock, flags);
-               stripIndex = pCh->Obuf_strip;
-
-               ip2trace (CHANN, ITRC_SICMD, 3, 2, stripIndex, pCh->Obuf_stuff );
-
-               // as long as there are packets for this channel...
-               bailout2 = 1000;
-               while ( --bailout2 && stripIndex != pCh->Obuf_stuff) {
-                       pRemove = &(pCh->Obuf[stripIndex]);
-
-                       // Must determine whether this be a data or command packet to
-                       // calculate correctly the header size and the amount of
-                       // flow-control credit this type of packet will use.
-                       if (PTYPE_OF(pRemove) == PTYPE_DATA) {
-                               flowsize = DATA_COUNT_OF(pRemove);
-                               packetSize = flowsize + sizeof(i2DataHeader);
-                       } else {
-                               flowsize = CMD_COUNT_OF(pRemove);
-                               packetSize = flowsize + sizeof(i2CmdHeader);
-                       }
-                       flowsize = CREDIT_USAGE(flowsize);
-                       paddedSize = roundup(packetSize, 2);
-
-                       ip2trace (CHANN, ITRC_SICMD, 4, 2, pB->i2eFifoRemains, paddedSize );
-
-                       // If we don't have enough credits from the board to send the data,
-                       // flag the channel that we are waiting for flow control credit, and
-                       // break out. This will clean up this channel and remove us from the
-                       // queue of hot things to do.
-
-                               ip2trace (CHANN, ITRC_SICMD, 5, 2, pCh->outfl.room, flowsize );
-
-                       if (pCh->outfl.room <= flowsize)        {
-                               // Do Not have the credits to send this packet.
-                               i2QueueNeeds(pB, pCh, NEED_CREDIT);
-                               notClogged = 0;
-                               break;   // So to do next channel
-                       }
-                       if ( (paddedSize > 0) 
-                               && ( 0 == i2Write2Fifo(pB, pRemove, paddedSize, 128))) {
-                               // Do Not have room in fifo to send this packet.
-                               notClogged = 0;
-                               i2QueueNeeds(pB, pCh, NEED_INLINE);     
-                               break;   // Break from the channel
-                       }
-#ifdef DEBUG_FIFO
-WriteDBGBuf("DATA", pRemove, paddedSize);
-#endif /* DEBUG_FIFO */
-                       pB->debugInlineCount++;
-
-                       pCh->icount.tx += flowsize;
-                       // Update current credits
-                       pCh->outfl.room -= flowsize;
-                       pCh->outfl.asof += flowsize;
-                       if (PTYPE_OF(pRemove) == PTYPE_DATA) {
-                               pCh->Obuf_char_count -= DATA_COUNT_OF(pRemove);
-                       }
-                       pRemove += packetSize;
-                       stripIndex += packetSize;
-
-                       ip2trace (CHANN, ITRC_SICMD, 6, 2, stripIndex, pCh->Obuf_strip);
-
-                       if (stripIndex >= OBUF_SIZE) {
-                               stripIndex = 0;
-                               pRemove = pCh->Obuf;
-
-                               ip2trace (CHANN, ITRC_SICMD, 7, 1, stripIndex );
-
-                       }
-               }       /* while */
-               if ( !bailout2 ) {
-                       ip2trace (CHANN, ITRC_ERROR, 3, 0 );
-               }
-               // Done with this channel. Move to next, removing this one from the
-               // queue of channels if we cleaned it out (i.e., didn't get clogged.
-               pCh->Obuf_strip = stripIndex;
-               write_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
-               if ( notClogged )
-               {
-
-                       ip2trace (CHANN, ITRC_SICMD, 8, 0 );
-
-                       if ( pCh->pTTY ) {
-                               ip2_owake(pCh->pTTY);
-                       }
-               }
-       }  // Either clogged or finished all the work
-
-       if ( !bailout ) {
-               ip2trace (ITRC_NO_PORT, ITRC_ERROR, 4, 0 );
-       }
-
-       ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_RETURN, 1,pB->i2Dbuf_strip);
-}
-
-//******************************************************************************
-// Function:   serviceOutgoingFifo(pB)
-// Parameters: Pointer to a board structure
-// Returns:    Nothing
-//
-// Description:
-// Helper routine to put data in the outgoing fifo, if we aren't already waiting
-// for something to be there. If the fifo has only room for a very little data,
-// go head and hit the board with a mailbox hit immediately. Otherwise, it will
-// have to happen later in the interrupt processing. Since this routine may be
-// called both at interrupt and foreground time, we must turn off interrupts
-// during the entire process.
-//******************************************************************************
-static void
-serviceOutgoingFifo(i2eBordStrPtr pB)
-{
-       // If we aren't currently waiting for the board to empty our fifo, service
-       // everything that is pending, in priority order (especially, Bypass before
-       // Inline).
-       if ( ! pB->i2eWaitingForEmptyFifo )
-       {
-               i2StuffFifoFlow(pB);
-               i2StuffFifoBypass(pB);
-               i2StuffFifoInline(pB);
-
-               iiSendPendingMail(pB);
-       } 
-}
-
-//******************************************************************************
-// Function:   i2ServiceBoard(pB)
-// Parameters: Pointer to a board structure
-// Returns:    Nothing
-//
-// Description:
-// Normally this is called from interrupt level, but there is deliberately
-// nothing in here specific to being called from interrupt level. All the
-// hardware-specific, interrupt-specific things happen at the outer levels.
-//
-// For example, a timer interrupt could drive this routine for some sort of
-// polled operation. The only requirement is that the programmer deal with any
-// atomiticity/concurrency issues that result.
-//
-// This routine responds to the board's having sent mailbox information to the
-// host (which would normally cause an interrupt). This routine reads the
-// incoming mailbox. If there is no data in it, this board did not create the
-// interrupt and/or has nothing to be done to it. (Except, if we have been
-// waiting to write mailbox data to it, we may do so.
-//
-// Based on the value in the mailbox, we may take various actions.
-//
-// No checking here of pB validity: after all, it shouldn't have been called by
-// the handler unless pB were on the list.
-//******************************************************************************
-static inline int
-i2ServiceBoard ( i2eBordStrPtr pB )
-{
-       unsigned inmail;
-       unsigned long flags;
-
-
-       /* This should be atomic because of the way we are called... */
-       if (NO_MAIL_HERE == ( inmail = pB->i2eStartMail ) ) {
-               inmail = iiGetMail(pB);
-       }
-       pB->i2eStartMail = NO_MAIL_HERE;
-
-       ip2trace (ITRC_NO_PORT, ITRC_INTR, 2, 1, inmail );
-
-       if (inmail != NO_MAIL_HERE) {
-               // If the board has gone fatal, nothing to do but hit a bit that will
-               // alert foreground tasks to protest!
-               if ( inmail & MB_FATAL_ERROR ) {
-                       pB->i2eFatal = 1;
-                       goto exit_i2ServiceBoard;
-               }
-
-               /* Assuming no fatal condition, we proceed to do work */
-               if ( inmail & MB_IN_STUFFED ) {
-                       pB->i2eFifoInInts++;
-                       i2StripFifo(pB);     /* There might be incoming packets */
-               }
-
-               if (inmail & MB_OUT_STRIPPED) {
-                       pB->i2eFifoOutInts++;
-                       write_lock_irqsave(&pB->write_fifo_spinlock, flags);
-                       pB->i2eFifoRemains = pB->i2eFifoSize;
-                       pB->i2eWaitingForEmptyFifo = 0;
-                       write_unlock_irqrestore(&pB->write_fifo_spinlock,
-                                       flags);
-
-                       ip2trace (ITRC_NO_PORT, ITRC_INTR, 30, 1, pB->i2eFifoRemains );
-
-               }
-               serviceOutgoingFifo(pB);
-       }
-
-       ip2trace (ITRC_NO_PORT, ITRC_INTR, 8, 0 );
-
-exit_i2ServiceBoard:
-
-       return 0;
-}
diff --git a/drivers/char/ip2/i2lib.h b/drivers/char/ip2/i2lib.h
deleted file mode 100644 (file)
index e559e9b..0000000
+++ /dev/null
@@ -1,351 +0,0 @@
-/*******************************************************************************
-*
-*   (c) 1998 by Computone Corporation
-*
-********************************************************************************
-*
-*
-*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
-*                serial I/O controllers.
-*
-*   DESCRIPTION: Header file for high level library functions
-*
-*******************************************************************************/
-#ifndef I2LIB_H
-#define I2LIB_H   1
-//------------------------------------------------------------------------------
-// I2LIB.H
-//
-// IntelliPort-II and IntelliPort-IIEX
-//
-// Defines, structure definitions, and external declarations for i2lib.c
-//------------------------------------------------------------------------------
-//--------------------------------------
-// Mandatory Includes:
-//--------------------------------------
-#include "ip2types.h"
-#include "i2ellis.h"
-#include "i2pack.h"
-#include "i2cmd.h"
-#include <linux/workqueue.h>
-
-//------------------------------------------------------------------------------
-// i2ChanStr -- Channel Structure:
-// Used to track per-channel information for the library routines using standard
-// loadware. Note also, a pointer to an array of these structures is patched
-// into the i2eBordStr (see i2ellis.h)
-//------------------------------------------------------------------------------
-//
-// If we make some limits on the maximum block sizes, we can avoid dealing with
-// buffer wrap. The wrapping of the buffer is based on where the start of the
-// packet is. Then there is always room for the packet contiguously.
-//
-// Maximum total length of an outgoing data or in-line command block. The limit
-// of 36 on data is quite arbitrary and based more on DOS memory limitations
-// than the board interface. However, for commands, the maximum packet length is
-// MAX_CMD_PACK_SIZE, because the field size for the count is only a few bits
-// (see I2PACK.H) in such packets. For data packets, the count field size is not
-// the limiting factor. As of this writing, MAX_OBUF_BLOCK < MAX_CMD_PACK_SIZE,
-// but be careful if wanting to modify either.
-//
-#define MAX_OBUF_BLOCK  36
-
-// Another note on maximum block sizes: we are buffering packets here. Data is
-// put into the buffer (if there is room) regardless of the credits from the
-// board. The board sends new credits whenever it has removed from his buffers a
-// number of characters equal to 80% of total buffer size. (Of course, the total
-// buffer size is what is reported when the very first set of flow control
-// status packets are received from the board. Therefore, to be robust, you must
-// always fill the board to at least 80% of the current credit limit, else you
-// might not give it enough to trigger a new report. These conditions are
-// obtained here so long as the maximum output block size is less than 20% the
-// size of the board's output buffers. This is true at present by "coincidence"
-// or "infernal knowledge": the board's output buffers are at least 700 bytes
-// long (20% = 140 bytes, at least). The 80% figure is "official", so the safest
-// strategy might be to trap the first flow control report and guarantee that
-// the effective maxObufBlock is the minimum of MAX_OBUF_BLOCK and 20% of first
-// reported buffer credit.
-//
-#define MAX_CBUF_BLOCK  6      // Maximum total length of a bypass command block
-
-#define IBUF_SIZE       512    // character capacity of input buffer per channel
-#define OBUF_SIZE       1024// character capacity of output buffer per channel
-#define CBUF_SIZE       10     // character capacity of output bypass buffer
-
-typedef struct _i2ChanStr
-{
-       // First, back-pointers so that given a pointer to this structure, you can
-       // determine the correct board and channel number to reference, (say, when
-       // issuing commands, etc. (Note, channel number is in infl.hd.i2sChannel.)
-
-       int      port_index;    // Index of port in channel structure array attached
-                                                       // to board structure.
-       PTTY     pTTY;          // Pointer to tty structure for port (OS specific)
-       USHORT   validity;      // Indicates whether the given channel has been
-                                                       // initialized, really exists (or is a missing
-                                                       // channel, e.g. channel 9 on an 8-port box.)
-
-       i2eBordStrPtr  pMyBord; // Back-pointer to this channel's board structure 
-
-       int      wopen;                 // waiting fer carrier
-
-       int      throttled;             // Set if upper layer can take no data
-
-       int      flags;         // Defined in tty.h
-
-       PWAITQ   open_wait;     // Pointer for OS sleep function.
-       PWAITQ   close_wait;    // Pointer for OS sleep function.
-       PWAITQ   delta_msr_wait;// Pointer for OS sleep function.
-       PWAITQ   dss_now_wait;  // Pointer for OS sleep function.
-
-       struct timer_list  BookmarkTimer;   // Used by i2DrainOutput
-       wait_queue_head_t pBookmarkWait;   // Used by i2DrainOutput
-
-       int      BaudBase;
-       int      BaudDivisor;
-
-       USHORT   ClosingDelay;
-       USHORT   ClosingWaitTime;
-
-       volatile
-       flowIn   infl;  // This structure is initialized as a completely
-                                       // formed flow-control command packet, and as such
-                                       // has the channel number, also the capacity and
-                                       // "as-of" data needed continuously.
-
-       USHORT   sinceLastFlow; // Counts the number of characters read from input
-                                                       // buffers, since the last time flow control info
-                                                       // was sent.
-
-       USHORT   whenSendFlow;  // Determines when new flow control is to be sent to
-                                                       // the board. Note unlike earlier manifestations of
-                                                       // the driver, these packets can be sent from
-                                                       // in-place.
-
-       USHORT   channelNeeds;  // Bit map of important things which must be done
-                                                       // for this channel. (See bits below )
-
-       volatile
-       flowStat outfl;         // Same type of structure is used to hold current
-                                                       // flow control information used to control our
-                                                       // output. "asof" is kept updated as data is sent,
-                                                       // and "room" never goes to zero.
-
-       // The incoming ring buffer
-       // Unlike the outgoing buffers, this holds raw data, not packets. The two
-       // extra bytes are used to hold the byte-padding when there is room for an
-       // odd number of bytes before we must wrap.
-       //
-       UCHAR    Ibuf[IBUF_SIZE + 2];
-       volatile
-       USHORT   Ibuf_stuff;     // Stuffing index
-       volatile
-       USHORT   Ibuf_strip;     // Stripping index
-
-       // The outgoing ring-buffer: Holds Data and command packets. N.B., even
-       // though these are in the channel structure, the channel is also written
-       // here, the easier to send it to the fifo when ready. HOWEVER, individual
-       // packets here are NOT padded to even length: the routines for writing
-       // blocks to the fifo will pad to even byte counts.
-       //
-       UCHAR   Obuf[OBUF_SIZE+MAX_OBUF_BLOCK+4];
-       volatile
-       USHORT  Obuf_stuff;     // Stuffing index
-       volatile
-       USHORT  Obuf_strip;     // Stripping index
-       int     Obuf_char_count;
-
-       // The outgoing bypass-command buffer. Unlike earlier manifestations, the
-       // flow control packets are sent directly from the structures. As above, the
-       // channel number is included in the packet, but they are NOT padded to even
-       // size.
-       //
-       UCHAR    Cbuf[CBUF_SIZE+MAX_CBUF_BLOCK+2];
-       volatile
-       USHORT   Cbuf_stuff;     // Stuffing index
-       volatile
-       USHORT   Cbuf_strip;     // Stripping index
-
-       // The temporary buffer for the Linux tty driver PutChar entry.
-       //
-       UCHAR    Pbuf[MAX_OBUF_BLOCK - sizeof (i2DataHeader)];
-       volatile
-       USHORT   Pbuf_stuff;     // Stuffing index
-
-       // The state of incoming data-set signals
-       //
-       USHORT   dataSetIn;     // Bit-mapped according to below. Also indicates
-                                                       // whether a break has been detected since last
-                                                       // inquiry.
-
-       // The state of outcoming data-set signals (as far as we can tell!)
-       //
-       USHORT   dataSetOut;     // Bit-mapped according to below. 
-
-       // Most recent hot-key identifier detected
-       //
-       USHORT   hotKeyIn;      // Hot key as sent by the board, HOT_CLEAR indicates
-                               // no hot key detected since last examined.
-
-       // Counter of outstanding requests for bookmarks
-       //
-       short   bookMarks;      // Number of outstanding bookmark requests, (+ive
-                                               // whenever a bookmark request if queued up, -ive
-                                               // whenever a bookmark is received).
-
-       // Misc options
-       //
-       USHORT   channelOptions;   // See below
-
-       // To store various incoming special packets
-       //
-       debugStat   channelStatus;
-       cntStat     channelRcount;
-       cntStat     channelTcount;
-       failStat    channelFail;
-
-       // To store the last values for line characteristics we sent to the board.
-       //
-       int     speed;
-
-       int flush_flags;
-
-       void (*trace)(unsigned short,unsigned char,unsigned char,unsigned long,...);
-
-       /*
-        * Kernel counters for the 4 input interrupts 
-        */
-       struct async_icount icount;
-
-       /*
-        *      Task queues for processing input packets from the board.
-        */
-       struct work_struct      tqueue_input;
-       struct work_struct      tqueue_status;
-       struct work_struct      tqueue_hangup;
-
-       rwlock_t Ibuf_spinlock;
-       rwlock_t Obuf_spinlock;
-       rwlock_t Cbuf_spinlock;
-       rwlock_t Pbuf_spinlock;
-
-} i2ChanStr, *i2ChanStrPtr;
-
-//---------------------------------------------------
-// Manifests and bit-maps for elements in i2ChanStr
-//---------------------------------------------------
-//
-// flush flags
-//
-#define STARTFL_FLAG 1
-#define STOPFL_FLAG  2
-
-// validity
-//
-#define CHANNEL_MAGIC_BITS 0xff00
-#define CHANNEL_MAGIC      0x5300   // (validity & CHANNEL_MAGIC_BITS) ==
-                                                                       // CHANNEL_MAGIC --> structure good
-
-#define CHANNEL_SUPPORT    0x0001   // Indicates channel is supported, exists,
-                                                                       // and passed P.O.S.T.
-
-// channelNeeds
-//
-#define NEED_FLOW    1  // Indicates flow control has been queued
-#define NEED_INLINE  2  // Indicates inline commands or data queued
-#define NEED_BYPASS  4  // Indicates bypass commands queued
-#define NEED_CREDIT  8  // Indicates would be sending except has not sufficient
-                                               // credit. The data is still in the channel structure,
-                                               // but the channel is not enqueued in the board
-                                               // structure again until there is a credit received from
-                                               // the board.
-
-// dataSetIn (Also the bits for i2GetStatus return value)
-//
-#define I2_DCD 1
-#define I2_CTS 2
-#define I2_DSR 4
-#define I2_RI  8
-
-// dataSetOut (Also the bits for i2GetStatus return value)
-//
-#define I2_DTR 1
-#define I2_RTS 2
-
-// i2GetStatus() can optionally clear these bits
-//
-#define I2_BRK    0x10  // A break was detected
-#define I2_PAR    0x20  // A parity error was received 
-#define I2_FRA    0x40  // A framing error was received
-#define I2_OVR    0x80  // An overrun error was received 
-
-// i2GetStatus() automatically clears these bits */
-//
-#define I2_DDCD   0x100 // DCD changed from its  former value
-#define I2_DCTS   0x200 // CTS changed from its former value 
-#define I2_DDSR   0x400 // DSR changed from its former value 
-#define I2_DRI    0x800 // RI changed from its former value 
-
-// hotKeyIn
-//
-#define HOT_CLEAR 0x1322   // Indicates that no hot-key has been detected
-
-// channelOptions
-//
-#define CO_NBLOCK_WRITE 1      // Writes don't block waiting for buffer. (Default
-                                                       // is, they do wait.)
-
-// fcmodes
-//
-#define I2_OUTFLOW_CTS  0x0001
-#define I2_INFLOW_RTS   0x0002
-#define I2_INFLOW_DSR   0x0004
-#define I2_INFLOW_DTR   0x0008
-#define I2_OUTFLOW_DSR  0x0010
-#define I2_OUTFLOW_DTR  0x0020
-#define I2_OUTFLOW_XON  0x0040
-#define I2_OUTFLOW_XANY 0x0080
-#define I2_INFLOW_XON   0x0100
-
-#define I2_CRTSCTS      (I2_OUTFLOW_CTS|I2_INFLOW_RTS)
-#define I2_IXANY_MODE   (I2_OUTFLOW_XON|I2_OUTFLOW_XANY)
-
-//-------------------------------------------
-// Macros used from user level like functions
-//-------------------------------------------
-
-// Macros to set and clear channel options
-//
-#define i2SetOption(pCh, option) pCh->channelOptions |= option
-#define i2ClrOption(pCh, option) pCh->channelOptions &= ~option
-
-// Macro to set fatal-error trap
-//
-#define i2SetFatalTrap(pB, routine) pB->i2eFatalTrap = routine
-
-//--------------------------------------------
-// Declarations and prototypes for i2lib.c
-//--------------------------------------------
-//
-static int  i2InitChannels(i2eBordStrPtr, int, i2ChanStrPtr);
-static int  i2QueueCommands(int, i2ChanStrPtr, int, int, cmdSyntaxPtr,...);
-static int  i2GetStatus(i2ChanStrPtr, int);
-static int  i2Input(i2ChanStrPtr);
-static int  i2InputFlush(i2ChanStrPtr);
-static int  i2Output(i2ChanStrPtr, const char *, int);
-static int  i2OutputFree(i2ChanStrPtr);
-static int  i2ServiceBoard(i2eBordStrPtr);
-static void i2DrainOutput(i2ChanStrPtr, int);
-
-#ifdef IP2DEBUG_TRACE
-void ip2trace(unsigned short,unsigned char,unsigned char,unsigned long,...);
-#else
-#define ip2trace(a,b,c,d...) do {} while (0)
-#endif
-
-// Argument to i2QueueCommands
-//
-#define C_IN_LINE 1
-#define C_BYPASS  0
-
-#endif   // I2LIB_H
diff --git a/drivers/char/ip2/i2pack.h b/drivers/char/ip2/i2pack.h
deleted file mode 100644 (file)
index 00342a6..0000000
+++ /dev/null
@@ -1,364 +0,0 @@
-/*******************************************************************************
-*
-*   (c) 1998 by Computone Corporation
-*
-********************************************************************************
-*
-*
-*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
-*                serial I/O controllers.
-*
-*   DESCRIPTION: Definitions of the packets used to transfer data and commands
-*                Host <--> Board. Information provided here is only applicable
-*                when the standard loadware is active.
-*
-*******************************************************************************/
-#ifndef I2PACK_H
-#define I2PACK_H  1
-
-//-----------------------------------------------
-// Revision History:
-//
-// 10 October 1991   MAG First draft
-// 24 February 1992  MAG Additions for 1.4.x loadware
-// 11 March 1992     MAG New status packets
-//
-//-----------------------------------------------
-
-//------------------------------------------------------------------------------
-// Packet Formats:
-//
-// Information passes between the host and board through the FIFO in packets.
-// These have headers which indicate the type of packet. Because the fifo data
-// path may be 16-bits wide, the protocol is constrained such that each packet
-// is always padded to an even byte count. (The lower-level interface routines
-// -- i2ellis.c -- are designed to do this).
-//
-// The sender (be it host or board) must place some number of complete packets
-// in the fifo, then place a message in the mailbox that packets are available.
-// Placing such a message interrupts the "receiver" (be it board or host), who
-// reads the mailbox message and determines that there are incoming packets
-// ready. Since there are no partial packets, and the length of a packet is
-// given in the header, the remainder of the packet can be read without checking
-// for FIFO empty condition. The process is repeated, packet by packet, until
-// the incoming FIFO is empty. Then the receiver uses the outbound mailbox to
-// signal the board that it has read the data. Only then can the sender place
-// additional data in the fifo.
-//------------------------------------------------------------------------------
-//
-//------------------------------------------------
-// Definition of Packet Header Area
-//------------------------------------------------
-//
-// Caution: these only define header areas. In actual use the data runs off
-// beyond the end of these structures.
-//
-// Since these structures are based on sequences of bytes which go to the board,
-// there cannot be ANY padding between the elements.
-#pragma pack(1)
-
-//----------------------------
-// DATA PACKETS
-//----------------------------
-
-typedef struct _i2DataHeader
-{
-       unsigned char i2sChannel;  /* The channel number: 0-255 */
-
-       // -- Bitfields are allocated LSB first --
-
-       // For incoming data, indicates whether this is an ordinary packet or a
-       // special one (e.g., hot key hit).
-       unsigned i2sId : 2 __attribute__ ((__packed__));
-
-       // For tagging data packets. There are flush commands which flush only data
-       // packets bearing a particular tag. (used in implementing IntelliView and
-       // IntelliPrint). THE TAG VALUE 0xf is RESERVED and must not be used (it has
-       // meaning internally to the loadware).
-       unsigned i2sTag : 4;
-
-       // These two bits determine the type of packet sent/received.
-       unsigned i2sType : 2;
-
-       // The count of data to follow: does not include the possible additional
-       // padding byte. MAXIMUM COUNT: 4094. The top four bits must be 0.
-       unsigned short i2sCount;
-
-} i2DataHeader, *i2DataHeaderPtr;
-
-// Structure is immediately followed by the data, proper.
-
-//----------------------------
-// NON-DATA PACKETS
-//----------------------------
-
-typedef struct _i2CmdHeader
-{
-       unsigned char i2sChannel;       // The channel number: 0-255 (Except where noted
-                                                               // - see below
-
-       // Number of bytes of commands, status or whatever to follow
-       unsigned i2sCount : 6;
-
-       // These two bits determine the type of packet sent/received.
-       unsigned i2sType : 2;
-
-} i2CmdHeader, *i2CmdHeaderPtr;
-
-// Structure is immediately followed by the applicable data.
-
-//---------------------------------------
-// Flow Control Packets (Outbound)
-//---------------------------------------
-
-// One type of outbound command packet is so important that the entire structure
-// is explicitly defined here. That is the flow-control packet. This is never
-// sent by user-level code (as would be the commands to raise/lower DTR, for
-// example). These are only sent by the library routines in response to reading
-// incoming data into the buffers.
-//
-// The parameters inside the command block are maintained in place, then the
-// block is sent at the appropriate time.
-
-typedef struct _flowIn
-{
-       i2CmdHeader    hd;      // Channel #, count, type (see above)
-       unsigned char  fcmd;    // The flow control command (37)
-       unsigned short asof;    // As of byte number "asof" (LSB first!) I have room
-                                                       // for "room" bytes
-       unsigned short room;
-} flowIn, *flowInPtr;
-
-//----------------------------------------
-// (Incoming) Status Packets
-//----------------------------------------
-
-// Incoming packets which are non-data packets are status packets. In this case,
-// the channel number in the header is unimportant. What follows are one or more
-// sub-packets, the first word of which consists of the channel (first or low
-// byte) and the status indicator (second or high byte), followed by possibly
-// more data.
-
-#define STAT_CTS_UP     0  /* CTS raised  (no other bytes) */
-#define STAT_CTS_DN     1  /* CTS dropped (no other bytes) */
-#define STAT_DCD_UP     2  /* DCD raised  (no other bytes) */
-#define STAT_DCD_DN     3  /* DCD dropped (no other bytes) */
-#define STAT_DSR_UP     4  /* DSR raised  (no other bytes) */
-#define STAT_DSR_DN     5  /* DSR dropped (no other bytes) */
-#define STAT_RI_UP      6  /* RI  raised  (no other bytes) */
-#define STAT_RI_DN      7  /* RI  dropped (no other bytes) */
-#define STAT_BRK_DET    8  /* BRK detect  (no other bytes) */
-#define STAT_FLOW       9  /* Flow control(-- more: see below */
-#define STAT_BMARK      10 /* Bookmark    (no other bytes)
-                                                       * Bookmark is sent as a response to
-                                                       * a command 60: request for bookmark
-                                                       */
-#define STAT_STATUS     11 /* Special packet: see below */
-#define STAT_TXCNT      12 /* Special packet: see below */
-#define STAT_RXCNT      13 /* Special packet: see below */
-#define STAT_BOXIDS     14 /* Special packet: see below */
-#define STAT_HWFAIL     15 /* Special packet: see below */
-
-#define STAT_MOD_ERROR  0xc0
-#define STAT_MODEM      0xc0/* If status & STAT_MOD_ERROR:
-                                                        * == STAT_MODEM, then this is a modem
-                                                        * status packet, given in response to a
-                                                        * CMD_DSS_NOW command.
-                                                        * The low nibble has each data signal:
-                                                        */
-#define STAT_MOD_DCD    0x8
-#define STAT_MOD_RI     0x4
-#define STAT_MOD_DSR    0x2
-#define STAT_MOD_CTS    0x1
-
-#define STAT_ERROR      0x80/* If status & STAT_MOD_ERROR
-                                                        * == STAT_ERROR, then
-                                                        * sort of error on the channel.
-                                                        * The remaining seven bits indicate
-                                                        * what sort of error it is.
-                                                        */
-/* The low three bits indicate parity, framing, or overrun errors */
-
-#define STAT_E_PARITY   4     /* Parity error */
-#define STAT_E_FRAMING  2     /* Framing error */
-#define STAT_E_OVERRUN  1     /* (uxart) overrun error */
-
-//---------------------------------------
-// STAT_FLOW packets
-//---------------------------------------
-
-typedef struct _flowStat
-{
-       unsigned short asof;
-       unsigned short room;
-}flowStat, *flowStatPtr;
-
-// flowStat packets are received from the board to regulate the flow of outgoing
-// data. A local copy of this structure is also kept to track the amount of
-// credits used and credits remaining. "room" is the amount of space in the
-// board's buffers, "as of" having received a certain byte number. When sending
-// data to the fifo, you must calculate how much buffer space your packet will
-// use.  Add this to the current "asof" and subtract it from the current "room".
-//
-// The calculation for the board's buffer is given by CREDIT_USAGE, where size
-// is the un-rounded count of either data characters or command characters.
-// (Which is to say, the count rounded up, plus two).
-
-#define CREDIT_USAGE(size) (((size) + 3) & ~1)
-
-//---------------------------------------
-// STAT_STATUS packets
-//---------------------------------------
-
-typedef  struct   _debugStat
-{
-       unsigned char d_ccsr;
-       unsigned char d_txinh;
-       unsigned char d_stat1;
-       unsigned char d_stat2;
-} debugStat, *debugStatPtr;
-
-// debugStat packets are sent to the host in response to a CMD_GET_STATUS
-// command.  Each byte is bit-mapped as described below:
-
-#define D_CCSR_XON      2     /* Has received XON, ready to transmit */
-#define D_CCSR_XOFF     4     /* Has received XOFF, not transmitting */
-#define D_CCSR_TXENAB   8     /* Transmitter is enabled */
-#define D_CCSR_RXENAB   0x80  /* Receiver is enabled */
-
-#define D_TXINH_BREAK   1     /* We are sending a break */
-#define D_TXINH_EMPTY   2     /* No data to send */
-#define D_TXINH_SUSP    4     /* Output suspended via command 57 */
-#define D_TXINH_CMD     8     /* We are processing an in-line command */
-#define D_TXINH_LCD     0x10  /* LCD diagnostics are running */
-#define D_TXINH_PAUSE   0x20  /* We are processing a PAUSE command */
-#define D_TXINH_DCD     0x40  /* DCD is low, preventing transmission */
-#define D_TXINH_DSR     0x80  /* DSR is low, preventing transmission */
-
-#define D_STAT1_TXEN    1     /* Transmit INTERRUPTS enabled */
-#define D_STAT1_RXEN    2     /* Receiver INTERRUPTS enabled */
-#define D_STAT1_MDEN    4     /* Modem (data set sigs) interrupts enabled */
-#define D_STAT1_RLM     8     /* Remote loopback mode selected */
-#define D_STAT1_LLM     0x10  /* Local internal loopback mode selected */
-#define D_STAT1_CTS     0x20  /* CTS is low, preventing transmission */
-#define D_STAT1_DTR     0x40  /* DTR is low, to stop remote transmission */
-#define D_STAT1_RTS     0x80  /* RTS is low, to stop remote transmission */
-
-#define D_STAT2_TXMT    1     /* Transmit buffers are all empty */
-#define D_STAT2_RXMT    2     /* Receive buffers are all empty */
-#define D_STAT2_RXINH   4     /* Loadware has tried to inhibit remote
-                                                          * transmission:  dropped DTR, sent XOFF,
-                                                          * whatever...
-                                                          */
-#define D_STAT2_RXFLO   8     /* Loadware can send no more data to host
-                                                          * until it receives a flow-control packet
-                                                          */
-//-----------------------------------------
-// STAT_TXCNT and STAT_RXCNT packets
-//----------------------------------------
-
-typedef  struct   _cntStat
-{
-       unsigned short cs_time;    // (Assumes host is little-endian!)
-       unsigned short cs_count;
-} cntStat, *cntStatPtr;
-
-// These packets are sent in response to a CMD_GET_RXCNT or a CMD_GET_TXCNT
-// bypass command. cs_time is a running 1 Millisecond counter which acts as a
-// time stamp. cs_count is a running counter of data sent or received from the
-// uxarts. (Not including data added by the chip itself, as with CRLF
-// processing).
-//------------------------------------------
-// STAT_HWFAIL packets
-//------------------------------------------
-
-typedef struct _failStat
-{
-       unsigned char fs_written;
-       unsigned char fs_read;
-       unsigned short fs_address;
-} failStat, *failStatPtr;
-
-// This packet is sent whenever the on-board diagnostic process detects an
-// error. At startup, this process is dormant. The host can wake it up by
-// issuing the bypass command CMD_HW_TEST. The process runs at low priority and
-// performs continuous hardware verification; writing data to certain on-board
-// registers, reading it back, and comparing. If it detects an error, this
-// packet is sent to the host, and the process goes dormant again until the host
-// sends another CMD_HW_TEST. It then continues with the next register to be
-// tested.
-
-//------------------------------------------------------------------------------
-// Macros to deal with the headers more easily! Note that these are defined so
-// they may be used as "left" as well as "right" expressions.
-//------------------------------------------------------------------------------
-
-// Given a pointer to the packet, reference the channel number
-//
-#define CHANNEL_OF(pP)  ((i2DataHeaderPtr)(pP))->i2sChannel
-
-// Given a pointer to the packet, reference the Packet type
-//
-#define PTYPE_OF(pP) ((i2DataHeaderPtr)(pP))->i2sType
-
-// The possible types of packets
-//
-#define PTYPE_DATA   0  /* Host <--> Board */
-#define PTYPE_BYPASS 1  /* Host ---> Board */
-#define PTYPE_INLINE 2  /* Host ---> Board */
-#define PTYPE_STATUS 2  /* Host <--- Board */
-
-// Given a pointer to a Data packet, reference the Tag
-//
-#define TAG_OF(pP) ((i2DataHeaderPtr)(pP))->i2sTag
-
-// Given a pointer to a Data packet, reference the data i.d.
-//
-#define ID_OF(pP)  ((i2DataHeaderPtr)(pP))->i2sId
-
-// The possible types of ID's
-//
-#define ID_ORDINARY_DATA   0
-#define ID_HOT_KEY         1
-
-// Given a pointer to a Data packet, reference the count
-//
-#define DATA_COUNT_OF(pP) ((i2DataHeaderPtr)(pP))->i2sCount
-
-// Given a pointer to a Data packet, reference the beginning of data
-//
-#define DATA_OF(pP) &((unsigned char *)(pP))[4] // 4 = size of header
-
-// Given a pointer to a Non-Data packet, reference the count
-//
-#define CMD_COUNT_OF(pP) ((i2CmdHeaderPtr)(pP))->i2sCount
-
-#define MAX_CMD_PACK_SIZE  62 // Maximum size of such a count
-
-// Given a pointer to a Non-Data packet, reference the beginning of data
-//
-#define CMD_OF(pP) &((unsigned char *)(pP))[2]  // 2 = size of header
-
-//--------------------------------
-// MailBox Bits:
-//--------------------------------
-
-//--------------------------
-// Outgoing (host to board)
-//--------------------------
-//
-#define MB_OUT_STUFFED     0x80  // Host has placed output in fifo 
-#define MB_IN_STRIPPED     0x40  // Host has read in all input from fifo 
-
-//--------------------------
-// Incoming (board to host)
-//--------------------------
-//
-#define MB_IN_STUFFED      0x80  // Board has placed input in fifo 
-#define MB_OUT_STRIPPED    0x40  // Board has read all output from fifo 
-#define MB_FATAL_ERROR     0x20  // Board has encountered a fatal error
-
-#pragma pack()                  // Reset padding to command-line default
-
-#endif      // I2PACK_H
-
diff --git a/drivers/char/ip2/ip2.h b/drivers/char/ip2/ip2.h
deleted file mode 100644 (file)
index 936ccc5..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*******************************************************************************
-*
-*   (c) 1998 by Computone Corporation
-*
-********************************************************************************
-*
-*
-*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
-*                serial I/O controllers.
-*
-*   DESCRIPTION: Driver constants for configuration and tuning
-*
-*   NOTES:
-*
-*******************************************************************************/
-#ifndef IP2_H
-#define IP2_H
-
-#include "ip2types.h"
-#include "i2cmd.h"
-
-/*************/
-/* Constants */
-/*************/
-
-/* Device major numbers - since version 2.0.26. */
-#define IP2_TTY_MAJOR      71
-#define IP2_CALLOUT_MAJOR  72
-#define IP2_IPL_MAJOR      73
-
-/* Board configuration array.
- * This array defines the hardware irq and address for up to IP2_MAX_BOARDS
- * (4 supported per ip2_types.h) ISA board addresses and irqs MUST be specified,
- * PCI and EISA boards are probed for and automagicly configed
- * iff the addresses are set to 1 and 2 respectivily.
- *    0x0100 - 0x03f0 == ISA
- *              1        == PCI
- *              2        == EISA
- *              0        == (skip this board)
- * This array defines the hardware addresses for them. Special 
- * addresses are EISA and PCI which go sniffing for boards. 
-
- * In a multiboard system the position in the array determines which port
- * devices are assigned to each board: 
- *             board 0 is assigned ttyF0.. to ttyF63, 
- *             board 1 is assigned ttyF64  to ttyF127,
- *             board 2 is assigned ttyF128 to ttyF191,
- *             board 3 is assigned ttyF192 to ttyF255. 
- *
- * In PCI and EISA bus systems each range is mapped to card in 
- * monotonically increasing slot number order, ISA position is as specified
- * here.
-
- * If the irqs are ALL set to 0,0,0,0 all boards operate in 
- * polled mode. For interrupt operation ISA boards require that the IRQ be 
- * specified, while PCI and EISA boards any nonzero entry 
- * will enable interrupts using the BIOS configured irq for the board. 
- * An invalid irq entry will default to polled mode for that card and print
- * console warning.
- * When the driver is loaded as a module these setting can be overridden on the 
- * modprobe command line or on an option line in /etc/modprobe.conf.
- * If the driver is built-in the configuration must be 
- * set here for ISA cards and address set to 1 and 2 for PCI and EISA.
- *
- * Here is an example that shows most if not all possibe combinations:
-
- *static ip2config_t ip2config =
- *{
- *     {11,1,0,0},             // irqs
- *     {                               // Addresses
- *             0x0308,         // Board 0, ttyF0   - ttyF63// ISA card at io=0x308, irq=11
- *             0x0001,         // Board 1, ttyF64  - ttyF127//PCI card configured by BIOS
- *             0x0000,         // Board 2, ttyF128 - ttyF191// Slot skipped
- *             0x0002          // Board 3, ttyF192 - ttyF255//EISA card configured by BIOS
- *                                                                                              // but polled not irq driven
- *     }
- *};
- */
-
- /* this structure is zeroed out because the suggested method is to configure
-  * the driver as a module, set up the parameters with an options line in
-  * /etc/modprobe.conf and load with modprobe or kmod, the kernel
-  * module loader
-  */
-
- /* This structure is NOW always initialized when the driver is initialized.
-  * Compiled in defaults MUST be added to the io and irq arrays in
-  * ip2.c.  Those values are configurable from insmod parameters in the
-  * case of modules or from command line parameters (ip2=io,irq) when
-  * compiled in.
-  */
-
-static ip2config_t ip2config =
-{
-       {0,0,0,0},              // irqs
-       {                               // Addresses
-       /* Do NOT set compile time defaults HERE!  Use the arrays in
-               ip2.c!  These WILL be overwritten!  =mhw= */
-               0x0000,         // Board 0, ttyF0   - ttyF63
-               0x0000,         // Board 1, ttyF64  - ttyF127
-               0x0000,         // Board 2, ttyF128 - ttyF191
-               0x0000          // Board 3, ttyF192 - ttyF255
-       }
-};
-
-#endif
diff --git a/drivers/char/ip2/ip2ioctl.h b/drivers/char/ip2/ip2ioctl.h
deleted file mode 100644 (file)
index aa0a9da..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*******************************************************************************
-*
-*   (c) 1998 by Computone Corporation
-*
-********************************************************************************
-*
-*
-*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
-*                serial I/O controllers.
-*
-*   DESCRIPTION: Driver constants for configuration and tuning
-*
-*   NOTES:
-*
-*******************************************************************************/
-
-#ifndef IP2IOCTL_H
-#define IP2IOCTL_H
-
-//*************
-//* Constants *
-//*************
-
-// High baud rates (if not defined elsewhere.
-#ifndef B153600   
-#      define B153600   0010005
-#endif
-#ifndef B307200   
-#      define B307200   0010006
-#endif
-#ifndef B921600   
-#      define B921600   0010007
-#endif
-
-#endif
diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c
deleted file mode 100644 (file)
index ea7a8fb..0000000
+++ /dev/null
@@ -1,3234 +0,0 @@
-/*
-*
-*   (c) 1999 by Computone Corporation
-*
-********************************************************************************
-*
-*   PACKAGE:     Linux tty Device Driver for IntelliPort family of multiport
-*                serial I/O controllers.
-*
-*   DESCRIPTION: Mainline code for the device driver
-*
-*******************************************************************************/
-// ToDo:
-//
-// Fix the immediate DSS_NOW problem.
-// Work over the channel stats return logic in ip2_ipl_ioctl so they
-//     make sense for all 256 possible channels and so the user space
-//     utilities will compile and work properly.
-//
-// Done:
-//
-// 1.2.14      /\/\|=mhw=|\/\/
-// Added bounds checking to ip2_ipl_ioctl to avoid potential terroristic acts.
-// Changed the definition of ip2trace to be more consistent with kernel style
-//     Thanks to Andreas Dilger <adilger@turbolabs.com> for these updates
-//
-// 1.2.13      /\/\|=mhw=|\/\/
-// DEVFS: Renamed ttf/{n} to tts/F{n} and cuf/{n} to cua/F{n} to conform
-//     to agreed devfs serial device naming convention.
-//
-// 1.2.12      /\/\|=mhw=|\/\/
-// Cleaned up some remove queue cut and paste errors
-//
-// 1.2.11      /\/\|=mhw=|\/\/
-// Clean up potential NULL pointer dereferences
-// Clean up devfs registration
-// Add kernel command line parsing for io and irq
-//     Compile defaults for io and irq are now set in ip2.c not ip2.h!
-// Reworked poll_only hack for explicit parameter setting
-//     You must now EXPLICITLY set poll_only = 1 or set all irqs to 0
-// Merged ip2_loadmain and old_ip2_init
-// Converted all instances of interruptible_sleep_on into queue calls
-//     Most of these had no race conditions but better to clean up now
-//
-// 1.2.10      /\/\|=mhw=|\/\/
-// Fixed the bottom half interrupt handler and enabled USE_IQI
-//     to split the interrupt handler into a formal top-half / bottom-half
-// Fixed timing window on high speed processors that queued messages to
-//     the outbound mail fifo faster than the board could handle.
-//
-// 1.2.9
-// Four box EX was barfing on >128k kmalloc, made structure smaller by
-// reducing output buffer size
-//
-// 1.2.8
-// Device file system support (MHW)
-//
-// 1.2.7 
-// Fixed
-// Reload of ip2 without unloading ip2main hangs system on cat of /proc/modules
-//
-// 1.2.6
-//Fixes DCD problems
-//     DCD was not reported when CLOCAL was set on call to TIOCMGET
-//
-//Enhancements:
-//     TIOCMGET requests and waits for status return
-//     No DSS interrupts enabled except for DCD when needed
-//
-// For internal use only
-//
-//#define IP2DEBUG_INIT
-//#define IP2DEBUG_OPEN
-//#define IP2DEBUG_WRITE
-//#define IP2DEBUG_READ
-//#define IP2DEBUG_IOCTL
-//#define IP2DEBUG_IPL
-
-//#define IP2DEBUG_TRACE
-//#define DEBUG_FIFO
-
-/************/
-/* Includes */
-/************/
-
-#include <linux/ctype.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/errno.h>
-#include <linux/module.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/major.h>
-#include <linux/wait.h>
-#include <linux/device.h>
-#include <linux/mutex.h>
-#include <linux/firmware.h>
-#include <linux/platform_device.h>
-
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/termios.h>
-#include <linux/tty_driver.h>
-#include <linux/serial.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-
-#include <linux/cdk.h>
-#include <linux/comstats.h>
-#include <linux/delay.h>
-#include <linux/bitops.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include <linux/vmalloc.h>
-#include <linux/init.h>
-
-#include <asm/uaccess.h>
-
-#include "ip2types.h"
-#include "ip2trace.h"
-#include "ip2ioctl.h"
-#include "ip2.h"
-#include "i2ellis.h"
-#include "i2lib.h"
-
-/*****************
- * /proc/ip2mem  *
- *****************/
-
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-
-static DEFINE_MUTEX(ip2_mutex);
-static const struct file_operations ip2mem_proc_fops;
-static const struct file_operations ip2_proc_fops;
-
-/********************/
-/* Type Definitions */
-/********************/
-
-/*************/
-/* Constants */
-/*************/
-
-/* String constants to identify ourselves */
-static const char pcName[] = "Computone IntelliPort Plus multiport driver";
-static const char pcVersion[] = "1.2.14";
-
-/* String constants for port names */
-static const char pcDriver_name[] = "ip2";
-static const char pcIpl[] = "ip2ipl";
-
-/***********************/
-/* Function Prototypes */
-/***********************/
-
-/* Global module entry functions */
-
-/* Private (static) functions */
-static int  ip2_open(PTTY, struct file *);
-static void ip2_close(PTTY, struct file *);
-static int  ip2_write(PTTY, const unsigned char *, int);
-static int  ip2_putchar(PTTY, unsigned char);
-static void ip2_flush_chars(PTTY);
-static int  ip2_write_room(PTTY);
-static int  ip2_chars_in_buf(PTTY);
-static void ip2_flush_buffer(PTTY);
-static int  ip2_ioctl(PTTY, UINT, ULONG);
-static void ip2_set_termios(PTTY, struct ktermios *);
-static void ip2_set_line_discipline(PTTY);
-static void ip2_throttle(PTTY);
-static void ip2_unthrottle(PTTY);
-static void ip2_stop(PTTY);
-static void ip2_start(PTTY);
-static void ip2_hangup(PTTY);
-static int  ip2_tiocmget(struct tty_struct *tty);
-static int  ip2_tiocmset(struct tty_struct *tty,
-                        unsigned int set, unsigned int clear);
-static int ip2_get_icount(struct tty_struct *tty,
-               struct serial_icounter_struct *icount);
-
-static void set_irq(int, int);
-static void ip2_interrupt_bh(struct work_struct *work);
-static irqreturn_t ip2_interrupt(int irq, void *dev_id);
-static void ip2_poll(unsigned long arg);
-static inline void service_all_boards(void);
-static void do_input(struct work_struct *);
-static void do_status(struct work_struct *);
-
-static void ip2_wait_until_sent(PTTY,int);
-
-static void set_params (i2ChanStrPtr, struct ktermios *);
-static int get_serial_info(i2ChanStrPtr, struct serial_struct __user *);
-static int set_serial_info(i2ChanStrPtr, struct serial_struct __user *);
-
-static ssize_t ip2_ipl_read(struct file *, char __user *, size_t, loff_t *);
-static ssize_t ip2_ipl_write(struct file *, const char __user *, size_t, loff_t *);
-static long ip2_ipl_ioctl(struct file *, UINT, ULONG);
-static int ip2_ipl_open(struct inode *, struct file *);
-
-static int DumpTraceBuffer(char __user *, int);
-static int DumpFifoBuffer( char __user *, int);
-
-static void ip2_init_board(int, const struct firmware *);
-static unsigned short find_eisa_board(int);
-static int ip2_setup(char *str);
-
-/***************/
-/* Static Data */
-/***************/
-
-static struct tty_driver *ip2_tty_driver;
-
-/* Here, then is a table of board pointers which the interrupt routine should
- * scan through to determine who it must service.
- */
-static unsigned short i2nBoards; // Number of boards here
-
-static i2eBordStrPtr i2BoardPtrTable[IP2_MAX_BOARDS];
-
-static i2ChanStrPtr  DevTable[IP2_MAX_PORTS];
-//DevTableMem just used to save addresses for kfree
-static void  *DevTableMem[IP2_MAX_BOARDS];
-
-/* This is the driver descriptor for the ip2ipl device, which is used to
- * download the loadware to the boards.
- */
-static const struct file_operations ip2_ipl = {
-       .owner          = THIS_MODULE,
-       .read           = ip2_ipl_read,
-       .write          = ip2_ipl_write,
-       .unlocked_ioctl = ip2_ipl_ioctl,
-       .open           = ip2_ipl_open,
-       .llseek         = noop_llseek,
-}; 
-
-static unsigned long irq_counter;
-static unsigned long bh_counter;
-
-// Use immediate queue to service interrupts
-#define USE_IQI
-//#define USE_IQ       // PCI&2.2 needs work
-
-/* The timer_list entry for our poll routine. If interrupt operation is not
- * selected, the board is serviced periodically to see if anything needs doing.
- */
-#define  POLL_TIMEOUT   (jiffies + 1)
-static DEFINE_TIMER(PollTimer, ip2_poll, 0, 0);
-
-#ifdef IP2DEBUG_TRACE
-/* Trace (debug) buffer data */
-#define TRACEMAX  1000
-static unsigned long tracebuf[TRACEMAX];
-static int tracestuff;
-static int tracestrip;
-static int tracewrap;
-#endif
-
-/**********/
-/* Macros */
-/**********/
-
-#ifdef IP2DEBUG_OPEN
-#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] ttyc=%d, modc=%x -> %s\n", \
-                   tty->name,(pCh->flags), \
-                   tty->count,/*GET_USE_COUNT(module)*/0,s)
-#else
-#define DBG_CNT(s)
-#endif
-
-/********/
-/* Code */
-/********/
-
-#include "i2ellis.c"    /* Extremely low-level interface services */
-#include "i2cmd.c"      /* Standard loadware command definitions */
-#include "i2lib.c"      /* High level interface services */
-
-/* Configuration area for modprobe */
-
-MODULE_AUTHOR("Doug McNash");
-MODULE_DESCRIPTION("Computone IntelliPort Plus Driver");
-MODULE_LICENSE("GPL");
-
-#define        MAX_CMD_STR     50
-
-static int poll_only;
-static char cmd[MAX_CMD_STR];
-
-static int Eisa_irq;
-static int Eisa_slot;
-
-static int iindx;
-static char rirqs[IP2_MAX_BOARDS];
-static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0};
-
-/* Note: Add compiled in defaults to these arrays, not to the structure
-       in ip2.h any longer.  That structure WILL get overridden
-       by these values, or command line values, or insmod values!!!  =mhw=
-*/
-static int io[IP2_MAX_BOARDS];
-static int irq[IP2_MAX_BOARDS] = { -1, -1, -1, -1 };
-
-MODULE_AUTHOR("Doug McNash");
-MODULE_DESCRIPTION("Computone IntelliPort Plus Driver");
-module_param_array(irq, int, NULL, 0);
-MODULE_PARM_DESC(irq, "Interrupts for IntelliPort Cards");
-module_param_array(io, int, NULL, 0);
-MODULE_PARM_DESC(io, "I/O ports for IntelliPort Cards");
-module_param(poll_only, bool, 0);
-MODULE_PARM_DESC(poll_only, "Do not use card interrupts");
-module_param_string(ip2, cmd, MAX_CMD_STR, 0);
-MODULE_PARM_DESC(ip2, "Contains module parameter passed with 'ip2='");
-
-/* for sysfs class support */
-static struct class *ip2_class;
-
-/* Some functions to keep track of what irqs we have */
-
-static int __init is_valid_irq(int irq)
-{
-       int *i = Valid_Irqs;
-       
-       while (*i != 0 && *i != irq)
-               i++;
-
-       return *i;
-}
-
-static void __init mark_requested_irq(char irq)
-{
-       rirqs[iindx++] = irq;
-}
-
-static int __exit clear_requested_irq(char irq)
-{
-       int i;
-       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
-               if (rirqs[i] == irq) {
-                       rirqs[i] = 0;
-                       return 1;
-               }
-       }
-       return 0;
-}
-
-static int have_requested_irq(char irq)
-{
-       /* array init to zeros so 0 irq will not be requested as a side
-        * effect */
-       int i;
-       for (i = 0; i < IP2_MAX_BOARDS; ++i)
-               if (rirqs[i] == irq)
-                       return 1;
-       return 0;
-}
-
-/******************************************************************************/
-/* Function:   cleanup_module()                                               */
-/* Parameters: None                                                           */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/* This is a required entry point for an installable module. It has to return */
-/* the device and the driver to a passive state. It should not be necessary   */
-/* to reset the board fully, especially as the loadware is downloaded         */
-/* externally rather than in the driver. We just want to disable the board    */
-/* and clear the loadware to a reset state. To allow this there has to be a   */
-/* way to detect whether the board has the loadware running at init time to   */
-/* handle subsequent installations of the driver. All memory allocated by the */
-/* driver should be returned since it may be unloaded from memory.            */
-/******************************************************************************/
-static void __exit ip2_cleanup_module(void)
-{
-       int err;
-       int i;
-
-       del_timer_sync(&PollTimer);
-
-       /* Reset the boards we have. */
-       for (i = 0; i < IP2_MAX_BOARDS; i++)
-               if (i2BoardPtrTable[i])
-                       iiReset(i2BoardPtrTable[i]);
-
-       /* The following is done at most once, if any boards were installed. */
-       for (i = 0; i < IP2_MAX_BOARDS; i++) {
-               if (i2BoardPtrTable[i]) {
-                       iiResetDelay(i2BoardPtrTable[i]);
-                       /* free io addresses and Tibet */
-                       release_region(ip2config.addr[i], 8);
-                       device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, 4 * i));
-                       device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR,
-                                               4 * i + 1));
-               }
-               /* Disable and remove interrupt handler. */
-               if (ip2config.irq[i] > 0 &&
-                               have_requested_irq(ip2config.irq[i])) {
-                       free_irq(ip2config.irq[i], (void *)&pcName);
-                       clear_requested_irq(ip2config.irq[i]);
-               }
-       }
-       class_destroy(ip2_class);
-       err = tty_unregister_driver(ip2_tty_driver);
-       if (err)
-               printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n",
-                               err);
-       put_tty_driver(ip2_tty_driver);
-       unregister_chrdev(IP2_IPL_MAJOR, pcIpl);
-       remove_proc_entry("ip2mem", NULL);
-
-       /* free memory */
-       for (i = 0; i < IP2_MAX_BOARDS; i++) {
-               void *pB;
-#ifdef CONFIG_PCI
-               if (ip2config.type[i] == PCI && ip2config.pci_dev[i]) {
-                       pci_disable_device(ip2config.pci_dev[i]);
-                       pci_dev_put(ip2config.pci_dev[i]);
-                       ip2config.pci_dev[i] = NULL;
-               }
-#endif
-               pB = i2BoardPtrTable[i];
-               if (pB != NULL) {
-                       kfree(pB);
-                       i2BoardPtrTable[i] = NULL;
-               }
-               if (DevTableMem[i] != NULL) {
-                       kfree(DevTableMem[i]);
-                       DevTableMem[i] = NULL;
-               }
-       }
-}
-module_exit(ip2_cleanup_module);
-
-static const struct tty_operations ip2_ops = {
-       .open            = ip2_open,
-       .close           = ip2_close,
-       .write           = ip2_write,
-       .put_char        = ip2_putchar,
-       .flush_chars     = ip2_flush_chars,
-       .write_room      = ip2_write_room,
-       .chars_in_buffer = ip2_chars_in_buf,
-       .flush_buffer    = ip2_flush_buffer,
-       .ioctl           = ip2_ioctl,
-       .throttle        = ip2_throttle,
-       .unthrottle      = ip2_unthrottle,
-       .set_termios     = ip2_set_termios,
-       .set_ldisc       = ip2_set_line_discipline,
-       .stop            = ip2_stop,
-       .start           = ip2_start,
-       .hangup          = ip2_hangup,
-       .tiocmget        = ip2_tiocmget,
-       .tiocmset        = ip2_tiocmset,
-       .get_icount      = ip2_get_icount,
-       .proc_fops       = &ip2_proc_fops,
-};
-
-/******************************************************************************/
-/* Function:   ip2_loadmain()                                                 */
-/* Parameters: irq, io from command line of insmod et. al.                    */
-/*             pointer to fip firmware and firmware size for boards          */
-/* Returns:    Success (0)                                                    */
-/*                                                                            */
-/* Description:                                                               */
-/* This was the required entry point for all drivers (now in ip2.c)           */
-/* It performs all                                                            */
-/* initialisation of the devices and driver structures, and registers itself  */
-/* with the relevant kernel modules.                                          */
-/******************************************************************************/
-/* IRQF_DISABLED - if set blocks all interrupts else only this line */
-/* IRQF_SHARED    - for shared irq PCI or maybe EISA only */
-/* SA_RANDOM   - can be source for cert. random number generators */
-#define IP2_SA_FLAGS   0
-
-
-static const struct firmware *ip2_request_firmware(void)
-{
-       struct platform_device *pdev;
-       const struct firmware *fw;
-
-       pdev = platform_device_register_simple("ip2", 0, NULL, 0);
-       if (IS_ERR(pdev)) {
-               printk(KERN_ERR "Failed to register platform device for ip2\n");
-               return NULL;
-       }
-       if (request_firmware(&fw, "intelliport2.bin", &pdev->dev)) {
-               printk(KERN_ERR "Failed to load firmware 'intelliport2.bin'\n");
-               fw = NULL;
-       }
-       platform_device_unregister(pdev);
-       return fw;
-}
-
-/******************************************************************************
- *     ip2_setup:
- *             str: kernel command line string
- *
- *     Can't autoprobe the boards so user must specify configuration on
- *     kernel command line.  Sane people build it modular but the others
- *     come here.
- *
- *     Alternating pairs of io,irq for up to 4 boards.
- *             ip2=io0,irq0,io1,irq1,io2,irq2,io3,irq3
- *
- *             io=0 => No board
- *             io=1 => PCI
- *             io=2 => EISA
- *             else => ISA I/O address
- *
- *             irq=0 or invalid for ISA will revert to polling mode
- *
- *             Any value = -1, do not overwrite compiled in value.
- *
- ******************************************************************************/
-static int __init ip2_setup(char *str)
-{
-       int j, ints[10];        /* 4 boards, 2 parameters + 2 */
-       unsigned int i;
-
-       str = get_options(str, ARRAY_SIZE(ints), ints);
-
-       for (i = 0, j = 1; i < 4; i++) {
-               if (j > ints[0])
-                       break;
-               if (ints[j] >= 0)
-                       io[i] = ints[j];
-               j++;
-               if (j > ints[0])
-                       break;
-               if (ints[j] >= 0)
-                       irq[i] = ints[j];
-               j++;
-       }
-       return 1;
-}
-__setup("ip2=", ip2_setup);
-
-static int __init ip2_loadmain(void)
-{
-       int i, j, box;
-       int err = 0;
-       i2eBordStrPtr pB = NULL;
-       int rc = -1;
-       const struct firmware *fw = NULL;
-       char *str;
-
-       str = cmd;
-
-       if (poll_only) {
-               /* Hard lock the interrupts to zero */
-               irq[0] = irq[1] = irq[2] = irq[3] = poll_only = 0;
-       }
-
-       /* Check module parameter with 'ip2=' has been passed or not */
-       if (!poll_only && (!strncmp(str, "ip2=", 4)))
-               ip2_setup(str);
-
-       ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0);
-
-       /* process command line arguments to modprobe or
-               insmod i.e. iop & irqp */
-       /* irqp and iop should ALWAYS be specified now...  But we check
-               them individually just to be sure, anyways... */
-       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
-               ip2config.addr[i] = io[i];
-               if (irq[i] >= 0)
-                       ip2config.irq[i] = irq[i];
-               else
-                       ip2config.irq[i] = 0;
-       /* This is a little bit of a hack.  If poll_only=1 on command
-          line back in ip2.c OR all IRQs on all specified boards are
-          explicitly set to 0, then drop to poll only mode and override
-          PCI or EISA interrupts.  This superceeds the old hack of
-          triggering if all interrupts were zero (like da default).
-          Still a hack but less prone to random acts of terrorism.
-
-          What we really should do, now that the IRQ default is set
-          to -1, is to use 0 as a hard coded, do not probe.
-
-               /\/\|=mhw=|\/\/
-       */
-               poll_only |= irq[i];
-       }
-       poll_only = !poll_only;
-
-       /* Announce our presence */
-       printk(KERN_INFO "%s version %s\n", pcName, pcVersion);
-
-       ip2_tty_driver = alloc_tty_driver(IP2_MAX_PORTS);
-       if (!ip2_tty_driver)
-               return -ENOMEM;
-
-       /* Initialise all the boards we can find (up to the maximum). */
-       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
-               switch (ip2config.addr[i]) {
-               case 0: /* skip this slot even if card is present */
-                       break;
-               default: /* ISA */
-                  /* ISA address must be specified */
-                       if (ip2config.addr[i] < 0x100 ||
-                                       ip2config.addr[i] > 0x3f8) {
-                               printk(KERN_ERR "IP2: Bad ISA board %d "
-                                               "address %x\n", i,
-                                               ip2config.addr[i]);
-                               ip2config.addr[i] = 0;
-                               break;
-                       }
-                       ip2config.type[i] = ISA;
-
-                       /* Check for valid irq argument, set for polling if
-                        * invalid */
-                       if (ip2config.irq[i] &&
-                                       !is_valid_irq(ip2config.irq[i])) {
-                               printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n",
-                                               ip2config.irq[i]);
-                               /* 0 is polling and is valid in that sense */
-                               ip2config.irq[i] = 0;
-                       }
-                       break;
-               case PCI:
-#ifdef CONFIG_PCI
-               {
-                       struct pci_dev *pdev = NULL;
-                       u32 addr;
-                       int status;
-
-                       pdev = pci_get_device(PCI_VENDOR_ID_COMPUTONE,
-                                       PCI_DEVICE_ID_COMPUTONE_IP2EX, pdev);
-                       if (pdev == NULL) {
-                               ip2config.addr[i] = 0;
-                               printk(KERN_ERR "IP2: PCI board %d not "
-                                               "found\n", i);
-                               break;
-                       }
-
-                       if (pci_enable_device(pdev)) {
-                               dev_err(&pdev->dev, "can't enable device\n");
-                               goto out;
-                       }
-                       ip2config.type[i] = PCI;
-                       ip2config.pci_dev[i] = pci_dev_get(pdev);
-                       status = pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1,
-                                       &addr);
-                       if (addr & 1)
-                               ip2config.addr[i] = (USHORT)(addr & 0xfffe);
-                       else
-                               dev_err(&pdev->dev, "I/O address error\n");
-
-                       ip2config.irq[i] = pdev->irq;
-out:
-                       pci_dev_put(pdev);
-               }
-#else
-                       printk(KERN_ERR "IP2: PCI card specified but PCI "
-                                       "support not enabled.\n");
-                       printk(KERN_ERR "IP2: Recompile kernel with CONFIG_PCI "
-                                       "defined!\n");
-#endif /* CONFIG_PCI */
-                       break;
-               case EISA:
-                       ip2config.addr[i] = find_eisa_board(Eisa_slot + 1);
-                       if (ip2config.addr[i] != 0) {
-                               /* Eisa_irq set as side effect, boo */
-                               ip2config.type[i] = EISA;
-                       } 
-                       ip2config.irq[i] = Eisa_irq;
-                       break;
-               }       /* switch */
-       }       /* for */
-
-       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
-               if (ip2config.addr[i]) {
-                       pB = kzalloc(sizeof(i2eBordStr), GFP_KERNEL);
-                       if (pB) {
-                               i2BoardPtrTable[i] = pB;
-                               iiSetAddress(pB, ip2config.addr[i],
-                                               ii2DelayTimer);
-                               iiReset(pB);
-                       } else
-                               printk(KERN_ERR "IP2: board memory allocation "
-                                               "error\n");
-               }
-       }
-       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
-               pB = i2BoardPtrTable[i];
-               if (pB != NULL) {
-                       iiResetDelay(pB);
-                       break;
-               }
-       }
-       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
-               /* We don't want to request the firmware unless we have at
-                  least one board */
-               if (i2BoardPtrTable[i] != NULL) {
-                       if (!fw)
-                               fw = ip2_request_firmware();
-                       if (!fw)
-                               break;
-                       ip2_init_board(i, fw);
-               }
-       }
-       if (fw)
-               release_firmware(fw);
-
-       ip2trace(ITRC_NO_PORT, ITRC_INIT, 2, 0);
-
-       ip2_tty_driver->owner               = THIS_MODULE;
-       ip2_tty_driver->name                 = "ttyF";
-       ip2_tty_driver->driver_name          = pcDriver_name;
-       ip2_tty_driver->major                = IP2_TTY_MAJOR;
-       ip2_tty_driver->minor_start          = 0;
-       ip2_tty_driver->type                 = TTY_DRIVER_TYPE_SERIAL;
-       ip2_tty_driver->subtype              = SERIAL_TYPE_NORMAL;
-       ip2_tty_driver->init_termios         = tty_std_termios;
-       ip2_tty_driver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL;
-       ip2_tty_driver->flags                = TTY_DRIVER_REAL_RAW |
-               TTY_DRIVER_DYNAMIC_DEV;
-       tty_set_operations(ip2_tty_driver, &ip2_ops);
-
-       ip2trace(ITRC_NO_PORT, ITRC_INIT, 3, 0);
-
-       err = tty_register_driver(ip2_tty_driver);
-       if (err) {
-               printk(KERN_ERR "IP2: failed to register tty driver\n");
-               put_tty_driver(ip2_tty_driver);
-               return err; /* leaking resources */
-       }
-
-       err = register_chrdev(IP2_IPL_MAJOR, pcIpl, &ip2_ipl);
-       if (err) {
-               printk(KERN_ERR "IP2: failed to register IPL device (%d)\n",
-                               err);
-       } else {
-               /* create the sysfs class */
-               ip2_class = class_create(THIS_MODULE, "ip2");
-               if (IS_ERR(ip2_class)) {
-                       err = PTR_ERR(ip2_class);
-                       goto out_chrdev;        
-               }
-       }
-       /* Register the read_procmem thing */
-       if (!proc_create("ip2mem",0,NULL,&ip2mem_proc_fops)) {
-               printk(KERN_ERR "IP2: failed to register read_procmem\n");
-               return -EIO; /* leaking resources */
-       }
-
-       ip2trace(ITRC_NO_PORT, ITRC_INIT, 4, 0);
-       /* Register the interrupt handler or poll handler, depending upon the
-        * specified interrupt.
-        */
-
-       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
-               if (ip2config.addr[i] == 0)
-                       continue;
-
-               pB = i2BoardPtrTable[i];
-               if (pB != NULL) {
-                       device_create(ip2_class, NULL,
-                                     MKDEV(IP2_IPL_MAJOR, 4 * i),
-                                     NULL, "ipl%d", i);
-                       device_create(ip2_class, NULL,
-                                     MKDEV(IP2_IPL_MAJOR, 4 * i + 1),
-                                     NULL, "stat%d", i);
-
-                       for (box = 0; box < ABS_MAX_BOXES; box++)
-                               for (j = 0; j < ABS_BIGGEST_BOX; j++)
-                                       if (pB->i2eChannelMap[box] & (1 << j))
-                                               tty_register_device(
-                                                       ip2_tty_driver,
-                                                       j + ABS_BIGGEST_BOX *
-                                                       (box+i*ABS_MAX_BOXES),
-                                                       NULL);
-               }
-
-               if (poll_only) {
-                       /* Poll only forces driver to only use polling and
-                          to ignore the probed PCI or EISA interrupts. */
-                       ip2config.irq[i] = CIR_POLL;
-               }
-               if (ip2config.irq[i] == CIR_POLL) {
-retry:
-                       if (!timer_pending(&PollTimer)) {
-                               mod_timer(&PollTimer, POLL_TIMEOUT);
-                               printk(KERN_INFO "IP2: polling\n");
-                       }
-               } else {
-                       if (have_requested_irq(ip2config.irq[i]))
-                               continue;
-                       rc = request_irq(ip2config.irq[i], ip2_interrupt,
-                               IP2_SA_FLAGS |
-                               (ip2config.type[i] == PCI ? IRQF_SHARED : 0),
-                               pcName, i2BoardPtrTable[i]);
-                       if (rc) {
-                               printk(KERN_ERR "IP2: request_irq failed: "
-                                               "error %d\n", rc);
-                               ip2config.irq[i] = CIR_POLL;
-                               printk(KERN_INFO "IP2: Polling %ld/sec.\n",
-                                               (POLL_TIMEOUT - jiffies));
-                               goto retry;
-                       }
-                       mark_requested_irq(ip2config.irq[i]);
-                       /* Initialise the interrupt handler bottom half
-                        * (aka slih). */
-               }
-       }
-
-       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
-               if (i2BoardPtrTable[i]) {
-                       /* set and enable board interrupt */
-                       set_irq(i, ip2config.irq[i]);
-               }
-       }
-
-       ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0);
-
-       return 0;
-
-out_chrdev:
-       unregister_chrdev(IP2_IPL_MAJOR, "ip2");
-       /* unregister and put tty here */
-       return err;
-}
-module_init(ip2_loadmain);
-
-/******************************************************************************/
-/* Function:   ip2_init_board()                                               */
-/* Parameters: Index of board in configuration structure                      */
-/* Returns:    Success (0)                                                    */
-/*                                                                            */
-/* Description:                                                               */
-/* This function initializes the specified board. The loadware is copied to   */
-/* the board, the channel structures are initialized, and the board details   */
-/* are reported on the console.                                               */
-/******************************************************************************/
-static void
-ip2_init_board(int boardnum, const struct firmware *fw)
-{
-       int i;
-       int nports = 0, nboxes = 0;
-       i2ChanStrPtr pCh;
-       i2eBordStrPtr pB = i2BoardPtrTable[boardnum];
-
-       if ( !iiInitialize ( pB ) ) {
-               printk ( KERN_ERR "IP2: Failed to initialize board at 0x%x, error %d\n",
-                        pB->i2eBase, pB->i2eError );
-               goto err_initialize;
-       }
-       printk(KERN_INFO "IP2: Board %d: addr=0x%x irq=%d\n", boardnum + 1,
-              ip2config.addr[boardnum], ip2config.irq[boardnum] );
-
-       if (!request_region( ip2config.addr[boardnum], 8, pcName )) {
-               printk(KERN_ERR "IP2: bad addr=0x%x\n", ip2config.addr[boardnum]);
-               goto err_initialize;
-       }
-
-       if ( iiDownloadAll ( pB, (loadHdrStrPtr)fw->data, 1, fw->size )
-           != II_DOWN_GOOD ) {
-               printk ( KERN_ERR "IP2: failed to download loadware\n" );
-               goto err_release_region;
-       } else {
-               printk ( KERN_INFO "IP2: fv=%d.%d.%d lv=%d.%d.%d\n",
-                        pB->i2ePom.e.porVersion,
-                        pB->i2ePom.e.porRevision,
-                        pB->i2ePom.e.porSubRev, pB->i2eLVersion,
-                        pB->i2eLRevision, pB->i2eLSub );
-       }
-
-       switch ( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) {
-
-       default:
-               printk( KERN_ERR "IP2: Unknown board type, ID = %x\n",
-                               pB->i2ePom.e.porID );
-               nports = 0;
-               goto err_release_region;
-               break;
-
-       case POR_ID_II_4: /* IntelliPort-II, ISA-4 (4xRJ45) */
-               printk ( KERN_INFO "IP2: ISA-4\n" );
-               nports = 4;
-               break;
-
-       case POR_ID_II_8: /* IntelliPort-II, 8-port using standard brick. */
-               printk ( KERN_INFO "IP2: ISA-8 std\n" );
-               nports = 8;
-               break;
-
-       case POR_ID_II_8R: /* IntelliPort-II, 8-port using RJ11's (no CTS) */
-               printk ( KERN_INFO "IP2: ISA-8 RJ11\n" );
-               nports = 8;
-               break;
-
-       case POR_ID_FIIEX: /* IntelliPort IIEX */
-       {
-               int portnum = IP2_PORTS_PER_BOARD * boardnum;
-               int            box;
-
-               for( box = 0; box < ABS_MAX_BOXES; ++box ) {
-                       if ( pB->i2eChannelMap[box] != 0 ) {
-                               ++nboxes;
-                       }
-                       for( i = 0; i < ABS_BIGGEST_BOX; ++i ) {
-                               if ( pB->i2eChannelMap[box] & 1<< i ) {
-                                       ++nports;
-                               }
-                       }
-               }
-               DevTableMem[boardnum] = pCh =
-                       kmalloc( sizeof(i2ChanStr) * nports, GFP_KERNEL );
-               if ( !pCh ) {
-                       printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n");
-                       goto err_release_region;
-               }
-               if ( !i2InitChannels( pB, nports, pCh ) ) {
-                       printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError);
-                       kfree ( pCh );
-                       goto err_release_region;
-               }
-               pB->i2eChannelPtr = &DevTable[portnum];
-               pB->i2eChannelCnt = ABS_MOST_PORTS;
-
-               for( box = 0; box < ABS_MAX_BOXES; ++box, portnum += ABS_BIGGEST_BOX ) {
-                       for( i = 0; i < ABS_BIGGEST_BOX; ++i ) {
-                               if ( pB->i2eChannelMap[box] & (1 << i) ) {
-                                       DevTable[portnum + i] = pCh;
-                                       pCh->port_index = portnum + i;
-                                       pCh++;
-                               }
-                       }
-               }
-               printk(KERN_INFO "IP2: EX box=%d ports=%d %d bit\n",
-                       nboxes, nports, pB->i2eDataWidth16 ? 16 : 8 );
-               }
-               goto ex_exit;
-       }
-       DevTableMem[boardnum] = pCh =
-               kmalloc ( sizeof (i2ChanStr) * nports, GFP_KERNEL );
-       if ( !pCh ) {
-               printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n");
-               goto err_release_region;
-       }
-       pB->i2eChannelPtr = pCh;
-       pB->i2eChannelCnt = nports;
-       if ( !i2InitChannels( pB, nports, pCh ) ) {
-               printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError);
-               kfree ( pCh );
-               goto err_release_region;
-       }
-       pB->i2eChannelPtr = &DevTable[IP2_PORTS_PER_BOARD * boardnum];
-
-       for( i = 0; i < pB->i2eChannelCnt; ++i ) {
-               DevTable[IP2_PORTS_PER_BOARD * boardnum + i] = pCh;
-               pCh->port_index = (IP2_PORTS_PER_BOARD * boardnum) + i;
-               pCh++;
-       }
-ex_exit:
-       INIT_WORK(&pB->tqueue_interrupt, ip2_interrupt_bh);
-       return;
-
-err_release_region:
-       release_region(ip2config.addr[boardnum], 8);
-err_initialize:
-       kfree ( pB );
-       i2BoardPtrTable[boardnum] = NULL;
-       return;
-}
-
-/******************************************************************************/
-/* Function:   find_eisa_board ( int start_slot )                             */
-/* Parameters: First slot to check                                            */
-/* Returns:    Address of EISA IntelliPort II controller                      */
-/*                                                                            */
-/* Description:                                                               */
-/* This function searches for an EISA IntelliPort controller, starting        */
-/* from the specified slot number. If the motherboard is not identified as an */
-/* EISA motherboard, or no valid board ID is selected it returns 0. Otherwise */
-/* it returns the base address of the controller.                             */
-/******************************************************************************/
-static unsigned short
-find_eisa_board( int start_slot )
-{
-       int i, j;
-       unsigned int idm = 0;
-       unsigned int idp = 0;
-       unsigned int base = 0;
-       unsigned int value;
-       int setup_address;
-       int setup_irq;
-       int ismine = 0;
-
-       /*
-        * First a check for an EISA motherboard, which we do by comparing the
-        * EISA ID registers for the system board and the first couple of slots.
-        * No slot ID should match the system board ID, but on an ISA or PCI
-        * machine the odds are that an empty bus will return similar values for
-        * each slot.
-        */
-       i = 0x0c80;
-       value = (inb(i) << 24) + (inb(i+1) << 16) + (inb(i+2) << 8) + inb(i+3);
-       for( i = 0x1c80; i <= 0x4c80; i += 0x1000 ) {
-               j = (inb(i)<<24)+(inb(i+1)<<16)+(inb(i+2)<<8)+inb(i+3);
-               if ( value == j )
-                       return 0;
-       }
-
-       /*
-        * OK, so we are inclined to believe that this is an EISA machine. Find
-        * an IntelliPort controller.
-        */
-       for( i = start_slot; i < 16; i++ ) {
-               base = i << 12;
-               idm = (inb(base + 0xc80) << 8) | (inb(base + 0xc81) & 0xff);
-               idp = (inb(base + 0xc82) << 8) | (inb(base + 0xc83) & 0xff);
-               ismine = 0;
-               if ( idm == 0x0e8e ) {
-                       if ( idp == 0x0281 || idp == 0x0218 ) {
-                               ismine = 1;
-                       } else if ( idp == 0x0282 || idp == 0x0283 ) {
-                               ismine = 3;     /* Can do edge-trigger */
-                       }
-                       if ( ismine ) {
-                               Eisa_slot = i;
-                               break;
-                       }
-               }
-       }
-       if ( !ismine )
-               return 0;
-
-       /* It's some sort of EISA card, but at what address is it configured? */
-
-       setup_address = base + 0xc88;
-       value = inb(base + 0xc86);
-       setup_irq = (value & 8) ? Valid_Irqs[value & 7] : 0;
-
-       if ( (ismine & 2) && !(value & 0x10) ) {
-               ismine = 1;     /* Could be edging, but not */
-       }
-
-       if ( Eisa_irq == 0 ) {
-               Eisa_irq = setup_irq;
-       } else if ( Eisa_irq != setup_irq ) {
-               printk ( KERN_ERR "IP2: EISA irq mismatch between EISA controllers\n" );
-       }
-
-#ifdef IP2DEBUG_INIT
-printk(KERN_DEBUG "Computone EISA board in slot %d, I.D. 0x%x%x, Address 0x%x",
-              base >> 12, idm, idp, setup_address);
-       if ( Eisa_irq ) {
-               printk(KERN_DEBUG ", Interrupt %d %s\n",
-                      setup_irq, (ismine & 2) ? "(edge)" : "(level)");
-       } else {
-               printk(KERN_DEBUG ", (polled)\n");
-       }
-#endif
-       return setup_address;
-}
-
-/******************************************************************************/
-/* Function:   set_irq()                                                      */
-/* Parameters: index to board in board table                                  */
-/*             IRQ to use                                                     */
-/* Returns:    Success (0)                                                    */
-/*                                                                            */
-/* Description:                                                               */
-/******************************************************************************/
-static void
-set_irq( int boardnum, int boardIrq )
-{
-       unsigned char tempCommand[16];
-       i2eBordStrPtr pB = i2BoardPtrTable[boardnum];
-       unsigned long flags;
-
-       /*
-        * Notify the boards they may generate interrupts. This is done by
-        * sending an in-line command to channel 0 on each board. This is why
-        * the channels have to be defined already. For each board, if the
-        * interrupt has never been defined, we must do so NOW, directly, since
-        * board will not send flow control or even give an interrupt until this
-        * is done.  If polling we must send 0 as the interrupt parameter.
-        */
-
-       // We will get an interrupt here at the end of this function
-
-       iiDisableMailIrq(pB);
-
-       /* We build up the entire packet header. */
-       CHANNEL_OF(tempCommand) = 0;
-       PTYPE_OF(tempCommand) = PTYPE_INLINE;
-       CMD_COUNT_OF(tempCommand) = 2;
-       (CMD_OF(tempCommand))[0] = CMDVALUE_IRQ;
-       (CMD_OF(tempCommand))[1] = boardIrq;
-       /*
-        * Write to FIFO; don't bother to adjust fifo capacity for this, since
-        * board will respond almost immediately after SendMail hit.
-        */
-       write_lock_irqsave(&pB->write_fifo_spinlock, flags);
-       iiWriteBuf(pB, tempCommand, 4);
-       write_unlock_irqrestore(&pB->write_fifo_spinlock, flags);
-       pB->i2eUsingIrq = boardIrq;
-       pB->i2eOutMailWaiting |= MB_OUT_STUFFED;
-
-       /* Need to update number of boards before you enable mailbox int */
-       ++i2nBoards;
-
-       CHANNEL_OF(tempCommand) = 0;
-       PTYPE_OF(tempCommand) = PTYPE_BYPASS;
-       CMD_COUNT_OF(tempCommand) = 6;
-       (CMD_OF(tempCommand))[0] = 88;  // SILO
-       (CMD_OF(tempCommand))[1] = 64;  // chars
-       (CMD_OF(tempCommand))[2] = 32;  // ms
-
-       (CMD_OF(tempCommand))[3] = 28;  // MAX_BLOCK
-       (CMD_OF(tempCommand))[4] = 64;  // chars
-
-       (CMD_OF(tempCommand))[5] = 87;  // HW_TEST
-       write_lock_irqsave(&pB->write_fifo_spinlock, flags);
-       iiWriteBuf(pB, tempCommand, 8);
-       write_unlock_irqrestore(&pB->write_fifo_spinlock, flags);
-
-       CHANNEL_OF(tempCommand) = 0;
-       PTYPE_OF(tempCommand) = PTYPE_BYPASS;
-       CMD_COUNT_OF(tempCommand) = 1;
-       (CMD_OF(tempCommand))[0] = 84;  /* get BOX_IDS */
-       iiWriteBuf(pB, tempCommand, 3);
-
-#ifdef XXX
-       // enable heartbeat for test porpoises
-       CHANNEL_OF(tempCommand) = 0;
-       PTYPE_OF(tempCommand) = PTYPE_BYPASS;
-       CMD_COUNT_OF(tempCommand) = 2;
-       (CMD_OF(tempCommand))[0] = 44;  /* get ping */
-       (CMD_OF(tempCommand))[1] = 200; /* 200 ms */
-       write_lock_irqsave(&pB->write_fifo_spinlock, flags);
-       iiWriteBuf(pB, tempCommand, 4);
-       write_unlock_irqrestore(&pB->write_fifo_spinlock, flags);
-#endif
-
-       iiEnableMailIrq(pB);
-       iiSendPendingMail(pB);
-}
-
-/******************************************************************************/
-/* Interrupt Handler Section                                                  */
-/******************************************************************************/
-
-static inline void
-service_all_boards(void)
-{
-       int i;
-       i2eBordStrPtr  pB;
-
-       /* Service every board on the list */
-       for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
-               pB = i2BoardPtrTable[i];
-               if ( pB ) {
-                       i2ServiceBoard( pB );
-               }
-       }
-}
-
-
-/******************************************************************************/
-/* Function:   ip2_interrupt_bh(work)                                         */
-/* Parameters: work - pointer to the board structure                          */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/*     Service the board in a bottom half interrupt handler and then         */
-/*     reenable the board's interrupts if it has an IRQ number               */
-/*                                                                            */
-/******************************************************************************/
-static void
-ip2_interrupt_bh(struct work_struct *work)
-{
-       i2eBordStrPtr pB = container_of(work, i2eBordStr, tqueue_interrupt);
-//     pB better well be set or we have a problem!  We can only get
-//     here from the IMMEDIATE queue.  Here, we process the boards.
-//     Checking pB doesn't cost much and it saves us from the sanity checkers.
-
-       bh_counter++; 
-
-       if ( pB ) {
-               i2ServiceBoard( pB );
-               if( pB->i2eUsingIrq ) {
-//                     Re-enable his interrupts
-                       iiEnableMailIrq(pB);
-               }
-       }
-}
-
-
-/******************************************************************************/
-/* Function:   ip2_interrupt(int irq, void *dev_id)    */
-/* Parameters: irq - interrupt number                                         */
-/*             pointer to optional device ID structure                        */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*     Our task here is simply to identify each board which needs servicing. */
-/*     If we are queuing then, queue it to be serviced, and disable its irq  */
-/*     mask otherwise process the board directly.                            */
-/*                                                                            */
-/*     We could queue by IRQ but that just complicates things on both ends   */
-/*     with very little gain in performance (how many instructions does      */
-/*     it take to iterate on the immediate queue).                           */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static void
-ip2_irq_work(i2eBordStrPtr pB)
-{
-#ifdef USE_IQI
-       if (NO_MAIL_HERE != ( pB->i2eStartMail = iiGetMail(pB))) {
-//             Disable his interrupt (will be enabled when serviced)
-//             This is mostly to protect from reentrancy.
-               iiDisableMailIrq(pB);
-
-//             Park the board on the immediate queue for processing.
-               schedule_work(&pB->tqueue_interrupt);
-
-//             Make sure the immediate queue is flagged to fire.
-       }
-#else
-
-//     We are using immediate servicing here.  This sucks and can
-//     cause all sorts of havoc with ppp and others.  The failsafe
-//     check on iiSendPendingMail could also throw a hairball.
-
-       i2ServiceBoard( pB );
-
-#endif /* USE_IQI */
-}
-
-static void
-ip2_polled_interrupt(void)
-{
-       int i;
-       i2eBordStrPtr  pB;
-
-       ip2trace(ITRC_NO_PORT, ITRC_INTR, 99, 1, 0);
-
-       /* Service just the boards on the list using this irq */
-       for( i = 0; i < i2nBoards; ++i ) {
-               pB = i2BoardPtrTable[i];
-
-//             Only process those boards which match our IRQ.
-//                     IRQ = 0 for polled boards, we won't poll "IRQ" boards
-
-               if (pB && pB->i2eUsingIrq == 0)
-                       ip2_irq_work(pB);
-       }
-
-       ++irq_counter;
-
-       ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 );
-}
-
-static irqreturn_t
-ip2_interrupt(int irq, void *dev_id)
-{
-       i2eBordStrPtr pB = dev_id;
-
-       ip2trace (ITRC_NO_PORT, ITRC_INTR, 99, 1, pB->i2eUsingIrq );
-
-       ip2_irq_work(pB);
-
-       ++irq_counter;
-
-       ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 );
-       return IRQ_HANDLED;
-}
-
-/******************************************************************************/
-/* Function:   ip2_poll(unsigned long arg)                                    */
-/* Parameters: ?                                                              */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/* This function calls the library routine i2ServiceBoard for each board in   */
-/* the board table. This is used instead of the interrupt routine when polled */
-/* mode is specified.                                                         */
-/******************************************************************************/
-static void
-ip2_poll(unsigned long arg)
-{
-       ip2trace (ITRC_NO_PORT, ITRC_INTR, 100, 0 );
-
-       // Just polled boards, IRQ = 0 will hit all non-interrupt boards.
-       // It will NOT poll boards handled by hard interrupts.
-       // The issue of queued BH interrupts is handled in ip2_interrupt().
-       ip2_polled_interrupt();
-
-       mod_timer(&PollTimer, POLL_TIMEOUT);
-
-       ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 );
-}
-
-static void do_input(struct work_struct *work)
-{
-       i2ChanStrPtr pCh = container_of(work, i2ChanStr, tqueue_input);
-       unsigned long flags;
-
-       ip2trace(CHANN, ITRC_INPUT, 21, 0 );
-
-       // Data input
-       if ( pCh->pTTY != NULL ) {
-               read_lock_irqsave(&pCh->Ibuf_spinlock, flags);
-               if (!pCh->throttled && (pCh->Ibuf_stuff != pCh->Ibuf_strip)) {
-                       read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
-                       i2Input( pCh );
-               } else
-                       read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
-       } else {
-               ip2trace(CHANN, ITRC_INPUT, 22, 0 );
-
-               i2InputFlush( pCh );
-       }
-}
-
-// code duplicated from n_tty (ldisc)
-static inline void  isig(int sig, struct tty_struct *tty, int flush)
-{
-       /* FIXME: This is completely bogus */
-       if (tty->pgrp)
-               kill_pgrp(tty->pgrp, sig, 1);
-       if (flush || !L_NOFLSH(tty)) {
-               if ( tty->ldisc->ops->flush_buffer )  
-                       tty->ldisc->ops->flush_buffer(tty);
-               i2InputFlush( tty->driver_data );
-       }
-}
-
-static void do_status(struct work_struct *work)
-{
-       i2ChanStrPtr pCh = container_of(work, i2ChanStr, tqueue_status);
-       int status;
-
-       status =  i2GetStatus( pCh, (I2_BRK|I2_PAR|I2_FRA|I2_OVR) );
-
-       ip2trace (CHANN, ITRC_STATUS, 21, 1, status );
-
-       if (pCh->pTTY && (status & (I2_BRK|I2_PAR|I2_FRA|I2_OVR)) ) {
-               if ( (status & I2_BRK) ) {
-                       // code duplicated from n_tty (ldisc)
-                       if (I_IGNBRK(pCh->pTTY))
-                               goto skip_this;
-                       if (I_BRKINT(pCh->pTTY)) {
-                               isig(SIGINT, pCh->pTTY, 1);
-                               goto skip_this;
-                       }
-                       wake_up_interruptible(&pCh->pTTY->read_wait);
-               }
-#ifdef NEVER_HAPPENS_AS_SETUP_XXX
-       // and can't work because we don't know the_char
-       // as the_char is reported on a separate path
-       // The intelligent board does this stuff as setup
-       {
-       char brkf = TTY_NORMAL;
-       unsigned char brkc = '\0';
-       unsigned char tmp;
-               if ( (status & I2_BRK) ) {
-                       brkf = TTY_BREAK;
-                       brkc = '\0';
-               } 
-               else if (status & I2_PAR) {
-                       brkf = TTY_PARITY;
-                       brkc = the_char;
-               } else if (status & I2_FRA) {
-                       brkf = TTY_FRAME;
-                       brkc = the_char;
-               } else if (status & I2_OVR) {
-                       brkf = TTY_OVERRUN;
-                       brkc = the_char;
-               }
-               tmp = pCh->pTTY->real_raw;
-               pCh->pTTY->real_raw = 0;
-               pCh->pTTY->ldisc->ops.receive_buf( pCh->pTTY, &brkc, &brkf, 1 );
-               pCh->pTTY->real_raw = tmp;
-       }
-#endif /* NEVER_HAPPENS_AS_SETUP_XXX */
-       }
-skip_this:
-
-       if ( status & (I2_DDCD | I2_DDSR | I2_DCTS | I2_DRI) ) {
-               wake_up_interruptible(&pCh->delta_msr_wait);
-
-               if ( (pCh->flags & ASYNC_CHECK_CD) && (status & I2_DDCD) ) {
-                       if ( status & I2_DCD ) {
-                               if ( pCh->wopen ) {
-                                       wake_up_interruptible ( &pCh->open_wait );
-                               }
-                       } else {
-                               if (pCh->pTTY &&  (!(pCh->pTTY->termios->c_cflag & CLOCAL)) ) {
-                                       tty_hangup( pCh->pTTY );
-                               }
-                       }
-               }
-       }
-
-       ip2trace (CHANN, ITRC_STATUS, 26, 0 );
-}
-
-/******************************************************************************/
-/* Device Open/Close/Ioctl Entry Point Section                                */
-/******************************************************************************/
-
-/******************************************************************************/
-/* Function:   open_sanity_check()                                            */
-/* Parameters: Pointer to tty structure                                       */
-/*             Pointer to file structure                                      */
-/* Returns:    Success or failure                                             */
-/*                                                                            */
-/* Description:                                                               */
-/* Verifies the structure magic numbers and cross links.                      */
-/******************************************************************************/
-#ifdef IP2DEBUG_OPEN
-static void 
-open_sanity_check( i2ChanStrPtr pCh, i2eBordStrPtr pBrd )
-{
-       if ( pBrd->i2eValid != I2E_MAGIC ) {
-               printk(KERN_ERR "IP2: invalid board structure\n" );
-       } else if ( pBrd != pCh->pMyBord ) {
-               printk(KERN_ERR "IP2: board structure pointer mismatch (%p)\n",
-                        pCh->pMyBord );
-       } else if ( pBrd->i2eChannelCnt < pCh->port_index ) {
-               printk(KERN_ERR "IP2: bad device index (%d)\n", pCh->port_index );
-       } else if (&((i2ChanStrPtr)pBrd->i2eChannelPtr)[pCh->port_index] != pCh) {
-       } else {
-               printk(KERN_INFO "IP2: all pointers check out!\n" );
-       }
-}
-#endif
-
-
-/******************************************************************************/
-/* Function:   ip2_open()                                                     */
-/* Parameters: Pointer to tty structure                                       */
-/*             Pointer to file structure                                      */
-/* Returns:    Success or failure                                             */
-/*                                                                            */
-/* Description: (MANDATORY)                                                   */
-/* A successful device open has to run a gauntlet of checks before it         */
-/* completes. After some sanity checking and pointer setup, the function      */
-/* blocks until all conditions are satisfied. It then initialises the port to */
-/* the default characteristics and returns.                                   */
-/******************************************************************************/
-static int
-ip2_open( PTTY tty, struct file *pFile )
-{
-       wait_queue_t wait;
-       int rc = 0;
-       int do_clocal = 0;
-       i2ChanStrPtr  pCh = DevTable[tty->index];
-
-       ip2trace (tty->index, ITRC_OPEN, ITRC_ENTER, 0 );
-
-       if ( pCh == NULL ) {
-               return -ENODEV;
-       }
-       /* Setup pointer links in device and tty structures */
-       pCh->pTTY = tty;
-       tty->driver_data = pCh;
-
-#ifdef IP2DEBUG_OPEN
-       printk(KERN_DEBUG \
-                       "IP2:open(tty=%p,pFile=%p):dev=%s,ch=%d,idx=%d\n",
-              tty, pFile, tty->name, pCh->infl.hd.i2sChannel, pCh->port_index);
-       open_sanity_check ( pCh, pCh->pMyBord );
-#endif
-
-       i2QueueCommands(PTYPE_INLINE, pCh, 100, 3, CMD_DTRUP,CMD_RTSUP,CMD_DCD_REP);
-       pCh->dataSetOut |= (I2_DTR | I2_RTS);
-       serviceOutgoingFifo( pCh->pMyBord );
-
-       /* Block here until the port is ready (per serial and istallion) */
-       /*
-        * 1. If the port is in the middle of closing wait for the completion
-        *    and then return the appropriate error.
-        */
-       init_waitqueue_entry(&wait, current);
-       add_wait_queue(&pCh->close_wait, &wait);
-       set_current_state( TASK_INTERRUPTIBLE );
-
-       if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) {
-               if ( pCh->flags & ASYNC_CLOSING ) {
-                       tty_unlock();
-                       schedule();
-                       tty_lock();
-               }
-               if ( tty_hung_up_p(pFile) ) {
-                       set_current_state( TASK_RUNNING );
-                       remove_wait_queue(&pCh->close_wait, &wait);
-                       return( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS;
-               }
-       }
-       set_current_state( TASK_RUNNING );
-       remove_wait_queue(&pCh->close_wait, &wait);
-
-       /*
-        * 3. Handle a non-blocking open of a normal port.
-        */
-       if ( (pFile->f_flags & O_NONBLOCK) || (tty->flags & (1<<TTY_IO_ERROR) )) {
-               pCh->flags |= ASYNC_NORMAL_ACTIVE;
-               goto noblock;
-       }
-       /*
-        * 4. Now loop waiting for the port to be free and carrier present
-        *    (if required).
-        */
-       if ( tty->termios->c_cflag & CLOCAL )
-               do_clocal = 1;
-
-#ifdef IP2DEBUG_OPEN
-       printk(KERN_DEBUG "OpenBlock: do_clocal = %d\n", do_clocal);
-#endif
-
-       ++pCh->wopen;
-
-       init_waitqueue_entry(&wait, current);
-       add_wait_queue(&pCh->open_wait, &wait);
-
-       for(;;) {
-               i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP);
-               pCh->dataSetOut |= (I2_DTR | I2_RTS);
-               set_current_state( TASK_INTERRUPTIBLE );
-               serviceOutgoingFifo( pCh->pMyBord );
-               if ( tty_hung_up_p(pFile) ) {
-                       set_current_state( TASK_RUNNING );
-                       remove_wait_queue(&pCh->open_wait, &wait);
-                       return ( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EBUSY : -ERESTARTSYS;
-               }
-               if (!(pCh->flags & ASYNC_CLOSING) && 
-                               (do_clocal || (pCh->dataSetIn & I2_DCD) )) {
-                       rc = 0;
-                       break;
-               }
-
-#ifdef IP2DEBUG_OPEN
-               printk(KERN_DEBUG "ASYNC_CLOSING = %s\n",
-                       (pCh->flags & ASYNC_CLOSING)?"True":"False");
-               printk(KERN_DEBUG "OpenBlock: waiting for CD or signal\n");
-#endif
-               ip2trace (CHANN, ITRC_OPEN, 3, 2, 0,
-                               (pCh->flags & ASYNC_CLOSING) );
-               /* check for signal */
-               if (signal_pending(current)) {
-                       rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS);
-                       break;
-               }
-               tty_unlock();
-               schedule();
-               tty_lock();
-       }
-       set_current_state( TASK_RUNNING );
-       remove_wait_queue(&pCh->open_wait, &wait);
-
-       --pCh->wopen; //why count?
-
-       ip2trace (CHANN, ITRC_OPEN, 4, 0 );
-
-       if (rc != 0 ) {
-               return rc;
-       }
-       pCh->flags |= ASYNC_NORMAL_ACTIVE;
-
-noblock:
-
-       /* first open - Assign termios structure to port */
-       if ( tty->count == 1 ) {
-               i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB);
-               /* Now we must send the termios settings to the loadware */
-               set_params( pCh, NULL );
-       }
-
-       /*
-        * Now set any i2lib options. These may go away if the i2lib code ends
-        * up rolled into the mainline.
-        */
-       pCh->channelOptions |= CO_NBLOCK_WRITE;
-
-#ifdef IP2DEBUG_OPEN
-       printk (KERN_DEBUG "IP2: open completed\n" );
-#endif
-       serviceOutgoingFifo( pCh->pMyBord );
-
-       ip2trace (CHANN, ITRC_OPEN, ITRC_RETURN, 0 );
-
-       return 0;
-}
-
-/******************************************************************************/
-/* Function:   ip2_close()                                                    */
-/* Parameters: Pointer to tty structure                                       */
-/*             Pointer to file structure                                      */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static void
-ip2_close( PTTY tty, struct file *pFile )
-{
-       i2ChanStrPtr  pCh = tty->driver_data;
-
-       if ( !pCh ) {
-               return;
-       }
-
-       ip2trace (CHANN, ITRC_CLOSE, ITRC_ENTER, 0 );
-
-#ifdef IP2DEBUG_OPEN
-       printk(KERN_DEBUG "IP2:close %s:\n",tty->name);
-#endif
-
-       if ( tty_hung_up_p ( pFile ) ) {
-
-               ip2trace (CHANN, ITRC_CLOSE, 2, 1, 2 );
-
-               return;
-       }
-       if ( tty->count > 1 ) { /* not the last close */
-
-               ip2trace (CHANN, ITRC_CLOSE, 2, 1, 3 );
-
-               return;
-       }
-       pCh->flags |= ASYNC_CLOSING;    // last close actually
-
-       tty->closing = 1;
-
-       if (pCh->ClosingWaitTime != ASYNC_CLOSING_WAIT_NONE) {
-               /*
-                * Before we drop DTR, make sure the transmitter has completely drained.
-                * This uses an timeout, after which the close
-                * completes.
-                */
-               ip2_wait_until_sent(tty, pCh->ClosingWaitTime );
-       }
-       /*
-        * At this point we stop accepting input. Here we flush the channel
-        * input buffer which will allow the board to send up more data. Any
-        * additional input is tossed at interrupt/poll time.
-        */
-       i2InputFlush( pCh );
-
-       /* disable DSS reporting */
-       i2QueueCommands(PTYPE_INLINE, pCh, 100, 4,
-                               CMD_DCD_NREP, CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP);
-       if (tty->termios->c_cflag & HUPCL) {
-               i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN);
-               pCh->dataSetOut &= ~(I2_DTR | I2_RTS);
-               i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25));
-       }
-
-       serviceOutgoingFifo ( pCh->pMyBord );
-
-       tty_ldisc_flush(tty);
-       tty_driver_flush_buffer(tty);
-       tty->closing = 0;
-       
-       pCh->pTTY = NULL;
-
-       if (pCh->wopen) {
-               if (pCh->ClosingDelay) {
-                       msleep_interruptible(jiffies_to_msecs(pCh->ClosingDelay));
-               }
-               wake_up_interruptible(&pCh->open_wait);
-       }
-
-       pCh->flags &=~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
-       wake_up_interruptible(&pCh->close_wait);
-
-#ifdef IP2DEBUG_OPEN
-       DBG_CNT("ip2_close: after wakeups--");
-#endif
-
-
-       ip2trace (CHANN, ITRC_CLOSE, ITRC_RETURN, 1, 1 );
-
-       return;
-}
-
-/******************************************************************************/
-/* Function:   ip2_hangup()                                                   */
-/* Parameters: Pointer to tty structure                                       */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static void
-ip2_hangup ( PTTY tty )
-{
-       i2ChanStrPtr  pCh = tty->driver_data;
-
-       if( !pCh ) {
-               return;
-       }
-
-       ip2trace (CHANN, ITRC_HANGUP, ITRC_ENTER, 0 );
-
-       ip2_flush_buffer(tty);
-
-       /* disable DSS reporting */
-
-       i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_DCD_NREP);
-       i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB);
-       if ( (tty->termios->c_cflag & HUPCL) ) {
-               i2QueueCommands(PTYPE_BYPASS, pCh, 0, 2, CMD_RTSDN, CMD_DTRDN);
-               pCh->dataSetOut &= ~(I2_DTR | I2_RTS);
-               i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25));
-       }
-       i2QueueCommands(PTYPE_INLINE, pCh, 1, 3, 
-                               CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP);
-       serviceOutgoingFifo ( pCh->pMyBord );
-
-       wake_up_interruptible ( &pCh->delta_msr_wait );
-
-       pCh->flags &= ~ASYNC_NORMAL_ACTIVE;
-       pCh->pTTY = NULL;
-       wake_up_interruptible ( &pCh->open_wait );
-
-       ip2trace (CHANN, ITRC_HANGUP, ITRC_RETURN, 0 );
-}
-
-/******************************************************************************/
-/******************************************************************************/
-/* Device Output Section                                                      */
-/******************************************************************************/
-/******************************************************************************/
-
-/******************************************************************************/
-/* Function:   ip2_write()                                                    */
-/* Parameters: Pointer to tty structure                                       */
-/*             Flag denoting data is in user (1) or kernel (0) space          */
-/*             Pointer to data                                                */
-/*             Number of bytes to write                                       */
-/* Returns:    Number of bytes actually written                               */
-/*                                                                            */
-/* Description: (MANDATORY)                                                   */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static int
-ip2_write( PTTY tty, const unsigned char *pData, int count)
-{
-       i2ChanStrPtr  pCh = tty->driver_data;
-       int bytesSent = 0;
-       unsigned long flags;
-
-       ip2trace (CHANN, ITRC_WRITE, ITRC_ENTER, 2, count, -1 );
-
-       /* Flush out any buffered data left over from ip2_putchar() calls. */
-       ip2_flush_chars( tty );
-
-       /* This is the actual move bit. Make sure it does what we need!!!!! */
-       write_lock_irqsave(&pCh->Pbuf_spinlock, flags);
-       bytesSent = i2Output( pCh, pData, count);
-       write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
-
-       ip2trace (CHANN, ITRC_WRITE, ITRC_RETURN, 1, bytesSent );
-
-       return bytesSent > 0 ? bytesSent : 0;
-}
-
-/******************************************************************************/
-/* Function:   ip2_putchar()                                                  */
-/* Parameters: Pointer to tty structure                                       */
-/*             Character to write                                             */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static int
-ip2_putchar( PTTY tty, unsigned char ch )
-{
-       i2ChanStrPtr  pCh = tty->driver_data;
-       unsigned long flags;
-
-//     ip2trace (CHANN, ITRC_PUTC, ITRC_ENTER, 1, ch );
-
-       write_lock_irqsave(&pCh->Pbuf_spinlock, flags);
-       pCh->Pbuf[pCh->Pbuf_stuff++] = ch;
-       if ( pCh->Pbuf_stuff == sizeof pCh->Pbuf ) {
-               write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
-               ip2_flush_chars( tty );
-       } else
-               write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
-       return 1;
-
-//     ip2trace (CHANN, ITRC_PUTC, ITRC_RETURN, 1, ch );
-}
-
-/******************************************************************************/
-/* Function:   ip2_flush_chars()                                              */
-/* Parameters: Pointer to tty structure                                       */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/******************************************************************************/
-static void
-ip2_flush_chars( PTTY tty )
-{
-       int   strip;
-       i2ChanStrPtr  pCh = tty->driver_data;
-       unsigned long flags;
-
-       write_lock_irqsave(&pCh->Pbuf_spinlock, flags);
-       if ( pCh->Pbuf_stuff ) {
-
-//             ip2trace (CHANN, ITRC_PUTC, 10, 1, strip );
-
-               //
-               // We may need to restart i2Output if it does not fullfill this request
-               //
-               strip = i2Output( pCh, pCh->Pbuf, pCh->Pbuf_stuff);
-               if ( strip != pCh->Pbuf_stuff ) {
-                       memmove( pCh->Pbuf, &pCh->Pbuf[strip], pCh->Pbuf_stuff - strip );
-               }
-               pCh->Pbuf_stuff -= strip;
-       }
-       write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
-}
-
-/******************************************************************************/
-/* Function:   ip2_write_room()                                               */
-/* Parameters: Pointer to tty structure                                       */
-/* Returns:    Number of bytes that the driver can accept                     */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/******************************************************************************/
-static int
-ip2_write_room ( PTTY tty )
-{
-       int bytesFree;
-       i2ChanStrPtr  pCh = tty->driver_data;
-       unsigned long flags;
-
-       read_lock_irqsave(&pCh->Pbuf_spinlock, flags);
-       bytesFree = i2OutputFree( pCh ) - pCh->Pbuf_stuff;
-       read_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
-
-       ip2trace (CHANN, ITRC_WRITE, 11, 1, bytesFree );
-
-       return ((bytesFree > 0) ? bytesFree : 0);
-}
-
-/******************************************************************************/
-/* Function:   ip2_chars_in_buf()                                             */
-/* Parameters: Pointer to tty structure                                       */
-/* Returns:    Number of bytes queued for transmission                        */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static int
-ip2_chars_in_buf ( PTTY tty )
-{
-       i2ChanStrPtr  pCh = tty->driver_data;
-       int rc;
-       unsigned long flags;
-
-       ip2trace (CHANN, ITRC_WRITE, 12, 1, pCh->Obuf_char_count + pCh->Pbuf_stuff );
-
-#ifdef IP2DEBUG_WRITE
-       printk (KERN_DEBUG "IP2: chars in buffer = %d (%d,%d)\n",
-                                pCh->Obuf_char_count + pCh->Pbuf_stuff,
-                                pCh->Obuf_char_count, pCh->Pbuf_stuff );
-#endif
-       read_lock_irqsave(&pCh->Obuf_spinlock, flags);
-       rc =  pCh->Obuf_char_count;
-       read_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
-       read_lock_irqsave(&pCh->Pbuf_spinlock, flags);
-       rc +=  pCh->Pbuf_stuff;
-       read_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
-       return rc;
-}
-
-/******************************************************************************/
-/* Function:   ip2_flush_buffer()                                             */
-/* Parameters: Pointer to tty structure                                       */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static void
-ip2_flush_buffer( PTTY tty )
-{
-       i2ChanStrPtr  pCh = tty->driver_data;
-       unsigned long flags;
-
-       ip2trace (CHANN, ITRC_FLUSH, ITRC_ENTER, 0 );
-
-#ifdef IP2DEBUG_WRITE
-       printk (KERN_DEBUG "IP2: flush buffer\n" );
-#endif
-       write_lock_irqsave(&pCh->Pbuf_spinlock, flags);
-       pCh->Pbuf_stuff = 0;
-       write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
-       i2FlushOutput( pCh );
-       ip2_owake(tty);
-
-       ip2trace (CHANN, ITRC_FLUSH, ITRC_RETURN, 0 );
-
-}
-
-/******************************************************************************/
-/* Function:   ip2_wait_until_sent()                                          */
-/* Parameters: Pointer to tty structure                                       */
-/*             Timeout for wait.                                              */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/* This function is used in place of the normal tty_wait_until_sent, which    */
-/* only waits for the driver buffers to be empty (or rather, those buffers    */
-/* reported by chars_in_buffer) which doesn't work for IP2 due to the         */
-/* indeterminate number of bytes buffered on the board.                       */
-/******************************************************************************/
-static void
-ip2_wait_until_sent ( PTTY tty, int timeout )
-{
-       int i = jiffies;
-       i2ChanStrPtr  pCh = tty->driver_data;
-
-       tty_wait_until_sent(tty, timeout );
-       if ( (i = timeout - (jiffies -i)) > 0)
-               i2DrainOutput( pCh, i );
-}
-
-/******************************************************************************/
-/******************************************************************************/
-/* Device Input Section                                                       */
-/******************************************************************************/
-/******************************************************************************/
-
-/******************************************************************************/
-/* Function:   ip2_throttle()                                                 */
-/* Parameters: Pointer to tty structure                                       */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static void
-ip2_throttle ( PTTY tty )
-{
-       i2ChanStrPtr  pCh = tty->driver_data;
-
-#ifdef IP2DEBUG_READ
-       printk (KERN_DEBUG "IP2: throttle\n" );
-#endif
-       /*
-        * Signal the poll/interrupt handlers not to forward incoming data to
-        * the line discipline. This will cause the buffers to fill up in the
-        * library and thus cause the library routines to send the flow control
-        * stuff.
-        */
-       pCh->throttled = 1;
-}
-
-/******************************************************************************/
-/* Function:   ip2_unthrottle()                                               */
-/* Parameters: Pointer to tty structure                                       */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static void
-ip2_unthrottle ( PTTY tty )
-{
-       i2ChanStrPtr  pCh = tty->driver_data;
-       unsigned long flags;
-
-#ifdef IP2DEBUG_READ
-       printk (KERN_DEBUG "IP2: unthrottle\n" );
-#endif
-
-       /* Pass incoming data up to the line discipline again. */
-       pCh->throttled = 0;
-       i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME);
-       serviceOutgoingFifo( pCh->pMyBord );
-       read_lock_irqsave(&pCh->Ibuf_spinlock, flags);
-       if ( pCh->Ibuf_stuff != pCh->Ibuf_strip ) {
-               read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
-#ifdef IP2DEBUG_READ
-               printk (KERN_DEBUG "i2Input called from unthrottle\n" );
-#endif
-               i2Input( pCh );
-       } else
-               read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
-}
-
-static void
-ip2_start ( PTTY tty )
-{
-       i2ChanStrPtr  pCh = DevTable[tty->index];
-
-       i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME);
-       i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_UNSUSPEND);
-       i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_RESUME);
-#ifdef IP2DEBUG_WRITE
-       printk (KERN_DEBUG "IP2: start tx\n" );
-#endif
-}
-
-static void
-ip2_stop ( PTTY tty )
-{
-       i2ChanStrPtr  pCh = DevTable[tty->index];
-
-       i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_SUSPEND);
-#ifdef IP2DEBUG_WRITE
-       printk (KERN_DEBUG "IP2: stop tx\n" );
-#endif
-}
-
-/******************************************************************************/
-/* Device Ioctl Section                                                       */
-/******************************************************************************/
-
-static int ip2_tiocmget(struct tty_struct *tty)
-{
-       i2ChanStrPtr pCh = DevTable[tty->index];
-#ifdef ENABLE_DSSNOW
-       wait_queue_t wait;
-#endif
-
-       if (pCh == NULL)
-               return -ENODEV;
-
-/*
-       FIXME - the following code is causing a NULL pointer dereference in
-       2.3.51 in an interrupt handler.  It's suppose to prompt the board
-       to return the DSS signal status immediately.  Why doesn't it do
-       the same thing in 2.2.14?
-*/
-
-/*     This thing is still busted in the 1.2.12 driver on 2.4.x
-       and even hoses the serial console so the oops can be trapped.
-               /\/\|=mhw=|\/\/                 */
-
-#ifdef ENABLE_DSSNOW
-       i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DSS_NOW);
-
-       init_waitqueue_entry(&wait, current);
-       add_wait_queue(&pCh->dss_now_wait, &wait);
-       set_current_state( TASK_INTERRUPTIBLE );
-
-       serviceOutgoingFifo( pCh->pMyBord );
-
-       schedule();
-
-       set_current_state( TASK_RUNNING );
-       remove_wait_queue(&pCh->dss_now_wait, &wait);
-
-       if (signal_pending(current)) {
-               return -EINTR;
-       }
-#endif
-       return  ((pCh->dataSetOut & I2_RTS) ? TIOCM_RTS : 0)
-             | ((pCh->dataSetOut & I2_DTR) ? TIOCM_DTR : 0)
-             | ((pCh->dataSetIn  & I2_DCD) ? TIOCM_CAR : 0)
-             | ((pCh->dataSetIn  & I2_RI)  ? TIOCM_RNG : 0)
-             | ((pCh->dataSetIn  & I2_DSR) ? TIOCM_DSR : 0)
-             | ((pCh->dataSetIn  & I2_CTS) ? TIOCM_CTS : 0);
-}
-
-static int ip2_tiocmset(struct tty_struct *tty,
-                       unsigned int set, unsigned int clear)
-{
-       i2ChanStrPtr pCh = DevTable[tty->index];
-
-       if (pCh == NULL)
-               return -ENODEV;
-
-       if (set & TIOCM_RTS) {
-               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSUP);
-               pCh->dataSetOut |= I2_RTS;
-       }
-       if (set & TIOCM_DTR) {
-               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRUP);
-               pCh->dataSetOut |= I2_DTR;
-       }
-
-       if (clear & TIOCM_RTS) {
-               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSDN);
-               pCh->dataSetOut &= ~I2_RTS;
-       }
-       if (clear & TIOCM_DTR) {
-               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRDN);
-               pCh->dataSetOut &= ~I2_DTR;
-       }
-       serviceOutgoingFifo( pCh->pMyBord );
-       return 0;
-}
-
-/******************************************************************************/
-/* Function:   ip2_ioctl()                                                    */
-/* Parameters: Pointer to tty structure                                       */
-/*             Pointer to file structure                                      */
-/*             Command                                                        */
-/*             Argument                                                       */
-/* Returns:    Success or failure                                             */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static int
-ip2_ioctl ( PTTY tty, UINT cmd, ULONG arg )
-{
-       wait_queue_t wait;
-       i2ChanStrPtr pCh = DevTable[tty->index];
-       i2eBordStrPtr pB;
-       struct async_icount cprev, cnow;        /* kernel counter temps */
-       int rc = 0;
-       unsigned long flags;
-       void __user *argp = (void __user *)arg;
-
-       if ( pCh == NULL )
-               return -ENODEV;
-
-       pB = pCh->pMyBord;
-
-       ip2trace (CHANN, ITRC_IOCTL, ITRC_ENTER, 2, cmd, arg );
-
-#ifdef IP2DEBUG_IOCTL
-       printk(KERN_DEBUG "IP2: ioctl cmd (%x), arg (%lx)\n", cmd, arg );
-#endif
-
-       switch(cmd) {
-       case TIOCGSERIAL:
-
-               ip2trace (CHANN, ITRC_IOCTL, 2, 1, rc );
-
-               rc = get_serial_info(pCh, argp);
-               if (rc)
-                       return rc;
-               break;
-
-       case TIOCSSERIAL:
-
-               ip2trace (CHANN, ITRC_IOCTL, 3, 1, rc );
-
-               rc = set_serial_info(pCh, argp);
-               if (rc)
-                       return rc;
-               break;
-
-       case TCXONC:
-               rc = tty_check_change(tty);
-               if (rc)
-                       return rc;
-               switch (arg) {
-               case TCOOFF:
-                       //return  -ENOIOCTLCMD;
-                       break;
-               case TCOON:
-                       //return  -ENOIOCTLCMD;
-                       break;
-               case TCIOFF:
-                       if (STOP_CHAR(tty) != __DISABLED_CHAR) {
-                               i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1,
-                                               CMD_XMIT_NOW(STOP_CHAR(tty)));
-                       }
-                       break;
-               case TCION:
-                       if (START_CHAR(tty) != __DISABLED_CHAR) {
-                               i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1,
-                                               CMD_XMIT_NOW(START_CHAR(tty)));
-                       }
-                       break;
-               default:
-                       return -EINVAL;
-               }
-               return 0;
-
-       case TCSBRK:   /* SVID version: non-zero arg --> no break */
-               rc = tty_check_change(tty);
-
-               ip2trace (CHANN, ITRC_IOCTL, 4, 1, rc );
-
-               if (!rc) {
-                       ip2_wait_until_sent(tty,0);
-                       if (!arg) {
-                               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SEND_BRK(250));
-                               serviceOutgoingFifo( pCh->pMyBord );
-                       }
-               }
-               break;
-
-       case TCSBRKP:  /* support for POSIX tcsendbreak() */
-               rc = tty_check_change(tty);
-
-               ip2trace (CHANN, ITRC_IOCTL, 5, 1, rc );
-
-               if (!rc) {
-                       ip2_wait_until_sent(tty,0);
-                       i2QueueCommands(PTYPE_INLINE, pCh, 100, 1,
-                               CMD_SEND_BRK(arg ? arg*100 : 250));
-                       serviceOutgoingFifo ( pCh->pMyBord );   
-               }
-               break;
-
-       case TIOCGSOFTCAR:
-
-               ip2trace (CHANN, ITRC_IOCTL, 6, 1, rc );
-
-                       rc = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *)argp);
-               if (rc) 
-                       return rc;
-       break;
-
-       case TIOCSSOFTCAR:
-
-               ip2trace (CHANN, ITRC_IOCTL, 7, 1, rc );
-
-               rc = get_user(arg,(unsigned long __user *) argp);
-               if (rc) 
-                       return rc;
-               tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL)
-                                        | (arg ? CLOCAL : 0));
-               
-               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:
-               write_lock_irqsave(&pB->read_fifo_spinlock, flags);
-               cprev = pCh->icount;     /* note the counters on entry */
-               write_unlock_irqrestore(&pB->read_fifo_spinlock, flags);
-               i2QueueCommands(PTYPE_BYPASS, pCh, 100, 4, 
-                                               CMD_DCD_REP, CMD_CTS_REP, CMD_DSR_REP, CMD_RI_REP);
-               init_waitqueue_entry(&wait, current);
-               add_wait_queue(&pCh->delta_msr_wait, &wait);
-               set_current_state( TASK_INTERRUPTIBLE );
-
-               serviceOutgoingFifo( pCh->pMyBord );
-               for(;;) {
-                       ip2trace (CHANN, ITRC_IOCTL, 10, 0 );
-
-                       schedule();
-
-                       ip2trace (CHANN, ITRC_IOCTL, 11, 0 );
-
-                       /* see if a signal did it */
-                       if (signal_pending(current)) {
-                               rc = -ERESTARTSYS;
-                               break;
-                       }
-                       write_lock_irqsave(&pB->read_fifo_spinlock, flags);
-                       cnow = pCh->icount; /* atomic copy */
-                       write_unlock_irqrestore(&pB->read_fifo_spinlock, flags);
-                       if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
-                               cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
-                               rc =  -EIO; /* no change => rc */
-                               break;
-                       }
-                       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;
-               }
-               set_current_state( TASK_RUNNING );
-               remove_wait_queue(&pCh->delta_msr_wait, &wait);
-
-               i2QueueCommands(PTYPE_BYPASS, pCh, 100, 3, 
-                                                CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP);
-               if ( ! (pCh->flags      & ASYNC_CHECK_CD)) {
-                       i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DCD_NREP);
-               }
-               serviceOutgoingFifo( pCh->pMyBord );
-               return rc;
-               break;
-
-       /*
-        * The rest are not supported by this driver. By returning -ENOIOCTLCMD they
-        * will be passed to the line discipline for it to handle.
-        */
-       case TIOCSERCONFIG:
-       case TIOCSERGWILD:
-       case TIOCSERGETLSR:
-       case TIOCSERSWILD:
-       case TIOCSERGSTRUCT:
-       case TIOCSERGETMULTI:
-       case TIOCSERSETMULTI:
-
-       default:
-               ip2trace (CHANN, ITRC_IOCTL, 12, 0 );
-
-               rc =  -ENOIOCTLCMD;
-               break;
-       }
-
-       ip2trace (CHANN, ITRC_IOCTL, ITRC_RETURN, 0 );
-
-       return rc;
-}
-
-static int ip2_get_icount(struct tty_struct *tty,
-               struct serial_icounter_struct *icount)
-{
-       i2ChanStrPtr pCh = DevTable[tty->index];
-       i2eBordStrPtr pB;
-       struct async_icount cnow;       /* kernel counter temp */
-       unsigned long flags;
-
-       if ( pCh == NULL )
-               return -ENODEV;
-
-       pB = pCh->pMyBord;
-
-       /*
-        * 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. The controller is quite capable of counting
-        * both, but this done to preserve compatibility with the standard
-        * serial driver.
-        */
-
-       write_lock_irqsave(&pB->read_fifo_spinlock, flags);
-       cnow = pCh->icount;
-       write_unlock_irqrestore(&pB->read_fifo_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;
-}
-
-/******************************************************************************/
-/* Function:   GetSerialInfo()                                                */
-/* Parameters: Pointer to channel structure                                   */
-/*             Pointer to old termios structure                               */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/* This is to support the setserial command, and requires processing of the   */
-/* standard Linux serial structure.                                           */
-/******************************************************************************/
-static int
-get_serial_info ( i2ChanStrPtr pCh, struct serial_struct __user *retinfo )
-{
-       struct serial_struct tmp;
-
-       memset ( &tmp, 0, sizeof(tmp) );
-       tmp.type = pCh->pMyBord->channelBtypes.bid_value[(pCh->port_index & (IP2_PORTS_PER_BOARD-1))/16];
-       if (BID_HAS_654(tmp.type)) {
-               tmp.type = PORT_16650;
-       } else {
-               tmp.type = PORT_CIRRUS;
-       }
-       tmp.line = pCh->port_index;
-       tmp.port = pCh->pMyBord->i2eBase;
-       tmp.irq  = ip2config.irq[pCh->port_index/64];
-       tmp.flags = pCh->flags;
-       tmp.baud_base = pCh->BaudBase;
-       tmp.close_delay = pCh->ClosingDelay;
-       tmp.closing_wait = pCh->ClosingWaitTime;
-       tmp.custom_divisor = pCh->BaudDivisor;
-       return copy_to_user(retinfo,&tmp,sizeof(*retinfo));
-}
-
-/******************************************************************************/
-/* Function:   SetSerialInfo()                                                */
-/* Parameters: Pointer to channel structure                                   */
-/*             Pointer to old termios structure                               */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/* This function provides support for setserial, which uses the TIOCSSERIAL   */
-/* ioctl. Not all setserial parameters are relevant. If the user attempts to  */
-/* change the IRQ, address or type of the port the ioctl fails.               */
-/******************************************************************************/
-static int
-set_serial_info( i2ChanStrPtr pCh, struct serial_struct __user *new_info )
-{
-       struct serial_struct ns;
-       int   old_flags, old_baud_divisor;
-
-       if (copy_from_user(&ns, new_info, sizeof (ns)))
-               return -EFAULT;
-
-       /*
-        * We don't allow setserial to change IRQ, board address, type or baud
-        * base. Also line nunber as such is meaningless but we use it for our
-        * array index so it is fixed also.
-        */
-       if ( (ns.irq        != ip2config.irq[pCh->port_index])
-           || ((int) ns.port      != ((int) (pCh->pMyBord->i2eBase)))
-           || (ns.baud_base != pCh->BaudBase)
-           || (ns.line      != pCh->port_index) ) {
-               return -EINVAL;
-       }
-
-       old_flags = pCh->flags;
-       old_baud_divisor = pCh->BaudDivisor;
-
-       if ( !capable(CAP_SYS_ADMIN) ) {
-               if ( ( ns.close_delay != pCh->ClosingDelay ) ||
-                   ( (ns.flags & ~ASYNC_USR_MASK) !=
-                     (pCh->flags & ~ASYNC_USR_MASK) ) ) {
-                       return -EPERM;
-               }
-
-               pCh->flags = (pCh->flags & ~ASYNC_USR_MASK) |
-                              (ns.flags & ASYNC_USR_MASK);
-               pCh->BaudDivisor = ns.custom_divisor;
-       } else {
-               pCh->flags = (pCh->flags & ~ASYNC_FLAGS) |
-                              (ns.flags & ASYNC_FLAGS);
-               pCh->BaudDivisor = ns.custom_divisor;
-               pCh->ClosingDelay = ns.close_delay * HZ/100;
-               pCh->ClosingWaitTime = ns.closing_wait * HZ/100;
-       }
-
-       if ( ( (old_flags & ASYNC_SPD_MASK) != (pCh->flags & ASYNC_SPD_MASK) )
-           || (old_baud_divisor != pCh->BaudDivisor) ) {
-               // Invalidate speed and reset parameters
-               set_params( pCh, NULL );
-       }
-
-       return 0;
-}
-
-/******************************************************************************/
-/* Function:   ip2_set_termios()                                              */
-/* Parameters: Pointer to tty structure                                       */
-/*             Pointer to old termios structure                               */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static void
-ip2_set_termios( PTTY tty, struct ktermios *old_termios )
-{
-       i2ChanStrPtr pCh = (i2ChanStrPtr)tty->driver_data;
-
-#ifdef IP2DEBUG_IOCTL
-       printk (KERN_DEBUG "IP2: set termios %p\n", old_termios );
-#endif
-
-       set_params( pCh, old_termios );
-}
-
-/******************************************************************************/
-/* Function:   ip2_set_line_discipline()                                      */
-/* Parameters: Pointer to tty structure                                       */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:  Does nothing                                                 */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static void
-ip2_set_line_discipline ( PTTY tty )
-{
-#ifdef IP2DEBUG_IOCTL
-       printk (KERN_DEBUG "IP2: set line discipline\n" );
-#endif
-
-       ip2trace (((i2ChanStrPtr)tty->driver_data)->port_index, ITRC_IOCTL, 16, 0 );
-
-}
-
-/******************************************************************************/
-/* Function:   SetLine Characteristics()                                      */
-/* Parameters: Pointer to channel structure                                   */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/* This routine is called to update the channel structure with the new line   */
-/* characteristics, and send the appropriate commands to the board when they  */
-/* change.                                                                    */
-/******************************************************************************/
-static void
-set_params( i2ChanStrPtr pCh, struct ktermios *o_tios )
-{
-       tcflag_t cflag, iflag, lflag;
-       char stop_char, start_char;
-       struct ktermios dummy;
-
-       lflag = pCh->pTTY->termios->c_lflag;
-       cflag = pCh->pTTY->termios->c_cflag;
-       iflag = pCh->pTTY->termios->c_iflag;
-
-       if (o_tios == NULL) {
-               dummy.c_lflag = ~lflag;
-               dummy.c_cflag = ~cflag;
-               dummy.c_iflag = ~iflag;
-               o_tios = &dummy;
-       }
-
-       {
-               switch ( cflag & CBAUD ) {
-               case B0:
-                       i2QueueCommands( PTYPE_BYPASS, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN);
-                       pCh->dataSetOut &= ~(I2_DTR | I2_RTS);
-                       i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25));
-                       pCh->pTTY->termios->c_cflag |= (CBAUD & o_tios->c_cflag);
-                       goto service_it;
-                       break;
-               case B38400:
-                       /*
-                        * This is the speed that is overloaded with all the other high
-                        * speeds, depending upon the flag settings.
-                        */
-                       if ( ( pCh->flags & ASYNC_SPD_MASK ) == ASYNC_SPD_HI ) {
-                               pCh->speed = CBR_57600;
-                       } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI ) {
-                               pCh->speed = CBR_115200;
-                       } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST ) {
-                               pCh->speed = CBR_C1;
-                       } else {
-                               pCh->speed = CBR_38400;
-                       }
-                       break;
-               case B50:      pCh->speed = CBR_50;      break;
-               case B75:      pCh->speed = CBR_75;      break;
-               case B110:     pCh->speed = CBR_110;     break;
-               case B134:     pCh->speed = CBR_134;     break;
-               case B150:     pCh->speed = CBR_150;     break;
-               case B200:     pCh->speed = CBR_200;     break;
-               case B300:     pCh->speed = CBR_300;     break;
-               case B600:     pCh->speed = CBR_600;     break;
-               case B1200:    pCh->speed = CBR_1200;    break;
-               case B1800:    pCh->speed = CBR_1800;    break;
-               case B2400:    pCh->speed = CBR_2400;    break;
-               case B4800:    pCh->speed = CBR_4800;    break;
-               case B9600:    pCh->speed = CBR_9600;    break;
-               case B19200:   pCh->speed = CBR_19200;   break;
-               case B57600:   pCh->speed = CBR_57600;   break;
-               case B115200:  pCh->speed = CBR_115200;  break;
-               case B153600:  pCh->speed = CBR_153600;  break;
-               case B230400:  pCh->speed = CBR_230400;  break;
-               case B307200:  pCh->speed = CBR_307200;  break;
-               case B460800:  pCh->speed = CBR_460800;  break;
-               case B921600:  pCh->speed = CBR_921600;  break;
-               default:       pCh->speed = CBR_9600;    break;
-               }
-               if ( pCh->speed == CBR_C1 ) {
-                       // Process the custom speed parameters.
-                       int bps = pCh->BaudBase / pCh->BaudDivisor;
-                       if ( bps == 921600 ) {
-                               pCh->speed = CBR_921600;
-                       } else {
-                               bps = bps/10;
-                               i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_BAUD_DEF1(bps) );
-                       }
-               }
-               i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_SETBAUD(pCh->speed));
-               
-               i2QueueCommands ( PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP);
-               pCh->dataSetOut |= (I2_DTR | I2_RTS);
-       }
-       if ( (CSTOPB & cflag) ^ (CSTOPB & o_tios->c_cflag)) 
-       {
-               i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, 
-                       CMD_SETSTOP( ( cflag & CSTOPB ) ? CST_2 : CST_1));
-       }
-       if (((PARENB|PARODD) & cflag) ^ ((PARENB|PARODD) & o_tios->c_cflag)) 
-       {
-               i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1,
-                       CMD_SETPAR( 
-                               (cflag & PARENB ?  (cflag & PARODD ? CSP_OD : CSP_EV) : CSP_NP)
-                       )
-               );
-       }
-       /* byte size and parity */
-       if ( (CSIZE & cflag)^(CSIZE & o_tios->c_cflag)) 
-       {
-               int datasize;
-               switch ( cflag & CSIZE ) {
-               case CS5: datasize = CSZ_5; break;
-               case CS6: datasize = CSZ_6; break;
-               case CS7: datasize = CSZ_7; break;
-               case CS8: datasize = CSZ_8; break;
-               default:  datasize = CSZ_5; break;      /* as per serial.c */
-               }
-               i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, CMD_SETBITS(datasize) );
-       }
-       /* Process CTS flow control flag setting */
-       if ( (cflag & CRTSCTS) ) {
-               i2QueueCommands(PTYPE_INLINE, pCh, 100,
-                                               2, CMD_CTSFL_ENAB, CMD_RTSFL_ENAB);
-       } else {
-               i2QueueCommands(PTYPE_INLINE, pCh, 100,
-                                               2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB);
-       }
-       //
-       // Process XON/XOFF flow control flags settings
-       //
-       stop_char = STOP_CHAR(pCh->pTTY);
-       start_char = START_CHAR(pCh->pTTY);
-
-       //////////// can't be \000
-       if (stop_char == __DISABLED_CHAR ) 
-       {
-               stop_char = ~__DISABLED_CHAR; 
-       }
-       if (start_char == __DISABLED_CHAR ) 
-       {
-               start_char = ~__DISABLED_CHAR;
-       }
-       /////////////////////////////////
-
-       if ( o_tios->c_cc[VSTART] != start_char ) 
-       {
-               i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXON(start_char));
-               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXON(start_char));
-       }
-       if ( o_tios->c_cc[VSTOP] != stop_char ) 
-       {
-                i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXOFF(stop_char));
-                i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXOFF(stop_char));
-       }
-       if (stop_char == __DISABLED_CHAR ) 
-       {
-               stop_char = ~__DISABLED_CHAR;  //TEST123
-               goto no_xoff;
-       }
-       if ((iflag & (IXOFF))^(o_tios->c_iflag & (IXOFF))) 
-       {
-               if ( iflag & IXOFF ) {  // Enable XOFF output flow control
-                       i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_XON));
-               } else {        // Disable XOFF output flow control
-no_xoff:
-                       i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_NONE));
-               }
-       }
-       if (start_char == __DISABLED_CHAR ) 
-       {
-               goto no_xon;
-       }
-       if ((iflag & (IXON|IXANY)) ^ (o_tios->c_iflag & (IXON|IXANY))) 
-       {
-               if ( iflag & IXON ) {
-                       if ( iflag & IXANY ) { // Enable XON/XANY output flow control
-                               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XANY));
-                       } else { // Enable XON output flow control
-                               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XON));
-                       }
-               } else { // Disable XON output flow control
-no_xon:
-                       i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_NONE));
-               }
-       }
-       if ( (iflag & ISTRIP) ^ ( o_tios->c_iflag & (ISTRIP)) ) 
-       {
-               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, 
-                               CMD_ISTRIP_OPT((iflag & ISTRIP ? 1 : 0)));
-       }
-       if ( (iflag & INPCK) ^ ( o_tios->c_iflag & (INPCK)) ) 
-       {
-               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, 
-                               CMD_PARCHK((iflag & INPCK) ? CPK_ENAB : CPK_DSAB));
-       }
-
-       if ( (iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) 
-                       ^       ( o_tios->c_iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) ) 
-       {
-               char brkrpt = 0;
-               char parrpt = 0;
-
-               if ( iflag & IGNBRK ) { /* Ignore breaks altogether */
-                       /* Ignore breaks altogether */
-                       i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_NREP);
-               } else {
-                       if ( iflag & BRKINT ) {
-                               if ( iflag & PARMRK ) {
-                                       brkrpt = 0x0a;  // exception an inline triple
-                               } else {
-                                       brkrpt = 0x1a;  // exception and NULL
-                               }
-                               brkrpt |= 0x04; // flush input
-                       } else {
-                               if ( iflag & PARMRK ) {
-                                       brkrpt = 0x0b;  //POSIX triple \0377 \0 \0
-                               } else {
-                                       brkrpt = 0x01;  // Null only
-                               }
-                       }
-                       i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_REP(brkrpt));
-               } 
-
-               if (iflag & IGNPAR) {
-                       parrpt = 0x20;
-                                                                                                       /* would be 2 for not cirrus bug */
-                                                                                                       /* would be 0x20 cept for cirrus bug */
-               } else {
-                       if ( iflag & PARMRK ) {
-                               /*
-                                * Replace error characters with 3-byte sequence (\0377,\0,char)
-                                */
-                               parrpt = 0x04 ;
-                               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_ISTRIP_OPT((char)0));
-                       } else {
-                               parrpt = 0x03;
-                       } 
-               }
-               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SET_ERROR(parrpt));
-       }
-       if (cflag & CLOCAL) {
-               // Status reporting fails for DCD if this is off
-               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_NREP);
-               pCh->flags &= ~ASYNC_CHECK_CD;
-       } else {
-               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_REP);
-               pCh->flags      |= ASYNC_CHECK_CD;
-       }
-
-service_it:
-       i2DrainOutput( pCh, 100 );              
-}
-
-/******************************************************************************/
-/* IPL Device Section                                                         */
-/******************************************************************************/
-
-/******************************************************************************/
-/* Function:   ip2_ipl_read()                                                  */
-/* Parameters: Pointer to device inode                                        */
-/*             Pointer to file structure                                      */
-/*             Pointer to data                                                */
-/*             Number of bytes to read                                        */
-/* Returns:    Success or failure                                             */
-/*                                                                            */
-/* Description:   Ugly                                                        */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-
-static 
-ssize_t
-ip2_ipl_read(struct file *pFile, char __user *pData, size_t count, loff_t *off )
-{
-       unsigned int minor = iminor(pFile->f_path.dentry->d_inode);
-       int rc = 0;
-
-#ifdef IP2DEBUG_IPL
-       printk (KERN_DEBUG "IP2IPL: read %p, %d bytes\n", pData, count );
-#endif
-
-       switch( minor ) {
-       case 0:     // IPL device
-               rc = -EINVAL;
-               break;
-       case 1:     // Status dump
-               rc = -EINVAL;
-               break;
-       case 2:     // Ping device
-               rc = -EINVAL;
-               break;
-       case 3:     // Trace device
-               rc = DumpTraceBuffer ( pData, count );
-               break;
-       case 4:     // Trace device
-               rc = DumpFifoBuffer ( pData, count );
-               break;
-       default:
-               rc = -ENODEV;
-               break;
-       }
-       return rc;
-}
-
-static int
-DumpFifoBuffer ( char __user *pData, int count )
-{
-#ifdef DEBUG_FIFO
-       int rc;
-       rc = copy_to_user(pData, DBGBuf, count);
-
-       printk(KERN_DEBUG "Last index %d\n", I );
-
-       return count;
-#endif /* DEBUG_FIFO */
-       return 0;
-}
-
-static int
-DumpTraceBuffer ( char __user *pData, int count )
-{
-#ifdef IP2DEBUG_TRACE
-       int rc;
-       int dumpcount;
-       int chunk;
-       int *pIndex = (int __user *)pData;
-
-       if ( count < (sizeof(int) * 6) ) {
-               return -EIO;
-       }
-       rc = put_user(tracewrap, pIndex );
-       rc = put_user(TRACEMAX, ++pIndex );
-       rc = put_user(tracestrip, ++pIndex );
-       rc = put_user(tracestuff, ++pIndex );
-       pData += sizeof(int) * 6;
-       count -= sizeof(int) * 6;
-
-       dumpcount = tracestuff - tracestrip;
-       if ( dumpcount < 0 ) {
-               dumpcount += TRACEMAX;
-       }
-       if ( dumpcount > count ) {
-               dumpcount = count;
-       }
-       chunk = TRACEMAX - tracestrip;
-       if ( dumpcount > chunk ) {
-               rc = copy_to_user(pData, &tracebuf[tracestrip],
-                             chunk * sizeof(tracebuf[0]) );
-               pData += chunk * sizeof(tracebuf[0]);
-               tracestrip = 0;
-               chunk = dumpcount - chunk;
-       } else {
-               chunk = dumpcount;
-       }
-       rc = copy_to_user(pData, &tracebuf[tracestrip],
-                     chunk * sizeof(tracebuf[0]) );
-       tracestrip += chunk;
-       tracewrap = 0;
-
-       rc = put_user(tracestrip, ++pIndex );
-       rc = put_user(tracestuff, ++pIndex );
-
-       return dumpcount;
-#else
-       return 0;
-#endif
-}
-
-/******************************************************************************/
-/* Function:   ip2_ipl_write()                                                 */
-/* Parameters:                                                                */
-/*             Pointer to file structure                                      */
-/*             Pointer to data                                                */
-/*             Number of bytes to write                                       */
-/* Returns:    Success or failure                                             */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static ssize_t
-ip2_ipl_write(struct file *pFile, const char __user *pData, size_t count, loff_t *off)
-{
-#ifdef IP2DEBUG_IPL
-       printk (KERN_DEBUG "IP2IPL: write %p, %d bytes\n", pData, count );
-#endif
-       return 0;
-}
-
-/******************************************************************************/
-/* Function:   ip2_ipl_ioctl()                                                */
-/* Parameters: Pointer to device inode                                        */
-/*             Pointer to file structure                                      */
-/*             Command                                                        */
-/*             Argument                                                       */
-/* Returns:    Success or failure                                             */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static long
-ip2_ipl_ioctl (struct file *pFile, UINT cmd, ULONG arg )
-{
-       unsigned int iplminor = iminor(pFile->f_path.dentry->d_inode);
-       int rc = 0;
-       void __user *argp = (void __user *)arg;
-       ULONG __user *pIndex = argp;
-       i2eBordStrPtr pB = i2BoardPtrTable[iplminor / 4];
-       i2ChanStrPtr pCh;
-
-#ifdef IP2DEBUG_IPL
-       printk (KERN_DEBUG "IP2IPL: ioctl cmd %d, arg %ld\n", cmd, arg );
-#endif
-
-       mutex_lock(&ip2_mutex);
-
-       switch ( iplminor ) {
-       case 0:     // IPL device
-               rc = -EINVAL;
-               break;
-       case 1:     // Status dump
-       case 5:
-       case 9:
-       case 13:
-               switch ( cmd ) {
-               case 64:        /* Driver - ip2stat */
-                       rc = put_user(-1, pIndex++ );
-                       rc = put_user(irq_counter, pIndex++  );
-                       rc = put_user(bh_counter, pIndex++  );
-                       break;
-
-               case 65:        /* Board  - ip2stat */
-                       if ( pB ) {
-                               rc = copy_to_user(argp, pB, sizeof(i2eBordStr));
-                               rc = put_user(inb(pB->i2eStatus),
-                                       (ULONG __user *)(arg + (ULONG)(&pB->i2eStatus) - (ULONG)pB ) );
-                       } else {
-                               rc = -ENODEV;
-                       }
-                       break;
-
-               default:
-                       if (cmd < IP2_MAX_PORTS) {
-                               pCh = DevTable[cmd];
-                               if ( pCh )
-                               {
-                                       rc = copy_to_user(argp, pCh, sizeof(i2ChanStr));
-                                       if (rc)
-                                               rc = -EFAULT;
-                               } else {
-                                       rc = -ENODEV;
-                               }
-                       } else {
-                               rc = -EINVAL;
-                       }
-               }
-               break;
-
-       case 2:     // Ping device
-               rc = -EINVAL;
-               break;
-       case 3:     // Trace device
-               /*
-                * akpm: This used to write a whole bunch of function addresses
-                * to userspace, which generated lots of put_user() warnings.
-                * I killed it all.  Just return "success" and don't do
-                * anything.
-                */
-               if (cmd == 1)
-                       rc = 0;
-               else
-                       rc = -EINVAL;
-               break;
-
-       default:
-               rc = -ENODEV;
-               break;
-       }
-       mutex_unlock(&ip2_mutex);
-       return rc;
-}
-
-/******************************************************************************/
-/* Function:   ip2_ipl_open()                                                 */
-/* Parameters: Pointer to device inode                                        */
-/*             Pointer to file structure                                      */
-/* Returns:    Success or failure                                             */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-static int
-ip2_ipl_open( struct inode *pInode, struct file *pFile )
-{
-
-#ifdef IP2DEBUG_IPL
-       printk (KERN_DEBUG "IP2IPL: open\n" );
-#endif
-       return 0;
-}
-
-static int
-proc_ip2mem_show(struct seq_file *m, void *v)
-{
-       i2eBordStrPtr  pB;
-       i2ChanStrPtr  pCh;
-       PTTY tty;
-       int i;
-
-#define FMTLINE        "%3d: 0x%08x 0x%08x 0%011o 0%011o\n"
-#define FMTLIN2        "     0x%04x 0x%04x tx flow 0x%x\n"
-#define FMTLIN3        "     0x%04x 0x%04x rc flow\n"
-
-       seq_printf(m,"\n");
-
-       for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
-               pB = i2BoardPtrTable[i];
-               if ( pB ) {
-                       seq_printf(m,"board %d:\n",i);
-                       seq_printf(m,"\tFifo rem: %d mty: %x outM %x\n",
-                               pB->i2eFifoRemains,pB->i2eWaitingForEmptyFifo,pB->i2eOutMailWaiting);
-               }
-       }
-
-       seq_printf(m,"#: tty flags, port flags,     cflags,     iflags\n");
-       for (i=0; i < IP2_MAX_PORTS; i++) {
-               pCh = DevTable[i];
-               if (pCh) {
-                       tty = pCh->pTTY;
-                       if (tty && tty->count) {
-                               seq_printf(m,FMTLINE,i,(int)tty->flags,pCh->flags,
-                                                                       tty->termios->c_cflag,tty->termios->c_iflag);
-
-                               seq_printf(m,FMTLIN2,
-                                               pCh->outfl.asof,pCh->outfl.room,pCh->channelNeeds);
-                               seq_printf(m,FMTLIN3,pCh->infl.asof,pCh->infl.room);
-                       }
-               }
-       }
-       return 0;
-}
-
-static int proc_ip2mem_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, proc_ip2mem_show, NULL);
-}
-
-static const struct file_operations ip2mem_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = proc_ip2mem_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-/*
- * This is the handler for /proc/tty/driver/ip2
- *
- * This stretch of code has been largely plagerized from at least three
- * different sources including ip2mkdev.c and a couple of other drivers.
- * The bugs are all mine.  :-) =mhw=
- */
-static int ip2_proc_show(struct seq_file *m, void *v)
-{
-       int     i, j, box;
-       int     boxes = 0;
-       int     ports = 0;
-       int     tports = 0;
-       i2eBordStrPtr  pB;
-       char *sep;
-
-       seq_printf(m, "ip2info: 1.0 driver: %s\n", pcVersion);
-       seq_printf(m, "Driver: SMajor=%d CMajor=%d IMajor=%d MaxBoards=%d MaxBoxes=%d MaxPorts=%d\n",
-                       IP2_TTY_MAJOR, IP2_CALLOUT_MAJOR, IP2_IPL_MAJOR,
-                       IP2_MAX_BOARDS, ABS_MAX_BOXES, ABS_BIGGEST_BOX);
-
-       for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
-               /* This need to be reset for a board by board count... */
-               boxes = 0;
-               pB = i2BoardPtrTable[i];
-               if( pB ) {
-                       switch( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) 
-                       {
-                       case POR_ID_FIIEX:
-                               seq_printf(m, "Board %d: EX ports=", i);
-                               sep = "";
-                               for( box = 0; box < ABS_MAX_BOXES; ++box )
-                               {
-                                       ports = 0;
-
-                                       if( pB->i2eChannelMap[box] != 0 ) ++boxes;
-                                       for( j = 0; j < ABS_BIGGEST_BOX; ++j ) 
-                                       {
-                                               if( pB->i2eChannelMap[box] & 1<< j ) {
-                                                       ++ports;
-                                               }
-                                       }
-                                       seq_printf(m, "%s%d", sep, ports);
-                                       sep = ",";
-                                       tports += ports;
-                               }
-                               seq_printf(m, " boxes=%d width=%d", boxes, pB->i2eDataWidth16 ? 16 : 8);
-                               break;
-
-                       case POR_ID_II_4:
-                               seq_printf(m, "Board %d: ISA-4 ports=4 boxes=1", i);
-                               tports = ports = 4;
-                               break;
-
-                       case POR_ID_II_8:
-                               seq_printf(m, "Board %d: ISA-8-std ports=8 boxes=1", i);
-                               tports = ports = 8;
-                               break;
-
-                       case POR_ID_II_8R:
-                               seq_printf(m, "Board %d: ISA-8-RJ11 ports=8 boxes=1", i);
-                               tports = ports = 8;
-                               break;
-
-                       default:
-                               seq_printf(m, "Board %d: unknown", i);
-                               /* Don't try and probe for minor numbers */
-                               tports = ports = 0;
-                       }
-
-               } else {
-                       /* Don't try and probe for minor numbers */
-                       seq_printf(m, "Board %d: vacant", i);
-                       tports = ports = 0;
-               }
-
-               if( tports ) {
-                       seq_puts(m, " minors=");
-                       sep = "";
-                       for ( box = 0; box < ABS_MAX_BOXES; ++box )
-                       {
-                               for ( j = 0; j < ABS_BIGGEST_BOX; ++j )
-                               {
-                                       if ( pB->i2eChannelMap[box] & (1 << j) )
-                                       {
-                                               seq_printf(m, "%s%d", sep,
-                                                       j + ABS_BIGGEST_BOX *
-                                                       (box+i*ABS_MAX_BOXES));
-                                               sep = ",";
-                                       }
-                               }
-                       }
-               }
-               seq_putc(m, '\n');
-       }
-       return 0;
- }
-
-static int ip2_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, ip2_proc_show, NULL);
-}
-
-static const struct file_operations ip2_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = ip2_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-/******************************************************************************/
-/* Function:   ip2trace()                                                     */
-/* Parameters: Value to add to trace buffer                                   */
-/* Returns:    Nothing                                                        */
-/*                                                                            */
-/* Description:                                                               */
-/*                                                                            */
-/*                                                                            */
-/******************************************************************************/
-#ifdef IP2DEBUG_TRACE
-void
-ip2trace (unsigned short pn, unsigned char cat, unsigned char label, unsigned long codes, ...)
-{
-       long flags;
-       unsigned long *pCode = &codes;
-       union ip2breadcrumb bc;
-       i2ChanStrPtr  pCh;
-
-
-       tracebuf[tracestuff++] = jiffies;
-       if ( tracestuff == TRACEMAX ) {
-               tracestuff = 0;
-       }
-       if ( tracestuff == tracestrip ) {
-               if ( ++tracestrip == TRACEMAX ) {
-                       tracestrip = 0;
-               }
-               ++tracewrap;
-       }
-
-       bc.hdr.port  = 0xff & pn;
-       bc.hdr.cat   = cat;
-       bc.hdr.codes = (unsigned char)( codes & 0xff );
-       bc.hdr.label = label;
-       tracebuf[tracestuff++] = bc.value;
-
-       for (;;) {
-               if ( tracestuff == TRACEMAX ) {
-                       tracestuff = 0;
-               }
-               if ( tracestuff == tracestrip ) {
-                       if ( ++tracestrip == TRACEMAX ) {
-                               tracestrip = 0;
-                       }
-                       ++tracewrap;
-               }
-
-               if ( !codes-- )
-                       break;
-
-               tracebuf[tracestuff++] = *++pCode;
-       }
-}
-#endif
-
-
-MODULE_LICENSE("GPL");
-
-static struct pci_device_id ip2main_pci_tbl[] __devinitdata __used = {
-       { PCI_DEVICE(PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_IP2EX) },
-       { }
-};
-
-MODULE_DEVICE_TABLE(pci, ip2main_pci_tbl);
-
-MODULE_FIRMWARE("intelliport2.bin");
diff --git a/drivers/char/ip2/ip2trace.h b/drivers/char/ip2/ip2trace.h
deleted file mode 100644 (file)
index da20435..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-
-//
-union ip2breadcrumb 
-{
-       struct { 
-               unsigned char port, cat, codes, label;
-       } __attribute__ ((packed)) hdr;
-       unsigned long value;
-};
-
-#define ITRC_NO_PORT   0xFF
-#define CHANN  (pCh->port_index)
-
-#define        ITRC_ERROR      '!'
-#define        ITRC_INIT       'A'
-#define        ITRC_OPEN       'B'
-#define        ITRC_CLOSE      'C'
-#define        ITRC_DRAIN      'D'
-#define        ITRC_IOCTL      'E'
-#define        ITRC_FLUSH      'F'
-#define        ITRC_STATUS     'G'
-#define        ITRC_HANGUP     'H'
-#define        ITRC_INTR       'I'
-#define        ITRC_SFLOW      'J'
-#define        ITRC_SBCMD      'K'
-#define        ITRC_SICMD      'L'
-#define        ITRC_MODEM      'M'
-#define        ITRC_INPUT      'N'
-#define        ITRC_OUTPUT     'O'
-#define        ITRC_PUTC       'P'
-#define        ITRC_QUEUE      'Q'
-#define        ITRC_STFLW      'R'
-#define        ITRC_SFIFO      'S'
-#define        ITRC_VERIFY     'V'
-#define        ITRC_WRITE      'W'
-
-#define        ITRC_ENTER      0x00
-#define        ITRC_RETURN     0xFF
-
-#define        ITRC_QUEUE_ROOM 2
-#define        ITRC_QUEUE_CMD  6
-
diff --git a/drivers/char/ip2/ip2types.h b/drivers/char/ip2/ip2types.h
deleted file mode 100644 (file)
index 9d67b26..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*******************************************************************************
-*
-*   (c) 1998 by Computone Corporation
-*
-********************************************************************************
-*
-*
-*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
-*                serial I/O controllers.
-*
-*   DESCRIPTION: Driver constants and type definitions.
-*
-*   NOTES:
-*
-*******************************************************************************/
-#ifndef IP2TYPES_H
-#define IP2TYPES_H
-
-//*************
-//* Constants *
-//*************
-
-// Define some limits for this driver. Ports per board is a hardware limitation
-// that will not change. Current hardware limits this to 64 ports per board.
-// Boards per driver is a self-imposed limit.
-//
-#define IP2_MAX_BOARDS        4
-#define IP2_PORTS_PER_BOARD   ABS_MOST_PORTS
-#define IP2_MAX_PORTS         (IP2_MAX_BOARDS*IP2_PORTS_PER_BOARD)
-
-#define ISA    0
-#define PCI    1
-#define EISA   2
-
-//********************
-//* Type Definitions *
-//********************
-
-typedef struct tty_struct *   PTTY;
-typedef wait_queue_head_t   PWAITQ;
-
-typedef unsigned char         UCHAR;
-typedef unsigned int          UINT;
-typedef unsigned short        USHORT;
-typedef unsigned long         ULONG;
-
-typedef struct 
-{
-       short irq[IP2_MAX_BOARDS]; 
-       unsigned short addr[IP2_MAX_BOARDS];
-       int type[IP2_MAX_BOARDS];
-#ifdef CONFIG_PCI
-       struct pci_dev *pci_dev[IP2_MAX_BOARDS];
-#endif
-} ip2config_t;
-
-#endif
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
deleted file mode 100644 (file)
index 0b26627..0000000
+++ /dev/null
@@ -1,4507 +0,0 @@
-/*****************************************************************************/
-
-/*
- *     istallion.c  -- stallion intelligent multiport serial driver.
- *
- *     Copyright (C) 1996-1999  Stallion Technologies
- *     Copyright (C) 1994-1996  Greg Ungerer.
- *
- *     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.
- *
- */
-
-/*****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/seq_file.h>
-#include <linux/cdk.h>
-#include <linux/comstats.h>
-#include <linux/istallion.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/wait.h>
-#include <linux/eisa.h>
-#include <linux/ctype.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-
-#include <linux/pci.h>
-
-/*****************************************************************************/
-
-/*
- *     Define different board types. Not all of the following board types
- *     are supported by this driver. But I will use the standard "assigned"
- *     board numbers. Currently supported boards are abbreviated as:
- *     ECP = EasyConnection 8/64, ONB = ONboard, BBY = Brumby and
- *     STAL = Stallion.
- */
-#define        BRD_UNKNOWN     0
-#define        BRD_STALLION    1
-#define        BRD_BRUMBY4     2
-#define        BRD_ONBOARD2    3
-#define        BRD_ONBOARD     4
-#define        BRD_ONBOARDE    7
-#define        BRD_ECP         23
-#define BRD_ECPE       24
-#define        BRD_ECPMC       25
-#define        BRD_ECPPCI      29
-
-#define        BRD_BRUMBY      BRD_BRUMBY4
-
-/*
- *     Define a configuration structure to hold the board configuration.
- *     Need to set this up in the code (for now) with the boards that are
- *     to be configured into the system. This is what needs to be modified
- *     when adding/removing/modifying boards. Each line entry in the
- *     stli_brdconf[] array is a board. Each line contains io/irq/memory
- *     ranges for that board (as well as what type of board it is).
- *     Some examples:
- *             { BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },
- *     This line will configure an EasyConnection 8/64 at io address 2a0,
- *     and shared memory address of cc000. Multiple EasyConnection 8/64
- *     boards can share the same shared memory address space. No interrupt
- *     is required for this board type.
- *     Another example:
- *             { BRD_ECPE, 0x5000, 0, 0x80000000, 0, 0 },
- *     This line will configure an EasyConnection 8/64 EISA in slot 5 and
- *     shared memory address of 0x80000000 (2 GByte). Multiple
- *     EasyConnection 8/64 EISA boards can share the same shared memory
- *     address space. No interrupt is required for this board type.
- *     Another example:
- *             { BRD_ONBOARD, 0x240, 0, 0xd0000, 0, 0 },
- *     This line will configure an ONboard (ISA type) at io address 240,
- *     and shared memory address of d0000. Multiple ONboards can share
- *     the same shared memory address space. No interrupt required.
- *     Another example:
- *             { BRD_BRUMBY4, 0x360, 0, 0xc8000, 0, 0 },
- *     This line will configure a Brumby board (any number of ports!) at
- *     io address 360 and shared memory address of c8000. All Brumby boards
- *     configured into a system must have their own separate io and memory
- *     addresses. No interrupt is required.
- *     Another example:
- *             { BRD_STALLION, 0x330, 0, 0xd0000, 0, 0 },
- *     This line will configure an original Stallion board at io address 330
- *     and shared memory address d0000 (this would only be valid for a "V4.0"
- *     or Rev.O Stallion board). All Stallion boards configured into the
- *     system must have their own separate io and memory addresses. No
- *     interrupt is required.
- */
-
-struct stlconf {
-       int             brdtype;
-       int             ioaddr1;
-       int             ioaddr2;
-       unsigned long   memaddr;
-       int             irq;
-       int             irqtype;
-};
-
-static unsigned int stli_nrbrds;
-
-/* stli_lock must NOT be taken holding brd_lock */
-static spinlock_t stli_lock;   /* TTY logic lock */
-static spinlock_t brd_lock;    /* Board logic lock */
-
-/*
- *     There is some experimental EISA board detection code in this driver.
- *     By default it is disabled, but for those that want to try it out,
- *     then set the define below to be 1.
- */
-#define        STLI_EISAPROBE  0
-
-/*****************************************************************************/
-
-/*
- *     Define some important driver characteristics. Device major numbers
- *     allocated as per Linux Device Registry.
- */
-#ifndef        STL_SIOMEMMAJOR
-#define        STL_SIOMEMMAJOR         28
-#endif
-#ifndef        STL_SERIALMAJOR
-#define        STL_SERIALMAJOR         24
-#endif
-#ifndef        STL_CALLOUTMAJOR
-#define        STL_CALLOUTMAJOR        25
-#endif
-
-/*****************************************************************************/
-
-/*
- *     Define our local driver identity first. Set up stuff to deal with
- *     all the local structures required by a serial tty driver.
- */
-static char    *stli_drvtitle = "Stallion Intelligent Multiport Serial Driver";
-static char    *stli_drvname = "istallion";
-static char    *stli_drvversion = "5.6.0";
-static char    *stli_serialname = "ttyE";
-
-static struct tty_driver       *stli_serial;
-static const struct tty_port_operations stli_port_ops;
-
-#define        STLI_TXBUFSIZE          4096
-
-/*
- *     Use a fast local buffer for cooked characters. Typically a whole
- *     bunch of cooked characters come in for a port, 1 at a time. So we
- *     save those up into a local buffer, then write out the whole lot
- *     with a large memcpy. Just use 1 buffer for all ports, since its
- *     use it is only need for short periods of time by each port.
- */
-static char                    *stli_txcookbuf;
-static int                     stli_txcooksize;
-static int                     stli_txcookrealsize;
-static struct tty_struct       *stli_txcooktty;
-
-/*
- *     Define a local default termios struct. All ports will be created
- *     with this termios initially. Basically all it defines is a raw port
- *     at 9600 baud, 8 data bits, no parity, 1 stop bit.
- */
-static struct ktermios         stli_deftermios = {
-       .c_cflag        = (B9600 | CS8 | CREAD | HUPCL | CLOCAL),
-       .c_cc           = INIT_C_CC,
-       .c_ispeed       = 9600,
-       .c_ospeed       = 9600,
-};
-
-/*
- *     Define global stats structures. Not used often, and can be
- *     re-used for each stats call.
- */
-static comstats_t      stli_comstats;
-static combrd_t                stli_brdstats;
-static struct asystats stli_cdkstats;
-
-/*****************************************************************************/
-
-static DEFINE_MUTEX(stli_brdslock);
-static struct stlibrd  *stli_brds[STL_MAXBRDS];
-
-static int             stli_shared;
-
-/*
- *     Per board state flags. Used with the state field of the board struct.
- *     Not really much here... All we need to do is keep track of whether
- *     the board has been detected, and whether it is actually running a slave
- *     or not.
- */
-#define        BST_FOUND       0
-#define        BST_STARTED     1
-#define        BST_PROBED      2
-
-/*
- *     Define the set of port state flags. These are marked for internal
- *     state purposes only, usually to do with the state of communications
- *     with the slave. Most of them need to be updated atomically, so always
- *     use the bit setting operations (unless protected by cli/sti).
- */
-#define        ST_OPENING      2
-#define        ST_CLOSING      3
-#define        ST_CMDING       4
-#define        ST_TXBUSY       5
-#define        ST_RXING        6
-#define        ST_DOFLUSHRX    7
-#define        ST_DOFLUSHTX    8
-#define        ST_DOSIGS       9
-#define        ST_RXSTOP       10
-#define        ST_GETSIGS      11
-
-/*
- *     Define an array of board names as printable strings. Handy for
- *     referencing boards when printing trace and stuff.
- */
-static char    *stli_brdnames[] = {
-       "Unknown",
-       "Stallion",
-       "Brumby",
-       "ONboard-MC",
-       "ONboard",
-       "Brumby",
-       "Brumby",
-       "ONboard-EI",
-       NULL,
-       "ONboard",
-       "ONboard-MC",
-       "ONboard-MC",
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       "EasyIO",
-       "EC8/32-AT",
-       "EC8/32-MC",
-       "EC8/64-AT",
-       "EC8/64-EI",
-       "EC8/64-MC",
-       "EC8/32-PCI",
-       "EC8/64-PCI",
-       "EasyIO-PCI",
-       "EC/RA-PCI",
-};
-
-/*****************************************************************************/
-
-/*
- *     Define some string labels for arguments passed from the module
- *     load line. These allow for easy board definitions, and easy
- *     modification of the io, memory and irq resoucres.
- */
-
-static char    *board0[8];
-static char    *board1[8];
-static char    *board2[8];
-static char    *board3[8];
-
-static char    **stli_brdsp[] = {
-       (char **) &board0,
-       (char **) &board1,
-       (char **) &board2,
-       (char **) &board3
-};
-
-/*
- *     Define a set of common board names, and types. This is used to
- *     parse any module arguments.
- */
-
-static struct stlibrdtype {
-       char    *name;
-       int     type;
-} stli_brdstr[] = {
-       { "stallion", BRD_STALLION },
-       { "1", BRD_STALLION },
-       { "brumby", BRD_BRUMBY },
-       { "brumby4", BRD_BRUMBY },
-       { "brumby/4", BRD_BRUMBY },
-       { "brumby-4", BRD_BRUMBY },
-       { "brumby8", BRD_BRUMBY },
-       { "brumby/8", BRD_BRUMBY },
-       { "brumby-8", BRD_BRUMBY },
-       { "brumby16", BRD_BRUMBY },
-       { "brumby/16", BRD_BRUMBY },
-       { "brumby-16", BRD_BRUMBY },
-       { "2", BRD_BRUMBY },
-       { "onboard2", BRD_ONBOARD2 },
-       { "onboard-2", BRD_ONBOARD2 },
-       { "onboard/2", BRD_ONBOARD2 },
-       { "onboard-mc", BRD_ONBOARD2 },
-       { "onboard/mc", BRD_ONBOARD2 },
-       { "onboard-mca", BRD_ONBOARD2 },
-       { "onboard/mca", BRD_ONBOARD2 },
-       { "3", BRD_ONBOARD2 },
-       { "onboard", BRD_ONBOARD },
-       { "onboardat", BRD_ONBOARD },
-       { "4", BRD_ONBOARD },
-       { "onboarde", BRD_ONBOARDE },
-       { "onboard-e", BRD_ONBOARDE },
-       { "onboard/e", BRD_ONBOARDE },
-       { "onboard-ei", BRD_ONBOARDE },
-       { "onboard/ei", BRD_ONBOARDE },
-       { "7", BRD_ONBOARDE },
-       { "ecp", BRD_ECP },
-       { "ecpat", BRD_ECP },
-       { "ec8/64", BRD_ECP },
-       { "ec8/64-at", BRD_ECP },
-       { "ec8/64-isa", BRD_ECP },
-       { "23", BRD_ECP },
-       { "ecpe", BRD_ECPE },
-       { "ecpei", BRD_ECPE },
-       { "ec8/64-e", BRD_ECPE },
-       { "ec8/64-ei", BRD_ECPE },
-       { "24", BRD_ECPE },
-       { "ecpmc", BRD_ECPMC },
-       { "ec8/64-mc", BRD_ECPMC },
-       { "ec8/64-mca", BRD_ECPMC },
-       { "25", BRD_ECPMC },
-       { "ecppci", BRD_ECPPCI },
-       { "ec/ra", BRD_ECPPCI },
-       { "ec/ra-pc", BRD_ECPPCI },
-       { "ec/ra-pci", BRD_ECPPCI },
-       { "29", BRD_ECPPCI },
-};
-
-/*
- *     Define the module agruments.
- */
-MODULE_AUTHOR("Greg Ungerer");
-MODULE_DESCRIPTION("Stallion Intelligent Multiport Serial Driver");
-MODULE_LICENSE("GPL");
-
-
-module_param_array(board0, charp, NULL, 0);
-MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,memaddr]");
-module_param_array(board1, charp, NULL, 0);
-MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,memaddr]");
-module_param_array(board2, charp, NULL, 0);
-MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,memaddr]");
-module_param_array(board3, charp, NULL, 0);
-MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,memaddr]");
-
-#if STLI_EISAPROBE != 0
-/*
- *     Set up a default memory address table for EISA board probing.
- *     The default addresses are all bellow 1Mbyte, which has to be the
- *     case anyway. They should be safe, since we only read values from
- *     them, and interrupts are disabled while we do it. If the higher
- *     memory support is compiled in then we also try probing around
- *     the 1Gb, 2Gb and 3Gb areas as well...
- */
-static unsigned long   stli_eisamemprobeaddrs[] = {
-       0xc0000,    0xd0000,    0xe0000,    0xf0000,
-       0x80000000, 0x80010000, 0x80020000, 0x80030000,
-       0x40000000, 0x40010000, 0x40020000, 0x40030000,
-       0xc0000000, 0xc0010000, 0xc0020000, 0xc0030000,
-       0xff000000, 0xff010000, 0xff020000, 0xff030000,
-};
-
-static int     stli_eisamempsize = ARRAY_SIZE(stli_eisamemprobeaddrs);
-#endif
-
-/*
- *     Define the Stallion PCI vendor and device IDs.
- */
-#ifndef PCI_DEVICE_ID_ECRA
-#define        PCI_DEVICE_ID_ECRA              0x0004
-#endif
-
-static struct pci_device_id istallion_pci_tbl[] = {
-       { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECRA), },
-       { 0 }
-};
-MODULE_DEVICE_TABLE(pci, istallion_pci_tbl);
-
-static struct pci_driver stli_pcidriver;
-
-/*****************************************************************************/
-
-/*
- *     Hardware configuration info for ECP boards. These defines apply
- *     to the directly accessible io ports of the ECP. There is a set of
- *     defines for each ECP board type, ISA, EISA, MCA and PCI.
- */
-#define        ECP_IOSIZE      4
-
-#define        ECP_MEMSIZE     (128 * 1024)
-#define        ECP_PCIMEMSIZE  (256 * 1024)
-
-#define        ECP_ATPAGESIZE  (4 * 1024)
-#define        ECP_MCPAGESIZE  (4 * 1024)
-#define        ECP_EIPAGESIZE  (64 * 1024)
-#define        ECP_PCIPAGESIZE (64 * 1024)
-
-#define        STL_EISAID      0x8c4e
-
-/*
- *     Important defines for the ISA class of ECP board.
- */
-#define        ECP_ATIREG      0
-#define        ECP_ATCONFR     1
-#define        ECP_ATMEMAR     2
-#define        ECP_ATMEMPR     3
-#define        ECP_ATSTOP      0x1
-#define        ECP_ATINTENAB   0x10
-#define        ECP_ATENABLE    0x20
-#define        ECP_ATDISABLE   0x00
-#define        ECP_ATADDRMASK  0x3f000
-#define        ECP_ATADDRSHFT  12
-
-/*
- *     Important defines for the EISA class of ECP board.
- */
-#define        ECP_EIIREG      0
-#define        ECP_EIMEMARL    1
-#define        ECP_EICONFR     2
-#define        ECP_EIMEMARH    3
-#define        ECP_EIENABLE    0x1
-#define        ECP_EIDISABLE   0x0
-#define        ECP_EISTOP      0x4
-#define        ECP_EIEDGE      0x00
-#define        ECP_EILEVEL     0x80
-#define        ECP_EIADDRMASKL 0x00ff0000
-#define        ECP_EIADDRSHFTL 16
-#define        ECP_EIADDRMASKH 0xff000000
-#define        ECP_EIADDRSHFTH 24
-#define        ECP_EIBRDENAB   0xc84
-
-#define        ECP_EISAID      0x4
-
-/*
- *     Important defines for the Micro-channel class of ECP board.
- *     (It has a lot in common with the ISA boards.)
- */
-#define        ECP_MCIREG      0
-#define        ECP_MCCONFR     1
-#define        ECP_MCSTOP      0x20
-#define        ECP_MCENABLE    0x80
-#define        ECP_MCDISABLE   0x00
-
-/*
- *     Important defines for the PCI class of ECP board.
- *     (It has a lot in common with the other ECP boards.)
- */
-#define        ECP_PCIIREG     0
-#define        ECP_PCICONFR    1
-#define        ECP_PCISTOP     0x01
-
-/*
- *     Hardware configuration info for ONboard and Brumby boards. These
- *     defines apply to the directly accessible io ports of these boards.
- */
-#define        ONB_IOSIZE      16
-#define        ONB_MEMSIZE     (64 * 1024)
-#define        ONB_ATPAGESIZE  (64 * 1024)
-#define        ONB_MCPAGESIZE  (64 * 1024)
-#define        ONB_EIMEMSIZE   (128 * 1024)
-#define        ONB_EIPAGESIZE  (64 * 1024)
-
-/*
- *     Important defines for the ISA class of ONboard board.
- */
-#define        ONB_ATIREG      0
-#define        ONB_ATMEMAR     1
-#define        ONB_ATCONFR     2
-#define        ONB_ATSTOP      0x4
-#define        ONB_ATENABLE    0x01
-#define        ONB_ATDISABLE   0x00
-#define        ONB_ATADDRMASK  0xff0000
-#define        ONB_ATADDRSHFT  16
-
-#define        ONB_MEMENABLO   0
-#define        ONB_MEMENABHI   0x02
-
-/*
- *     Important defines for the EISA class of ONboard board.
- */
-#define        ONB_EIIREG      0
-#define        ONB_EIMEMARL    1
-#define        ONB_EICONFR     2
-#define        ONB_EIMEMARH    3
-#define        ONB_EIENABLE    0x1
-#define        ONB_EIDISABLE   0x0
-#define        ONB_EISTOP      0x4
-#define        ONB_EIEDGE      0x00
-#define        ONB_EILEVEL     0x80
-#define        ONB_EIADDRMASKL 0x00ff0000
-#define        ONB_EIADDRSHFTL 16
-#define        ONB_EIADDRMASKH 0xff000000
-#define        ONB_EIADDRSHFTH 24
-#define        ONB_EIBRDENAB   0xc84
-
-#define        ONB_EISAID      0x1
-
-/*
- *     Important defines for the Brumby boards. They are pretty simple,
- *     there is not much that is programmably configurable.
- */
-#define        BBY_IOSIZE      16
-#define        BBY_MEMSIZE     (64 * 1024)
-#define        BBY_PAGESIZE    (16 * 1024)
-
-#define        BBY_ATIREG      0
-#define        BBY_ATCONFR     1
-#define        BBY_ATSTOP      0x4
-
-/*
- *     Important defines for the Stallion boards. They are pretty simple,
- *     there is not much that is programmably configurable.
- */
-#define        STAL_IOSIZE     16
-#define        STAL_MEMSIZE    (64 * 1024)
-#define        STAL_PAGESIZE   (64 * 1024)
-
-/*
- *     Define the set of status register values for EasyConnection panels.
- *     The signature will return with the status value for each panel. From
- *     this we can determine what is attached to the board - before we have
- *     actually down loaded any code to it.
- */
-#define        ECH_PNLSTATUS   2
-#define        ECH_PNL16PORT   0x20
-#define        ECH_PNLIDMASK   0x07
-#define        ECH_PNLXPID     0x40
-#define        ECH_PNLINTRPEND 0x80
-
-/*
- *     Define some macros to do things to the board. Even those these boards
- *     are somewhat related there is often significantly different ways of
- *     doing some operation on it (like enable, paging, reset, etc). So each
- *     board class has a set of functions which do the commonly required
- *     operations. The macros below basically just call these functions,
- *     generally checking for a NULL function - which means that the board
- *     needs nothing done to it to achieve this operation!
- */
-#define        EBRDINIT(brdp)                                          \
-       if (brdp->init != NULL)                                 \
-               (* brdp->init)(brdp)
-
-#define        EBRDENABLE(brdp)                                        \
-       if (brdp->enable != NULL)                               \
-               (* brdp->enable)(brdp);
-
-#define        EBRDDISABLE(brdp)                                       \
-       if (brdp->disable != NULL)                              \
-               (* brdp->disable)(brdp);
-
-#define        EBRDINTR(brdp)                                          \
-       if (brdp->intr != NULL)                                 \
-               (* brdp->intr)(brdp);
-
-#define        EBRDRESET(brdp)                                         \
-       if (brdp->reset != NULL)                                \
-               (* brdp->reset)(brdp);
-
-#define        EBRDGETMEMPTR(brdp,offset)                              \
-       (* brdp->getmemptr)(brdp, offset, __LINE__)
-
-/*
- *     Define the maximal baud rate, and the default baud base for ports.
- */
-#define        STL_MAXBAUD     460800
-#define        STL_BAUDBASE    115200
-#define        STL_CLOSEDELAY  (5 * HZ / 10)
-
-/*****************************************************************************/
-
-/*
- *     Define macros to extract a brd or port number from a minor number.
- */
-#define        MINOR2BRD(min)          (((min) & 0xc0) >> 6)
-#define        MINOR2PORT(min)         ((min) & 0x3f)
-
-/*****************************************************************************/
-
-/*
- *     Prototype all functions in this driver!
- */
-
-static int     stli_parsebrd(struct stlconf *confp, char **argp);
-static int     stli_open(struct tty_struct *tty, struct file *filp);
-static void    stli_close(struct tty_struct *tty, struct file *filp);
-static int     stli_write(struct tty_struct *tty, const unsigned char *buf, int count);
-static int     stli_putchar(struct tty_struct *tty, unsigned char ch);
-static void    stli_flushchars(struct tty_struct *tty);
-static int     stli_writeroom(struct tty_struct *tty);
-static int     stli_charsinbuffer(struct tty_struct *tty);
-static int     stli_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
-static void    stli_settermios(struct tty_struct *tty, struct ktermios *old);
-static void    stli_throttle(struct tty_struct *tty);
-static void    stli_unthrottle(struct tty_struct *tty);
-static void    stli_stop(struct tty_struct *tty);
-static void    stli_start(struct tty_struct *tty);
-static void    stli_flushbuffer(struct tty_struct *tty);
-static int     stli_breakctl(struct tty_struct *tty, int state);
-static void    stli_waituntilsent(struct tty_struct *tty, int timeout);
-static void    stli_sendxchar(struct tty_struct *tty, char ch);
-static void    stli_hangup(struct tty_struct *tty);
-
-static int     stli_brdinit(struct stlibrd *brdp);
-static int     stli_startbrd(struct stlibrd *brdp);
-static ssize_t stli_memread(struct file *fp, char __user *buf, size_t count, loff_t *offp);
-static ssize_t stli_memwrite(struct file *fp, const char __user *buf, size_t count, loff_t *offp);
-static long    stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg);
-static void    stli_brdpoll(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp);
-static void    stli_poll(unsigned long arg);
-static int     stli_hostcmd(struct stlibrd *brdp, struct stliport *portp);
-static int     stli_initopen(struct tty_struct *tty, struct stlibrd *brdp, struct stliport *portp);
-static int     stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait);
-static int     stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait);
-static int     stli_setport(struct tty_struct *tty);
-static int     stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback);
-static void    stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback);
-static void    __stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback);
-static void    stli_dodelaycmd(struct stliport *portp, cdkctrl_t __iomem *cp);
-static void    stli_mkasyport(struct tty_struct *tty, struct stliport *portp, asyport_t *pp, struct ktermios *tiosp);
-static void    stli_mkasysigs(asysigs_t *sp, int dtr, int rts);
-static long    stli_mktiocm(unsigned long sigvalue);
-static void    stli_read(struct stlibrd *brdp, struct stliport *portp);
-static int     stli_getserial(struct stliport *portp, struct serial_struct __user *sp);
-static int     stli_setserial(struct tty_struct *tty, struct serial_struct __user *sp);
-static int     stli_getbrdstats(combrd_t __user *bp);
-static int     stli_getportstats(struct tty_struct *tty, struct stliport *portp, comstats_t __user *cp);
-static int     stli_portcmdstats(struct tty_struct *tty, struct stliport *portp);
-static int     stli_clrportstats(struct stliport *portp, comstats_t __user *cp);
-static int     stli_getportstruct(struct stliport __user *arg);
-static int     stli_getbrdstruct(struct stlibrd __user *arg);
-static struct stlibrd *stli_allocbrd(void);
-
-static void    stli_ecpinit(struct stlibrd *brdp);
-static void    stli_ecpenable(struct stlibrd *brdp);
-static void    stli_ecpdisable(struct stlibrd *brdp);
-static void __iomem *stli_ecpgetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
-static void    stli_ecpreset(struct stlibrd *brdp);
-static void    stli_ecpintr(struct stlibrd *brdp);
-static void    stli_ecpeiinit(struct stlibrd *brdp);
-static void    stli_ecpeienable(struct stlibrd *brdp);
-static void    stli_ecpeidisable(struct stlibrd *brdp);
-static void __iomem *stli_ecpeigetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
-static void    stli_ecpeireset(struct stlibrd *brdp);
-static void    stli_ecpmcenable(struct stlibrd *brdp);
-static void    stli_ecpmcdisable(struct stlibrd *brdp);
-static void __iomem *stli_ecpmcgetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
-static void    stli_ecpmcreset(struct stlibrd *brdp);
-static void    stli_ecppciinit(struct stlibrd *brdp);
-static void __iomem *stli_ecppcigetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
-static void    stli_ecppcireset(struct stlibrd *brdp);
-
-static void    stli_onbinit(struct stlibrd *brdp);
-static void    stli_onbenable(struct stlibrd *brdp);
-static void    stli_onbdisable(struct stlibrd *brdp);
-static void __iomem *stli_onbgetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
-static void    stli_onbreset(struct stlibrd *brdp);
-static void    stli_onbeinit(struct stlibrd *brdp);
-static void    stli_onbeenable(struct stlibrd *brdp);
-static void    stli_onbedisable(struct stlibrd *brdp);
-static void __iomem *stli_onbegetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
-static void    stli_onbereset(struct stlibrd *brdp);
-static void    stli_bbyinit(struct stlibrd *brdp);
-static void __iomem *stli_bbygetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
-static void    stli_bbyreset(struct stlibrd *brdp);
-static void    stli_stalinit(struct stlibrd *brdp);
-static void __iomem *stli_stalgetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
-static void    stli_stalreset(struct stlibrd *brdp);
-
-static struct stliport *stli_getport(unsigned int brdnr, unsigned int panelnr, unsigned int portnr);
-
-static int     stli_initecp(struct stlibrd *brdp);
-static int     stli_initonb(struct stlibrd *brdp);
-#if STLI_EISAPROBE != 0
-static int     stli_eisamemprobe(struct stlibrd *brdp);
-#endif
-static int     stli_initports(struct stlibrd *brdp);
-
-/*****************************************************************************/
-
-/*
- *     Define the driver info for a user level shared memory device. This
- *     device will work sort of like the /dev/kmem device - except that it
- *     will give access to the shared memory on the Stallion intelligent
- *     board. This is also a very useful debugging tool.
- */
-static const struct file_operations    stli_fsiomem = {
-       .owner          = THIS_MODULE,
-       .read           = stli_memread,
-       .write          = stli_memwrite,
-       .unlocked_ioctl = stli_memioctl,
-       .llseek         = default_llseek,
-};
-
-/*****************************************************************************/
-
-/*
- *     Define a timer_list entry for our poll routine. The slave board
- *     is polled every so often to see if anything needs doing. This is
- *     much cheaper on host cpu than using interrupts. It turns out to
- *     not increase character latency by much either...
- */
-static DEFINE_TIMER(stli_timerlist, stli_poll, 0, 0);
-
-static int     stli_timeron;
-
-/*
- *     Define the calculation for the timeout routine.
- */
-#define        STLI_TIMEOUT    (jiffies + 1)
-
-/*****************************************************************************/
-
-static struct class *istallion_class;
-
-static void stli_cleanup_ports(struct stlibrd *brdp)
-{
-       struct stliport *portp;
-       unsigned int j;
-       struct tty_struct *tty;
-
-       for (j = 0; j < STL_MAXPORTS; j++) {
-               portp = brdp->ports[j];
-               if (portp != NULL) {
-                       tty = tty_port_tty_get(&portp->port);
-                       if (tty != NULL) {
-                               tty_hangup(tty);
-                               tty_kref_put(tty);
-                       }
-                       kfree(portp);
-               }
-       }
-}
-
-/*****************************************************************************/
-
-/*
- *     Parse the supplied argument string, into the board conf struct.
- */
-
-static int stli_parsebrd(struct stlconf *confp, char **argp)
-{
-       unsigned int i;
-       char *sp;
-
-       if (argp[0] == NULL || *argp[0] == 0)
-               return 0;
-
-       for (sp = argp[0], i = 0; ((*sp != 0) && (i < 25)); sp++, i++)
-               *sp = tolower(*sp);
-
-       for (i = 0; i < ARRAY_SIZE(stli_brdstr); i++) {
-               if (strcmp(stli_brdstr[i].name, argp[0]) == 0)
-                       break;
-       }
-       if (i == ARRAY_SIZE(stli_brdstr)) {
-               printk(KERN_WARNING "istallion: unknown board name, %s?\n", argp[0]);
-               return 0;
-       }
-
-       confp->brdtype = stli_brdstr[i].type;
-       if (argp[1] != NULL && *argp[1] != 0)
-               confp->ioaddr1 = simple_strtoul(argp[1], NULL, 0);
-       if (argp[2] !=  NULL && *argp[2] != 0)
-               confp->memaddr = simple_strtoul(argp[2], NULL, 0);
-       return(1);
-}
-
-/*****************************************************************************/
-
-/*
- *     On the first open of the device setup the port hardware, and
- *     initialize the per port data structure. Since initializing the port
- *     requires several commands to the board we will need to wait for any
- *     other open that is already initializing the port.
- *
- *     Locking: protected by the port mutex.
- */
-
-static int stli_activate(struct tty_port *port, struct tty_struct *tty)
-{
-       struct stliport *portp = container_of(port, struct stliport, port);
-       struct stlibrd *brdp = stli_brds[portp->brdnr];
-       int rc;
-
-       if ((rc = stli_initopen(tty, brdp, portp)) >= 0)
-               clear_bit(TTY_IO_ERROR, &tty->flags);
-       wake_up_interruptible(&portp->raw_wait);
-       return rc;
-}
-
-static int stli_open(struct tty_struct *tty, struct file *filp)
-{
-       struct stlibrd *brdp;
-       struct stliport *portp;
-       unsigned int minordev, brdnr, portnr;
-
-       minordev = tty->index;
-       brdnr = MINOR2BRD(minordev);
-       if (brdnr >= stli_nrbrds)
-               return -ENODEV;
-       brdp = stli_brds[brdnr];
-       if (brdp == NULL)
-               return -ENODEV;
-       if (!test_bit(BST_STARTED, &brdp->state))
-               return -ENODEV;
-       portnr = MINOR2PORT(minordev);
-       if (portnr > brdp->nrports)
-               return -ENODEV;
-
-       portp = brdp->ports[portnr];
-       if (portp == NULL)
-               return -ENODEV;
-       if (portp->devnr < 1)
-               return -ENODEV;
-
-       tty->driver_data = portp;
-       return tty_port_open(&portp->port, tty, filp);
-}
-
-
-/*****************************************************************************/
-
-static void stli_shutdown(struct tty_port *port)
-{
-       struct stlibrd *brdp;
-       unsigned long ftype;
-       unsigned long flags;
-       struct stliport *portp = container_of(port, struct stliport, port);
-
-       if (portp->brdnr >= stli_nrbrds)
-               return;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return;
-
-       /*
-        *      May want to wait for data to drain before closing. The BUSY
-        *      flag keeps track of whether we are still transmitting or not.
-        *      It is updated by messages from the slave - indicating when all
-        *      chars really have drained.
-        */
-
-       if (!test_bit(ST_CLOSING, &portp->state))
-               stli_rawclose(brdp, portp, 0, 0);
-
-       spin_lock_irqsave(&stli_lock, flags);
-       clear_bit(ST_TXBUSY, &portp->state);
-       clear_bit(ST_RXSTOP, &portp->state);
-       spin_unlock_irqrestore(&stli_lock, flags);
-
-       ftype = FLUSHTX | FLUSHRX;
-       stli_cmdwait(brdp, portp, A_FLUSH, &ftype, sizeof(u32), 0);
-}
-
-static void stli_close(struct tty_struct *tty, struct file *filp)
-{
-       struct stliport *portp = tty->driver_data;
-       unsigned long flags;
-       if (portp == NULL)
-               return;
-       spin_lock_irqsave(&stli_lock, flags);
-       /*      Flush any internal buffering out first */
-       if (tty == stli_txcooktty)
-               stli_flushchars(tty);
-       spin_unlock_irqrestore(&stli_lock, flags);
-       tty_port_close(&portp->port, tty, filp);
-}
-
-/*****************************************************************************/
-
-/*
- *     Carry out first open operations on a port. This involves a number of
- *     commands to be sent to the slave. We need to open the port, set the
- *     notification events, set the initial port settings, get and set the
- *     initial signal values. We sleep and wait in between each one. But
- *     this still all happens pretty quickly.
- */
-
-static int stli_initopen(struct tty_struct *tty,
-                               struct stlibrd *brdp, struct stliport *portp)
-{
-       asynotify_t nt;
-       asyport_t aport;
-       int rc;
-
-       if ((rc = stli_rawopen(brdp, portp, 0, 1)) < 0)
-               return rc;
-
-       memset(&nt, 0, sizeof(asynotify_t));
-       nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK);
-       nt.signal = SG_DCD;
-       if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt,
-           sizeof(asynotify_t), 0)) < 0)
-               return rc;
-
-       stli_mkasyport(tty, portp, &aport, tty->termios);
-       if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport,
-           sizeof(asyport_t), 0)) < 0)
-               return rc;
-
-       set_bit(ST_GETSIGS, &portp->state);
-       if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig,
-           sizeof(asysigs_t), 1)) < 0)
-               return rc;
-       if (test_and_clear_bit(ST_GETSIGS, &portp->state))
-               portp->sigs = stli_mktiocm(portp->asig.sigvalue);
-       stli_mkasysigs(&portp->asig, 1, 1);
-       if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
-           sizeof(asysigs_t), 0)) < 0)
-               return rc;
-
-       return 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Send an open message to the slave. This will sleep waiting for the
- *     acknowledgement, so must have user context. We need to co-ordinate
- *     with close events here, since we don't want open and close events
- *     to overlap.
- */
-
-static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait)
-{
-       cdkhdr_t __iomem *hdrp;
-       cdkctrl_t __iomem *cp;
-       unsigned char __iomem *bits;
-       unsigned long flags;
-       int rc;
-
-/*
- *     Send a message to the slave to open this port.
- */
-
-/*
- *     Slave is already closing this port. This can happen if a hangup
- *     occurs on this port. So we must wait until it is complete. The
- *     order of opens and closes may not be preserved across shared
- *     memory, so we must wait until it is complete.
- */
-       wait_event_interruptible_tty(portp->raw_wait,
-                       !test_bit(ST_CLOSING, &portp->state));
-       if (signal_pending(current)) {
-               return -ERESTARTSYS;
-       }
-
-/*
- *     Everything is ready now, so write the open message into shared
- *     memory. Once the message is in set the service bits to say that
- *     this port wants service.
- */
-       spin_lock_irqsave(&brd_lock, flags);
-       EBRDENABLE(brdp);
-       cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
-       writel(arg, &cp->openarg);
-       writeb(1, &cp->open);
-       hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-       bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
-               portp->portidx;
-       writeb(readb(bits) | portp->portbit, bits);
-       EBRDDISABLE(brdp);
-
-       if (wait == 0) {
-               spin_unlock_irqrestore(&brd_lock, flags);
-               return 0;
-       }
-
-/*
- *     Slave is in action, so now we must wait for the open acknowledgment
- *     to come back.
- */
-       rc = 0;
-       set_bit(ST_OPENING, &portp->state);
-       spin_unlock_irqrestore(&brd_lock, flags);
-
-       wait_event_interruptible_tty(portp->raw_wait,
-                       !test_bit(ST_OPENING, &portp->state));
-       if (signal_pending(current))
-               rc = -ERESTARTSYS;
-
-       if ((rc == 0) && (portp->rc != 0))
-               rc = -EIO;
-       return rc;
-}
-
-/*****************************************************************************/
-
-/*
- *     Send a close message to the slave. Normally this will sleep waiting
- *     for the acknowledgement, but if wait parameter is 0 it will not. If
- *     wait is true then must have user context (to sleep).
- */
-
-static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait)
-{
-       cdkhdr_t __iomem *hdrp;
-       cdkctrl_t __iomem *cp;
-       unsigned char __iomem *bits;
-       unsigned long flags;
-       int rc;
-
-/*
- *     Slave is already closing this port. This can happen if a hangup
- *     occurs on this port.
- */
-       if (wait) {
-               wait_event_interruptible_tty(portp->raw_wait,
-                               !test_bit(ST_CLOSING, &portp->state));
-               if (signal_pending(current)) {
-                       return -ERESTARTSYS;
-               }
-       }
-
-/*
- *     Write the close command into shared memory.
- */
-       spin_lock_irqsave(&brd_lock, flags);
-       EBRDENABLE(brdp);
-       cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
-       writel(arg, &cp->closearg);
-       writeb(1, &cp->close);
-       hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-       bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
-               portp->portidx;
-       writeb(readb(bits) |portp->portbit, bits);
-       EBRDDISABLE(brdp);
-
-       set_bit(ST_CLOSING, &portp->state);
-       spin_unlock_irqrestore(&brd_lock, flags);
-
-       if (wait == 0)
-               return 0;
-
-/*
- *     Slave is in action, so now we must wait for the open acknowledgment
- *     to come back.
- */
-       rc = 0;
-       wait_event_interruptible_tty(portp->raw_wait,
-                       !test_bit(ST_CLOSING, &portp->state));
-       if (signal_pending(current))
-               rc = -ERESTARTSYS;
-
-       if ((rc == 0) && (portp->rc != 0))
-               rc = -EIO;
-       return rc;
-}
-
-/*****************************************************************************/
-
-/*
- *     Send a command to the slave and wait for the response. This must
- *     have user context (it sleeps). This routine is generic in that it
- *     can send any type of command. Its purpose is to wait for that command
- *     to complete (as opposed to initiating the command then returning).
- */
-
-static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback)
-{
-       /*
-        * no need for wait_event_tty because clearing ST_CMDING cannot block
-        * on BTM
-        */
-       wait_event_interruptible(portp->raw_wait,
-                       !test_bit(ST_CMDING, &portp->state));
-       if (signal_pending(current))
-               return -ERESTARTSYS;
-
-       stli_sendcmd(brdp, portp, cmd, arg, size, copyback);
-
-       wait_event_interruptible(portp->raw_wait,
-                       !test_bit(ST_CMDING, &portp->state));
-       if (signal_pending(current))
-               return -ERESTARTSYS;
-
-       if (portp->rc != 0)
-               return -EIO;
-       return 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Send the termios settings for this port to the slave. This sleeps
- *     waiting for the command to complete - so must have user context.
- */
-
-static int stli_setport(struct tty_struct *tty)
-{
-       struct stliport *portp = tty->driver_data;
-       struct stlibrd *brdp;
-       asyport_t aport;
-
-       if (portp == NULL)
-               return -ENODEV;
-       if (portp->brdnr >= stli_nrbrds)
-               return -ENODEV;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return -ENODEV;
-
-       stli_mkasyport(tty, portp, &aport, tty->termios);
-       return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0));
-}
-
-/*****************************************************************************/
-
-static int stli_carrier_raised(struct tty_port *port)
-{
-       struct stliport *portp = container_of(port, struct stliport, port);
-       return (portp->sigs & TIOCM_CD) ? 1 : 0;
-}
-
-static void stli_dtr_rts(struct tty_port *port, int on)
-{
-       struct stliport *portp = container_of(port, struct stliport, port);
-       struct stlibrd *brdp = stli_brds[portp->brdnr];
-       stli_mkasysigs(&portp->asig, on, on);
-       if (stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
-               sizeof(asysigs_t), 0) < 0)
-                       printk(KERN_WARNING "istallion: dtr set failed.\n");
-}
-
-
-/*****************************************************************************/
-
-/*
- *     Write routine. Take the data and put it in the shared memory ring
- *     queue. If port is not already sending chars then need to mark the
- *     service bits for this port.
- */
-
-static int stli_write(struct tty_struct *tty, const unsigned char *buf, int count)
-{
-       cdkasy_t __iomem *ap;
-       cdkhdr_t __iomem *hdrp;
-       unsigned char __iomem *bits;
-       unsigned char __iomem *shbuf;
-       unsigned char *chbuf;
-       struct stliport *portp;
-       struct stlibrd *brdp;
-       unsigned int len, stlen, head, tail, size;
-       unsigned long flags;
-
-       if (tty == stli_txcooktty)
-               stli_flushchars(tty);
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return 0;
-       if (portp->brdnr >= stli_nrbrds)
-               return 0;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return 0;
-       chbuf = (unsigned char *) buf;
-
-/*
- *     All data is now local, shove as much as possible into shared memory.
- */
-       spin_lock_irqsave(&brd_lock, flags);
-       EBRDENABLE(brdp);
-       ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
-       head = (unsigned int) readw(&ap->txq.head);
-       tail = (unsigned int) readw(&ap->txq.tail);
-       if (tail != ((unsigned int) readw(&ap->txq.tail)))
-               tail = (unsigned int) readw(&ap->txq.tail);
-       size = portp->txsize;
-       if (head >= tail) {
-               len = size - (head - tail) - 1;
-               stlen = size - head;
-       } else {
-               len = tail - head - 1;
-               stlen = len;
-       }
-
-       len = min(len, (unsigned int)count);
-       count = 0;
-       shbuf = (char __iomem *) EBRDGETMEMPTR(brdp, portp->txoffset);
-
-       while (len > 0) {
-               stlen = min(len, stlen);
-               memcpy_toio(shbuf + head, chbuf, stlen);
-               chbuf += stlen;
-               len -= stlen;
-               count += stlen;
-               head += stlen;
-               if (head >= size) {
-                       head = 0;
-                       stlen = tail;
-               }
-       }
-
-       ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
-       writew(head, &ap->txq.head);
-       if (test_bit(ST_TXBUSY, &portp->state)) {
-               if (readl(&ap->changed.data) & DT_TXEMPTY)
-                       writel(readl(&ap->changed.data) & ~DT_TXEMPTY, &ap->changed.data);
-       }
-       hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-       bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
-               portp->portidx;
-       writeb(readb(bits) | portp->portbit, bits);
-       set_bit(ST_TXBUSY, &portp->state);
-       EBRDDISABLE(brdp);
-       spin_unlock_irqrestore(&brd_lock, flags);
-
-       return(count);
-}
-
-/*****************************************************************************/
-
-/*
- *     Output a single character. We put it into a temporary local buffer
- *     (for speed) then write out that buffer when the flushchars routine
- *     is called. There is a safety catch here so that if some other port
- *     writes chars before the current buffer has been, then we write them
- *     first them do the new ports.
- */
-
-static int stli_putchar(struct tty_struct *tty, unsigned char ch)
-{
-       if (tty != stli_txcooktty) {
-               if (stli_txcooktty != NULL)
-                       stli_flushchars(stli_txcooktty);
-               stli_txcooktty = tty;
-       }
-
-       stli_txcookbuf[stli_txcooksize++] = ch;
-       return 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Transfer characters from the local TX cooking buffer to the board.
- *     We sort of ignore the tty that gets passed in here. We rely on the
- *     info stored with the TX cook buffer to tell us which port to flush
- *     the data on. In any case we clean out the TX cook buffer, for re-use
- *     by someone else.
- */
-
-static void stli_flushchars(struct tty_struct *tty)
-{
-       cdkhdr_t __iomem *hdrp;
-       unsigned char __iomem *bits;
-       cdkasy_t __iomem *ap;
-       struct tty_struct *cooktty;
-       struct stliport *portp;
-       struct stlibrd *brdp;
-       unsigned int len, stlen, head, tail, size, count, cooksize;
-       unsigned char *buf;
-       unsigned char __iomem *shbuf;
-       unsigned long flags;
-
-       cooksize = stli_txcooksize;
-       cooktty = stli_txcooktty;
-       stli_txcooksize = 0;
-       stli_txcookrealsize = 0;
-       stli_txcooktty = NULL;
-
-       if (cooktty == NULL)
-               return;
-       if (tty != cooktty)
-               tty = cooktty;
-       if (cooksize == 0)
-               return;
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-       if (portp->brdnr >= stli_nrbrds)
-               return;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       EBRDENABLE(brdp);
-
-       ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
-       head = (unsigned int) readw(&ap->txq.head);
-       tail = (unsigned int) readw(&ap->txq.tail);
-       if (tail != ((unsigned int) readw(&ap->txq.tail)))
-               tail = (unsigned int) readw(&ap->txq.tail);
-       size = portp->txsize;
-       if (head >= tail) {
-               len = size - (head - tail) - 1;
-               stlen = size - head;
-       } else {
-               len = tail - head - 1;
-               stlen = len;
-       }
-
-       len = min(len, cooksize);
-       count = 0;
-       shbuf = EBRDGETMEMPTR(brdp, portp->txoffset);
-       buf = stli_txcookbuf;
-
-       while (len > 0) {
-               stlen = min(len, stlen);
-               memcpy_toio(shbuf + head, buf, stlen);
-               buf += stlen;
-               len -= stlen;
-               count += stlen;
-               head += stlen;
-               if (head >= size) {
-                       head = 0;
-                       stlen = tail;
-               }
-       }
-
-       ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
-       writew(head, &ap->txq.head);
-
-       if (test_bit(ST_TXBUSY, &portp->state)) {
-               if (readl(&ap->changed.data) & DT_TXEMPTY)
-                       writel(readl(&ap->changed.data) & ~DT_TXEMPTY, &ap->changed.data);
-       }
-       hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-       bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
-               portp->portidx;
-       writeb(readb(bits) | portp->portbit, bits);
-       set_bit(ST_TXBUSY, &portp->state);
-
-       EBRDDISABLE(brdp);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-static int stli_writeroom(struct tty_struct *tty)
-{
-       cdkasyrq_t __iomem *rp;
-       struct stliport *portp;
-       struct stlibrd *brdp;
-       unsigned int head, tail, len;
-       unsigned long flags;
-
-       if (tty == stli_txcooktty) {
-               if (stli_txcookrealsize != 0) {
-                       len = stli_txcookrealsize - stli_txcooksize;
-                       return len;
-               }
-       }
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return 0;
-       if (portp->brdnr >= stli_nrbrds)
-               return 0;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return 0;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       EBRDENABLE(brdp);
-       rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->txq;
-       head = (unsigned int) readw(&rp->head);
-       tail = (unsigned int) readw(&rp->tail);
-       if (tail != ((unsigned int) readw(&rp->tail)))
-               tail = (unsigned int) readw(&rp->tail);
-       len = (head >= tail) ? (portp->txsize - (head - tail)) : (tail - head);
-       len--;
-       EBRDDISABLE(brdp);
-       spin_unlock_irqrestore(&brd_lock, flags);
-
-       if (tty == stli_txcooktty) {
-               stli_txcookrealsize = len;
-               len -= stli_txcooksize;
-       }
-       return len;
-}
-
-/*****************************************************************************/
-
-/*
- *     Return the number of characters in the transmit buffer. Normally we
- *     will return the number of chars in the shared memory ring queue.
- *     We need to kludge around the case where the shared memory buffer is
- *     empty but not all characters have drained yet, for this case just
- *     return that there is 1 character in the buffer!
- */
-
-static int stli_charsinbuffer(struct tty_struct *tty)
-{
-       cdkasyrq_t __iomem *rp;
-       struct stliport *portp;
-       struct stlibrd *brdp;
-       unsigned int head, tail, len;
-       unsigned long flags;
-
-       if (tty == stli_txcooktty)
-               stli_flushchars(tty);
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return 0;
-       if (portp->brdnr >= stli_nrbrds)
-               return 0;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return 0;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       EBRDENABLE(brdp);
-       rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->txq;
-       head = (unsigned int) readw(&rp->head);
-       tail = (unsigned int) readw(&rp->tail);
-       if (tail != ((unsigned int) readw(&rp->tail)))
-               tail = (unsigned int) readw(&rp->tail);
-       len = (head >= tail) ? (head - tail) : (portp->txsize - (tail - head));
-       if ((len == 0) && test_bit(ST_TXBUSY, &portp->state))
-               len = 1;
-       EBRDDISABLE(brdp);
-       spin_unlock_irqrestore(&brd_lock, flags);
-
-       return len;
-}
-
-/*****************************************************************************/
-
-/*
- *     Generate the serial struct info.
- */
-
-static int stli_getserial(struct stliport *portp, struct serial_struct __user *sp)
-{
-       struct serial_struct sio;
-       struct stlibrd *brdp;
-
-       memset(&sio, 0, sizeof(struct serial_struct));
-       sio.type = PORT_UNKNOWN;
-       sio.line = portp->portnr;
-       sio.irq = 0;
-       sio.flags = portp->port.flags;
-       sio.baud_base = portp->baud_base;
-       sio.close_delay = portp->port.close_delay;
-       sio.closing_wait = portp->closing_wait;
-       sio.custom_divisor = portp->custom_divisor;
-       sio.xmit_fifo_size = 0;
-       sio.hub6 = 0;
-
-       brdp = stli_brds[portp->brdnr];
-       if (brdp != NULL)
-               sio.port = brdp->iobase;
-               
-       return copy_to_user(sp, &sio, sizeof(struct serial_struct)) ?
-                       -EFAULT : 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Set port according to the serial struct info.
- *     At this point we do not do any auto-configure stuff, so we will
- *     just quietly ignore any requests to change irq, etc.
- */
-
-static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *sp)
-{
-       struct serial_struct sio;
-       int rc;
-       struct stliport *portp = tty->driver_data;
-
-       if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
-               return -EFAULT;
-       if (!capable(CAP_SYS_ADMIN)) {
-               if ((sio.baud_base != portp->baud_base) ||
-                   (sio.close_delay != portp->port.close_delay) ||
-                   ((sio.flags & ~ASYNC_USR_MASK) !=
-                   (portp->port.flags & ~ASYNC_USR_MASK)))
-                       return -EPERM;
-       } 
-
-       portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) |
-               (sio.flags & ASYNC_USR_MASK);
-       portp->baud_base = sio.baud_base;
-       portp->port.close_delay = sio.close_delay;
-       portp->closing_wait = sio.closing_wait;
-       portp->custom_divisor = sio.custom_divisor;
-
-       if ((rc = stli_setport(tty)) < 0)
-               return rc;
-       return 0;
-}
-
-/*****************************************************************************/
-
-static int stli_tiocmget(struct tty_struct *tty)
-{
-       struct stliport *portp = tty->driver_data;
-       struct stlibrd *brdp;
-       int rc;
-
-       if (portp == NULL)
-               return -ENODEV;
-       if (portp->brdnr >= stli_nrbrds)
-               return 0;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return 0;
-       if (tty->flags & (1 << TTY_IO_ERROR))
-               return -EIO;
-
-       if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS,
-                              &portp->asig, sizeof(asysigs_t), 1)) < 0)
-               return rc;
-
-       return stli_mktiocm(portp->asig.sigvalue);
-}
-
-static int stli_tiocmset(struct tty_struct *tty,
-                        unsigned int set, unsigned int clear)
-{
-       struct stliport *portp = tty->driver_data;
-       struct stlibrd *brdp;
-       int rts = -1, dtr = -1;
-
-       if (portp == NULL)
-               return -ENODEV;
-       if (portp->brdnr >= stli_nrbrds)
-               return 0;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return 0;
-       if (tty->flags & (1 << TTY_IO_ERROR))
-               return -EIO;
-
-       if (set & TIOCM_RTS)
-               rts = 1;
-       if (set & TIOCM_DTR)
-               dtr = 1;
-       if (clear & TIOCM_RTS)
-               rts = 0;
-       if (clear & TIOCM_DTR)
-               dtr = 0;
-
-       stli_mkasysigs(&portp->asig, dtr, rts);
-
-       return stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
-                           sizeof(asysigs_t), 0);
-}
-
-static int stli_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
-{
-       struct stliport *portp;
-       struct stlibrd *brdp;
-       int rc;
-       void __user *argp = (void __user *)arg;
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return -ENODEV;
-       if (portp->brdnr >= stli_nrbrds)
-               return 0;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return 0;
-
-       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-           (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) {
-               if (tty->flags & (1 << TTY_IO_ERROR))
-                       return -EIO;
-       }
-
-       rc = 0;
-
-       switch (cmd) {
-       case TIOCGSERIAL:
-               rc = stli_getserial(portp, argp);
-               break;
-       case TIOCSSERIAL:
-               rc = stli_setserial(tty, argp);
-               break;
-       case STL_GETPFLAG:
-               rc = put_user(portp->pflag, (unsigned __user *)argp);
-               break;
-       case STL_SETPFLAG:
-               if ((rc = get_user(portp->pflag, (unsigned __user *)argp)) == 0)
-                       stli_setport(tty);
-               break;
-       case COM_GETPORTSTATS:
-               rc = stli_getportstats(tty, portp, argp);
-               break;
-       case COM_CLRPORTSTATS:
-               rc = stli_clrportstats(portp, argp);
-               break;
-       case TIOCSERCONFIG:
-       case TIOCSERGWILD:
-       case TIOCSERSWILD:
-       case TIOCSERGETLSR:
-       case TIOCSERGSTRUCT:
-       case TIOCSERGETMULTI:
-       case TIOCSERSETMULTI:
-       default:
-               rc = -ENOIOCTLCMD;
-               break;
-       }
-
-       return rc;
-}
-
-/*****************************************************************************/
-
-/*
- *     This routine assumes that we have user context and can sleep.
- *     Looks like it is true for the current ttys implementation..!!
- */
-
-static void stli_settermios(struct tty_struct *tty, struct ktermios *old)
-{
-       struct stliport *portp;
-       struct stlibrd *brdp;
-       struct ktermios *tiosp;
-       asyport_t aport;
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-       if (portp->brdnr >= stli_nrbrds)
-               return;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return;
-
-       tiosp = tty->termios;
-
-       stli_mkasyport(tty, portp, &aport, tiosp);
-       stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0);
-       stli_mkasysigs(&portp->asig, ((tiosp->c_cflag & CBAUD) ? 1 : 0), -1);
-       stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
-               sizeof(asysigs_t), 0);
-       if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0))
-               tty->hw_stopped = 0;
-       if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL))
-               wake_up_interruptible(&portp->port.open_wait);
-}
-
-/*****************************************************************************/
-
-/*
- *     Attempt to flow control who ever is sending us data. We won't really
- *     do any flow control action here. We can't directly, and even if we
- *     wanted to we would have to send a command to the slave. The slave
- *     knows how to flow control, and will do so when its buffers reach its
- *     internal high water marks. So what we will do is set a local state
- *     bit that will stop us sending any RX data up from the poll routine
- *     (which is the place where RX data from the slave is handled).
- */
-
-static void stli_throttle(struct tty_struct *tty)
-{
-       struct stliport *portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-       set_bit(ST_RXSTOP, &portp->state);
-}
-
-/*****************************************************************************/
-
-/*
- *     Unflow control the device sending us data... That means that all
- *     we have to do is clear the RXSTOP state bit. The next poll call
- *     will then be able to pass the RX data back up.
- */
-
-static void stli_unthrottle(struct tty_struct *tty)
-{
-       struct stliport *portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-       clear_bit(ST_RXSTOP, &portp->state);
-}
-
-/*****************************************************************************/
-
-/*
- *     Stop the transmitter.
- */
-
-static void stli_stop(struct tty_struct *tty)
-{
-}
-
-/*****************************************************************************/
-
-/*
- *     Start the transmitter again.
- */
-
-static void stli_start(struct tty_struct *tty)
-{
-}
-
-/*****************************************************************************/
-
-
-/*
- *     Hangup this port. This is pretty much like closing the port, only
- *     a little more brutal. No waiting for data to drain. Shutdown the
- *     port and maybe drop signals. This is rather tricky really. We want
- *     to close the port as well.
- */
-
-static void stli_hangup(struct tty_struct *tty)
-{
-       struct stliport *portp = tty->driver_data;
-       tty_port_hangup(&portp->port);
-}
-
-/*****************************************************************************/
-
-/*
- *     Flush characters from the lower buffer. We may not have user context
- *     so we cannot sleep waiting for it to complete. Also we need to check
- *     if there is chars for this port in the TX cook buffer, and flush them
- *     as well.
- */
-
-static void stli_flushbuffer(struct tty_struct *tty)
-{
-       struct stliport *portp;
-       struct stlibrd *brdp;
-       unsigned long ftype, flags;
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-       if (portp->brdnr >= stli_nrbrds)
-               return;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       if (tty == stli_txcooktty) {
-               stli_txcooktty = NULL;
-               stli_txcooksize = 0;
-               stli_txcookrealsize = 0;
-       }
-       if (test_bit(ST_CMDING, &portp->state)) {
-               set_bit(ST_DOFLUSHTX, &portp->state);
-       } else {
-               ftype = FLUSHTX;
-               if (test_bit(ST_DOFLUSHRX, &portp->state)) {
-                       ftype |= FLUSHRX;
-                       clear_bit(ST_DOFLUSHRX, &portp->state);
-               }
-               __stli_sendcmd(brdp, portp, A_FLUSH, &ftype, sizeof(u32), 0);
-       }
-       spin_unlock_irqrestore(&brd_lock, flags);
-       tty_wakeup(tty);
-}
-
-/*****************************************************************************/
-
-static int stli_breakctl(struct tty_struct *tty, int state)
-{
-       struct stlibrd  *brdp;
-       struct stliport *portp;
-       long            arg;
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return -EINVAL;
-       if (portp->brdnr >= stli_nrbrds)
-               return -EINVAL;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return -EINVAL;
-
-       arg = (state == -1) ? BREAKON : BREAKOFF;
-       stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(long), 0);
-       return 0;
-}
-
-/*****************************************************************************/
-
-static void stli_waituntilsent(struct tty_struct *tty, int timeout)
-{
-       struct stliport *portp;
-       unsigned long tend;
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-
-       if (timeout == 0)
-               timeout = HZ;
-       tend = jiffies + timeout;
-
-       while (test_bit(ST_TXBUSY, &portp->state)) {
-               if (signal_pending(current))
-                       break;
-               msleep_interruptible(20);
-               if (time_after_eq(jiffies, tend))
-                       break;
-       }
-}
-
-/*****************************************************************************/
-
-static void stli_sendxchar(struct tty_struct *tty, char ch)
-{
-       struct stlibrd  *brdp;
-       struct stliport *portp;
-       asyctrl_t       actrl;
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-       if (portp->brdnr >= stli_nrbrds)
-               return;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return;
-
-       memset(&actrl, 0, sizeof(asyctrl_t));
-       if (ch == STOP_CHAR(tty)) {
-               actrl.rxctrl = CT_STOPFLOW;
-       } else if (ch == START_CHAR(tty)) {
-               actrl.rxctrl = CT_STARTFLOW;
-       } else {
-               actrl.txctrl = CT_SENDCHR;
-               actrl.tximdch = ch;
-       }
-       stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0);
-}
-
-static void stli_portinfo(struct seq_file *m, struct stlibrd *brdp, struct stliport *portp, int portnr)
-{
-       char *uart;
-       int rc;
-
-       rc = stli_portcmdstats(NULL, portp);
-
-       uart = "UNKNOWN";
-       if (test_bit(BST_STARTED, &brdp->state)) {
-               switch (stli_comstats.hwid) {
-               case 0: uart = "2681"; break;
-               case 1: uart = "SC26198"; break;
-               default:uart = "CD1400"; break;
-               }
-       }
-       seq_printf(m, "%d: uart:%s ", portnr, uart);
-
-       if (test_bit(BST_STARTED, &brdp->state) && rc >= 0) {
-               char sep;
-
-               seq_printf(m, "tx:%d rx:%d", (int) stli_comstats.txtotal,
-                       (int) stli_comstats.rxtotal);
-
-               if (stli_comstats.rxframing)
-                       seq_printf(m, " fe:%d",
-                               (int) stli_comstats.rxframing);
-               if (stli_comstats.rxparity)
-                       seq_printf(m, " pe:%d",
-                               (int) stli_comstats.rxparity);
-               if (stli_comstats.rxbreaks)
-                       seq_printf(m, " brk:%d",
-                               (int) stli_comstats.rxbreaks);
-               if (stli_comstats.rxoverrun)
-                       seq_printf(m, " oe:%d",
-                               (int) stli_comstats.rxoverrun);
-
-               sep = ' ';
-               if (stli_comstats.signals & TIOCM_RTS) {
-                       seq_printf(m, "%c%s", sep, "RTS");
-                       sep = '|';
-               }
-               if (stli_comstats.signals & TIOCM_CTS) {
-                       seq_printf(m, "%c%s", sep, "CTS");
-                       sep = '|';
-               }
-               if (stli_comstats.signals & TIOCM_DTR) {
-                       seq_printf(m, "%c%s", sep, "DTR");
-                       sep = '|';
-               }
-               if (stli_comstats.signals & TIOCM_CD) {
-                       seq_printf(m, "%c%s", sep, "DCD");
-                       sep = '|';
-               }
-               if (stli_comstats.signals & TIOCM_DSR) {
-                       seq_printf(m, "%c%s", sep, "DSR");
-                       sep = '|';
-               }
-       }
-       seq_putc(m, '\n');
-}
-
-/*****************************************************************************/
-
-/*
- *     Port info, read from the /proc file system.
- */
-
-static int stli_proc_show(struct seq_file *m, void *v)
-{
-       struct stlibrd *brdp;
-       struct stliport *portp;
-       unsigned int brdnr, portnr, totalport;
-
-       totalport = 0;
-
-       seq_printf(m, "%s: version %s\n", stli_drvtitle, stli_drvversion);
-
-/*
- *     We scan through for each board, panel and port. The offset is
- *     calculated on the fly, and irrelevant ports are skipped.
- */
-       for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) {
-               brdp = stli_brds[brdnr];
-               if (brdp == NULL)
-                       continue;
-               if (brdp->state == 0)
-                       continue;
-
-               totalport = brdnr * STL_MAXPORTS;
-               for (portnr = 0; (portnr < brdp->nrports); portnr++,
-                   totalport++) {
-                       portp = brdp->ports[portnr];
-                       if (portp == NULL)
-                               continue;
-                       stli_portinfo(m, brdp, portp, totalport);
-               }
-       }
-       return 0;
-}
-
-static int stli_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, stli_proc_show, NULL);
-}
-
-static const struct file_operations stli_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = stli_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-/*****************************************************************************/
-
-/*
- *     Generic send command routine. This will send a message to the slave,
- *     of the specified type with the specified argument. Must be very
- *     careful of data that will be copied out from shared memory -
- *     containing command results. The command completion is all done from
- *     a poll routine that does not have user context. Therefore you cannot
- *     copy back directly into user space, or to the kernel stack of a
- *     process. This routine does not sleep, so can be called from anywhere.
- *
- *     The caller must hold the brd_lock (see also stli_sendcmd the usual
- *     entry point)
- */
-
-static void __stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback)
-{
-       cdkhdr_t __iomem *hdrp;
-       cdkctrl_t __iomem *cp;
-       unsigned char __iomem *bits;
-
-       if (test_bit(ST_CMDING, &portp->state)) {
-               printk(KERN_ERR "istallion: command already busy, cmd=%x!\n",
-                               (int) cmd);
-               return;
-       }
-
-       EBRDENABLE(brdp);
-       cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
-       if (size > 0) {
-               memcpy_toio((void __iomem *) &(cp->args[0]), arg, size);
-               if (copyback) {
-                       portp->argp = arg;
-                       portp->argsize = size;
-               }
-       }
-       writel(0, &cp->status);
-       writel(cmd, &cp->cmd);
-       hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-       bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
-               portp->portidx;
-       writeb(readb(bits) | portp->portbit, bits);
-       set_bit(ST_CMDING, &portp->state);
-       EBRDDISABLE(brdp);
-}
-
-static void stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback)
-{
-       unsigned long           flags;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       __stli_sendcmd(brdp, portp, cmd, arg, size, copyback);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Read data from shared memory. This assumes that the shared memory
- *     is enabled and that interrupts are off. Basically we just empty out
- *     the shared memory buffer into the tty buffer. Must be careful to
- *     handle the case where we fill up the tty buffer, but still have
- *     more chars to unload.
- */
-
-static void stli_read(struct stlibrd *brdp, struct stliport *portp)
-{
-       cdkasyrq_t __iomem *rp;
-       char __iomem *shbuf;
-       struct tty_struct       *tty;
-       unsigned int head, tail, size;
-       unsigned int len, stlen;
-
-       if (test_bit(ST_RXSTOP, &portp->state))
-               return;
-       tty = tty_port_tty_get(&portp->port);
-       if (tty == NULL)
-               return;
-
-       rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->rxq;
-       head = (unsigned int) readw(&rp->head);
-       if (head != ((unsigned int) readw(&rp->head)))
-               head = (unsigned int) readw(&rp->head);
-       tail = (unsigned int) readw(&rp->tail);
-       size = portp->rxsize;
-       if (head >= tail) {
-               len = head - tail;
-               stlen = len;
-       } else {
-               len = size - (tail - head);
-               stlen = size - tail;
-       }
-
-       len = tty_buffer_request_room(tty, len);
-
-       shbuf = (char __iomem *) EBRDGETMEMPTR(brdp, portp->rxoffset);
-
-       while (len > 0) {
-               unsigned char *cptr;
-
-               stlen = min(len, stlen);
-               tty_prepare_flip_string(tty, &cptr, stlen);
-               memcpy_fromio(cptr, shbuf + tail, stlen);
-               len -= stlen;
-               tail += stlen;
-               if (tail >= size) {
-                       tail = 0;
-                       stlen = head;
-               }
-       }
-       rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->rxq;
-       writew(tail, &rp->tail);
-
-       if (head != tail)
-               set_bit(ST_RXING, &portp->state);
-
-       tty_schedule_flip(tty);
-       tty_kref_put(tty);
-}
-
-/*****************************************************************************/
-
-/*
- *     Set up and carry out any delayed commands. There is only a small set
- *     of slave commands that can be done "off-level". So it is not too
- *     difficult to deal with them here.
- */
-
-static void stli_dodelaycmd(struct stliport *portp, cdkctrl_t __iomem *cp)
-{
-       int cmd;
-
-       if (test_bit(ST_DOSIGS, &portp->state)) {
-               if (test_bit(ST_DOFLUSHTX, &portp->state) &&
-                   test_bit(ST_DOFLUSHRX, &portp->state))
-                       cmd = A_SETSIGNALSF;
-               else if (test_bit(ST_DOFLUSHTX, &portp->state))
-                       cmd = A_SETSIGNALSFTX;
-               else if (test_bit(ST_DOFLUSHRX, &portp->state))
-                       cmd = A_SETSIGNALSFRX;
-               else
-                       cmd = A_SETSIGNALS;
-               clear_bit(ST_DOFLUSHTX, &portp->state);
-               clear_bit(ST_DOFLUSHRX, &portp->state);
-               clear_bit(ST_DOSIGS, &portp->state);
-               memcpy_toio((void __iomem *) &(cp->args[0]), (void *) &portp->asig,
-                       sizeof(asysigs_t));
-               writel(0, &cp->status);
-               writel(cmd, &cp->cmd);
-               set_bit(ST_CMDING, &portp->state);
-       } else if (test_bit(ST_DOFLUSHTX, &portp->state) ||
-           test_bit(ST_DOFLUSHRX, &portp->state)) {
-               cmd = ((test_bit(ST_DOFLUSHTX, &portp->state)) ? FLUSHTX : 0);
-               cmd |= ((test_bit(ST_DOFLUSHRX, &portp->state)) ? FLUSHRX : 0);
-               clear_bit(ST_DOFLUSHTX, &portp->state);
-               clear_bit(ST_DOFLUSHRX, &portp->state);
-               memcpy_toio((void __iomem *) &(cp->args[0]), (void *) &cmd, sizeof(int));
-               writel(0, &cp->status);
-               writel(A_FLUSH, &cp->cmd);
-               set_bit(ST_CMDING, &portp->state);
-       }
-}
-
-/*****************************************************************************/
-
-/*
- *     Host command service checking. This handles commands or messages
- *     coming from the slave to the host. Must have board shared memory
- *     enabled and interrupts off when called. Notice that by servicing the
- *     read data last we don't need to change the shared memory pointer
- *     during processing (which is a slow IO operation).
- *     Return value indicates if this port is still awaiting actions from
- *     the slave (like open, command, or even TX data being sent). If 0
- *     then port is still busy, otherwise no longer busy.
- */
-
-static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp)
-{
-       cdkasy_t __iomem *ap;
-       cdkctrl_t __iomem *cp;
-       struct tty_struct *tty;
-       asynotify_t nt;
-       unsigned long oldsigs;
-       int rc, donerx;
-
-       ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
-       cp = &ap->ctrl;
-
-/*
- *     Check if we are waiting for an open completion message.
- */
-       if (test_bit(ST_OPENING, &portp->state)) {
-               rc = readl(&cp->openarg);
-               if (readb(&cp->open) == 0 && rc != 0) {
-                       if (rc > 0)
-                               rc--;
-                       writel(0, &cp->openarg);
-                       portp->rc = rc;
-                       clear_bit(ST_OPENING, &portp->state);
-                       wake_up_interruptible(&portp->raw_wait);
-               }
-       }
-
-/*
- *     Check if we are waiting for a close completion message.
- */
-       if (test_bit(ST_CLOSING, &portp->state)) {
-               rc = (int) readl(&cp->closearg);
-               if (readb(&cp->close) == 0 && rc != 0) {
-                       if (rc > 0)
-                               rc--;
-                       writel(0, &cp->closearg);
-                       portp->rc = rc;
-                       clear_bit(ST_CLOSING, &portp->state);
-                       wake_up_interruptible(&portp->raw_wait);
-               }
-       }
-
-/*
- *     Check if we are waiting for a command completion message. We may
- *     need to copy out the command results associated with this command.
- */
-       if (test_bit(ST_CMDING, &portp->state)) {
-               rc = readl(&cp->status);
-               if (readl(&cp->cmd) == 0 && rc != 0) {
-                       if (rc > 0)
-                               rc--;
-                       if (portp->argp != NULL) {
-                               memcpy_fromio(portp->argp, (void __iomem *) &(cp->args[0]),
-                                       portp->argsize);
-                               portp->argp = NULL;
-                       }
-                       writel(0, &cp->status);
-                       portp->rc = rc;
-                       clear_bit(ST_CMDING, &portp->state);
-                       stli_dodelaycmd(portp, cp);
-                       wake_up_interruptible(&portp->raw_wait);
-               }
-       }
-
-/*
- *     Check for any notification messages ready. This includes lots of
- *     different types of events - RX chars ready, RX break received,
- *     TX data low or empty in the slave, modem signals changed state.
- */
-       donerx = 0;
-
-       if (ap->notify) {
-               nt = ap->changed;
-               ap->notify = 0;
-               tty = tty_port_tty_get(&portp->port);
-
-               if (nt.signal & SG_DCD) {
-                       oldsigs = portp->sigs;
-                       portp->sigs = stli_mktiocm(nt.sigvalue);
-                       clear_bit(ST_GETSIGS, &portp->state);
-                       if ((portp->sigs & TIOCM_CD) &&
-                           ((oldsigs & TIOCM_CD) == 0))
-                               wake_up_interruptible(&portp->port.open_wait);
-                       if ((oldsigs & TIOCM_CD) &&
-                           ((portp->sigs & TIOCM_CD) == 0)) {
-                               if (portp->port.flags & ASYNC_CHECK_CD) {
-                                       if (tty)
-                                               tty_hangup(tty);
-                               }
-                       }
-               }
-
-               if (nt.data & DT_TXEMPTY)
-                       clear_bit(ST_TXBUSY, &portp->state);
-               if (nt.data & (DT_TXEMPTY | DT_TXLOW)) {
-                       if (tty != NULL) {
-                               tty_wakeup(tty);
-                               EBRDENABLE(brdp);
-                       }
-               }
-
-               if ((nt.data & DT_RXBREAK) && (portp->rxmarkmsk & BRKINT)) {
-                       if (tty != NULL) {
-                               tty_insert_flip_char(tty, 0, TTY_BREAK);
-                               if (portp->port.flags & ASYNC_SAK) {
-                                       do_SAK(tty);
-                                       EBRDENABLE(brdp);
-                               }
-                               tty_schedule_flip(tty);
-                       }
-               }
-               tty_kref_put(tty);
-
-               if (nt.data & DT_RXBUSY) {
-                       donerx++;
-                       stli_read(brdp, portp);
-               }
-       }
-
-/*
- *     It might seem odd that we are checking for more RX chars here.
- *     But, we need to handle the case where the tty buffer was previously
- *     filled, but we had more characters to pass up. The slave will not
- *     send any more RX notify messages until the RX buffer has been emptied.
- *     But it will leave the service bits on (since the buffer is not empty).
- *     So from here we can try to process more RX chars.
- */
-       if ((!donerx) && test_bit(ST_RXING, &portp->state)) {
-               clear_bit(ST_RXING, &portp->state);
-               stli_read(brdp, portp);
-       }
-
-       return((test_bit(ST_OPENING, &portp->state) ||
-               test_bit(ST_CLOSING, &portp->state) ||
-               test_bit(ST_CMDING, &portp->state) ||
-               test_bit(ST_TXBUSY, &portp->state) ||
-               test_bit(ST_RXING, &portp->state)) ? 0 : 1);
-}
-
-/*****************************************************************************/
-
-/*
- *     Service all ports on a particular board. Assumes that the boards
- *     shared memory is enabled, and that the page pointer is pointed
- *     at the cdk header structure.
- */
-
-static void stli_brdpoll(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp)
-{
-       struct stliport *portp;
-       unsigned char hostbits[(STL_MAXCHANS / 8) + 1];
-       unsigned char slavebits[(STL_MAXCHANS / 8) + 1];
-       unsigned char __iomem *slavep;
-       int bitpos, bitat, bitsize;
-       int channr, nrdevs, slavebitchange;
-
-       bitsize = brdp->bitsize;
-       nrdevs = brdp->nrdevs;
-
-/*
- *     Check if slave wants any service. Basically we try to do as
- *     little work as possible here. There are 2 levels of service
- *     bits. So if there is nothing to do we bail early. We check
- *     8 service bits at a time in the inner loop, so we can bypass
- *     the lot if none of them want service.
- */
-       memcpy_fromio(&hostbits[0], (((unsigned char __iomem *) hdrp) + brdp->hostoffset),
-               bitsize);
-
-       memset(&slavebits[0], 0, bitsize);
-       slavebitchange = 0;
-
-       for (bitpos = 0; (bitpos < bitsize); bitpos++) {
-               if (hostbits[bitpos] == 0)
-                       continue;
-               channr = bitpos * 8;
-               for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) {
-                       if (hostbits[bitpos] & bitat) {
-                               portp = brdp->ports[(channr - 1)];
-                               if (stli_hostcmd(brdp, portp)) {
-                                       slavebitchange++;
-                                       slavebits[bitpos] |= bitat;
-                               }
-                       }
-               }
-       }
-
-/*
- *     If any of the ports are no longer busy then update them in the
- *     slave request bits. We need to do this after, since a host port
- *     service may initiate more slave requests.
- */
-       if (slavebitchange) {
-               hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-               slavep = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset;
-               for (bitpos = 0; (bitpos < bitsize); bitpos++) {
-                       if (readb(slavebits + bitpos))
-                               writeb(readb(slavep + bitpos) & ~slavebits[bitpos], slavebits + bitpos);
-               }
-       }
-}
-
-/*****************************************************************************/
-
-/*
- *     Driver poll routine. This routine polls the boards in use and passes
- *     messages back up to host when necessary. This is actually very
- *     CPU efficient, since we will always have the kernel poll clock, it
- *     adds only a few cycles when idle (since board service can be
- *     determined very easily), but when loaded generates no interrupts
- *     (with their expensive associated context change).
- */
-
-static void stli_poll(unsigned long arg)
-{
-       cdkhdr_t __iomem *hdrp;
-       struct stlibrd *brdp;
-       unsigned int brdnr;
-
-       mod_timer(&stli_timerlist, STLI_TIMEOUT);
-
-/*
- *     Check each board and do any servicing required.
- */
-       for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) {
-               brdp = stli_brds[brdnr];
-               if (brdp == NULL)
-                       continue;
-               if (!test_bit(BST_STARTED, &brdp->state))
-                       continue;
-
-               spin_lock(&brd_lock);
-               EBRDENABLE(brdp);
-               hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-               if (readb(&hdrp->hostreq))
-                       stli_brdpoll(brdp, hdrp);
-               EBRDDISABLE(brdp);
-               spin_unlock(&brd_lock);
-       }
-}
-
-/*****************************************************************************/
-
-/*
- *     Translate the termios settings into the port setting structure of
- *     the slave.
- */
-
-static void stli_mkasyport(struct tty_struct *tty, struct stliport *portp,
-                               asyport_t *pp, struct ktermios *tiosp)
-{
-       memset(pp, 0, sizeof(asyport_t));
-
-/*
- *     Start of by setting the baud, char size, parity and stop bit info.
- */
-       pp->baudout = tty_get_baud_rate(tty);
-       if ((tiosp->c_cflag & CBAUD) == B38400) {
-               if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                       pp->baudout = 57600;
-               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                       pp->baudout = 115200;
-               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-                       pp->baudout = 230400;
-               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-                       pp->baudout = 460800;
-               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
-                       pp->baudout = (portp->baud_base / portp->custom_divisor);
-       }
-       if (pp->baudout > STL_MAXBAUD)
-               pp->baudout = STL_MAXBAUD;
-       pp->baudin = pp->baudout;
-
-       switch (tiosp->c_cflag & CSIZE) {
-       case CS5:
-               pp->csize = 5;
-               break;
-       case CS6:
-               pp->csize = 6;
-               break;
-       case CS7:
-               pp->csize = 7;
-               break;
-       default:
-               pp->csize = 8;
-               break;
-       }
-
-       if (tiosp->c_cflag & CSTOPB)
-               pp->stopbs = PT_STOP2;
-       else
-               pp->stopbs = PT_STOP1;
-
-       if (tiosp->c_cflag & PARENB) {
-               if (tiosp->c_cflag & PARODD)
-                       pp->parity = PT_ODDPARITY;
-               else
-                       pp->parity = PT_EVENPARITY;
-       } else {
-               pp->parity = PT_NOPARITY;
-       }
-
-/*
- *     Set up any flow control options enabled.
- */
-       if (tiosp->c_iflag & IXON) {
-               pp->flow |= F_IXON;
-               if (tiosp->c_iflag & IXANY)
-                       pp->flow |= F_IXANY;
-       }
-       if (tiosp->c_cflag & CRTSCTS)
-               pp->flow |= (F_RTSFLOW | F_CTSFLOW);
-
-       pp->startin = tiosp->c_cc[VSTART];
-       pp->stopin = tiosp->c_cc[VSTOP];
-       pp->startout = tiosp->c_cc[VSTART];
-       pp->stopout = tiosp->c_cc[VSTOP];
-
-/*
- *     Set up the RX char marking mask with those RX error types we must
- *     catch. We can get the slave to help us out a little here, it will
- *     ignore parity errors and breaks for us, and mark parity errors in
- *     the data stream.
- */
-       if (tiosp->c_iflag & IGNPAR)
-               pp->iflag |= FI_IGNRXERRS;
-       if (tiosp->c_iflag & IGNBRK)
-               pp->iflag |= FI_IGNBREAK;
-
-       portp->rxmarkmsk = 0;
-       if (tiosp->c_iflag & (INPCK | PARMRK))
-               pp->iflag |= FI_1MARKRXERRS;
-       if (tiosp->c_iflag & BRKINT)
-               portp->rxmarkmsk |= BRKINT;
-
-/*
- *     Set up clocal processing as required.
- */
-       if (tiosp->c_cflag & CLOCAL)
-               portp->port.flags &= ~ASYNC_CHECK_CD;
-       else
-               portp->port.flags |= ASYNC_CHECK_CD;
-
-/*
- *     Transfer any persistent flags into the asyport structure.
- */
-       pp->pflag = (portp->pflag & 0xffff);
-       pp->vmin = (portp->pflag & P_RXIMIN) ? 1 : 0;
-       pp->vtime = (portp->pflag & P_RXITIME) ? 1 : 0;
-       pp->cc[1] = (portp->pflag & P_RXTHOLD) ? 1 : 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Construct a slave signals structure for setting the DTR and RTS
- *     signals as specified.
- */
-
-static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts)
-{
-       memset(sp, 0, sizeof(asysigs_t));
-       if (dtr >= 0) {
-               sp->signal |= SG_DTR;
-               sp->sigvalue |= ((dtr > 0) ? SG_DTR : 0);
-       }
-       if (rts >= 0) {
-               sp->signal |= SG_RTS;
-               sp->sigvalue |= ((rts > 0) ? SG_RTS : 0);
-       }
-}
-
-/*****************************************************************************/
-
-/*
- *     Convert the signals returned from the slave into a local TIOCM type
- *     signals value. We keep them locally in TIOCM format.
- */
-
-static long stli_mktiocm(unsigned long sigvalue)
-{
-       long    tiocm = 0;
-       tiocm |= ((sigvalue & SG_DCD) ? TIOCM_CD : 0);
-       tiocm |= ((sigvalue & SG_CTS) ? TIOCM_CTS : 0);
-       tiocm |= ((sigvalue & SG_RI) ? TIOCM_RI : 0);
-       tiocm |= ((sigvalue & SG_DSR) ? TIOCM_DSR : 0);
-       tiocm |= ((sigvalue & SG_DTR) ? TIOCM_DTR : 0);
-       tiocm |= ((sigvalue & SG_RTS) ? TIOCM_RTS : 0);
-       return(tiocm);
-}
-
-/*****************************************************************************/
-
-/*
- *     All panels and ports actually attached have been worked out. All
- *     we need to do here is set up the appropriate per port data structures.
- */
-
-static int stli_initports(struct stlibrd *brdp)
-{
-       struct stliport *portp;
-       unsigned int i, panelnr, panelport;
-
-       for (i = 0, panelnr = 0, panelport = 0; (i < brdp->nrports); i++) {
-               portp = kzalloc(sizeof(struct stliport), GFP_KERNEL);
-               if (!portp) {
-                       printk(KERN_WARNING "istallion: failed to allocate port structure\n");
-                       continue;
-               }
-               tty_port_init(&portp->port);
-               portp->port.ops = &stli_port_ops;
-               portp->magic = STLI_PORTMAGIC;
-               portp->portnr = i;
-               portp->brdnr = brdp->brdnr;
-               portp->panelnr = panelnr;
-               portp->baud_base = STL_BAUDBASE;
-               portp->port.close_delay = STL_CLOSEDELAY;
-               portp->closing_wait = 30 * HZ;
-               init_waitqueue_head(&portp->port.open_wait);
-               init_waitqueue_head(&portp->port.close_wait);
-               init_waitqueue_head(&portp->raw_wait);
-               panelport++;
-               if (panelport >= brdp->panels[panelnr]) {
-                       panelport = 0;
-                       panelnr++;
-               }
-               brdp->ports[i] = portp;
-       }
-
-       return 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     All the following routines are board specific hardware operations.
- */
-
-static void stli_ecpinit(struct stlibrd *brdp)
-{
-       unsigned long   memconf;
-
-       outb(ECP_ATSTOP, (brdp->iobase + ECP_ATCONFR));
-       udelay(10);
-       outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
-       udelay(100);
-
-       memconf = (brdp->memaddr & ECP_ATADDRMASK) >> ECP_ATADDRSHFT;
-       outb(memconf, (brdp->iobase + ECP_ATMEMAR));
-}
-
-/*****************************************************************************/
-
-static void stli_ecpenable(struct stlibrd *brdp)
-{      
-       outb(ECP_ATENABLE, (brdp->iobase + ECP_ATCONFR));
-}
-
-/*****************************************************************************/
-
-static void stli_ecpdisable(struct stlibrd *brdp)
-{      
-       outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
-}
-
-/*****************************************************************************/
-
-static void __iomem *stli_ecpgetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
-{      
-       void __iomem *ptr;
-       unsigned char val;
-
-       if (offset > brdp->memsize) {
-               printk(KERN_ERR "istallion: shared memory pointer=%x out of "
-                               "range at line=%d(%d), brd=%d\n",
-                       (int) offset, line, __LINE__, brdp->brdnr);
-               ptr = NULL;
-               val = 0;
-       } else {
-               ptr = brdp->membase + (offset % ECP_ATPAGESIZE);
-               val = (unsigned char) (offset / ECP_ATPAGESIZE);
-       }
-       outb(val, (brdp->iobase + ECP_ATMEMPR));
-       return(ptr);
-}
-
-/*****************************************************************************/
-
-static void stli_ecpreset(struct stlibrd *brdp)
-{      
-       outb(ECP_ATSTOP, (brdp->iobase + ECP_ATCONFR));
-       udelay(10);
-       outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
-       udelay(500);
-}
-
-/*****************************************************************************/
-
-static void stli_ecpintr(struct stlibrd *brdp)
-{      
-       outb(0x1, brdp->iobase);
-}
-
-/*****************************************************************************/
-
-/*
- *     The following set of functions act on ECP EISA boards.
- */
-
-static void stli_ecpeiinit(struct stlibrd *brdp)
-{
-       unsigned long   memconf;
-
-       outb(0x1, (brdp->iobase + ECP_EIBRDENAB));
-       outb(ECP_EISTOP, (brdp->iobase + ECP_EICONFR));
-       udelay(10);
-       outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
-       udelay(500);
-
-       memconf = (brdp->memaddr & ECP_EIADDRMASKL) >> ECP_EIADDRSHFTL;
-       outb(memconf, (brdp->iobase + ECP_EIMEMARL));
-       memconf = (brdp->memaddr & ECP_EIADDRMASKH) >> ECP_EIADDRSHFTH;
-       outb(memconf, (brdp->iobase + ECP_EIMEMARH));
-}
-
-/*****************************************************************************/
-
-static void stli_ecpeienable(struct stlibrd *brdp)
-{      
-       outb(ECP_EIENABLE, (brdp->iobase + ECP_EICONFR));
-}
-
-/*****************************************************************************/
-
-static void stli_ecpeidisable(struct stlibrd *brdp)
-{      
-       outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
-}
-
-/*****************************************************************************/
-
-static void __iomem *stli_ecpeigetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
-{      
-       void __iomem *ptr;
-       unsigned char   val;
-
-       if (offset > brdp->memsize) {
-               printk(KERN_ERR "istallion: shared memory pointer=%x out of "
-                               "range at line=%d(%d), brd=%d\n",
-                       (int) offset, line, __LINE__, brdp->brdnr);
-               ptr = NULL;
-               val = 0;
-       } else {
-               ptr = brdp->membase + (offset % ECP_EIPAGESIZE);
-               if (offset < ECP_EIPAGESIZE)
-                       val = ECP_EIENABLE;
-               else
-                       val = ECP_EIENABLE | 0x40;
-       }
-       outb(val, (brdp->iobase + ECP_EICONFR));
-       return(ptr);
-}
-
-/*****************************************************************************/
-
-static void stli_ecpeireset(struct stlibrd *brdp)
-{      
-       outb(ECP_EISTOP, (brdp->iobase + ECP_EICONFR));
-       udelay(10);
-       outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
-       udelay(500);
-}
-
-/*****************************************************************************/
-
-/*
- *     The following set of functions act on ECP MCA boards.
- */
-
-static void stli_ecpmcenable(struct stlibrd *brdp)
-{      
-       outb(ECP_MCENABLE, (brdp->iobase + ECP_MCCONFR));
-}
-
-/*****************************************************************************/
-
-static void stli_ecpmcdisable(struct stlibrd *brdp)
-{      
-       outb(ECP_MCDISABLE, (brdp->iobase + ECP_MCCONFR));
-}
-
-/*****************************************************************************/
-
-static void __iomem *stli_ecpmcgetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
-{      
-       void __iomem *ptr;
-       unsigned char val;
-
-       if (offset > brdp->memsize) {
-               printk(KERN_ERR "istallion: shared memory pointer=%x out of "
-                               "range at line=%d(%d), brd=%d\n",
-                       (int) offset, line, __LINE__, brdp->brdnr);
-               ptr = NULL;
-               val = 0;
-       } else {
-               ptr = brdp->membase + (offset % ECP_MCPAGESIZE);
-               val = ((unsigned char) (offset / ECP_MCPAGESIZE)) | ECP_MCENABLE;
-       }
-       outb(val, (brdp->iobase + ECP_MCCONFR));
-       return(ptr);
-}
-
-/*****************************************************************************/
-
-static void stli_ecpmcreset(struct stlibrd *brdp)
-{      
-       outb(ECP_MCSTOP, (brdp->iobase + ECP_MCCONFR));
-       udelay(10);
-       outb(ECP_MCDISABLE, (brdp->iobase + ECP_MCCONFR));
-       udelay(500);
-}
-
-/*****************************************************************************/
-
-/*
- *     The following set of functions act on ECP PCI boards.
- */
-
-static void stli_ecppciinit(struct stlibrd *brdp)
-{
-       outb(ECP_PCISTOP, (brdp->iobase + ECP_PCICONFR));
-       udelay(10);
-       outb(0, (brdp->iobase + ECP_PCICONFR));
-       udelay(500);
-}
-
-/*****************************************************************************/
-
-static void __iomem *stli_ecppcigetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
-{      
-       void __iomem *ptr;
-       unsigned char   val;
-
-       if (offset > brdp->memsize) {
-               printk(KERN_ERR "istallion: shared memory pointer=%x out of "
-                               "range at line=%d(%d), board=%d\n",
-                               (int) offset, line, __LINE__, brdp->brdnr);
-               ptr = NULL;
-               val = 0;
-       } else {
-               ptr = brdp->membase + (offset % ECP_PCIPAGESIZE);
-               val = (offset / ECP_PCIPAGESIZE) << 1;
-       }
-       outb(val, (brdp->iobase + ECP_PCICONFR));
-       return(ptr);
-}
-
-/*****************************************************************************/
-
-static void stli_ecppcireset(struct stlibrd *brdp)
-{      
-       outb(ECP_PCISTOP, (brdp->iobase + ECP_PCICONFR));
-       udelay(10);
-       outb(0, (brdp->iobase + ECP_PCICONFR));
-       udelay(500);
-}
-
-/*****************************************************************************/
-
-/*
- *     The following routines act on ONboards.
- */
-
-static void stli_onbinit(struct stlibrd *brdp)
-{
-       unsigned long   memconf;
-
-       outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR));
-       udelay(10);
-       outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
-       mdelay(1000);
-
-       memconf = (brdp->memaddr & ONB_ATADDRMASK) >> ONB_ATADDRSHFT;
-       outb(memconf, (brdp->iobase + ONB_ATMEMAR));
-       outb(0x1, brdp->iobase);
-       mdelay(1);
-}
-
-/*****************************************************************************/
-
-static void stli_onbenable(struct stlibrd *brdp)
-{      
-       outb((brdp->enabval | ONB_ATENABLE), (brdp->iobase + ONB_ATCONFR));
-}
-
-/*****************************************************************************/
-
-static void stli_onbdisable(struct stlibrd *brdp)
-{      
-       outb((brdp->enabval | ONB_ATDISABLE), (brdp->iobase + ONB_ATCONFR));
-}
-
-/*****************************************************************************/
-
-static void __iomem *stli_onbgetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
-{      
-       void __iomem *ptr;
-
-       if (offset > brdp->memsize) {
-               printk(KERN_ERR "istallion: shared memory pointer=%x out of "
-                               "range at line=%d(%d), brd=%d\n",
-                               (int) offset, line, __LINE__, brdp->brdnr);
-               ptr = NULL;
-       } else {
-               ptr = brdp->membase + (offset % ONB_ATPAGESIZE);
-       }
-       return(ptr);
-}
-
-/*****************************************************************************/
-
-static void stli_onbreset(struct stlibrd *brdp)
-{      
-       outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR));
-       udelay(10);
-       outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
-       mdelay(1000);
-}
-
-/*****************************************************************************/
-
-/*
- *     The following routines act on ONboard EISA.
- */
-
-static void stli_onbeinit(struct stlibrd *brdp)
-{
-       unsigned long   memconf;
-
-       outb(0x1, (brdp->iobase + ONB_EIBRDENAB));
-       outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
-       udelay(10);
-       outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
-       mdelay(1000);
-
-       memconf = (brdp->memaddr & ONB_EIADDRMASKL) >> ONB_EIADDRSHFTL;
-       outb(memconf, (brdp->iobase + ONB_EIMEMARL));
-       memconf = (brdp->memaddr & ONB_EIADDRMASKH) >> ONB_EIADDRSHFTH;
-       outb(memconf, (brdp->iobase + ONB_EIMEMARH));
-       outb(0x1, brdp->iobase);
-       mdelay(1);
-}
-
-/*****************************************************************************/
-
-static void stli_onbeenable(struct stlibrd *brdp)
-{      
-       outb(ONB_EIENABLE, (brdp->iobase + ONB_EICONFR));
-}
-
-/*****************************************************************************/
-
-static void stli_onbedisable(struct stlibrd *brdp)
-{      
-       outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
-}
-
-/*****************************************************************************/
-
-static void __iomem *stli_onbegetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
-{      
-       void __iomem *ptr;
-       unsigned char val;
-
-       if (offset > brdp->memsize) {
-               printk(KERN_ERR "istallion: shared memory pointer=%x out of "
-                               "range at line=%d(%d), brd=%d\n",
-                       (int) offset, line, __LINE__, brdp->brdnr);
-               ptr = NULL;
-               val = 0;
-       } else {
-               ptr = brdp->membase + (offset % ONB_EIPAGESIZE);
-               if (offset < ONB_EIPAGESIZE)
-                       val = ONB_EIENABLE;
-               else
-                       val = ONB_EIENABLE | 0x40;
-       }
-       outb(val, (brdp->iobase + ONB_EICONFR));
-       return(ptr);
-}
-
-/*****************************************************************************/
-
-static void stli_onbereset(struct stlibrd *brdp)
-{      
-       outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
-       udelay(10);
-       outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
-       mdelay(1000);
-}
-
-/*****************************************************************************/
-
-/*
- *     The following routines act on Brumby boards.
- */
-
-static void stli_bbyinit(struct stlibrd *brdp)
-{
-       outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR));
-       udelay(10);
-       outb(0, (brdp->iobase + BBY_ATCONFR));
-       mdelay(1000);
-       outb(0x1, brdp->iobase);
-       mdelay(1);
-}
-
-/*****************************************************************************/
-
-static void __iomem *stli_bbygetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
-{      
-       void __iomem *ptr;
-       unsigned char val;
-
-       BUG_ON(offset > brdp->memsize);
-
-       ptr = brdp->membase + (offset % BBY_PAGESIZE);
-       val = (unsigned char) (offset / BBY_PAGESIZE);
-       outb(val, (brdp->iobase + BBY_ATCONFR));
-       return(ptr);
-}
-
-/*****************************************************************************/
-
-static void stli_bbyreset(struct stlibrd *brdp)
-{      
-       outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR));
-       udelay(10);
-       outb(0, (brdp->iobase + BBY_ATCONFR));
-       mdelay(1000);
-}
-
-/*****************************************************************************/
-
-/*
- *     The following routines act on original old Stallion boards.
- */
-
-static void stli_stalinit(struct stlibrd *brdp)
-{
-       outb(0x1, brdp->iobase);
-       mdelay(1000);
-}
-
-/*****************************************************************************/
-
-static void __iomem *stli_stalgetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
-{      
-       BUG_ON(offset > brdp->memsize);
-       return brdp->membase + (offset % STAL_PAGESIZE);
-}
-
-/*****************************************************************************/
-
-static void stli_stalreset(struct stlibrd *brdp)
-{      
-       u32 __iomem *vecp;
-
-       vecp = (u32 __iomem *) (brdp->membase + 0x30);
-       writel(0xffff0000, vecp);
-       outb(0, brdp->iobase);
-       mdelay(1000);
-}
-
-/*****************************************************************************/
-
-/*
- *     Try to find an ECP board and initialize it. This handles only ECP
- *     board types.
- */
-
-static int stli_initecp(struct stlibrd *brdp)
-{
-       cdkecpsig_t sig;
-       cdkecpsig_t __iomem *sigsp;
-       unsigned int status, nxtid;
-       char *name;
-       int retval, panelnr, nrports;
-
-       if ((brdp->iobase == 0) || (brdp->memaddr == 0)) {
-               retval = -ENODEV;
-               goto err;
-       }
-
-       brdp->iosize = ECP_IOSIZE;
-
-       if (!request_region(brdp->iobase, brdp->iosize, "istallion")) {
-               retval = -EIO;
-               goto err;
-       }
-
-/*
- *     Based on the specific board type setup the common vars to access
- *     and enable shared memory. Set all board specific information now
- *     as well.
- */
-       switch (brdp->brdtype) {
-       case BRD_ECP:
-               brdp->memsize = ECP_MEMSIZE;
-               brdp->pagesize = ECP_ATPAGESIZE;
-               brdp->init = stli_ecpinit;
-               brdp->enable = stli_ecpenable;
-               brdp->reenable = stli_ecpenable;
-               brdp->disable = stli_ecpdisable;
-               brdp->getmemptr = stli_ecpgetmemptr;
-               brdp->intr = stli_ecpintr;
-               brdp->reset = stli_ecpreset;
-               name = "serial(EC8/64)";
-               break;
-
-       case BRD_ECPE:
-               brdp->memsize = ECP_MEMSIZE;
-               brdp->pagesize = ECP_EIPAGESIZE;
-               brdp->init = stli_ecpeiinit;
-               brdp->enable = stli_ecpeienable;
-               brdp->reenable = stli_ecpeienable;
-               brdp->disable = stli_ecpeidisable;
-               brdp->getmemptr = stli_ecpeigetmemptr;
-               brdp->intr = stli_ecpintr;
-               brdp->reset = stli_ecpeireset;
-               name = "serial(EC8/64-EI)";
-               break;
-
-       case BRD_ECPMC:
-               brdp->memsize = ECP_MEMSIZE;
-               brdp->pagesize = ECP_MCPAGESIZE;
-               brdp->init = NULL;
-               brdp->enable = stli_ecpmcenable;
-               brdp->reenable = stli_ecpmcenable;
-               brdp->disable = stli_ecpmcdisable;
-               brdp->getmemptr = stli_ecpmcgetmemptr;
-               brdp->intr = stli_ecpintr;
-               brdp->reset = stli_ecpmcreset;
-               name = "serial(EC8/64-MCA)";
-               break;
-
-       case BRD_ECPPCI:
-               brdp->memsize = ECP_PCIMEMSIZE;
-               brdp->pagesize = ECP_PCIPAGESIZE;
-               brdp->init = stli_ecppciinit;
-               brdp->enable = NULL;
-               brdp->reenable = NULL;
-               brdp->disable = NULL;
-               brdp->getmemptr = stli_ecppcigetmemptr;
-               brdp->intr = stli_ecpintr;
-               brdp->reset = stli_ecppcireset;
-               name = "serial(EC/RA-PCI)";
-               break;
-
-       default:
-               retval = -EINVAL;
-               goto err_reg;
-       }
-
-/*
- *     The per-board operations structure is all set up, so now let's go
- *     and get the board operational. Firstly initialize board configuration
- *     registers. Set the memory mapping info so we can get at the boards
- *     shared memory.
- */
-       EBRDINIT(brdp);
-
-       brdp->membase = ioremap_nocache(brdp->memaddr, brdp->memsize);
-       if (brdp->membase == NULL) {
-               retval = -ENOMEM;
-               goto err_reg;
-       }
-
-/*
- *     Now that all specific code is set up, enable the shared memory and
- *     look for the a signature area that will tell us exactly what board
- *     this is, and what it is connected to it.
- */
-       EBRDENABLE(brdp);
-       sigsp = (cdkecpsig_t __iomem *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
-       memcpy_fromio(&sig, sigsp, sizeof(cdkecpsig_t));
-       EBRDDISABLE(brdp);
-
-       if (sig.magic != cpu_to_le32(ECP_MAGIC)) {
-               retval = -ENODEV;
-               goto err_unmap;
-       }
-
-/*
- *     Scan through the signature looking at the panels connected to the
- *     board. Calculate the total number of ports as we go.
- */
-       for (panelnr = 0, nxtid = 0; (panelnr < STL_MAXPANELS); panelnr++) {
-               status = sig.panelid[nxtid];
-               if ((status & ECH_PNLIDMASK) != nxtid)
-                       break;
-
-               brdp->panelids[panelnr] = status;
-               nrports = (status & ECH_PNL16PORT) ? 16 : 8;
-               if ((nrports == 16) && ((status & ECH_PNLXPID) == 0))
-                       nxtid++;
-               brdp->panels[panelnr] = nrports;
-               brdp->nrports += nrports;
-               nxtid++;
-               brdp->nrpanels++;
-       }
-
-
-       set_bit(BST_FOUND, &brdp->state);
-       return 0;
-err_unmap:
-       iounmap(brdp->membase);
-       brdp->membase = NULL;
-err_reg:
-       release_region(brdp->iobase, brdp->iosize);
-err:
-       return retval;
-}
-
-/*****************************************************************************/
-
-/*
- *     Try to find an ONboard, Brumby or Stallion board and initialize it.
- *     This handles only these board types.
- */
-
-static int stli_initonb(struct stlibrd *brdp)
-{
-       cdkonbsig_t sig;
-       cdkonbsig_t __iomem *sigsp;
-       char *name;
-       int i, retval;
-
-/*
- *     Do a basic sanity check on the IO and memory addresses.
- */
-       if (brdp->iobase == 0 || brdp->memaddr == 0) {
-               retval = -ENODEV;
-               goto err;
-       }
-
-       brdp->iosize = ONB_IOSIZE;
-       
-       if (!request_region(brdp->iobase, brdp->iosize, "istallion")) {
-               retval = -EIO;
-               goto err;
-       }
-
-/*
- *     Based on the specific board type setup the common vars to access
- *     and enable shared memory. Set all board specific information now
- *     as well.
- */
-       switch (brdp->brdtype) {
-       case BRD_ONBOARD:
-       case BRD_ONBOARD2:
-               brdp->memsize = ONB_MEMSIZE;
-               brdp->pagesize = ONB_ATPAGESIZE;
-               brdp->init = stli_onbinit;
-               brdp->enable = stli_onbenable;
-               brdp->reenable = stli_onbenable;
-               brdp->disable = stli_onbdisable;
-               brdp->getmemptr = stli_onbgetmemptr;
-               brdp->intr = stli_ecpintr;
-               brdp->reset = stli_onbreset;
-               if (brdp->memaddr > 0x100000)
-                       brdp->enabval = ONB_MEMENABHI;
-               else
-                       brdp->enabval = ONB_MEMENABLO;
-               name = "serial(ONBoard)";
-               break;
-
-       case BRD_ONBOARDE:
-               brdp->memsize = ONB_EIMEMSIZE;
-               brdp->pagesize = ONB_EIPAGESIZE;
-               brdp->init = stli_onbeinit;
-               brdp->enable = stli_onbeenable;
-               brdp->reenable = stli_onbeenable;
-               brdp->disable = stli_onbedisable;
-               brdp->getmemptr = stli_onbegetmemptr;
-               brdp->intr = stli_ecpintr;
-               brdp->reset = stli_onbereset;
-               name = "serial(ONBoard/E)";
-               break;
-
-       case BRD_BRUMBY4:
-               brdp->memsize = BBY_MEMSIZE;
-               brdp->pagesize = BBY_PAGESIZE;
-               brdp->init = stli_bbyinit;
-               brdp->enable = NULL;
-               brdp->reenable = NULL;
-               brdp->disable = NULL;
-               brdp->getmemptr = stli_bbygetmemptr;
-               brdp->intr = stli_ecpintr;
-               brdp->reset = stli_bbyreset;
-               name = "serial(Brumby)";
-               break;
-
-       case BRD_STALLION:
-               brdp->memsize = STAL_MEMSIZE;
-               brdp->pagesize = STAL_PAGESIZE;
-               brdp->init = stli_stalinit;
-               brdp->enable = NULL;
-               brdp->reenable = NULL;
-               brdp->disable = NULL;
-               brdp->getmemptr = stli_stalgetmemptr;
-               brdp->intr = stli_ecpintr;
-               brdp->reset = stli_stalreset;
-               name = "serial(Stallion)";
-               break;
-
-       default:
-               retval = -EINVAL;
-               goto err_reg;
-       }
-
-/*
- *     The per-board operations structure is all set up, so now let's go
- *     and get the board operational. Firstly initialize board configuration
- *     registers. Set the memory mapping info so we can get at the boards
- *     shared memory.
- */
-       EBRDINIT(brdp);
-
-       brdp->membase = ioremap_nocache(brdp->memaddr, brdp->memsize);
-       if (brdp->membase == NULL) {
-               retval = -ENOMEM;
-               goto err_reg;
-       }
-
-/*
- *     Now that all specific code is set up, enable the shared memory and
- *     look for the a signature area that will tell us exactly what board
- *     this is, and how many ports.
- */
-       EBRDENABLE(brdp);
-       sigsp = (cdkonbsig_t __iomem *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
-       memcpy_fromio(&sig, sigsp, sizeof(cdkonbsig_t));
-       EBRDDISABLE(brdp);
-
-       if (sig.magic0 != cpu_to_le16(ONB_MAGIC0) ||
-           sig.magic1 != cpu_to_le16(ONB_MAGIC1) ||
-           sig.magic2 != cpu_to_le16(ONB_MAGIC2) ||
-           sig.magic3 != cpu_to_le16(ONB_MAGIC3)) {
-               retval = -ENODEV;
-               goto err_unmap;
-       }
-
-/*
- *     Scan through the signature alive mask and calculate how many ports
- *     there are on this board.
- */
-       brdp->nrpanels = 1;
-       if (sig.amask1) {
-               brdp->nrports = 32;
-       } else {
-               for (i = 0; (i < 16); i++) {
-                       if (((sig.amask0 << i) & 0x8000) == 0)
-                               break;
-               }
-               brdp->nrports = i;
-       }
-       brdp->panels[0] = brdp->nrports;
-
-
-       set_bit(BST_FOUND, &brdp->state);
-       return 0;
-err_unmap:
-       iounmap(brdp->membase);
-       brdp->membase = NULL;
-err_reg:
-       release_region(brdp->iobase, brdp->iosize);
-err:
-       return retval;
-}
-
-/*****************************************************************************/
-
-/*
- *     Start up a running board. This routine is only called after the
- *     code has been down loaded to the board and is operational. It will
- *     read in the memory map, and get the show on the road...
- */
-
-static int stli_startbrd(struct stlibrd *brdp)
-{
-       cdkhdr_t __iomem *hdrp;
-       cdkmem_t __iomem *memp;
-       cdkasy_t __iomem *ap;
-       unsigned long flags;
-       unsigned int portnr, nrdevs, i;
-       struct stliport *portp;
-       int rc = 0;
-       u32 memoff;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       EBRDENABLE(brdp);
-       hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-       nrdevs = hdrp->nrdevs;
-
-#if 0
-       printk("%s(%d): CDK version %d.%d.%d --> "
-               "nrdevs=%d memp=%x hostp=%x slavep=%x\n",
-                __FILE__, __LINE__, readb(&hdrp->ver_release), readb(&hdrp->ver_modification),
-                readb(&hdrp->ver_fix), nrdevs, (int) readl(&hdrp->memp), readl(&hdrp->hostp),
-                readl(&hdrp->slavep));
-#endif
-
-       if (nrdevs < (brdp->nrports + 1)) {
-               printk(KERN_ERR "istallion: slave failed to allocate memory for "
-                               "all devices, devices=%d\n", nrdevs);
-               brdp->nrports = nrdevs - 1;
-       }
-       brdp->nrdevs = nrdevs;
-       brdp->hostoffset = hdrp->hostp - CDK_CDKADDR;
-       brdp->slaveoffset = hdrp->slavep - CDK_CDKADDR;
-       brdp->bitsize = (nrdevs + 7) / 8;
-       memoff = readl(&hdrp->memp);
-       if (memoff > brdp->memsize) {
-               printk(KERN_ERR "istallion: corrupted shared memory region?\n");
-               rc = -EIO;
-               goto stli_donestartup;
-       }
-       memp = (cdkmem_t __iomem *) EBRDGETMEMPTR(brdp, memoff);
-       if (readw(&memp->dtype) != TYP_ASYNCTRL) {
-               printk(KERN_ERR "istallion: no slave control device found\n");
-               goto stli_donestartup;
-       }
-       memp++;
-
-/*
- *     Cycle through memory allocation of each port. We are guaranteed to
- *     have all ports inside the first page of slave window, so no need to
- *     change pages while reading memory map.
- */
-       for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++, memp++) {
-               if (readw(&memp->dtype) != TYP_ASYNC)
-                       break;
-               portp = brdp->ports[portnr];
-               if (portp == NULL)
-                       break;
-               portp->devnr = i;
-               portp->addr = readl(&memp->offset);
-               portp->reqbit = (unsigned char) (0x1 << (i * 8 / nrdevs));
-               portp->portidx = (unsigned char) (i / 8);
-               portp->portbit = (unsigned char) (0x1 << (i % 8));
-       }
-
-       writeb(0xff, &hdrp->slavereq);
-
-/*
- *     For each port setup a local copy of the RX and TX buffer offsets
- *     and sizes. We do this separate from the above, because we need to
- *     move the shared memory page...
- */
-       for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++) {
-               portp = brdp->ports[portnr];
-               if (portp == NULL)
-                       break;
-               if (portp->addr == 0)
-                       break;
-               ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
-               if (ap != NULL) {
-                       portp->rxsize = readw(&ap->rxq.size);
-                       portp->txsize = readw(&ap->txq.size);
-                       portp->rxoffset = readl(&ap->rxq.offset);
-                       portp->txoffset = readl(&ap->txq.offset);
-               }
-       }
-
-stli_donestartup:
-       EBRDDISABLE(brdp);
-       spin_unlock_irqrestore(&brd_lock, flags);
-
-       if (rc == 0)
-               set_bit(BST_STARTED, &brdp->state);
-
-       if (! stli_timeron) {
-               stli_timeron++;
-               mod_timer(&stli_timerlist, STLI_TIMEOUT);
-       }
-
-       return rc;
-}
-
-/*****************************************************************************/
-
-/*
- *     Probe and initialize the specified board.
- */
-
-static int __devinit stli_brdinit(struct stlibrd *brdp)
-{
-       int retval;
-
-       switch (brdp->brdtype) {
-       case BRD_ECP:
-       case BRD_ECPE:
-       case BRD_ECPMC:
-       case BRD_ECPPCI:
-               retval = stli_initecp(brdp);
-               break;
-       case BRD_ONBOARD:
-       case BRD_ONBOARDE:
-       case BRD_ONBOARD2:
-       case BRD_BRUMBY4:
-       case BRD_STALLION:
-               retval = stli_initonb(brdp);
-               break;
-       default:
-               printk(KERN_ERR "istallion: board=%d is unknown board "
-                               "type=%d\n", brdp->brdnr, brdp->brdtype);
-               retval = -ENODEV;
-       }
-
-       if (retval)
-               return retval;
-
-       stli_initports(brdp);
-       printk(KERN_INFO "istallion: %s found, board=%d io=%x mem=%x "
-               "nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype],
-               brdp->brdnr, brdp->iobase, (int) brdp->memaddr,
-               brdp->nrpanels, brdp->nrports);
-       return 0;
-}
-
-#if STLI_EISAPROBE != 0
-/*****************************************************************************/
-
-/*
- *     Probe around trying to find where the EISA boards shared memory
- *     might be. This is a bit if hack, but it is the best we can do.
- */
-
-static int stli_eisamemprobe(struct stlibrd *brdp)
-{
-       cdkecpsig_t     ecpsig, __iomem *ecpsigp;
-       cdkonbsig_t     onbsig, __iomem *onbsigp;
-       int             i, foundit;
-
-/*
- *     First up we reset the board, to get it into a known state. There
- *     is only 2 board types here we need to worry about. Don;t use the
- *     standard board init routine here, it programs up the shared
- *     memory address, and we don't know it yet...
- */
-       if (brdp->brdtype == BRD_ECPE) {
-               outb(0x1, (brdp->iobase + ECP_EIBRDENAB));
-               outb(ECP_EISTOP, (brdp->iobase + ECP_EICONFR));
-               udelay(10);
-               outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
-               udelay(500);
-               stli_ecpeienable(brdp);
-       } else if (brdp->brdtype == BRD_ONBOARDE) {
-               outb(0x1, (brdp->iobase + ONB_EIBRDENAB));
-               outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
-               udelay(10);
-               outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
-               mdelay(100);
-               outb(0x1, brdp->iobase);
-               mdelay(1);
-               stli_onbeenable(brdp);
-       } else {
-               return -ENODEV;
-       }
-
-       foundit = 0;
-       brdp->memsize = ECP_MEMSIZE;
-
-/*
- *     Board shared memory is enabled, so now we have a poke around and
- *     see if we can find it.
- */
-       for (i = 0; (i < stli_eisamempsize); i++) {
-               brdp->memaddr = stli_eisamemprobeaddrs[i];
-               brdp->membase = ioremap_nocache(brdp->memaddr, brdp->memsize);
-               if (brdp->membase == NULL)
-                       continue;
-
-               if (brdp->brdtype == BRD_ECPE) {
-                       ecpsigp = stli_ecpeigetmemptr(brdp,
-                               CDK_SIGADDR, __LINE__);
-                       memcpy_fromio(&ecpsig, ecpsigp, sizeof(cdkecpsig_t));
-                       if (ecpsig.magic == cpu_to_le32(ECP_MAGIC))
-                               foundit = 1;
-               } else {
-                       onbsigp = (cdkonbsig_t __iomem *) stli_onbegetmemptr(brdp,
-                               CDK_SIGADDR, __LINE__);
-                       memcpy_fromio(&onbsig, onbsigp, sizeof(cdkonbsig_t));
-                       if ((onbsig.magic0 == cpu_to_le16(ONB_MAGIC0)) &&
-                           (onbsig.magic1 == cpu_to_le16(ONB_MAGIC1)) &&
-                           (onbsig.magic2 == cpu_to_le16(ONB_MAGIC2)) &&
-                           (onbsig.magic3 == cpu_to_le16(ONB_MAGIC3)))
-                               foundit = 1;
-               }
-
-               iounmap(brdp->membase);
-               if (foundit)
-                       break;
-       }
-
-/*
- *     Regardless of whether we found the shared memory or not we must
- *     disable the region. After that return success or failure.
- */
-       if (brdp->brdtype == BRD_ECPE)
-               stli_ecpeidisable(brdp);
-       else
-               stli_onbedisable(brdp);
-
-       if (! foundit) {
-               brdp->memaddr = 0;
-               brdp->membase = NULL;
-               printk(KERN_ERR "istallion: failed to probe shared memory "
-                               "region for %s in EISA slot=%d\n",
-                       stli_brdnames[brdp->brdtype], (brdp->iobase >> 12));
-               return -ENODEV;
-       }
-       return 0;
-}
-#endif
-
-static int stli_getbrdnr(void)
-{
-       unsigned int i;
-
-       for (i = 0; i < STL_MAXBRDS; i++) {
-               if (!stli_brds[i]) {
-                       if (i >= stli_nrbrds)
-                               stli_nrbrds = i + 1;
-                       return i;
-               }
-       }
-       return -1;
-}
-
-#if STLI_EISAPROBE != 0
-/*****************************************************************************/
-
-/*
- *     Probe around and try to find any EISA boards in system. The biggest
- *     problem here is finding out what memory address is associated with
- *     an EISA board after it is found. The registers of the ECPE and
- *     ONboardE are not readable - so we can't read them from there. We
- *     don't have access to the EISA CMOS (or EISA BIOS) so we don't
- *     actually have any way to find out the real value. The best we can
- *     do is go probing around in the usual places hoping we can find it.
- */
-
-static int __init stli_findeisabrds(void)
-{
-       struct stlibrd *brdp;
-       unsigned int iobase, eid, i;
-       int brdnr, found = 0;
-
-/*
- *     Firstly check if this is an EISA system.  If this is not an EISA system then
- *     don't bother going any further!
- */
-       if (EISA_bus)
-               return 0;
-
-/*
- *     Looks like an EISA system, so go searching for EISA boards.
- */
-       for (iobase = 0x1000; (iobase <= 0xc000); iobase += 0x1000) {
-               outb(0xff, (iobase + 0xc80));
-               eid = inb(iobase + 0xc80);
-               eid |= inb(iobase + 0xc81) << 8;
-               if (eid != STL_EISAID)
-                       continue;
-
-/*
- *             We have found a board. Need to check if this board was
- *             statically configured already (just in case!).
- */
-               for (i = 0; (i < STL_MAXBRDS); i++) {
-                       brdp = stli_brds[i];
-                       if (brdp == NULL)
-                               continue;
-                       if (brdp->iobase == iobase)
-                               break;
-               }
-               if (i < STL_MAXBRDS)
-                       continue;
-
-/*
- *             We have found a Stallion board and it is not configured already.
- *             Allocate a board structure and initialize it.
- */
-               if ((brdp = stli_allocbrd()) == NULL)
-                       return found ? : -ENOMEM;
-               brdnr = stli_getbrdnr();
-               if (brdnr < 0)
-                       return found ? : -ENOMEM;
-               brdp->brdnr = (unsigned int)brdnr;
-               eid = inb(iobase + 0xc82);
-               if (eid == ECP_EISAID)
-                       brdp->brdtype = BRD_ECPE;
-               else if (eid == ONB_EISAID)
-                       brdp->brdtype = BRD_ONBOARDE;
-               else
-                       brdp->brdtype = BRD_UNKNOWN;
-               brdp->iobase = iobase;
-               outb(0x1, (iobase + 0xc84));
-               if (stli_eisamemprobe(brdp))
-                       outb(0, (iobase + 0xc84));
-               if (stli_brdinit(brdp) < 0) {
-                       kfree(brdp);
-                       continue;
-               }
-
-               stli_brds[brdp->brdnr] = brdp;
-               found++;
-
-               for (i = 0; i < brdp->nrports; i++)
-                       tty_register_device(stli_serial,
-                                       brdp->brdnr * STL_MAXPORTS + i, NULL);
-       }
-
-       return found;
-}
-#else
-static inline int stli_findeisabrds(void) { return 0; }
-#endif
-
-/*****************************************************************************/
-
-/*
- *     Find the next available board number that is free.
- */
-
-/*****************************************************************************/
-
-/*
- *     We have a Stallion board. Allocate a board structure and
- *     initialize it. Read its IO and MEMORY resources from PCI
- *     configuration space.
- */
-
-static int __devinit stli_pciprobe(struct pci_dev *pdev,
-               const struct pci_device_id *ent)
-{
-       struct stlibrd *brdp;
-       unsigned int i;
-       int brdnr, retval = -EIO;
-
-       retval = pci_enable_device(pdev);
-       if (retval)
-               goto err;
-       brdp = stli_allocbrd();
-       if (brdp == NULL) {
-               retval = -ENOMEM;
-               goto err;
-       }
-       mutex_lock(&stli_brdslock);
-       brdnr = stli_getbrdnr();
-       if (brdnr < 0) {
-               printk(KERN_INFO "istallion: too many boards found, "
-                       "maximum supported %d\n", STL_MAXBRDS);
-               mutex_unlock(&stli_brdslock);
-               retval = -EIO;
-               goto err_fr;
-       }
-       brdp->brdnr = (unsigned int)brdnr;
-       stli_brds[brdp->brdnr] = brdp;
-       mutex_unlock(&stli_brdslock);
-       brdp->brdtype = BRD_ECPPCI;
-/*
- *     We have all resources from the board, so lets setup the actual
- *     board structure now.
- */
-       brdp->iobase = pci_resource_start(pdev, 3);
-       brdp->memaddr = pci_resource_start(pdev, 2);
-       retval = stli_brdinit(brdp);
-       if (retval)
-               goto err_null;
-
-       set_bit(BST_PROBED, &brdp->state);
-       pci_set_drvdata(pdev, brdp);
-
-       EBRDENABLE(brdp);
-       brdp->enable = NULL;
-       brdp->disable = NULL;
-
-       for (i = 0; i < brdp->nrports; i++)
-               tty_register_device(stli_serial, brdp->brdnr * STL_MAXPORTS + i,
-                               &pdev->dev);
-
-       return 0;
-err_null:
-       stli_brds[brdp->brdnr] = NULL;
-err_fr:
-       kfree(brdp);
-err:
-       return retval;
-}
-
-static void __devexit stli_pciremove(struct pci_dev *pdev)
-{
-       struct stlibrd *brdp = pci_get_drvdata(pdev);
-
-       stli_cleanup_ports(brdp);
-
-       iounmap(brdp->membase);
-       if (brdp->iosize > 0)
-               release_region(brdp->iobase, brdp->iosize);
-
-       stli_brds[brdp->brdnr] = NULL;
-       kfree(brdp);
-}
-
-static struct pci_driver stli_pcidriver = {
-       .name = "istallion",
-       .id_table = istallion_pci_tbl,
-       .probe = stli_pciprobe,
-       .remove = __devexit_p(stli_pciremove)
-};
-/*****************************************************************************/
-
-/*
- *     Allocate a new board structure. Fill out the basic info in it.
- */
-
-static struct stlibrd *stli_allocbrd(void)
-{
-       struct stlibrd *brdp;
-
-       brdp = kzalloc(sizeof(struct stlibrd), GFP_KERNEL);
-       if (!brdp) {
-               printk(KERN_ERR "istallion: failed to allocate memory "
-                               "(size=%Zd)\n", sizeof(struct stlibrd));
-               return NULL;
-       }
-       brdp->magic = STLI_BOARDMAGIC;
-       return brdp;
-}
-
-/*****************************************************************************/
-
-/*
- *     Scan through all the boards in the configuration and see what we
- *     can find.
- */
-
-static int __init stli_initbrds(void)
-{
-       struct stlibrd *brdp, *nxtbrdp;
-       struct stlconf conf;
-       unsigned int i, j, found = 0;
-       int retval;
-
-       for (stli_nrbrds = 0; stli_nrbrds < ARRAY_SIZE(stli_brdsp);
-                       stli_nrbrds++) {
-               memset(&conf, 0, sizeof(conf));
-               if (stli_parsebrd(&conf, stli_brdsp[stli_nrbrds]) == 0)
-                       continue;
-               if ((brdp = stli_allocbrd()) == NULL)
-                       continue;
-               brdp->brdnr = stli_nrbrds;
-               brdp->brdtype = conf.brdtype;
-               brdp->iobase = conf.ioaddr1;
-               brdp->memaddr = conf.memaddr;
-               if (stli_brdinit(brdp) < 0) {
-                       kfree(brdp);
-                       continue;
-               }
-               stli_brds[brdp->brdnr] = brdp;
-               found++;
-
-               for (i = 0; i < brdp->nrports; i++)
-                       tty_register_device(stli_serial,
-                                       brdp->brdnr * STL_MAXPORTS + i, NULL);
-       }
-
-       retval = stli_findeisabrds();
-       if (retval > 0)
-               found += retval;
-
-/*
- *     All found boards are initialized. Now for a little optimization, if
- *     no boards are sharing the "shared memory" regions then we can just
- *     leave them all enabled. This is in fact the usual case.
- */
-       stli_shared = 0;
-       if (stli_nrbrds > 1) {
-               for (i = 0; (i < stli_nrbrds); i++) {
-                       brdp = stli_brds[i];
-                       if (brdp == NULL)
-                               continue;
-                       for (j = i + 1; (j < stli_nrbrds); j++) {
-                               nxtbrdp = stli_brds[j];
-                               if (nxtbrdp == NULL)
-                                       continue;
-                               if ((brdp->membase >= nxtbrdp->membase) &&
-                                   (brdp->membase <= (nxtbrdp->membase +
-                                   nxtbrdp->memsize - 1))) {
-                                       stli_shared++;
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       if (stli_shared == 0) {
-               for (i = 0; (i < stli_nrbrds); i++) {
-                       brdp = stli_brds[i];
-                       if (brdp == NULL)
-                               continue;
-                       if (test_bit(BST_FOUND, &brdp->state)) {
-                               EBRDENABLE(brdp);
-                               brdp->enable = NULL;
-                               brdp->disable = NULL;
-                       }
-               }
-       }
-
-       retval = pci_register_driver(&stli_pcidriver);
-       if (retval && found == 0) {
-               printk(KERN_ERR "Neither isa nor eisa cards found nor pci "
-                               "driver can be registered!\n");
-               goto err;
-       }
-
-       return 0;
-err:
-       return retval;
-}
-
-/*****************************************************************************/
-
-/*
- *     Code to handle an "staliomem" read operation. This device is the 
- *     contents of the board shared memory. It is used for down loading
- *     the slave image (and debugging :-)
- */
-
-static ssize_t stli_memread(struct file *fp, char __user *buf, size_t count, loff_t *offp)
-{
-       unsigned long flags;
-       void __iomem *memptr;
-       struct stlibrd *brdp;
-       unsigned int brdnr;
-       int size, n;
-       void *p;
-       loff_t off = *offp;
-
-       brdnr = iminor(fp->f_path.dentry->d_inode);
-       if (brdnr >= stli_nrbrds)
-               return -ENODEV;
-       brdp = stli_brds[brdnr];
-       if (brdp == NULL)
-               return -ENODEV;
-       if (brdp->state == 0)
-               return -ENODEV;
-       if (off >= brdp->memsize || off + count < off)
-               return 0;
-
-       size = min(count, (size_t)(brdp->memsize - off));
-
-       /*
-        *      Copy the data a page at a time
-        */
-
-       p = (void *)__get_free_page(GFP_KERNEL);
-       if(p == NULL)
-               return -ENOMEM;
-
-       while (size > 0) {
-               spin_lock_irqsave(&brd_lock, flags);
-               EBRDENABLE(brdp);
-               memptr = EBRDGETMEMPTR(brdp, off);
-               n = min(size, (int)(brdp->pagesize - (((unsigned long) off) % brdp->pagesize)));
-               n = min(n, (int)PAGE_SIZE);
-               memcpy_fromio(p, memptr, n);
-               EBRDDISABLE(brdp);
-               spin_unlock_irqrestore(&brd_lock, flags);
-               if (copy_to_user(buf, p, n)) {
-                       count = -EFAULT;
-                       goto out;
-               }
-               off += n;
-               buf += n;
-               size -= n;
-       }
-out:
-       *offp = off;
-       free_page((unsigned long)p);
-       return count;
-}
-
-/*****************************************************************************/
-
-/*
- *     Code to handle an "staliomem" write operation. This device is the 
- *     contents of the board shared memory. It is used for down loading
- *     the slave image (and debugging :-)
- *
- *     FIXME: copy under lock
- */
-
-static ssize_t stli_memwrite(struct file *fp, const char __user *buf, size_t count, loff_t *offp)
-{
-       unsigned long flags;
-       void __iomem *memptr;
-       struct stlibrd *brdp;
-       char __user *chbuf;
-       unsigned int brdnr;
-       int size, n;
-       void *p;
-       loff_t off = *offp;
-
-       brdnr = iminor(fp->f_path.dentry->d_inode);
-
-       if (brdnr >= stli_nrbrds)
-               return -ENODEV;
-       brdp = stli_brds[brdnr];
-       if (brdp == NULL)
-               return -ENODEV;
-       if (brdp->state == 0)
-               return -ENODEV;
-       if (off >= brdp->memsize || off + count < off)
-               return 0;
-
-       chbuf = (char __user *) buf;
-       size = min(count, (size_t)(brdp->memsize - off));
-
-       /*
-        *      Copy the data a page at a time
-        */
-
-       p = (void *)__get_free_page(GFP_KERNEL);
-       if(p == NULL)
-               return -ENOMEM;
-
-       while (size > 0) {
-               n = min(size, (int)(brdp->pagesize - (((unsigned long) off) % brdp->pagesize)));
-               n = min(n, (int)PAGE_SIZE);
-               if (copy_from_user(p, chbuf, n)) {
-                       if (count == 0)
-                               count = -EFAULT;
-                       goto out;
-               }
-               spin_lock_irqsave(&brd_lock, flags);
-               EBRDENABLE(brdp);
-               memptr = EBRDGETMEMPTR(brdp, off);
-               memcpy_toio(memptr, p, n);
-               EBRDDISABLE(brdp);
-               spin_unlock_irqrestore(&brd_lock, flags);
-               off += n;
-               chbuf += n;
-               size -= n;
-       }
-out:
-       free_page((unsigned long) p);
-       *offp = off;
-       return count;
-}
-
-/*****************************************************************************/
-
-/*
- *     Return the board stats structure to user app.
- */
-
-static int stli_getbrdstats(combrd_t __user *bp)
-{
-       struct stlibrd *brdp;
-       unsigned int i;
-
-       if (copy_from_user(&stli_brdstats, bp, sizeof(combrd_t)))
-               return -EFAULT;
-       if (stli_brdstats.brd >= STL_MAXBRDS)
-               return -ENODEV;
-       brdp = stli_brds[stli_brdstats.brd];
-       if (brdp == NULL)
-               return -ENODEV;
-
-       memset(&stli_brdstats, 0, sizeof(combrd_t));
-
-       stli_brdstats.brd = brdp->brdnr;
-       stli_brdstats.type = brdp->brdtype;
-       stli_brdstats.hwid = 0;
-       stli_brdstats.state = brdp->state;
-       stli_brdstats.ioaddr = brdp->iobase;
-       stli_brdstats.memaddr = brdp->memaddr;
-       stli_brdstats.nrpanels = brdp->nrpanels;
-       stli_brdstats.nrports = brdp->nrports;
-       for (i = 0; (i < brdp->nrpanels); i++) {
-               stli_brdstats.panels[i].panel = i;
-               stli_brdstats.panels[i].hwid = brdp->panelids[i];
-               stli_brdstats.panels[i].nrports = brdp->panels[i];
-       }
-
-       if (copy_to_user(bp, &stli_brdstats, sizeof(combrd_t)))
-               return -EFAULT;
-       return 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Resolve the referenced port number into a port struct pointer.
- */
-
-static struct stliport *stli_getport(unsigned int brdnr, unsigned int panelnr,
-               unsigned int portnr)
-{
-       struct stlibrd *brdp;
-       unsigned int i;
-
-       if (brdnr >= STL_MAXBRDS)
-               return NULL;
-       brdp = stli_brds[brdnr];
-       if (brdp == NULL)
-               return NULL;
-       for (i = 0; (i < panelnr); i++)
-               portnr += brdp->panels[i];
-       if (portnr >= brdp->nrports)
-               return NULL;
-       return brdp->ports[portnr];
-}
-
-/*****************************************************************************/
-
-/*
- *     Return the port stats structure to user app. A NULL port struct
- *     pointer passed in means that we need to find out from the app
- *     what port to get stats for (used through board control device).
- */
-
-static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp)
-{
-       unsigned long   flags;
-       struct stlibrd  *brdp;
-       int             rc;
-
-       memset(&stli_comstats, 0, sizeof(comstats_t));
-
-       if (portp == NULL)
-               return -ENODEV;
-       brdp = stli_brds[portp->brdnr];
-       if (brdp == NULL)
-               return -ENODEV;
-
-       mutex_lock(&portp->port.mutex);
-       if (test_bit(BST_STARTED, &brdp->state)) {
-               if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS,
-                   &stli_cdkstats, sizeof(asystats_t), 1)) < 0) {
-                       mutex_unlock(&portp->port.mutex);
-                       return rc;
-               }
-       } else {
-               memset(&stli_cdkstats, 0, sizeof(asystats_t));
-       }
-
-       stli_comstats.brd = portp->brdnr;
-       stli_comstats.panel = portp->panelnr;
-       stli_comstats.port = portp->portnr;
-       stli_comstats.state = portp->state;
-       stli_comstats.flags = portp->port.flags;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       if (tty != NULL) {
-               if (portp->port.tty == tty) {
-                       stli_comstats.ttystate = tty->flags;
-                       stli_comstats.rxbuffered = -1;
-                       if (tty->termios != NULL) {
-                               stli_comstats.cflags = tty->termios->c_cflag;
-                               stli_comstats.iflags = tty->termios->c_iflag;
-                               stli_comstats.oflags = tty->termios->c_oflag;
-                               stli_comstats.lflags = tty->termios->c_lflag;
-                       }
-               }
-       }
-       spin_unlock_irqrestore(&brd_lock, flags);
-
-       stli_comstats.txtotal = stli_cdkstats.txchars;
-       stli_comstats.rxtotal = stli_cdkstats.rxchars + stli_cdkstats.ringover;
-       stli_comstats.txbuffered = stli_cdkstats.txringq;
-       stli_comstats.rxbuffered += stli_cdkstats.rxringq;
-       stli_comstats.rxoverrun = stli_cdkstats.overruns;
-       stli_comstats.rxparity = stli_cdkstats.parity;
-       stli_comstats.rxframing = stli_cdkstats.framing;
-       stli_comstats.rxlost = stli_cdkstats.ringover;
-       stli_comstats.rxbreaks = stli_cdkstats.rxbreaks;
-       stli_comstats.txbreaks = stli_cdkstats.txbreaks;
-       stli_comstats.txxon = stli_cdkstats.txstart;
-       stli_comstats.txxoff = stli_cdkstats.txstop;
-       stli_comstats.rxxon = stli_cdkstats.rxstart;
-       stli_comstats.rxxoff = stli_cdkstats.rxstop;
-       stli_comstats.rxrtsoff = stli_cdkstats.rtscnt / 2;
-       stli_comstats.rxrtson = stli_cdkstats.rtscnt - stli_comstats.rxrtsoff;
-       stli_comstats.modem = stli_cdkstats.dcdcnt;
-       stli_comstats.hwid = stli_cdkstats.hwid;
-       stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals);
-       mutex_unlock(&portp->port.mutex);
-
-       return 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Return the port stats structure to user app. A NULL port struct
- *     pointer passed in means that we need to find out from the app
- *     what port to get stats for (used through board control device).
- */
-
-static int stli_getportstats(struct tty_struct *tty, struct stliport *portp,
-                                                       comstats_t __user *cp)
-{
-       struct stlibrd *brdp;
-       int rc;
-
-       if (!portp) {
-               if (copy_from_user(&stli_comstats, cp, sizeof(comstats_t)))
-                       return -EFAULT;
-               portp = stli_getport(stli_comstats.brd, stli_comstats.panel,
-                       stli_comstats.port);
-               if (!portp)
-                       return -ENODEV;
-       }
-
-       brdp = stli_brds[portp->brdnr];
-       if (!brdp)
-               return -ENODEV;
-
-       if ((rc = stli_portcmdstats(tty, portp)) < 0)
-               return rc;
-
-       return copy_to_user(cp, &stli_comstats, sizeof(comstats_t)) ?
-                       -EFAULT : 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Clear the port stats structure. We also return it zeroed out...
- */
-
-static int stli_clrportstats(struct stliport *portp, comstats_t __user *cp)
-{
-       struct stlibrd *brdp;
-       int rc;
-
-       if (!portp) {
-               if (copy_from_user(&stli_comstats, cp, sizeof(comstats_t)))
-                       return -EFAULT;
-               portp = stli_getport(stli_comstats.brd, stli_comstats.panel,
-                       stli_comstats.port);
-               if (!portp)
-                       return -ENODEV;
-       }
-
-       brdp = stli_brds[portp->brdnr];
-       if (!brdp)
-               return -ENODEV;
-
-       mutex_lock(&portp->port.mutex);
-
-       if (test_bit(BST_STARTED, &brdp->state)) {
-               if ((rc = stli_cmdwait(brdp, portp, A_CLEARSTATS, NULL, 0, 0)) < 0) {
-                       mutex_unlock(&portp->port.mutex);
-                       return rc;
-               }
-       }
-
-       memset(&stli_comstats, 0, sizeof(comstats_t));
-       stli_comstats.brd = portp->brdnr;
-       stli_comstats.panel = portp->panelnr;
-       stli_comstats.port = portp->portnr;
-       mutex_unlock(&portp->port.mutex);
-
-       if (copy_to_user(cp, &stli_comstats, sizeof(comstats_t)))
-               return -EFAULT;
-       return 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Return the entire driver ports structure to a user app.
- */
-
-static int stli_getportstruct(struct stliport __user *arg)
-{
-       struct stliport stli_dummyport;
-       struct stliport *portp;
-
-       if (copy_from_user(&stli_dummyport, arg, sizeof(struct stliport)))
-               return -EFAULT;
-       portp = stli_getport(stli_dummyport.brdnr, stli_dummyport.panelnr,
-                stli_dummyport.portnr);
-       if (!portp)
-               return -ENODEV;
-       if (copy_to_user(arg, portp, sizeof(struct stliport)))
-               return -EFAULT;
-       return 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Return the entire driver board structure to a user app.
- */
-
-static int stli_getbrdstruct(struct stlibrd __user *arg)
-{
-       struct stlibrd stli_dummybrd;
-       struct stlibrd *brdp;
-
-       if (copy_from_user(&stli_dummybrd, arg, sizeof(struct stlibrd)))
-               return -EFAULT;
-       if (stli_dummybrd.brdnr >= STL_MAXBRDS)
-               return -ENODEV;
-       brdp = stli_brds[stli_dummybrd.brdnr];
-       if (!brdp)
-               return -ENODEV;
-       if (copy_to_user(arg, brdp, sizeof(struct stlibrd)))
-               return -EFAULT;
-       return 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     The "staliomem" device is also required to do some special operations on
- *     the board. We need to be able to send an interrupt to the board,
- *     reset it, and start/stop it.
- */
-
-static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
-{
-       struct stlibrd *brdp;
-       int brdnr, rc, done;
-       void __user *argp = (void __user *)arg;
-
-/*
- *     First up handle the board independent ioctls.
- */
-       done = 0;
-       rc = 0;
-
-       switch (cmd) {
-       case COM_GETPORTSTATS:
-               rc = stli_getportstats(NULL, NULL, argp);
-               done++;
-               break;
-       case COM_CLRPORTSTATS:
-               rc = stli_clrportstats(NULL, argp);
-               done++;
-               break;
-       case COM_GETBRDSTATS:
-               rc = stli_getbrdstats(argp);
-               done++;
-               break;
-       case COM_READPORT:
-               rc = stli_getportstruct(argp);
-               done++;
-               break;
-       case COM_READBOARD:
-               rc = stli_getbrdstruct(argp);
-               done++;
-               break;
-       }
-       if (done)
-               return rc;
-
-/*
- *     Now handle the board specific ioctls. These all depend on the
- *     minor number of the device they were called from.
- */
-       brdnr = iminor(fp->f_dentry->d_inode);
-       if (brdnr >= STL_MAXBRDS)
-               return -ENODEV;
-       brdp = stli_brds[brdnr];
-       if (!brdp)
-               return -ENODEV;
-       if (brdp->state == 0)
-               return -ENODEV;
-
-       switch (cmd) {
-       case STL_BINTR:
-               EBRDINTR(brdp);
-               break;
-       case STL_BSTART:
-               rc = stli_startbrd(brdp);
-               break;
-       case STL_BSTOP:
-               clear_bit(BST_STARTED, &brdp->state);
-               break;
-       case STL_BRESET:
-               clear_bit(BST_STARTED, &brdp->state);
-               EBRDRESET(brdp);
-               if (stli_shared == 0) {
-                       if (brdp->reenable != NULL)
-                               (* brdp->reenable)(brdp);
-               }
-               break;
-       default:
-               rc = -ENOIOCTLCMD;
-               break;
-       }
-       return rc;
-}
-
-static const struct tty_operations stli_ops = {
-       .open = stli_open,
-       .close = stli_close,
-       .write = stli_write,
-       .put_char = stli_putchar,
-       .flush_chars = stli_flushchars,
-       .write_room = stli_writeroom,
-       .chars_in_buffer = stli_charsinbuffer,
-       .ioctl = stli_ioctl,
-       .set_termios = stli_settermios,
-       .throttle = stli_throttle,
-       .unthrottle = stli_unthrottle,
-       .stop = stli_stop,
-       .start = stli_start,
-       .hangup = stli_hangup,
-       .flush_buffer = stli_flushbuffer,
-       .break_ctl = stli_breakctl,
-       .wait_until_sent = stli_waituntilsent,
-       .send_xchar = stli_sendxchar,
-       .tiocmget = stli_tiocmget,
-       .tiocmset = stli_tiocmset,
-       .proc_fops = &stli_proc_fops,
-};
-
-static const struct tty_port_operations stli_port_ops = {
-       .carrier_raised = stli_carrier_raised,
-       .dtr_rts = stli_dtr_rts,
-       .activate = stli_activate,
-       .shutdown = stli_shutdown,
-};
-
-/*****************************************************************************/
-/*
- *     Loadable module initialization stuff.
- */
-
-static void istallion_cleanup_isa(void)
-{
-       struct stlibrd  *brdp;
-       unsigned int j;
-
-       for (j = 0; (j < stli_nrbrds); j++) {
-               if ((brdp = stli_brds[j]) == NULL ||
-                               test_bit(BST_PROBED, &brdp->state))
-                       continue;
-
-               stli_cleanup_ports(brdp);
-
-               iounmap(brdp->membase);
-               if (brdp->iosize > 0)
-                       release_region(brdp->iobase, brdp->iosize);
-               kfree(brdp);
-               stli_brds[j] = NULL;
-       }
-}
-
-static int __init istallion_module_init(void)
-{
-       unsigned int i;
-       int retval;
-
-       printk(KERN_INFO "%s: version %s\n", stli_drvtitle, stli_drvversion);
-
-       spin_lock_init(&stli_lock);
-       spin_lock_init(&brd_lock);
-
-       stli_txcookbuf = kmalloc(STLI_TXBUFSIZE, GFP_KERNEL);
-       if (!stli_txcookbuf) {
-               printk(KERN_ERR "istallion: failed to allocate memory "
-                               "(size=%d)\n", STLI_TXBUFSIZE);
-               retval = -ENOMEM;
-               goto err;
-       }
-
-       stli_serial = alloc_tty_driver(STL_MAXBRDS * STL_MAXPORTS);
-       if (!stli_serial) {
-               retval = -ENOMEM;
-               goto err_free;
-       }
-
-       stli_serial->owner = THIS_MODULE;
-       stli_serial->driver_name = stli_drvname;
-       stli_serial->name = stli_serialname;
-       stli_serial->major = STL_SERIALMAJOR;
-       stli_serial->minor_start = 0;
-       stli_serial->type = TTY_DRIVER_TYPE_SERIAL;
-       stli_serial->subtype = SERIAL_TYPE_NORMAL;
-       stli_serial->init_termios = stli_deftermios;
-       stli_serial->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
-       tty_set_operations(stli_serial, &stli_ops);
-
-       retval = tty_register_driver(stli_serial);
-       if (retval) {
-               printk(KERN_ERR "istallion: failed to register serial driver\n");
-               goto err_ttyput;
-       }
-
-       retval = stli_initbrds();
-       if (retval)
-               goto err_ttyunr;
-
-/*
- *     Set up a character driver for the shared memory region. We need this
- *     to down load the slave code image. Also it is a useful debugging tool.
- */
-       retval = register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem);
-       if (retval) {
-               printk(KERN_ERR "istallion: failed to register serial memory "
-                               "device\n");
-               goto err_deinit;
-       }
-
-       istallion_class = class_create(THIS_MODULE, "staliomem");
-       for (i = 0; i < 4; i++)
-               device_create(istallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i),
-                             NULL, "staliomem%d", i);
-
-       return 0;
-err_deinit:
-       pci_unregister_driver(&stli_pcidriver);
-       istallion_cleanup_isa();
-err_ttyunr:
-       tty_unregister_driver(stli_serial);
-err_ttyput:
-       put_tty_driver(stli_serial);
-err_free:
-       kfree(stli_txcookbuf);
-err:
-       return retval;
-}
-
-/*****************************************************************************/
-
-static void __exit istallion_module_exit(void)
-{
-       unsigned int j;
-
-       printk(KERN_INFO "Unloading %s: version %s\n", stli_drvtitle,
-               stli_drvversion);
-
-       if (stli_timeron) {
-               stli_timeron = 0;
-               del_timer_sync(&stli_timerlist);
-       }
-
-       unregister_chrdev(STL_SIOMEMMAJOR, "staliomem");
-
-       for (j = 0; j < 4; j++)
-               device_destroy(istallion_class, MKDEV(STL_SIOMEMMAJOR, j));
-       class_destroy(istallion_class);
-
-       pci_unregister_driver(&stli_pcidriver);
-       istallion_cleanup_isa();
-
-       tty_unregister_driver(stli_serial);
-       put_tty_driver(stli_serial);
-
-       kfree(stli_txcookbuf);
-}
-
-module_init(istallion_module_init);
-module_exit(istallion_module_exit);
diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c
deleted file mode 100644 (file)
index 602643a..0000000
+++ /dev/null
@@ -1,1560 +0,0 @@
-/*
- *      linux/drivers/char/riscom.c  -- RISCom/8 multiport serial driver.
- *
- *      Copyright (C) 1994-1996  Dmitry Gorodchanin (pgmdsg@ibi.com)
- *
- *      This code is loosely based on the Linux serial driver, written by
- *      Linus Torvalds, Theodore T'so and others. The RISCom/8 card
- *      programming info was obtained from various drivers for other OSes
- *     (FreeBSD, ISC, etc), but no source code from those drivers were
- *     directly included in this driver.
- *
- *
- *      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.
- *
- *     Revision 1.1
- *
- *     ChangeLog:
- *     Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 27-Jun-2001
- *     - get rid of check_region and several cleanups
- */
-
-#include <linux/module.h>
-
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/errno.h>
-#include <linux/tty.h>
-#include <linux/mm.h>
-#include <linux/serial.h>
-#include <linux/fcntl.h>
-#include <linux/major.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/tty_flip.h>
-#include <linux/spinlock.h>
-#include <linux/device.h>
-
-#include <linux/uaccess.h>
-
-#include "riscom8.h"
-#include "riscom8_reg.h"
-
-/* Am I paranoid or not ? ;-) */
-#define RISCOM_PARANOIA_CHECK
-
-/*
- * Crazy InteliCom/8 boards sometimes have swapped CTS & DSR signals.
- * You can slightly speed up things by #undefing the following option,
- * if you are REALLY sure that your board is correct one.
- */
-
-#define RISCOM_BRAIN_DAMAGED_CTS
-
-/*
- * The following defines are mostly for testing purposes. But if you need
- * some nice reporting in your syslog, you can define them also.
- */
-#undef RC_REPORT_FIFO
-#undef RC_REPORT_OVERRUN
-
-
-#define RISCOM_LEGAL_FLAGS \
-       (ASYNC_HUP_NOTIFY   | ASYNC_SAK          | ASYNC_SPLIT_TERMIOS   | \
-        ASYNC_SPD_HI       | ASYNC_SPEED_VHI    | ASYNC_SESSION_LOCKOUT | \
-        ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
-
-static struct tty_driver *riscom_driver;
-
-static DEFINE_SPINLOCK(riscom_lock);
-
-static struct riscom_board rc_board[RC_NBOARD] =  {
-       {
-               .base   = RC_IOBASE1,
-       },
-       {
-               .base   = RC_IOBASE2,
-       },
-       {
-               .base   = RC_IOBASE3,
-       },
-       {
-               .base   = RC_IOBASE4,
-       },
-};
-
-static struct riscom_port rc_port[RC_NBOARD * RC_NPORT];
-
-/* RISCom/8 I/O ports addresses (without address translation) */
-static unsigned short rc_ioport[] =  {
-#if 1
-       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0c,
-#else
-       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0c, 0x10,
-       0x11, 0x12, 0x18, 0x28, 0x31, 0x32, 0x39, 0x3a, 0x40, 0x41, 0x61, 0x62,
-       0x63, 0x64, 0x6b, 0x70, 0x71, 0x78, 0x7a, 0x7b, 0x7f, 0x100, 0x101
-#endif
-};
-#define RC_NIOPORT     ARRAY_SIZE(rc_ioport)
-
-
-static int rc_paranoia_check(struct riscom_port const *port,
-                                   char *name, const char *routine)
-{
-#ifdef RISCOM_PARANOIA_CHECK
-       static const char badmagic[] = KERN_INFO
-               "rc: Warning: bad riscom port magic number for device %s in %s\n";
-       static const char badinfo[] = KERN_INFO
-               "rc: Warning: null riscom port for device %s in %s\n";
-
-       if (!port) {
-               printk(badinfo, name, routine);
-               return 1;
-       }
-       if (port->magic != RISCOM8_MAGIC) {
-               printk(badmagic, name, routine);
-               return 1;
-       }
-#endif
-       return 0;
-}
-
-/*
- *
- *  Service functions for RISCom/8 driver.
- *
- */
-
-/* Get board number from pointer */
-static inline int board_No(struct riscom_board const *bp)
-{
-       return bp - rc_board;
-}
-
-/* Get port number from pointer */
-static inline int port_No(struct riscom_port const *port)
-{
-       return RC_PORT(port - rc_port);
-}
-
-/* Get pointer to board from pointer to port */
-static inline struct riscom_board *port_Board(struct riscom_port const *port)
-{
-       return &rc_board[RC_BOARD(port - rc_port)];
-}
-
-/* Input Byte from CL CD180 register */
-static inline unsigned char rc_in(struct riscom_board const *bp,
-                                                       unsigned short reg)
-{
-       return inb(bp->base + RC_TO_ISA(reg));
-}
-
-/* Output Byte to CL CD180 register */
-static inline void rc_out(struct riscom_board const *bp, unsigned short reg,
-                         unsigned char val)
-{
-       outb(val, bp->base + RC_TO_ISA(reg));
-}
-
-/* Wait for Channel Command Register ready */
-static void rc_wait_CCR(struct riscom_board const *bp)
-{
-       unsigned long delay;
-
-       /* FIXME: need something more descriptive then 100000 :) */
-       for (delay = 100000; delay; delay--)
-               if (!rc_in(bp, CD180_CCR))
-                       return;
-
-       printk(KERN_INFO "rc%d: Timeout waiting for CCR.\n", board_No(bp));
-}
-
-/*
- *  RISCom/8 probe functions.
- */
-
-static int rc_request_io_range(struct riscom_board * const bp)
-{
-       int i;
-
-       for (i = 0; i < RC_NIOPORT; i++)
-               if (!request_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1,
-                                  "RISCom/8"))  {
-                       goto out_release;
-               }
-       return 0;
-out_release:
-       printk(KERN_INFO "rc%d: Skipping probe at 0x%03x. IO address in use.\n",
-                        board_No(bp), bp->base);
-       while (--i >= 0)
-               release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1);
-       return 1;
-}
-
-static void rc_release_io_range(struct riscom_board * const bp)
-{
-       int i;
-
-       for (i = 0; i < RC_NIOPORT; i++)
-               release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1);
-}
-
-/* Reset and setup CD180 chip */
-static void __init rc_init_CD180(struct riscom_board const *bp)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&riscom_lock, flags);
-
-       rc_out(bp, RC_CTOUT, 0);                   /* Clear timeout        */
-       rc_wait_CCR(bp);                           /* Wait for CCR ready   */
-       rc_out(bp, CD180_CCR, CCR_HARDRESET);      /* Reset CD180 chip     */
-       spin_unlock_irqrestore(&riscom_lock, flags);
-       msleep(50);                                /* Delay 0.05 sec       */
-       spin_lock_irqsave(&riscom_lock, flags);
-       rc_out(bp, CD180_GIVR, RC_ID);             /* Set ID for this chip */
-       rc_out(bp, CD180_GICR, 0);                 /* Clear all bits       */
-       rc_out(bp, CD180_PILR1, RC_ACK_MINT);      /* Prio for modem intr  */
-       rc_out(bp, CD180_PILR2, RC_ACK_TINT);      /* Prio for tx intr     */
-       rc_out(bp, CD180_PILR3, RC_ACK_RINT);      /* Prio for rx intr     */
-
-       /* Setting up prescaler. We need 4 ticks per 1 ms */
-       rc_out(bp, CD180_PPRH, (RC_OSCFREQ/(1000000/RISCOM_TPS)) >> 8);
-       rc_out(bp, CD180_PPRL, (RC_OSCFREQ/(1000000/RISCOM_TPS)) & 0xff);
-
-       spin_unlock_irqrestore(&riscom_lock, flags);
-}
-
-/* Main probing routine, also sets irq. */
-static int __init rc_probe(struct riscom_board *bp)
-{
-       unsigned char val1, val2;
-       int irqs = 0;
-       int retries;
-
-       bp->irq = 0;
-
-       if (rc_request_io_range(bp))
-               return 1;
-
-       /* Are the I/O ports here ? */
-       rc_out(bp, CD180_PPRL, 0x5a);
-       outb(0xff, 0x80);
-       val1 = rc_in(bp, CD180_PPRL);
-       rc_out(bp, CD180_PPRL, 0xa5);
-       outb(0x00, 0x80);
-       val2 = rc_in(bp, CD180_PPRL);
-
-       if ((val1 != 0x5a) || (val2 != 0xa5))  {
-               printk(KERN_ERR "rc%d: RISCom/8 Board at 0x%03x not found.\n",
-                      board_No(bp), bp->base);
-               goto out_release;
-       }
-
-       /* It's time to find IRQ for this board */
-       for (retries = 0; retries < 5 && irqs <= 0; retries++) {
-               irqs = probe_irq_on();
-               rc_init_CD180(bp);               /* Reset CD180 chip         */
-               rc_out(bp, CD180_CAR, 2);        /* Select port 2            */
-               rc_wait_CCR(bp);
-               rc_out(bp, CD180_CCR, CCR_TXEN); /* Enable transmitter       */
-               rc_out(bp, CD180_IER, IER_TXRDY);/* Enable tx empty intr     */
-               msleep(50);
-               irqs = probe_irq_off(irqs);
-               val1 = rc_in(bp, RC_BSR);       /* Get Board Status reg      */
-               val2 = rc_in(bp, RC_ACK_TINT);  /* ACK interrupt             */
-               rc_init_CD180(bp);              /* Reset CD180 again         */
-
-               if ((val1 & RC_BSR_TINT) || (val2 != (RC_ID | GIVR_IT_TX)))  {
-                       printk(KERN_ERR "rc%d: RISCom/8 Board at 0x%03x not "
-                                       "found.\n", board_No(bp), bp->base);
-                       goto out_release;
-               }
-       }
-
-       if (irqs <= 0)  {
-               printk(KERN_ERR "rc%d: Can't find IRQ for RISCom/8 board "
-                               "at 0x%03x.\n", board_No(bp), bp->base);
-               goto out_release;
-       }
-       bp->irq = irqs;
-       bp->flags |= RC_BOARD_PRESENT;
-
-       printk(KERN_INFO "rc%d: RISCom/8 Rev. %c board detected at "
-                        "0x%03x, IRQ %d.\n",
-              board_No(bp),
-              (rc_in(bp, CD180_GFRCR) & 0x0f) + 'A',   /* Board revision */
-              bp->base, bp->irq);
-
-       return 0;
-out_release:
-       rc_release_io_range(bp);
-       return 1;
-}
-
-/*
- *
- *  Interrupt processing routines.
- *
- */
-
-static struct riscom_port *rc_get_port(struct riscom_board const *bp,
-                                              unsigned char const *what)
-{
-       unsigned char channel;
-       struct riscom_port *port;
-
-       channel = rc_in(bp, CD180_GICR) >> GICR_CHAN_OFF;
-       if (channel < CD180_NCH)  {
-               port = &rc_port[board_No(bp) * RC_NPORT + channel];
-               if (port->port.flags & ASYNC_INITIALIZED)
-                       return port;
-       }
-       printk(KERN_ERR "rc%d: %s interrupt from invalid port %d\n",
-              board_No(bp), what, channel);
-       return NULL;
-}
-
-static void rc_receive_exc(struct riscom_board const *bp)
-{
-       struct riscom_port *port;
-       struct tty_struct *tty;
-       unsigned char status;
-       unsigned char ch, flag;
-
-       port = rc_get_port(bp, "Receive");
-       if (port == NULL)
-               return;
-
-       tty = tty_port_tty_get(&port->port);
-
-#ifdef RC_REPORT_OVERRUN
-       status = rc_in(bp, CD180_RCSR);
-       if (status & RCSR_OE)
-               port->overrun++;
-       status &= port->mark_mask;
-#else
-       status = rc_in(bp, CD180_RCSR) & port->mark_mask;
-#endif
-       ch = rc_in(bp, CD180_RDR);
-       if (!status)
-               goto out;
-       if (status & RCSR_TOUT)  {
-               printk(KERN_WARNING "rc%d: port %d: Receiver timeout. "
-                                   "Hardware problems ?\n",
-                      board_No(bp), port_No(port));
-               goto out;
-
-       } else if (status & RCSR_BREAK)  {
-               printk(KERN_INFO "rc%d: port %d: Handling break...\n",
-                      board_No(bp), port_No(port));
-               flag = TTY_BREAK;
-               if (tty && (port->port.flags & ASYNC_SAK))
-                       do_SAK(tty);
-
-       } else if (status & RCSR_PE)
-               flag = TTY_PARITY;
-
-       else if (status & RCSR_FE)
-               flag = TTY_FRAME;
-
-       else if (status & RCSR_OE)
-               flag = TTY_OVERRUN;
-       else
-               flag = TTY_NORMAL;
-
-       if (tty) {
-               tty_insert_flip_char(tty, ch, flag);
-               tty_flip_buffer_push(tty);
-       }
-out:
-       tty_kref_put(tty);
-}
-
-static void rc_receive(struct riscom_board const *bp)
-{
-       struct riscom_port *port;
-       struct tty_struct *tty;
-       unsigned char count;
-
-       port = rc_get_port(bp, "Receive");
-       if (port == NULL)
-               return;
-
-       tty = tty_port_tty_get(&port->port);
-
-       count = rc_in(bp, CD180_RDCR);
-
-#ifdef RC_REPORT_FIFO
-       port->hits[count > 8 ? 9 : count]++;
-#endif
-
-       while (count--)  {
-               u8 ch = rc_in(bp, CD180_RDR);
-               if (tty)
-                       tty_insert_flip_char(tty, ch, TTY_NORMAL);
-       }
-       if (tty) {
-               tty_flip_buffer_push(tty);
-               tty_kref_put(tty);
-       }
-}
-
-static void rc_transmit(struct riscom_board const *bp)
-{
-       struct riscom_port *port;
-       struct tty_struct *tty;
-       unsigned char count;
-
-       port = rc_get_port(bp, "Transmit");
-       if (port == NULL)
-               return;
-
-       tty = tty_port_tty_get(&port->port);
-
-       if (port->IER & IER_TXEMPTY) {
-               /* FIFO drained */
-               rc_out(bp, CD180_CAR, port_No(port));
-               port->IER &= ~IER_TXEMPTY;
-               rc_out(bp, CD180_IER, port->IER);
-               goto out;
-       }
-
-       if ((port->xmit_cnt <= 0 && !port->break_length)
-           || (tty && (tty->stopped || tty->hw_stopped)))  {
-               rc_out(bp, CD180_CAR, port_No(port));
-               port->IER &= ~IER_TXRDY;
-               rc_out(bp, CD180_IER, port->IER);
-               goto out;
-       }
-
-       if (port->break_length)  {
-               if (port->break_length > 0)  {
-                       if (port->COR2 & COR2_ETC)  {
-                               rc_out(bp, CD180_TDR, CD180_C_ESC);
-                               rc_out(bp, CD180_TDR, CD180_C_SBRK);
-                               port->COR2 &= ~COR2_ETC;
-                       }
-                       count = min_t(int, port->break_length, 0xff);
-                       rc_out(bp, CD180_TDR, CD180_C_ESC);
-                       rc_out(bp, CD180_TDR, CD180_C_DELAY);
-                       rc_out(bp, CD180_TDR, count);
-                       port->break_length -= count;
-                       if (port->break_length == 0)
-                               port->break_length--;
-               } else  {
-                       rc_out(bp, CD180_TDR, CD180_C_ESC);
-                       rc_out(bp, CD180_TDR, CD180_C_EBRK);
-                       rc_out(bp, CD180_COR2, port->COR2);
-                       rc_wait_CCR(bp);
-                       rc_out(bp, CD180_CCR, CCR_CORCHG2);
-                       port->break_length = 0;
-               }
-               goto out;
-       }
-
-       count = CD180_NFIFO;
-       do {
-               rc_out(bp, CD180_TDR, port->port.xmit_buf[port->xmit_tail++]);
-               port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
-               if (--port->xmit_cnt <= 0)
-                       break;
-       } while (--count > 0);
-
-       if (port->xmit_cnt <= 0)  {
-               rc_out(bp, CD180_CAR, port_No(port));
-               port->IER &= ~IER_TXRDY;
-               rc_out(bp, CD180_IER, port->IER);
-       }
-       if (tty && port->xmit_cnt <= port->wakeup_chars)
-               tty_wakeup(tty);
-out:
-       tty_kref_put(tty);
-}
-
-static void rc_check_modem(struct riscom_board const *bp)
-{
-       struct riscom_port *port;
-       struct tty_struct *tty;
-       unsigned char mcr;
-
-       port = rc_get_port(bp, "Modem");
-       if (port == NULL)
-               return;
-
-       tty = tty_port_tty_get(&port->port);
-
-       mcr = rc_in(bp, CD180_MCR);
-       if (mcr & MCR_CDCHG) {
-               if (rc_in(bp, CD180_MSVR) & MSVR_CD)
-                       wake_up_interruptible(&port->port.open_wait);
-               else if (tty)
-                       tty_hangup(tty);
-       }
-
-#ifdef RISCOM_BRAIN_DAMAGED_CTS
-       if (mcr & MCR_CTSCHG)  {
-               if (rc_in(bp, CD180_MSVR) & MSVR_CTS)  {
-                       port->IER |= IER_TXRDY;
-                       if (tty) {
-                               tty->hw_stopped = 0;
-                               if (port->xmit_cnt <= port->wakeup_chars)
-                                       tty_wakeup(tty);
-                       }
-               } else  {
-                       if (tty)
-                               tty->hw_stopped = 1;
-                       port->IER &= ~IER_TXRDY;
-               }
-               rc_out(bp, CD180_IER, port->IER);
-       }
-       if (mcr & MCR_DSRCHG)  {
-               if (rc_in(bp, CD180_MSVR) & MSVR_DSR)  {
-                       port->IER |= IER_TXRDY;
-                       if (tty) {
-                               tty->hw_stopped = 0;
-                               if (port->xmit_cnt <= port->wakeup_chars)
-                                       tty_wakeup(tty);
-                       }
-               } else  {
-                       if (tty)
-                               tty->hw_stopped = 1;
-                       port->IER &= ~IER_TXRDY;
-               }
-               rc_out(bp, CD180_IER, port->IER);
-       }
-#endif /* RISCOM_BRAIN_DAMAGED_CTS */
-
-       /* Clear change bits */
-       rc_out(bp, CD180_MCR, 0);
-       tty_kref_put(tty);
-}
-
-/* The main interrupt processing routine */
-static irqreturn_t rc_interrupt(int dummy, void *dev_id)
-{
-       unsigned char status;
-       unsigned char ack;
-       struct riscom_board *bp = dev_id;
-       unsigned long loop = 0;
-       int handled = 0;
-
-       if (!(bp->flags & RC_BOARD_ACTIVE))
-               return IRQ_NONE;
-
-       while ((++loop < 16) && ((status = ~(rc_in(bp, RC_BSR))) &
-                                (RC_BSR_TOUT | RC_BSR_TINT |
-                                 RC_BSR_MINT | RC_BSR_RINT))) {
-               handled = 1;
-               if (status & RC_BSR_TOUT)
-                       printk(KERN_WARNING "rc%d: Got timeout. Hardware "
-                                           "error?\n", board_No(bp));
-               else if (status & RC_BSR_RINT) {
-                       ack = rc_in(bp, RC_ACK_RINT);
-                       if (ack == (RC_ID | GIVR_IT_RCV))
-                               rc_receive(bp);
-                       else if (ack == (RC_ID | GIVR_IT_REXC))
-                               rc_receive_exc(bp);
-                       else
-                               printk(KERN_WARNING "rc%d: Bad receive ack "
-                                                   "0x%02x.\n",
-                                      board_No(bp), ack);
-               } else if (status & RC_BSR_TINT) {
-                       ack = rc_in(bp, RC_ACK_TINT);
-                       if (ack == (RC_ID | GIVR_IT_TX))
-                               rc_transmit(bp);
-                       else
-                               printk(KERN_WARNING "rc%d: Bad transmit ack "
-                                                   "0x%02x.\n",
-                                      board_No(bp), ack);
-               } else /* if (status & RC_BSR_MINT) */ {
-                       ack = rc_in(bp, RC_ACK_MINT);
-                       if (ack == (RC_ID | GIVR_IT_MODEM))
-                               rc_check_modem(bp);
-                       else
-                               printk(KERN_WARNING "rc%d: Bad modem ack "
-                                                   "0x%02x.\n",
-                                      board_No(bp), ack);
-               }
-               rc_out(bp, CD180_EOIR, 0);   /* Mark end of interrupt */
-               rc_out(bp, RC_CTOUT, 0);     /* Clear timeout flag    */
-       }
-       return IRQ_RETVAL(handled);
-}
-
-/*
- *  Routines for open & close processing.
- */
-
-/* Called with disabled interrupts */
-static int rc_setup_board(struct riscom_board *bp)
-{
-       int error;
-
-       if (bp->flags & RC_BOARD_ACTIVE)
-               return 0;
-
-       error = request_irq(bp->irq, rc_interrupt, IRQF_DISABLED,
-                           "RISCom/8", bp);
-       if (error)
-               return error;
-
-       rc_out(bp, RC_CTOUT, 0);                /* Just in case         */
-       bp->DTR = ~0;
-       rc_out(bp, RC_DTR, bp->DTR);            /* Drop DTR on all ports */
-
-       bp->flags |= RC_BOARD_ACTIVE;
-
-       return 0;
-}
-
-/* Called with disabled interrupts */
-static void rc_shutdown_board(struct riscom_board *bp)
-{
-       if (!(bp->flags & RC_BOARD_ACTIVE))
-               return;
-
-       bp->flags &= ~RC_BOARD_ACTIVE;
-
-       free_irq(bp->irq, NULL);
-
-       bp->DTR = ~0;
-       rc_out(bp, RC_DTR, bp->DTR);           /* Drop DTR on all ports */
-
-}
-
-/*
- * Setting up port characteristics.
- * Must be called with disabled interrupts
- */
-static void rc_change_speed(struct tty_struct *tty, struct riscom_board *bp,
-                                               struct riscom_port *port)
-{
-       unsigned long baud;
-       long tmp;
-       unsigned char cor1 = 0, cor3 = 0;
-       unsigned char mcor1 = 0, mcor2 = 0;
-
-       port->IER  = 0;
-       port->COR2 = 0;
-       port->MSVR = MSVR_RTS;
-
-       baud = tty_get_baud_rate(tty);
-
-       /* Select port on the board */
-       rc_out(bp, CD180_CAR, port_No(port));
-
-       if (!baud)  {
-               /* Drop DTR & exit */
-               bp->DTR |= (1u << port_No(port));
-               rc_out(bp, RC_DTR, bp->DTR);
-               return;
-       } else  {
-               /* Set DTR on */
-               bp->DTR &= ~(1u << port_No(port));
-               rc_out(bp, RC_DTR, bp->DTR);
-       }
-
-       /*
-        * Now we must calculate some speed depended things
-        */
-
-       /* Set baud rate for port */
-       tmp = (((RC_OSCFREQ + baud/2) / baud +
-               CD180_TPC/2) / CD180_TPC);
-
-       rc_out(bp, CD180_RBPRH, (tmp >> 8) & 0xff);
-       rc_out(bp, CD180_TBPRH, (tmp >> 8) & 0xff);
-       rc_out(bp, CD180_RBPRL, tmp & 0xff);
-       rc_out(bp, CD180_TBPRL, tmp & 0xff);
-
-       baud = (baud + 5) / 10;   /* Estimated CPS */
-
-       /* Two timer ticks seems enough to wakeup something like SLIP driver */
-       tmp = ((baud + HZ/2) / HZ) * 2 - CD180_NFIFO;
-       port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
-                                             SERIAL_XMIT_SIZE - 1 : tmp);
-
-       /* Receiver timeout will be transmission time for 1.5 chars */
-       tmp = (RISCOM_TPS + RISCOM_TPS/2 + baud/2) / baud;
-       tmp = (tmp > 0xff) ? 0xff : tmp;
-       rc_out(bp, CD180_RTPR, tmp);
-
-       switch (C_CSIZE(tty)) {
-       case CS5:
-               cor1 |= COR1_5BITS;
-               break;
-       case CS6:
-               cor1 |= COR1_6BITS;
-               break;
-       case CS7:
-               cor1 |= COR1_7BITS;
-               break;
-       case CS8:
-               cor1 |= COR1_8BITS;
-               break;
-       }
-       if (C_CSTOPB(tty))
-               cor1 |= COR1_2SB;
-
-       cor1 |= COR1_IGNORE;
-       if (C_PARENB(tty)) {
-               cor1 |= COR1_NORMPAR;
-               if (C_PARODD(tty))
-                       cor1 |= COR1_ODDP;
-               if (I_INPCK(tty))
-                       cor1 &= ~COR1_IGNORE;
-       }
-       /* Set marking of some errors */
-       port->mark_mask = RCSR_OE | RCSR_TOUT;
-       if (I_INPCK(tty))
-               port->mark_mask |= RCSR_FE | RCSR_PE;
-       if (I_BRKINT(tty) || I_PARMRK(tty))
-               port->mark_mask |= RCSR_BREAK;
-       if (I_IGNPAR(tty))
-               port->mark_mask &= ~(RCSR_FE | RCSR_PE);
-       if (I_IGNBRK(tty)) {
-               port->mark_mask &= ~RCSR_BREAK;
-               if (I_IGNPAR(tty))
-                       /* Real raw mode. Ignore all */
-                       port->mark_mask &= ~RCSR_OE;
-       }
-       /* Enable Hardware Flow Control */
-       if (C_CRTSCTS(tty))  {
-#ifdef RISCOM_BRAIN_DAMAGED_CTS
-               port->IER |= IER_DSR | IER_CTS;
-               mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
-               mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
-               tty->hw_stopped = !(rc_in(bp, CD180_MSVR) &
-                                               (MSVR_CTS|MSVR_DSR));
-#else
-               port->COR2 |= COR2_CTSAE;
-#endif
-       }
-       /* Enable Software Flow Control. FIXME: I'm not sure about this */
-       /* Some people reported that it works, but I still doubt */
-       if (I_IXON(tty))  {
-               port->COR2 |= COR2_TXIBE;
-               cor3 |= (COR3_FCT | COR3_SCDE);
-               if (I_IXANY(tty))
-                       port->COR2 |= COR2_IXM;
-               rc_out(bp, CD180_SCHR1, START_CHAR(tty));
-               rc_out(bp, CD180_SCHR2, STOP_CHAR(tty));
-               rc_out(bp, CD180_SCHR3, START_CHAR(tty));
-               rc_out(bp, CD180_SCHR4, STOP_CHAR(tty));
-       }
-       if (!C_CLOCAL(tty))  {
-               /* Enable CD check */
-               port->IER |= IER_CD;
-               mcor1 |= MCOR1_CDZD;
-               mcor2 |= MCOR2_CDOD;
-       }
-
-       if (C_CREAD(tty))
-               /* Enable receiver */
-               port->IER |= IER_RXD;
-
-       /* Set input FIFO size (1-8 bytes) */
-       cor3 |= RISCOM_RXFIFO;
-       /* Setting up CD180 channel registers */
-       rc_out(bp, CD180_COR1, cor1);
-       rc_out(bp, CD180_COR2, port->COR2);
-       rc_out(bp, CD180_COR3, cor3);
-       /* Make CD180 know about registers change */
-       rc_wait_CCR(bp);
-       rc_out(bp, CD180_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
-       /* Setting up modem option registers */
-       rc_out(bp, CD180_MCOR1, mcor1);
-       rc_out(bp, CD180_MCOR2, mcor2);
-       /* Enable CD180 transmitter & receiver */
-       rc_wait_CCR(bp);
-       rc_out(bp, CD180_CCR, CCR_TXEN | CCR_RXEN);
-       /* Enable interrupts */
-       rc_out(bp, CD180_IER, port->IER);
-       /* And finally set RTS on */
-       rc_out(bp, CD180_MSVR, port->MSVR);
-}
-
-/* Must be called with interrupts enabled */
-static int rc_activate_port(struct tty_port *port, struct tty_struct *tty)
-{
-       struct riscom_port *rp = container_of(port, struct riscom_port, port);
-       struct riscom_board *bp = port_Board(rp);
-       unsigned long flags;
-
-       if (tty_port_alloc_xmit_buf(port) < 0)
-               return -ENOMEM;
-
-       spin_lock_irqsave(&riscom_lock, flags);
-
-       clear_bit(TTY_IO_ERROR, &tty->flags);
-       bp->count++;
-       rp->xmit_cnt = rp->xmit_head = rp->xmit_tail = 0;
-       rc_change_speed(tty, bp, rp);
-       spin_unlock_irqrestore(&riscom_lock, flags);
-       return 0;
-}
-
-/* Must be called with interrupts disabled */
-static void rc_shutdown_port(struct tty_struct *tty,
-                       struct riscom_board *bp, struct riscom_port *port)
-{
-#ifdef RC_REPORT_OVERRUN
-       printk(KERN_INFO "rc%d: port %d: Total %ld overruns were detected.\n",
-              board_No(bp), port_No(port), port->overrun);
-#endif
-#ifdef RC_REPORT_FIFO
-       {
-               int i;
-
-               printk(KERN_INFO "rc%d: port %d: FIFO hits [ ",
-                      board_No(bp), port_No(port));
-               for (i = 0; i < 10; i++)
-                       printk("%ld ", port->hits[i]);
-               printk("].\n");
-       }
-#endif
-       tty_port_free_xmit_buf(&port->port);
-
-       /* Select port */
-       rc_out(bp, CD180_CAR, port_No(port));
-       /* Reset port */
-       rc_wait_CCR(bp);
-       rc_out(bp, CD180_CCR, CCR_SOFTRESET);
-       /* Disable all interrupts from this port */
-       port->IER = 0;
-       rc_out(bp, CD180_IER, port->IER);
-
-       set_bit(TTY_IO_ERROR, &tty->flags);
-
-       if (--bp->count < 0)  {
-               printk(KERN_INFO "rc%d: rc_shutdown_port: "
-                                "bad board count: %d\n",
-                      board_No(bp), bp->count);
-               bp->count = 0;
-       }
-       /*
-        * If this is the last opened port on the board
-        * shutdown whole board
-        */
-       if (!bp->count)
-               rc_shutdown_board(bp);
-}
-
-static int carrier_raised(struct tty_port *port)
-{
-       struct riscom_port *p = container_of(port, struct riscom_port, port);
-       struct riscom_board *bp = port_Board(p);
-       unsigned long flags;
-       int CD;
-       
-       spin_lock_irqsave(&riscom_lock, flags);
-       rc_out(bp, CD180_CAR, port_No(p));
-       CD = rc_in(bp, CD180_MSVR) & MSVR_CD;
-       rc_out(bp, CD180_MSVR, MSVR_RTS);
-       bp->DTR &= ~(1u << port_No(p));
-       rc_out(bp, RC_DTR, bp->DTR);
-       spin_unlock_irqrestore(&riscom_lock, flags);
-       return CD;
-}
-
-static void dtr_rts(struct tty_port *port, int onoff)
-{
-       struct riscom_port *p = container_of(port, struct riscom_port, port);
-       struct riscom_board *bp = port_Board(p);
-       unsigned long flags;
-
-       spin_lock_irqsave(&riscom_lock, flags);
-       bp->DTR &= ~(1u << port_No(p));
-       if (onoff == 0)
-               bp->DTR |= (1u << port_No(p));
-       rc_out(bp, RC_DTR, bp->DTR);
-       spin_unlock_irqrestore(&riscom_lock, flags);
-}
-
-static int rc_open(struct tty_struct *tty, struct file *filp)
-{
-       int board;
-       int error;
-       struct riscom_port *port;
-       struct riscom_board *bp;
-
-       board = RC_BOARD(tty->index);
-       if (board >= RC_NBOARD || !(rc_board[board].flags & RC_BOARD_PRESENT))
-               return -ENODEV;
-
-       bp = &rc_board[board];
-       port = rc_port + board * RC_NPORT + RC_PORT(tty->index);
-       if (rc_paranoia_check(port, tty->name, "rc_open"))
-               return -ENODEV;
-
-       error = rc_setup_board(bp);
-       if (error)
-               return error;
-
-       tty->driver_data = port;
-       return tty_port_open(&port->port, tty, filp);
-}
-
-static void rc_flush_buffer(struct tty_struct *tty)
-{
-       struct riscom_port *port = tty->driver_data;
-       unsigned long flags;
-
-       if (rc_paranoia_check(port, tty->name, "rc_flush_buffer"))
-               return;
-
-       spin_lock_irqsave(&riscom_lock, flags);
-       port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
-       spin_unlock_irqrestore(&riscom_lock, flags);
-
-       tty_wakeup(tty);
-}
-
-static void rc_close_port(struct tty_port *port)
-{
-       unsigned long flags;
-       struct riscom_port *rp = container_of(port, struct riscom_port, port);
-       struct riscom_board *bp = port_Board(rp);
-       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.
-        */
-
-       spin_lock_irqsave(&riscom_lock, flags);
-       rp->IER &= ~IER_RXD;
-
-       rp->IER &= ~IER_TXRDY;
-       rp->IER |= IER_TXEMPTY;
-       rc_out(bp, CD180_CAR, port_No(rp));
-       rc_out(bp, CD180_IER, rp->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 (rp->IER & IER_TXEMPTY) {
-               spin_unlock_irqrestore(&riscom_lock, flags);
-               msleep_interruptible(jiffies_to_msecs(rp->timeout));
-               spin_lock_irqsave(&riscom_lock, flags);
-               if (time_after(jiffies, timeout))
-                       break;
-       }
-       rc_shutdown_port(port->tty, bp, rp);
-       spin_unlock_irqrestore(&riscom_lock, flags);
-}
-
-static void rc_close(struct tty_struct *tty, struct file *filp)
-{
-       struct riscom_port *port = tty->driver_data;
-
-       if (!port || rc_paranoia_check(port, tty->name, "close"))
-               return;
-       tty_port_close(&port->port, tty, filp);
-}
-
-static int rc_write(struct tty_struct *tty,
-                   const unsigned char *buf, int count)
-{
-       struct riscom_port *port = tty->driver_data;
-       struct riscom_board *bp;
-       int c, total = 0;
-       unsigned long flags;
-
-       if (rc_paranoia_check(port, tty->name, "rc_write"))
-               return 0;
-
-       bp = port_Board(port);
-
-       while (1) {
-               spin_lock_irqsave(&riscom_lock, flags);
-
-               c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
-                                         SERIAL_XMIT_SIZE - port->xmit_head));
-               if (c <= 0)
-                       break;  /* lock continues to be held */
-
-               memcpy(port->port.xmit_buf + port->xmit_head, buf, c);
-               port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
-               port->xmit_cnt += c;
-
-               spin_unlock_irqrestore(&riscom_lock, flags);
-
-               buf += c;
-               count -= c;
-               total += c;
-       }
-
-       if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
-           !(port->IER & IER_TXRDY)) {
-               port->IER |= IER_TXRDY;
-               rc_out(bp, CD180_CAR, port_No(port));
-               rc_out(bp, CD180_IER, port->IER);
-       }
-
-       spin_unlock_irqrestore(&riscom_lock, flags);
-
-       return total;
-}
-
-static int rc_put_char(struct tty_struct *tty, unsigned char ch)
-{
-       struct riscom_port *port = tty->driver_data;
-       unsigned long flags;
-       int ret = 0;
-
-       if (rc_paranoia_check(port, tty->name, "rc_put_char"))
-               return 0;
-
-       spin_lock_irqsave(&riscom_lock, flags);
-
-       if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1)
-               goto out;
-
-       port->port.xmit_buf[port->xmit_head++] = ch;
-       port->xmit_head &= SERIAL_XMIT_SIZE - 1;
-       port->xmit_cnt++;
-       ret = 1;
-
-out:
-       spin_unlock_irqrestore(&riscom_lock, flags);
-       return ret;
-}
-
-static void rc_flush_chars(struct tty_struct *tty)
-{
-       struct riscom_port *port = tty->driver_data;
-       unsigned long flags;
-
-       if (rc_paranoia_check(port, tty->name, "rc_flush_chars"))
-               return;
-
-       if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped)
-               return;
-
-       spin_lock_irqsave(&riscom_lock, flags);
-
-       port->IER |= IER_TXRDY;
-       rc_out(port_Board(port), CD180_CAR, port_No(port));
-       rc_out(port_Board(port), CD180_IER, port->IER);
-
-       spin_unlock_irqrestore(&riscom_lock, flags);
-}
-
-static int rc_write_room(struct tty_struct *tty)
-{
-       struct riscom_port *port = tty->driver_data;
-       int     ret;
-
-       if (rc_paranoia_check(port, tty->name, "rc_write_room"))
-               return 0;
-
-       ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
-       if (ret < 0)
-               ret = 0;
-       return ret;
-}
-
-static int rc_chars_in_buffer(struct tty_struct *tty)
-{
-       struct riscom_port *port = tty->driver_data;
-
-       if (rc_paranoia_check(port, tty->name, "rc_chars_in_buffer"))
-               return 0;
-
-       return port->xmit_cnt;
-}
-
-static int rc_tiocmget(struct tty_struct *tty)
-{
-       struct riscom_port *port = tty->driver_data;
-       struct riscom_board *bp;
-       unsigned char status;
-       unsigned int result;
-       unsigned long flags;
-
-       if (rc_paranoia_check(port, tty->name, __func__))
-               return -ENODEV;
-
-       bp = port_Board(port);
-
-       spin_lock_irqsave(&riscom_lock, flags);
-
-       rc_out(bp, CD180_CAR, port_No(port));
-       status = rc_in(bp, CD180_MSVR);
-       result = rc_in(bp, RC_RI) & (1u << port_No(port)) ? 0 : TIOCM_RNG;
-
-       spin_unlock_irqrestore(&riscom_lock, flags);
-
-       result |= ((status & MSVR_RTS) ? TIOCM_RTS : 0)
-               | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
-               | ((status & MSVR_CD)  ? TIOCM_CAR : 0)
-               | ((status & MSVR_DSR) ? TIOCM_DSR : 0)
-               | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
-       return result;
-}
-
-static int rc_tiocmset(struct tty_struct *tty,
-                                      unsigned int set, unsigned int clear)
-{
-       struct riscom_port *port = tty->driver_data;
-       unsigned long flags;
-       struct riscom_board *bp;
-
-       if (rc_paranoia_check(port, tty->name, __func__))
-               return -ENODEV;
-
-       bp = port_Board(port);
-
-       spin_lock_irqsave(&riscom_lock, flags);
-
-       if (set & TIOCM_RTS)
-               port->MSVR |= MSVR_RTS;
-       if (set & TIOCM_DTR)
-               bp->DTR &= ~(1u << port_No(port));
-
-       if (clear & TIOCM_RTS)
-               port->MSVR &= ~MSVR_RTS;
-       if (clear & TIOCM_DTR)
-               bp->DTR |= (1u << port_No(port));
-
-       rc_out(bp, CD180_CAR, port_No(port));
-       rc_out(bp, CD180_MSVR, port->MSVR);
-       rc_out(bp, RC_DTR, bp->DTR);
-
-       spin_unlock_irqrestore(&riscom_lock, flags);
-
-       return 0;
-}
-
-static int rc_send_break(struct tty_struct *tty, int length)
-{
-       struct riscom_port *port = tty->driver_data;
-       struct riscom_board *bp = port_Board(port);
-       unsigned long flags;
-
-       if (length == 0 || length == -1)
-               return -EOPNOTSUPP;
-
-       spin_lock_irqsave(&riscom_lock, flags);
-
-       port->break_length = RISCOM_TPS / HZ * length;
-       port->COR2 |= COR2_ETC;
-       port->IER  |= IER_TXRDY;
-       rc_out(bp, CD180_CAR, port_No(port));
-       rc_out(bp, CD180_COR2, port->COR2);
-       rc_out(bp, CD180_IER, port->IER);
-       rc_wait_CCR(bp);
-       rc_out(bp, CD180_CCR, CCR_CORCHG2);
-       rc_wait_CCR(bp);
-
-       spin_unlock_irqrestore(&riscom_lock, flags);
-       return 0;
-}
-
-static int rc_set_serial_info(struct tty_struct *tty, struct riscom_port *port,
-                                    struct serial_struct __user *newinfo)
-{
-       struct serial_struct tmp;
-       struct riscom_board *bp = port_Board(port);
-       int change_speed;
-
-       if (copy_from_user(&tmp, newinfo, sizeof(tmp)))
-               return -EFAULT;
-
-       mutex_lock(&port->port.mutex);
-       change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
-                       (tmp.flags & ASYNC_SPD_MASK));
-
-       if (!capable(CAP_SYS_ADMIN)) {
-               if ((tmp.close_delay != port->port.close_delay) ||
-                   (tmp.closing_wait != port->port.closing_wait) ||
-                   ((tmp.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) |
-                              (tmp.flags & ASYNC_USR_MASK));
-       } else  {
-               port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
-                              (tmp.flags & ASYNC_FLAGS));
-               port->port.close_delay = tmp.close_delay;
-               port->port.closing_wait = tmp.closing_wait;
-       }
-       if (change_speed)  {
-               unsigned long flags;
-
-               spin_lock_irqsave(&riscom_lock, flags);
-               rc_change_speed(tty, bp, port);
-               spin_unlock_irqrestore(&riscom_lock, flags);
-       }
-       mutex_unlock(&port->port.mutex);
-       return 0;
-}
-
-static int rc_get_serial_info(struct riscom_port *port,
-                                    struct serial_struct __user *retinfo)
-{
-       struct serial_struct tmp;
-       struct riscom_board *bp = port_Board(port);
-
-       memset(&tmp, 0, sizeof(tmp));
-       tmp.type = PORT_CIRRUS;
-       tmp.line = port - rc_port;
-
-       mutex_lock(&port->port.mutex);
-       tmp.port = bp->base;
-       tmp.irq  = bp->irq;
-       tmp.flags = port->port.flags;
-       tmp.baud_base = (RC_OSCFREQ + CD180_TPC/2) / CD180_TPC;
-       tmp.close_delay = port->port.close_delay * HZ/100;
-       tmp.closing_wait = port->port.closing_wait * HZ/100;
-       mutex_unlock(&port->port.mutex);
-       tmp.xmit_fifo_size = CD180_NFIFO;
-       return copy_to_user(retinfo, &tmp, sizeof(tmp)) ? -EFAULT : 0;
-}
-
-static int rc_ioctl(struct tty_struct *tty,
-                   unsigned int cmd, unsigned long arg)
-{
-       struct riscom_port *port = tty->driver_data;
-       void __user *argp = (void __user *)arg;
-       int retval;
-
-       if (rc_paranoia_check(port, tty->name, "rc_ioctl"))
-               return -ENODEV;
-
-       switch (cmd) {
-       case TIOCGSERIAL:
-               retval = rc_get_serial_info(port, argp);
-               break;
-       case TIOCSSERIAL:
-               retval = rc_set_serial_info(tty, port, argp);
-               break;
-       default:
-               retval = -ENOIOCTLCMD;
-       }
-       return retval;
-}
-
-static void rc_throttle(struct tty_struct *tty)
-{
-       struct riscom_port *port = tty->driver_data;
-       struct riscom_board *bp;
-       unsigned long flags;
-
-       if (rc_paranoia_check(port, tty->name, "rc_throttle"))
-               return;
-       bp = port_Board(port);
-
-       spin_lock_irqsave(&riscom_lock, flags);
-       port->MSVR &= ~MSVR_RTS;
-       rc_out(bp, CD180_CAR, port_No(port));
-       if (I_IXOFF(tty)) {
-               rc_wait_CCR(bp);
-               rc_out(bp, CD180_CCR, CCR_SSCH2);
-               rc_wait_CCR(bp);
-       }
-       rc_out(bp, CD180_MSVR, port->MSVR);
-       spin_unlock_irqrestore(&riscom_lock, flags);
-}
-
-static void rc_unthrottle(struct tty_struct *tty)
-{
-       struct riscom_port *port = tty->driver_data;
-       struct riscom_board *bp;
-       unsigned long flags;
-
-       if (rc_paranoia_check(port, tty->name, "rc_unthrottle"))
-               return;
-       bp = port_Board(port);
-
-       spin_lock_irqsave(&riscom_lock, flags);
-       port->MSVR |= MSVR_RTS;
-       rc_out(bp, CD180_CAR, port_No(port));
-       if (I_IXOFF(tty))  {
-               rc_wait_CCR(bp);
-               rc_out(bp, CD180_CCR, CCR_SSCH1);
-               rc_wait_CCR(bp);
-       }
-       rc_out(bp, CD180_MSVR, port->MSVR);
-       spin_unlock_irqrestore(&riscom_lock, flags);
-}
-
-static void rc_stop(struct tty_struct *tty)
-{
-       struct riscom_port *port = tty->driver_data;
-       struct riscom_board *bp;
-       unsigned long flags;
-
-       if (rc_paranoia_check(port, tty->name, "rc_stop"))
-               return;
-
-       bp = port_Board(port);
-
-       spin_lock_irqsave(&riscom_lock, flags);
-       port->IER &= ~IER_TXRDY;
-       rc_out(bp, CD180_CAR, port_No(port));
-       rc_out(bp, CD180_IER, port->IER);
-       spin_unlock_irqrestore(&riscom_lock, flags);
-}
-
-static void rc_start(struct tty_struct *tty)
-{
-       struct riscom_port *port = tty->driver_data;
-       struct riscom_board *bp;
-       unsigned long flags;
-
-       if (rc_paranoia_check(port, tty->name, "rc_start"))
-               return;
-
-       bp = port_Board(port);
-
-       spin_lock_irqsave(&riscom_lock, flags);
-
-       if (port->xmit_cnt && port->port.xmit_buf && !(port->IER & IER_TXRDY)) {
-               port->IER |= IER_TXRDY;
-               rc_out(bp, CD180_CAR, port_No(port));
-               rc_out(bp, CD180_IER, port->IER);
-       }
-       spin_unlock_irqrestore(&riscom_lock, flags);
-}
-
-static void rc_hangup(struct tty_struct *tty)
-{
-       struct riscom_port *port = tty->driver_data;
-
-       if (rc_paranoia_check(port, tty->name, "rc_hangup"))
-               return;
-
-       tty_port_hangup(&port->port);
-}
-
-static void rc_set_termios(struct tty_struct *tty,
-                                       struct ktermios *old_termios)
-{
-       struct riscom_port *port = tty->driver_data;
-       unsigned long flags;
-
-       if (rc_paranoia_check(port, tty->name, "rc_set_termios"))
-               return;
-
-       spin_lock_irqsave(&riscom_lock, flags);
-       rc_change_speed(tty, port_Board(port), port);
-       spin_unlock_irqrestore(&riscom_lock, flags);
-
-       if ((old_termios->c_cflag & CRTSCTS) &&
-           !(tty->termios->c_cflag & CRTSCTS)) {
-               tty->hw_stopped = 0;
-               rc_start(tty);
-       }
-}
-
-static const struct tty_operations riscom_ops = {
-       .open  = rc_open,
-       .close = rc_close,
-       .write = rc_write,
-       .put_char = rc_put_char,
-       .flush_chars = rc_flush_chars,
-       .write_room = rc_write_room,
-       .chars_in_buffer = rc_chars_in_buffer,
-       .flush_buffer = rc_flush_buffer,
-       .ioctl = rc_ioctl,
-       .throttle = rc_throttle,
-       .unthrottle = rc_unthrottle,
-       .set_termios = rc_set_termios,
-       .stop = rc_stop,
-       .start = rc_start,
-       .hangup = rc_hangup,
-       .tiocmget = rc_tiocmget,
-       .tiocmset = rc_tiocmset,
-       .break_ctl = rc_send_break,
-};
-
-static const struct tty_port_operations riscom_port_ops = {
-       .carrier_raised = carrier_raised,
-       .dtr_rts = dtr_rts,
-       .shutdown = rc_close_port,
-       .activate = rc_activate_port,
-};
-
-
-static int __init rc_init_drivers(void)
-{
-       int error;
-       int i;
-
-       riscom_driver = alloc_tty_driver(RC_NBOARD * RC_NPORT);
-       if (!riscom_driver)
-               return -ENOMEM;
-
-       riscom_driver->owner = THIS_MODULE;
-       riscom_driver->name = "ttyL";
-       riscom_driver->major = RISCOM8_NORMAL_MAJOR;
-       riscom_driver->type = TTY_DRIVER_TYPE_SERIAL;
-       riscom_driver->subtype = SERIAL_TYPE_NORMAL;
-       riscom_driver->init_termios = tty_std_termios;
-       riscom_driver->init_termios.c_cflag =
-               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-       riscom_driver->init_termios.c_ispeed = 9600;
-       riscom_driver->init_termios.c_ospeed = 9600;
-       riscom_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_HARDWARE_BREAK;
-       tty_set_operations(riscom_driver, &riscom_ops);
-       error = tty_register_driver(riscom_driver);
-       if (error != 0) {
-               put_tty_driver(riscom_driver);
-               printk(KERN_ERR "rc: Couldn't register RISCom/8 driver, "
-                               "error = %d\n", error);
-               return 1;
-       }
-       memset(rc_port, 0, sizeof(rc_port));
-       for (i = 0; i < RC_NPORT * RC_NBOARD; i++)  {
-               tty_port_init(&rc_port[i].port);
-               rc_port[i].port.ops = &riscom_port_ops;
-               rc_port[i].magic = RISCOM8_MAGIC;
-       }
-       return 0;
-}
-
-static void rc_release_drivers(void)
-{
-       tty_unregister_driver(riscom_driver);
-       put_tty_driver(riscom_driver);
-}
-
-#ifndef MODULE
-/*
- * Called at boot time.
- *
- * You can specify IO base for up to RC_NBOARD cards,
- * using line "riscom8=0xiobase1,0xiobase2,.." at LILO prompt.
- * Note that there will be no probing at default
- * addresses in this case.
- *
- */
-static int __init riscom8_setup(char *str)
-{
-       int ints[RC_NBOARD];
-       int i;
-
-       str = get_options(str, ARRAY_SIZE(ints), ints);
-
-       for (i = 0; i < RC_NBOARD; i++) {
-               if (i < ints[0])
-                       rc_board[i].base = ints[i+1];
-               else
-                       rc_board[i].base = 0;
-       }
-       return 1;
-}
-
-__setup("riscom8=", riscom8_setup);
-#endif
-
-static char banner[] __initdata =
-       KERN_INFO "rc: SDL RISCom/8 card driver v1.1, (c) D.Gorodchanin "
-                 "1994-1996.\n";
-static char no_boards_msg[] __initdata =
-       KERN_INFO "rc: No RISCom/8 boards detected.\n";
-
-/*
- * This routine must be called by kernel at boot time
- */
-static int __init riscom8_init(void)
-{
-       int i;
-       int found = 0;
-
-       printk(banner);
-
-       if (rc_init_drivers())
-               return -EIO;
-
-       for (i = 0; i < RC_NBOARD; i++)
-               if (rc_board[i].base && !rc_probe(&rc_board[i]))
-                       found++;
-       if (!found)  {
-               rc_release_drivers();
-               printk(no_boards_msg);
-               return -EIO;
-       }
-       return 0;
-}
-
-#ifdef MODULE
-static int iobase;
-static int iobase1;
-static int iobase2;
-static int iobase3;
-module_param(iobase, int, 0);
-module_param(iobase1, int, 0);
-module_param(iobase2, int, 0);
-module_param(iobase3, int, 0);
-
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV_MAJOR(RISCOM8_NORMAL_MAJOR);
-#endif /* MODULE */
-
-/*
- * You can setup up to 4 boards (current value of RC_NBOARD)
- * by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter.
- *
- */
-static int __init riscom8_init_module(void)
-{
-#ifdef MODULE
-       int i;
-
-       if (iobase || iobase1 || iobase2 || iobase3) {
-               for (i = 0; i < RC_NBOARD; i++)
-                       rc_board[i].base = 0;
-       }
-
-       if (iobase)
-               rc_board[0].base = iobase;
-       if (iobase1)
-               rc_board[1].base = iobase1;
-       if (iobase2)
-               rc_board[2].base = iobase2;
-       if (iobase3)
-               rc_board[3].base = iobase3;
-#endif /* MODULE */
-
-       return riscom8_init();
-}
-
-static void __exit riscom8_exit_module(void)
-{
-       int i;
-
-       rc_release_drivers();
-       for (i = 0; i < RC_NBOARD; i++)
-               if (rc_board[i].flags & RC_BOARD_PRESENT)
-                       rc_release_io_range(&rc_board[i]);
-
-}
-
-module_init(riscom8_init_module);
-module_exit(riscom8_exit_module);
diff --git a/drivers/char/riscom8.h b/drivers/char/riscom8.h
deleted file mode 100644 (file)
index c9876b3..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- *      linux/drivers/char/riscom8.h  -- RISCom/8 multiport serial driver.
- *
- *      Copyright (C) 1994-1996  Dmitry Gorodchanin (pgmdsg@ibi.com)
- *
- *      This code is loosely based on the Linux serial driver, written by
- *      Linus Torvalds, Theodore T'so and others. The RISCom/8 card 
- *      programming info was obtained from various drivers for other OSes 
- *     (FreeBSD, ISC, etc), but no source code from those drivers were 
- *     directly included in this driver.
- *
- *
- *      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.
- */
-
-#ifndef __LINUX_RISCOM8_H
-#define __LINUX_RISCOM8_H
-
-#include <linux/serial.h>
-
-#ifdef __KERNEL__
-
-#define RC_NBOARD              4
-/* NOTE: RISCom decoder recognizes 16 addresses... */
-#define RC_NPORT               8  
-#define RC_BOARD(line)         (((line) >> 3) & 0x07)
-#define RC_PORT(line)          ((line) & (RC_NPORT - 1))
-
-/* Ticks per sec. Used for setting receiver timeout and break length */
-#define RISCOM_TPS             4000
-
-/* Yeah, after heavy testing I decided it must be 6.
- * Sure, You can change it if needed.
- */
-#define RISCOM_RXFIFO          6       /* Max. receiver FIFO size (1-8) */
-
-#define RISCOM8_MAGIC          0x0907
-
-#define RC_IOBASE1     0x220
-#define RC_IOBASE2     0x240
-#define RC_IOBASE3     0x250
-#define RC_IOBASE4     0x260
-
-struct riscom_board {
-       unsigned long   flags;
-       unsigned short  base;
-       unsigned char   irq;
-       signed   char   count;
-       unsigned char   DTR;
-};
-
-#define RC_BOARD_PRESENT       0x00000001
-#define RC_BOARD_ACTIVE                0x00000002
-       
-struct riscom_port {
-       int                     magic;
-       struct                  tty_port port;
-       int                     baud_base;
-       int                     timeout;
-       int                     custom_divisor;
-       int                     xmit_head;
-       int                     xmit_tail;
-       int                     xmit_cnt;
-       short                   wakeup_chars;
-       short                   break_length;
-       unsigned char           mark_mask;
-       unsigned char           IER;
-       unsigned char           MSVR;
-       unsigned char           COR2;
-#ifdef RC_REPORT_OVERRUN
-       unsigned long           overrun;
-#endif 
-#ifdef RC_REPORT_FIFO
-       unsigned long           hits[10];
-#endif
-};
-
-#endif /* __KERNEL__ */
-#endif /* __LINUX_RISCOM8_H */
diff --git a/drivers/char/riscom8_reg.h b/drivers/char/riscom8_reg.h
deleted file mode 100644 (file)
index a32475e..0000000
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- *      linux/drivers/char/riscom8_reg.h  -- RISCom/8 multiport serial driver.
- */
-
-/*
- * Definitions for RISCom/8 Async Mux card by SDL Communications, Inc.
- */
-
-/*
- * Address mapping between Cirrus Logic CD180 chip internal registers
- * and ISA port addresses:
- *
- *      CL-CD180                A6  A5   A4  A3                      A2 A1 A0
- *      ISA             A15 A14 A13 A12  A11 A10 A9 A8  A7 A6 A5 A4  A3 A2 A1 A0
- */
-#define RC_TO_ISA(r)    ((((r)&0x07)<<1) | (((r)&~0x07)<<7))
-
-
-/* RISCom/8 On-Board Registers (assuming address translation) */
-
-#define RC_RI           0x100   /* Ring Indicator Register (R/O)           */
-#define RC_DTR          0x100   /* DTR Register (W/O)                      */
-#define RC_BSR          0x101   /* Board Status Register (R/O)             */
-#define RC_CTOUT        0x101   /* Clear Timeout (W/O)                     */
-
-
-/* Board Status Register */
-
-#define RC_BSR_TOUT     0x08     /* Hardware Timeout                       */
-#define RC_BSR_RINT     0x04     /* Receiver Interrupt                     */
-#define RC_BSR_TINT     0x02     /* Transmitter Interrupt                  */
-#define RC_BSR_MINT     0x01     /* Modem Ctl Interrupt                    */
-
-
-/* On-board oscillator frequency (in Hz) */
-#define RC_OSCFREQ      9830400
-
-/* Values of choice for Interrupt ACKs */
-#define RC_ACK_MINT     0x81    /* goes to PILR1                           */
-#define RC_ACK_RINT     0x82    /* goes to PILR3                           */
-#define RC_ACK_TINT     0x84    /* goes to PILR2                           */
-
-/* Chip ID (sorry, only one chip now) */
-#define RC_ID           0x10
-
-/* Definitions for Cirrus Logic CL-CD180 8-port async mux chip */
-#define CD180_NCH       8       /* Total number of channels                */
-#define CD180_TPC       16      /* Ticks per character                     */
-#define CD180_NFIFO    8       /* TX FIFO size                            */
-
-
-/* Global registers */
-
-#define CD180_GIVR      0x40    /* Global Interrupt Vector Register        */
-#define CD180_GICR      0x41    /* Global Interrupting Channel Register    */
-#define CD180_PILR1     0x61    /* Priority Interrupt Level Register 1     */
-#define CD180_PILR2     0x62    /* Priority Interrupt Level Register 2     */
-#define CD180_PILR3     0x63    /* Priority Interrupt Level Register 3     */
-#define CD180_CAR       0x64    /* Channel Access Register                 */
-#define CD180_GFRCR     0x6b    /* Global Firmware Revision Code Register  */
-#define CD180_PPRH      0x70    /* Prescaler Period Register High          */
-#define CD180_PPRL      0x71    /* Prescaler Period Register Low           */
-#define CD180_RDR       0x78    /* Receiver Data Register                  */
-#define CD180_RCSR      0x7a    /* Receiver Character Status Register      */
-#define CD180_TDR       0x7b    /* Transmit Data Register                  */
-#define CD180_EOIR      0x7f    /* End of Interrupt Register               */
-
-
-/* Channel Registers */
-
-#define CD180_CCR       0x01    /* Channel Command Register                */
-#define CD180_IER       0x02    /* Interrupt Enable Register               */
-#define CD180_COR1      0x03    /* Channel Option Register 1               */
-#define CD180_COR2      0x04    /* Channel Option Register 2               */
-#define CD180_COR3      0x05    /* Channel Option Register 3               */
-#define CD180_CCSR      0x06    /* Channel Control Status Register         */
-#define CD180_RDCR      0x07    /* Receive Data Count Register             */
-#define CD180_SCHR1     0x09    /* Special Character Register 1            */
-#define CD180_SCHR2     0x0a    /* Special Character Register 2            */
-#define CD180_SCHR3     0x0b    /* Special Character Register 3            */
-#define CD180_SCHR4     0x0c    /* Special Character Register 4            */
-#define CD180_MCOR1     0x10    /* Modem Change Option 1 Register          */
-#define CD180_MCOR2     0x11    /* Modem Change Option 2 Register          */
-#define CD180_MCR       0x12    /* Modem Change Register                   */
-#define CD180_RTPR      0x18    /* Receive Timeout Period Register         */
-#define CD180_MSVR      0x28    /* Modem Signal Value Register             */
-#define CD180_RBPRH     0x31    /* Receive Baud Rate Period Register High  */
-#define CD180_RBPRL     0x32    /* Receive Baud Rate Period Register Low   */
-#define CD180_TBPRH     0x39    /* Transmit Baud Rate Period Register High */
-#define CD180_TBPRL     0x3a    /* Transmit Baud Rate Period Register Low  */
-
-
-/* Global Interrupt Vector Register (R/W) */
-
-#define GIVR_ITMASK     0x07     /* Interrupt type mask                     */
-#define  GIVR_IT_MODEM   0x01    /* Modem Signal Change Interrupt           */
-#define  GIVR_IT_TX      0x02    /* Transmit Data Interrupt                 */
-#define  GIVR_IT_RCV     0x03    /* Receive Good Data Interrupt             */
-#define  GIVR_IT_REXC    0x07    /* Receive Exception Interrupt             */
-
-
-/* Global Interrupt Channel Register (R/W) */
-#define GICR_CHAN       0x1c    /* Channel Number Mask                     */
-#define GICR_CHAN_OFF   2       /* Channel Number Offset                   */
-
-
-/* Channel Address Register (R/W) */
-
-#define CAR_CHAN        0x07    /* Channel Number Mask                     */
-#define CAR_A7          0x08    /* A7 Address Extension (unused)           */
-
-
-/* Receive Character Status Register (R/O) */
-
-#define RCSR_TOUT       0x80    /* Rx Timeout                              */
-#define RCSR_SCDET      0x70    /* Special Character Detected Mask         */
-#define  RCSR_NO_SC      0x00   /* No Special Characters Detected          */
-#define  RCSR_SC_1       0x10   /* Special Char 1 (or 1 & 3) Detected      */
-#define  RCSR_SC_2       0x20   /* Special Char 2 (or 2 & 4) Detected      */
-#define  RCSR_SC_3       0x30   /* Special Char 3 Detected                 */
-#define  RCSR_SC_4       0x40   /* Special Char 4 Detected                 */
-#define RCSR_BREAK      0x08    /* Break has been detected                 */
-#define RCSR_PE         0x04    /* Parity Error                            */
-#define RCSR_FE         0x02    /* Frame Error                             */
-#define RCSR_OE         0x01    /* Overrun Error                           */
-
-
-/* Channel Command Register (R/W) (commands in groups can be OR-ed) */
-
-#define CCR_HARDRESET   0x81    /* Reset the chip                          */
-
-#define CCR_SOFTRESET   0x80    /* Soft Channel Reset                      */
-
-#define CCR_CORCHG1     0x42    /* Channel Option Register 1 Changed       */
-#define CCR_CORCHG2     0x44    /* Channel Option Register 2 Changed       */
-#define CCR_CORCHG3     0x48    /* Channel Option Register 3 Changed       */
-
-#define CCR_SSCH1       0x21    /* Send Special Character 1                */
-
-#define CCR_SSCH2       0x22    /* Send Special Character 2                */
-
-#define CCR_SSCH3       0x23    /* Send Special Character 3                */
-
-#define CCR_SSCH4       0x24    /* Send Special Character 4                */
-
-#define CCR_TXEN        0x18    /* Enable Transmitter                      */
-#define CCR_RXEN        0x12    /* Enable Receiver                         */
-
-#define CCR_TXDIS       0x14    /* Disable Transmitter                     */
-#define CCR_RXDIS       0x11    /* Disable Receiver                        */
-
-
-/* Interrupt Enable Register (R/W) */
-
-#define IER_DSR         0x80    /* Enable interrupt on DSR change          */
-#define IER_CD          0x40    /* Enable interrupt on CD change           */
-#define IER_CTS         0x20    /* Enable interrupt on CTS change          */
-#define IER_RXD         0x10    /* Enable interrupt on Receive Data        */
-#define IER_RXSC        0x08    /* Enable interrupt on Receive Spec. Char  */
-#define IER_TXRDY       0x04    /* Enable interrupt on TX FIFO empty       */
-#define IER_TXEMPTY     0x02    /* Enable interrupt on TX completely empty */
-#define IER_RET         0x01    /* Enable interrupt on RX Exc. Timeout     */
-
-
-/* Channel Option Register 1 (R/W) */
-
-#define COR1_ODDP       0x80    /* Odd Parity                              */
-#define COR1_PARMODE    0x60    /* Parity Mode mask                        */
-#define  COR1_NOPAR      0x00   /* No Parity                               */
-#define  COR1_FORCEPAR   0x20   /* Force Parity                            */
-#define  COR1_NORMPAR    0x40   /* Normal Parity                           */
-#define COR1_IGNORE     0x10    /* Ignore Parity on RX                     */
-#define COR1_STOPBITS   0x0c    /* Number of Stop Bits                     */
-#define  COR1_1SB        0x00   /* 1 Stop Bit                              */
-#define  COR1_15SB       0x04   /* 1.5 Stop Bits                           */
-#define  COR1_2SB        0x08   /* 2 Stop Bits                             */
-#define COR1_CHARLEN    0x03    /* Character Length                        */
-#define  COR1_5BITS      0x00   /* 5 bits                                  */
-#define  COR1_6BITS      0x01   /* 6 bits                                  */
-#define  COR1_7BITS      0x02   /* 7 bits                                  */
-#define  COR1_8BITS      0x03   /* 8 bits                                  */
-
-
-/* Channel Option Register 2 (R/W) */
-
-#define COR2_IXM        0x80    /* Implied XON mode                        */
-#define COR2_TXIBE      0x40    /* Enable In-Band (XON/XOFF) Flow Control  */
-#define COR2_ETC        0x20    /* Embedded Tx Commands Enable             */
-#define COR2_LLM        0x10    /* Local Loopback Mode                     */
-#define COR2_RLM        0x08    /* Remote Loopback Mode                    */
-#define COR2_RTSAO      0x04    /* RTS Automatic Output Enable             */
-#define COR2_CTSAE      0x02    /* CTS Automatic Enable                    */
-#define COR2_DSRAE      0x01    /* DSR Automatic Enable                    */
-
-
-/* Channel Option Register 3 (R/W) */
-
-#define COR3_XONCH      0x80    /* XON is a pair of characters (1 & 3)     */
-#define COR3_XOFFCH     0x40    /* XOFF is a pair of characters (2 & 4)    */
-#define COR3_FCT        0x20    /* Flow-Control Transparency Mode          */
-#define COR3_SCDE       0x10    /* Special Character Detection Enable      */
-#define COR3_RXTH       0x0f    /* RX FIFO Threshold value (1-8)           */
-
-
-/* Channel Control Status Register (R/O) */
-
-#define CCSR_RXEN       0x80    /* Receiver Enabled                        */
-#define CCSR_RXFLOFF    0x40    /* Receive Flow Off (XOFF was sent)        */
-#define CCSR_RXFLON     0x20    /* Receive Flow On (XON was sent)          */
-#define CCSR_TXEN       0x08    /* Transmitter Enabled                     */
-#define CCSR_TXFLOFF    0x04    /* Transmit Flow Off (got XOFF)            */
-#define CCSR_TXFLON     0x02    /* Transmit Flow On (got XON)              */
-
-
-/* Modem Change Option Register 1 (R/W) */
-
-#define MCOR1_DSRZD     0x80    /* Detect 0->1 transition of DSR           */
-#define MCOR1_CDZD      0x40    /* Detect 0->1 transition of CD            */
-#define MCOR1_CTSZD     0x20    /* Detect 0->1 transition of CTS           */
-#define MCOR1_DTRTH     0x0f    /* Auto DTR flow control Threshold (1-8)   */
-#define  MCOR1_NODTRFC   0x0     /* Automatic DTR flow control disabled     */
-
-
-/* Modem Change Option Register 2 (R/W) */
-
-#define MCOR2_DSROD     0x80    /* Detect 1->0 transition of DSR           */
-#define MCOR2_CDOD      0x40    /* Detect 1->0 transition of CD            */
-#define MCOR2_CTSOD     0x20    /* Detect 1->0 transition of CTS           */
-
-
-/* Modem Change Register (R/W) */
-
-#define MCR_DSRCHG      0x80    /* DSR Changed                             */
-#define MCR_CDCHG       0x40    /* CD Changed                              */
-#define MCR_CTSCHG      0x20    /* CTS Changed                             */
-
-
-/* Modem Signal Value Register (R/W) */
-
-#define MSVR_DSR        0x80    /* Current state of DSR input              */
-#define MSVR_CD         0x40    /* Current state of CD input               */
-#define MSVR_CTS        0x20    /* Current state of CTS input              */
-#define MSVR_DTR        0x02    /* Current state of DTR output             */
-#define MSVR_RTS        0x01    /* Current state of RTS output             */
-
-
-/* Escape characters */
-
-#define CD180_C_ESC     0x00    /* Escape character                        */
-#define CD180_C_SBRK    0x81    /* Start sending BREAK                     */
-#define CD180_C_DELAY   0x82    /* Delay output                            */
-#define CD180_C_EBRK    0x83    /* Stop sending BREAK                      */
diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c
deleted file mode 100644 (file)
index 674af69..0000000
+++ /dev/null
@@ -1,2489 +0,0 @@
-/*
- * linux/drivers/char/serial167.c
- *
- * Driver for MVME166/7 board serial ports, which are via a CD2401.
- * Based very much on cyclades.c.
- *
- * MVME166/7 work by Richard Hirst [richard@sleepie.demon.co.uk]
- *
- * ==============================================================
- *
- * static char rcsid[] =
- * "$Revision: 1.36.1.4 $$Date: 1995/03/29 06:14:14 $";
- *
- *  linux/kernel/cyclades.c
- *
- * Maintained by Marcio Saito (cyclades@netcom.com) and
- * Randolph Bentson (bentson@grieg.seaslug.org)
- *
- * 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.
- *
- * This version does not support shared irq's.
- *
- * $Log: cyclades.c,v $
- * Revision 1.36.1.4  1995/03/29  06:14:14  bentson
- * disambiguate between Cyclom-16Y and Cyclom-32Ye;
- *
- * Changes:
- *
- * 200 lines of changes record removed - RGH 11-10-95, starting work on
- * converting this to drive serial ports on mvme166 (cd2401).
- *
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 2000/08/25
- * - get rid of verify_area
- * - use get_user to access memory from userspace in set_threshold,
- *   set_default_threshold and set_timeout
- * - don't use the panic function in serial167_init
- * - do resource release on failure on serial167_init
- * - include missing restore_flags in mvme167_serial_console_setup
- *
- * Kars de Jong <jongk@linux-m68k.org> - 2004/09/06
- * - replace bottom half handler with task queue handler
- */
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/tty.h>
-#include <linux/interrupt.h>
-#include <linux/serial.h>
-#include <linux/serialP.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/serial167.h>
-#include <linux/delay.h>
-#include <linux/major.h>
-#include <linux/mm.h>
-#include <linux/console.h>
-#include <linux/module.h>
-#include <linux/bitops.h>
-#include <linux/tty_flip.h>
-#include <linux/gfp.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/mvme16xhw.h>
-#include <asm/bootinfo.h>
-#include <asm/setup.h>
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-
-#include <asm/uaccess.h>
-#include <linux/init.h>
-
-#define SERIAL_PARANOIA_CHECK
-#undef  SERIAL_DEBUG_OPEN
-#undef  SERIAL_DEBUG_THROTTLE
-#undef  SERIAL_DEBUG_OTHER
-#undef  SERIAL_DEBUG_IO
-#undef  SERIAL_DEBUG_COUNT
-#undef  SERIAL_DEBUG_DTR
-#undef  CYCLOM_16Y_HACK
-#define  CYCLOM_ENABLE_MONITORING
-
-#define WAKEUP_CHARS 256
-
-#define STD_COM_FLAGS (0)
-
-static struct tty_driver *cy_serial_driver;
-extern int serial_console;
-static struct cyclades_port *serial_console_info = NULL;
-static unsigned int serial_console_cflag = 0;
-u_char initial_console_speed;
-
-/* Base address of cd2401 chip on mvme166/7 */
-
-#define BASE_ADDR (0xfff45000)
-#define pcc2chip       ((volatile u_char *)0xfff42000)
-#define PccSCCMICR     0x1d
-#define PccSCCTICR     0x1e
-#define PccSCCRICR     0x1f
-#define PccTPIACKR     0x25
-#define PccRPIACKR     0x27
-#define PccIMLR                0x3f
-
-/* This is the per-port data structure */
-struct cyclades_port cy_port[] = {
-       /* CARD#  */
-       {-1},                   /* ttyS0 */
-       {-1},                   /* ttyS1 */
-       {-1},                   /* ttyS2 */
-       {-1},                   /* ttyS3 */
-};
-
-#define NR_PORTS        ARRAY_SIZE(cy_port)
-
-/*
- * 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->flags.
- *         0,     1,     2,     3,     4,     5,     6,     7,     8,     9,
- *        10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
- *                                                  HI            VHI
- */
-static int baud_table[] = {
-       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
-       1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000,
-       0
-};
-
-#if 0
-static char baud_co[] = {      /* 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 char baud_bpr[] = {     /* 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
-};
-#endif
-
-/* I think 166 brd clocks 2401 at 20MHz.... */
-
-/* These values are written directly to tcor, and >> 5 for writing to rcor */
-static u_char baud_co[] = {    /* 20 MHz clock option table */
-       0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x60, 0x60, 0x40,
-       0x40, 0x40, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-/* These values written directly to tbpr/rbpr */
-static u_char baud_bpr[] = {   /* 20 MHz baud rate period table */
-       0x00, 0xc0, 0x80, 0x58, 0x6c, 0x40, 0xc0, 0x81, 0x40, 0x81,
-       0x57, 0x40, 0x81, 0x40, 0x81, 0x40, 0x2b, 0x20, 0x15, 0x10
-};
-
-static u_char baud_cor4[] = {  /* receive threshold */
-       0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
-       0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07
-};
-
-static void shutdown(struct cyclades_port *);
-static int startup(struct cyclades_port *);
-static void cy_throttle(struct tty_struct *);
-static void cy_unthrottle(struct tty_struct *);
-static void config_setup(struct cyclades_port *);
-#ifdef CYCLOM_SHOW_STATUS
-static void show_status(int);
-#endif
-
-/*
- * I have my own version of udelay(), as it is needed when initialising
- * the chip, before the delay loop has been calibrated.  Should probably
- * reference one of the vmechip2 or pccchip2 counter for an accurate
- * delay, but this wild guess will do for now.
- */
-
-void my_udelay(long us)
-{
-       u_char x;
-       volatile u_char *p = &x;
-       int i;
-
-       while (us--)
-               for (i = 100; i; i--)
-                       x |= *p;
-}
-
-static inline int serial_paranoia_check(struct cyclades_port *info, char *name,
-               const char *routine)
-{
-#ifdef SERIAL_PARANOIA_CHECK
-       if (!info) {
-               printk("Warning: null cyclades_port for (%s) in %s\n", name,
-                               routine);
-               return 1;
-       }
-
-       if (info < &cy_port[0] || info >= &cy_port[NR_PORTS]) {
-               printk("Warning: cyclades_port out of range for (%s) in %s\n",
-                               name, routine);
-               return 1;
-       }
-
-       if (info->magic != CYCLADES_MAGIC) {
-               printk("Warning: bad magic number for serial struct (%s) in "
-                               "%s\n", name, routine);
-               return 1;
-       }
-#endif
-       return 0;
-}                              /* serial_paranoia_check */
-
-#if 0
-/* The following diagnostic routines allow the driver to spew
-   information on the screen, even (especially!) during interrupts.
- */
-void SP(char *data)
-{
-       unsigned long flags;
-       local_irq_save(flags);
-       printk(KERN_EMERG "%s", data);
-       local_irq_restore(flags);
-}
-
-char scrn[2];
-void CP(char data)
-{
-       unsigned long flags;
-       local_irq_save(flags);
-       scrn[0] = data;
-       printk(KERN_EMERG "%c", scrn);
-       local_irq_restore(flags);
-}                              /* CP */
-
-void CP1(int data)
-{
-       (data < 10) ? CP(data + '0') : CP(data + 'A' - 10);
-}                              /* CP1 */
-void CP2(int data)
-{
-       CP1((data >> 4) & 0x0f);
-       CP1(data & 0x0f);
-}                              /* CP2 */
-void CP4(int data)
-{
-       CP2((data >> 8) & 0xff);
-       CP2(data & 0xff);
-}                              /* CP4 */
-void CP8(long data)
-{
-       CP4((data >> 16) & 0xffff);
-       CP4(data & 0xffff);
-}                              /* CP8 */
-#endif
-
-/* 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.
- */
-u_short write_cy_cmd(volatile u_char * base_addr, u_char cmd)
-{
-       unsigned long flags;
-       volatile int i;
-
-       local_irq_save(flags);
-       /* Check to see that the previous command has completed */
-       for (i = 0; i < 100; i++) {
-               if (base_addr[CyCCR] == 0) {
-                       break;
-               }
-               my_udelay(10L);
-       }
-       /* if the CCR never cleared, the previous command
-          didn't finish within the "reasonable time" */
-       if (i == 10) {
-               local_irq_restore(flags);
-               return (-1);
-       }
-
-       /* Issue the new command */
-       base_addr[CyCCR] = cmd;
-       local_irq_restore(flags);
-       return (0);
-}                              /* write_cy_cmd */
-
-/* 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_port *info = tty->driver_data;
-       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
-       int channel;
-       unsigned long flags;
-
-#ifdef SERIAL_DEBUG_OTHER
-       printk("cy_stop %s\n", tty->name);      /* */
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_stop"))
-               return;
-
-       channel = info->line;
-
-       local_irq_save(flags);
-       base_addr[CyCAR] = (u_char) (channel);  /* index channel */
-       base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
-       local_irq_restore(flags);
-}                              /* cy_stop */
-
-static void cy_start(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
-       int channel;
-       unsigned long flags;
-
-#ifdef SERIAL_DEBUG_OTHER
-       printk("cy_start %s\n", tty->name);     /* */
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_start"))
-               return;
-
-       channel = info->line;
-
-       local_irq_save(flags);
-       base_addr[CyCAR] = (u_char) (channel);
-       base_addr[CyIER] |= CyTxMpty;
-       local_irq_restore(flags);
-}                              /* cy_start */
-
-/* The real interrupt service routines are called
-   whenever the card wants its hand held--chars
-   received, out buffer empty, modem change, etc.
- */
-static irqreturn_t cd2401_rxerr_interrupt(int irq, void *dev_id)
-{
-       struct tty_struct *tty;
-       struct cyclades_port *info;
-       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
-       unsigned char err, rfoc;
-       int channel;
-       char data;
-
-       /* determine the channel and change to that context */
-       channel = (u_short) (base_addr[CyLICR] >> 2);
-       info = &cy_port[channel];
-       info->last_active = jiffies;
-
-       if ((err = base_addr[CyRISR]) & CyTIMEOUT) {
-               /* This is a receive timeout interrupt, ignore it */
-               base_addr[CyREOIR] = CyNOTRANS;
-               return IRQ_HANDLED;
-       }
-
-       /* Read a byte of data if there is any - assume the error
-        * is associated with this character */
-
-       if ((rfoc = base_addr[CyRFOC]) != 0)
-               data = base_addr[CyRDR];
-       else
-               data = 0;
-
-       /* if there is nowhere to put the data, discard it */
-       if (info->tty == 0) {
-               base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;
-               return IRQ_HANDLED;
-       } else {                /* there is an open port for this data */
-               tty = info->tty;
-               if (err & info->ignore_status_mask) {
-                       base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;
-                       return IRQ_HANDLED;
-               }
-               if (tty_buffer_request_room(tty, 1) != 0) {
-                       if (err & info->read_status_mask) {
-                               if (err & CyBREAK) {
-                                       tty_insert_flip_char(tty, data,
-                                                            TTY_BREAK);
-                                       if (info->flags & ASYNC_SAK) {
-                                               do_SAK(tty);
-                                       }
-                               } else if (err & CyFRAME) {
-                                       tty_insert_flip_char(tty, data,
-                                                            TTY_FRAME);
-                               } else if (err & CyPARITY) {
-                                       tty_insert_flip_char(tty, data,
-                                                            TTY_PARITY);
-                               } else if (err & CyOVERRUN) {
-                                       tty_insert_flip_char(tty, 0,
-                                                            TTY_OVERRUN);
-                                       /*
-                                          If the flip buffer itself is
-                                          overflowing, we still lose
-                                          the next incoming character.
-                                        */
-                                       if (tty_buffer_request_room(tty, 1) !=
-                                           0) {
-                                               tty_insert_flip_char(tty, data,
-                                                                    TTY_FRAME);
-                                       }
-                                       /* 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);
-                               }
-                       } else {
-                               tty_insert_flip_char(tty, data, TTY_NORMAL);
-                       }
-               } else {
-                       /* there was a software buffer overrun
-                          and nothing could be done about it!!! */
-               }
-       }
-       tty_schedule_flip(tty);
-       /* end of service */
-       base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;
-       return IRQ_HANDLED;
-}                              /* cy_rxerr_interrupt */
-
-static irqreturn_t cd2401_modem_interrupt(int irq, void *dev_id)
-{
-       struct cyclades_port *info;
-       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
-       int channel;
-       int mdm_change;
-       int mdm_status;
-
-       /* determine the channel and change to that context */
-       channel = (u_short) (base_addr[CyLICR] >> 2);
-       info = &cy_port[channel];
-       info->last_active = jiffies;
-
-       mdm_change = base_addr[CyMISR];
-       mdm_status = base_addr[CyMSVR1];
-
-       if (info->tty == 0) {   /* nowhere to put the data, ignore it */
-               ;
-       } else {
-               if ((mdm_change & CyDCD)
-                   && (info->flags & ASYNC_CHECK_CD)) {
-                       if (mdm_status & CyDCD) {
-/* CP('!'); */
-                               wake_up_interruptible(&info->open_wait);
-                       } else {
-/* CP('@'); */
-                               tty_hangup(info->tty);
-                               wake_up_interruptible(&info->open_wait);
-                               info->flags &= ~ASYNC_NORMAL_ACTIVE;
-                       }
-               }
-               if ((mdm_change & CyCTS)
-                   && (info->flags & ASYNC_CTS_FLOW)) {
-                       if (info->tty->stopped) {
-                               if (mdm_status & CyCTS) {
-                                       /* !!! cy_start isn't used because... */
-                                       info->tty->stopped = 0;
-                                       base_addr[CyIER] |= CyTxMpty;
-                                       tty_wakeup(info->tty);
-                               }
-                       } else {
-                               if (!(mdm_status & CyCTS)) {
-                                       /* !!! cy_stop isn't used because... */
-                                       info->tty->stopped = 1;
-                                       base_addr[CyIER] &=
-                                           ~(CyTxMpty | CyTxRdy);
-                               }
-                       }
-               }
-               if (mdm_status & CyDSR) {
-               }
-       }
-       base_addr[CyMEOIR] = 0;
-       return IRQ_HANDLED;
-}                              /* cy_modem_interrupt */
-
-static irqreturn_t cd2401_tx_interrupt(int irq, void *dev_id)
-{
-       struct cyclades_port *info;
-       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
-       int channel;
-       int char_count, saved_cnt;
-       int outch;
-
-       /* determine the channel and change to that context */
-       channel = (u_short) (base_addr[CyLICR] >> 2);
-
-       /* validate the port number (as configured and open) */
-       if ((channel < 0) || (NR_PORTS <= channel)) {
-               base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
-               base_addr[CyTEOIR] = CyNOTRANS;
-               return IRQ_HANDLED;
-       }
-       info = &cy_port[channel];
-       info->last_active = jiffies;
-       if (info->tty == 0) {
-               base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
-               base_addr[CyTEOIR] = CyNOTRANS;
-               return IRQ_HANDLED;
-       }
-
-       /* load the on-chip space available for outbound data */
-       saved_cnt = char_count = base_addr[CyTFTC];
-
-       if (info->x_char) {     /* send special char */
-               outch = info->x_char;
-               base_addr[CyTDR] = outch;
-               char_count--;
-               info->x_char = 0;
-       }
-
-       if (info->x_break) {
-               /*  The Cirrus chip requires the "Embedded Transmit
-                  Commands" of start break, delay, and end break
-                  sequences to be sent.  The duration of the
-                  break is given in TICs, which runs at HZ
-                  (typically 100) and the PPR runs at 200 Hz,
-                  so the delay is duration * 200/HZ, and thus a
-                  break can run from 1/100 sec to about 5/4 sec.
-                  Need to check these values - RGH 141095.
-                */
-               base_addr[CyTDR] = 0;   /* start break */
-               base_addr[CyTDR] = 0x81;
-               base_addr[CyTDR] = 0;   /* delay a bit */
-               base_addr[CyTDR] = 0x82;
-               base_addr[CyTDR] = info->x_break * 200 / HZ;
-               base_addr[CyTDR] = 0;   /* terminate break */
-               base_addr[CyTDR] = 0x83;
-               char_count -= 7;
-               info->x_break = 0;
-       }
-
-       while (char_count > 0) {
-               if (!info->xmit_cnt) {
-                       base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
-                       break;
-               }
-               if (info->xmit_buf == 0) {
-                       base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
-                       break;
-               }
-               if (info->tty->stopped || info->tty->hw_stopped) {
-                       base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
-                       break;
-               }
-               /* 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->xmit_buf[info->xmit_tail];
-               if (outch) {
-                       info->xmit_cnt--;
-                       info->xmit_tail = (info->xmit_tail + 1)
-                           & (PAGE_SIZE - 1);
-                       base_addr[CyTDR] = outch;
-                       char_count--;
-               } else {
-                       if (char_count > 1) {
-                               info->xmit_cnt--;
-                               info->xmit_tail = (info->xmit_tail + 1)
-                                   & (PAGE_SIZE - 1);
-                               base_addr[CyTDR] = outch;
-                               base_addr[CyTDR] = 0;
-                               char_count--;
-                               char_count--;
-                       } else {
-                               break;
-                       }
-               }
-       }
-
-       if (info->xmit_cnt < WAKEUP_CHARS)
-               tty_wakeup(info->tty);
-
-       base_addr[CyTEOIR] = (char_count != saved_cnt) ? 0 : CyNOTRANS;
-       return IRQ_HANDLED;
-}                              /* cy_tx_interrupt */
-
-static irqreturn_t cd2401_rx_interrupt(int irq, void *dev_id)
-{
-       struct tty_struct *tty;
-       struct cyclades_port *info;
-       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
-       int channel;
-       char data;
-       int char_count;
-       int save_cnt;
-
-       /* determine the channel and change to that context */
-       channel = (u_short) (base_addr[CyLICR] >> 2);
-       info = &cy_port[channel];
-       info->last_active = jiffies;
-       save_cnt = char_count = base_addr[CyRFOC];
-
-               /* if there is nowhere to put the data, discard it */
-       if (info->tty == 0) {
-               while (char_count--) {
-                       data = base_addr[CyRDR];
-               }
-       } else {                /* there is an open port for this data */
-               tty = info->tty;
-               /* load # characters available from the chip */
-
-#ifdef CYCLOM_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
-               while (char_count--) {
-                       data = base_addr[CyRDR];
-                       tty_insert_flip_char(tty, data, TTY_NORMAL);
-#ifdef CYCLOM_16Y_HACK
-                       udelay(10L);
-#endif
-               }
-               tty_schedule_flip(tty);
-       }
-       /* end of service */
-       base_addr[CyREOIR] = save_cnt ? 0 : CyNOTRANS;
-       return IRQ_HANDLED;
-}                              /* cy_rx_interrupt */
-
-/* This is called whenever a port becomes active;
-   interrupts are enabled and DTR & RTS are turned on.
- */
-static int startup(struct cyclades_port *info)
-{
-       unsigned long flags;
-       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
-       int channel;
-
-       if (info->flags & ASYNC_INITIALIZED) {
-               return 0;
-       }
-
-       if (!info->type) {
-               if (info->tty) {
-                       set_bit(TTY_IO_ERROR, &info->tty->flags);
-               }
-               return 0;
-       }
-       if (!info->xmit_buf) {
-               info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
-               if (!info->xmit_buf) {
-                       return -ENOMEM;
-               }
-       }
-
-       config_setup(info);
-
-       channel = info->line;
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("startup channel %d\n", channel);
-#endif
-
-       local_irq_save(flags);
-       base_addr[CyCAR] = (u_char) channel;
-       write_cy_cmd(base_addr, CyENB_RCVR | CyENB_XMTR);
-
-       base_addr[CyCAR] = (u_char) channel;    /* !!! Is this needed? */
-       base_addr[CyMSVR1] = CyRTS;
-/* CP('S');CP('1'); */
-       base_addr[CyMSVR2] = CyDTR;
-
-#ifdef SERIAL_DEBUG_DTR
-       printk("cyc: %d: raising DTR\n", __LINE__);
-       printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
-              base_addr[CyMSVR2]);
-#endif
-
-       base_addr[CyIER] |= CyRxData;
-       info->flags |= ASYNC_INITIALIZED;
-
-       if (info->tty) {
-               clear_bit(TTY_IO_ERROR, &info->tty->flags);
-       }
-       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-
-       local_irq_restore(flags);
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk(" done\n");
-#endif
-       return 0;
-}                              /* startup */
-
-void start_xmit(struct cyclades_port *info)
-{
-       unsigned long flags;
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       int channel;
-
-       channel = info->line;
-       local_irq_save(flags);
-       base_addr[CyCAR] = channel;
-       base_addr[CyIER] |= CyTxMpty;
-       local_irq_restore(flags);
-}                              /* 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 shutdown(struct cyclades_port *info)
-{
-       unsigned long flags;
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       int channel;
-
-       if (!(info->flags & ASYNC_INITIALIZED)) {
-/* CP('$'); */
-               return;
-       }
-
-       channel = info->line;
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("shutdown channel %d\n", channel);
-#endif
-
-       /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE
-          SENT BEFORE DROPPING THE LINE !!!  (Perhaps
-          set some flag that is read when XMTY happens.)
-          Other choices are to delay some fixed interval
-          or schedule some later processing.
-        */
-       local_irq_save(flags);
-       if (info->xmit_buf) {
-               free_page((unsigned long)info->xmit_buf);
-               info->xmit_buf = NULL;
-       }
-
-       base_addr[CyCAR] = (u_char) channel;
-       if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
-               base_addr[CyMSVR1] = 0;
-/* CP('C');CP('1'); */
-               base_addr[CyMSVR2] = 0;
-#ifdef SERIAL_DEBUG_DTR
-               printk("cyc: %d: dropping DTR\n", __LINE__);
-               printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
-                      base_addr[CyMSVR2]);
-#endif
-       }
-       write_cy_cmd(base_addr, CyDIS_RCVR);
-       /* it may be appropriate to clear _XMIT at
-          some later date (after testing)!!! */
-
-       if (info->tty) {
-               set_bit(TTY_IO_ERROR, &info->tty->flags);
-       }
-       info->flags &= ~ASYNC_INITIALIZED;
-       local_irq_restore(flags);
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk(" done\n");
-#endif
-}                              /* shutdown */
-
-/*
- * This routine finds or computes the various line characteristics.
- */
-static void config_setup(struct cyclades_port *info)
-{
-       unsigned long flags;
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       int channel;
-       unsigned cflag;
-       int i;
-       unsigned char ti, need_init_chan = 0;
-
-       if (!info->tty || !info->tty->termios) {
-               return;
-       }
-       if (info->line == -1) {
-               return;
-       }
-       cflag = info->tty->termios->c_cflag;
-
-       /* baud rate */
-       i = cflag & CBAUD;
-#ifdef CBAUDEX
-/* Starting with kernel 1.1.65, there is direct support for
-   higher baud rates.  The following code supports those
-   changes.  The conditional aspect allows this driver to be
-   used for earlier as well as later kernel versions.  (The
-   mapping is slightly different from serial.c because there
-   is still the possibility of supporting 75 kbit/sec with
-   the Cyclades board.)
- */
-       if (i & CBAUDEX) {
-               if (i == B57600)
-                       i = 16;
-               else if (i == B115200)
-                       i = 18;
-#ifdef B78600
-               else if (i == B78600)
-                       i = 17;
-#endif
-               else
-                       info->tty->termios->c_cflag &= ~CBAUDEX;
-       }
-#endif
-       if (i == 15) {
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                       i += 1;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                       i += 3;
-       }
-       /* Don't ever change the speed of the console port.  It will
-        * run at the speed specified in bootinfo, or at 19.2K */
-       /* Actually, it should run at whatever speed 166Bug was using */
-       /* Note info->timeout isn't used at present */
-       if (info != serial_console_info) {
-               info->tbpr = baud_bpr[i];       /* Tx BPR */
-               info->tco = baud_co[i]; /* Tx CO */
-               info->rbpr = baud_bpr[i];       /* Rx BPR */
-               info->rco = baud_co[i] >> 5;    /* Rx CO */
-               if (baud_table[i] == 134) {
-                       info->timeout =
-                           (info->xmit_fifo_size * HZ * 30 / 269) + 2;
-                       /* get it right for 134.5 baud */
-               } 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->cor7 = 0;
-       info->cor6 = 0;
-       info->cor5 = 0;
-       info->cor4 = (info->default_threshold ? info->default_threshold : baud_cor4[i]);        /* receive threshold */
-       /* Following two lines added 101295, RGH. */
-       /* It is obviously wrong to access CyCORx, and not info->corx here,
-        * try and remember to fix it later! */
-       channel = info->line;
-       base_addr[CyCAR] = (u_char) channel;
-       if (C_CLOCAL(info->tty)) {
-               if (base_addr[CyIER] & CyMdmCh)
-                       base_addr[CyIER] &= ~CyMdmCh;   /* without modem intr */
-               /* ignore 1->0 modem transitions */
-               if (base_addr[CyCOR4] & (CyDSR | CyCTS | CyDCD))
-                       base_addr[CyCOR4] &= ~(CyDSR | CyCTS | CyDCD);
-               /* ignore 0->1 modem transitions */
-               if (base_addr[CyCOR5] & (CyDSR | CyCTS | CyDCD))
-                       base_addr[CyCOR5] &= ~(CyDSR | CyCTS | CyDCD);
-       } else {
-               if ((base_addr[CyIER] & CyMdmCh) != CyMdmCh)
-                       base_addr[CyIER] |= CyMdmCh;    /* with modem intr */
-               /* act on 1->0 modem transitions */
-               if ((base_addr[CyCOR4] & (CyDSR | CyCTS | CyDCD)) !=
-                   (CyDSR | CyCTS | CyDCD))
-                       base_addr[CyCOR4] |= CyDSR | CyCTS | CyDCD;
-               /* act on 0->1 modem transitions */
-               if ((base_addr[CyCOR5] & (CyDSR | CyCTS | CyDCD)) !=
-                   (CyDSR | CyCTS | CyDCD))
-                       base_addr[CyCOR5] |= CyDSR | CyCTS | CyDCD;
-       }
-       info->cor3 = (cflag & CSTOPB) ? Cy_2_STOP : Cy_1_STOP;
-       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 & PARENB) {
-               if (cflag & PARODD) {
-                       info->cor1 |= CyPARITY_O;
-               } else {
-                       info->cor1 |= CyPARITY_E;
-               }
-       } else {
-               info->cor1 |= CyPARITY_NONE;
-       }
-
-       /* CTS flow control flag */
-#if 0
-       /* Don't complcate matters for now! RGH 141095 */
-       if (cflag & CRTSCTS) {
-               info->flags |= ASYNC_CTS_FLOW;
-               info->cor2 |= CyCtsAE;
-       } else {
-               info->flags &= ~ASYNC_CTS_FLOW;
-               info->cor2 &= ~CyCtsAE;
-       }
-#endif
-       if (cflag & CLOCAL)
-               info->flags &= ~ASYNC_CHECK_CD;
-       else
-               info->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 = info->line;
-
-       local_irq_save(flags);
-       base_addr[CyCAR] = (u_char) channel;
-
-       /* CyCMR set once only in mvme167_init_serial() */
-       if (base_addr[CyLICR] != channel << 2)
-               base_addr[CyLICR] = channel << 2;
-       if (base_addr[CyLIVR] != 0x5c)
-               base_addr[CyLIVR] = 0x5c;
-
-       /* tx and rx baud rate */
-
-       if (base_addr[CyCOR1] != info->cor1)
-               need_init_chan = 1;
-       if (base_addr[CyTCOR] != info->tco)
-               base_addr[CyTCOR] = info->tco;
-       if (base_addr[CyTBPR] != info->tbpr)
-               base_addr[CyTBPR] = info->tbpr;
-       if (base_addr[CyRCOR] != info->rco)
-               base_addr[CyRCOR] = info->rco;
-       if (base_addr[CyRBPR] != info->rbpr)
-               base_addr[CyRBPR] = info->rbpr;
-
-       /* set line characteristics  according configuration */
-
-       if (base_addr[CySCHR1] != START_CHAR(info->tty))
-               base_addr[CySCHR1] = START_CHAR(info->tty);
-       if (base_addr[CySCHR2] != STOP_CHAR(info->tty))
-               base_addr[CySCHR2] = STOP_CHAR(info->tty);
-       if (base_addr[CySCRL] != START_CHAR(info->tty))
-               base_addr[CySCRL] = START_CHAR(info->tty);
-       if (base_addr[CySCRH] != START_CHAR(info->tty))
-               base_addr[CySCRH] = START_CHAR(info->tty);
-       if (base_addr[CyCOR1] != info->cor1)
-               base_addr[CyCOR1] = info->cor1;
-       if (base_addr[CyCOR2] != info->cor2)
-               base_addr[CyCOR2] = info->cor2;
-       if (base_addr[CyCOR3] != info->cor3)
-               base_addr[CyCOR3] = info->cor3;
-       if (base_addr[CyCOR4] != info->cor4)
-               base_addr[CyCOR4] = info->cor4;
-       if (base_addr[CyCOR5] != info->cor5)
-               base_addr[CyCOR5] = info->cor5;
-       if (base_addr[CyCOR6] != info->cor6)
-               base_addr[CyCOR6] = info->cor6;
-       if (base_addr[CyCOR7] != info->cor7)
-               base_addr[CyCOR7] = info->cor7;
-
-       if (need_init_chan)
-               write_cy_cmd(base_addr, CyINIT_CHAN);
-
-       base_addr[CyCAR] = (u_char) channel;    /* !!! Is this needed? */
-
-       /* 2ms default rx timeout */
-       ti = info->default_timeout ? info->default_timeout : 0x02;
-       if (base_addr[CyRTPRL] != ti)
-               base_addr[CyRTPRL] = ti;
-       if (base_addr[CyRTPRH] != 0)
-               base_addr[CyRTPRH] = 0;
-
-       /* Set up RTS here also ????? RGH 141095 */
-       if (i == 0) {           /* baud rate is zero, turn off line */
-               if ((base_addr[CyMSVR2] & CyDTR) == CyDTR)
-                       base_addr[CyMSVR2] = 0;
-#ifdef SERIAL_DEBUG_DTR
-               printk("cyc: %d: dropping DTR\n", __LINE__);
-               printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
-                      base_addr[CyMSVR2]);
-#endif
-       } else {
-               if ((base_addr[CyMSVR2] & CyDTR) != CyDTR)
-                       base_addr[CyMSVR2] = CyDTR;
-#ifdef SERIAL_DEBUG_DTR
-               printk("cyc: %d: raising DTR\n", __LINE__);
-               printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
-                      base_addr[CyMSVR2]);
-#endif
-       }
-
-       if (info->tty) {
-               clear_bit(TTY_IO_ERROR, &info->tty->flags);
-       }
-
-       local_irq_restore(flags);
-
-}                              /* config_setup */
-
-static int cy_put_char(struct tty_struct *tty, unsigned char ch)
-{
-       struct cyclades_port *info = tty->driver_data;
-       unsigned long flags;
-
-#ifdef SERIAL_DEBUG_IO
-       printk("cy_put_char %s(0x%02x)\n", tty->name, ch);
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_put_char"))
-               return 0;
-
-       if (!info->xmit_buf)
-               return 0;
-
-       local_irq_save(flags);
-       if (info->xmit_cnt >= PAGE_SIZE - 1) {
-               local_irq_restore(flags);
-               return 0;
-       }
-
-       info->xmit_buf[info->xmit_head++] = ch;
-       info->xmit_head &= PAGE_SIZE - 1;
-       info->xmit_cnt++;
-       local_irq_restore(flags);
-       return 1;
-}                              /* cy_put_char */
-
-static void cy_flush_chars(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-       unsigned long flags;
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       int channel;
-
-#ifdef SERIAL_DEBUG_IO
-       printk("cy_flush_chars %s\n", tty->name);       /* */
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_flush_chars"))
-               return;
-
-       if (info->xmit_cnt <= 0 || tty->stopped
-           || tty->hw_stopped || !info->xmit_buf)
-               return;
-
-       channel = info->line;
-
-       local_irq_save(flags);
-       base_addr[CyCAR] = channel;
-       base_addr[CyIER] |= CyTxMpty;
-       local_irq_restore(flags);
-}                              /* cy_flush_chars */
-
-/* This routine gets called when tty_write has put something into
-    the write_queue.  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, total = 0;
-
-#ifdef SERIAL_DEBUG_IO
-       printk("cy_write %s\n", tty->name);     /* */
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_write")) {
-               return 0;
-       }
-
-       if (!info->xmit_buf) {
-               return 0;
-       }
-
-       while (1) {
-               local_irq_save(flags);
-               c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-                                         SERIAL_XMIT_SIZE - info->xmit_head));
-               if (c <= 0) {
-                       local_irq_restore(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;
-               local_irq_restore(flags);
-
-               buf += c;
-               count -= c;
-               total += c;
-       }
-
-       if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
-               start_xmit(info);
-       }
-       return total;
-}                              /* cy_write */
-
-static int cy_write_room(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-       int ret;
-
-#ifdef SERIAL_DEBUG_IO
-       printk("cy_write_room %s\n", tty->name);        /* */
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_write_room"))
-               return 0;
-       ret = PAGE_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;
-
-#ifdef SERIAL_DEBUG_IO
-       printk("cy_chars_in_buffer %s %d\n", tty->name, info->xmit_cnt);        /* */
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer"))
-               return 0;
-
-       return info->xmit_cnt;
-}                              /* cy_chars_in_buffer */
-
-static void cy_flush_buffer(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-       unsigned long flags;
-
-#ifdef SERIAL_DEBUG_IO
-       printk("cy_flush_buffer %s\n", tty->name);      /* */
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_flush_buffer"))
-               return;
-       local_irq_save(flags);
-       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-       local_irq_restore(flags);
-       tty_wakeup(tty);
-}                              /* cy_flush_buffer */
-
-/* This routine is called by the upper-layer tty layer to signal
-   that incoming characters should be throttled or that the
-   throttle should be released.
- */
-static void cy_throttle(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-       unsigned long flags;
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       int channel;
-
-#ifdef SERIAL_DEBUG_THROTTLE
-       char buf[64];
-
-       printk("throttle %s: %d....\n", tty_name(tty, buf),
-              tty->ldisc.chars_in_buffer(tty));
-       printk("cy_throttle %s\n", tty->name);
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_nthrottle")) {
-               return;
-       }
-
-       if (I_IXOFF(tty)) {
-               info->x_char = STOP_CHAR(tty);
-               /* Should use the "Send Special Character" feature!!! */
-       }
-
-       channel = info->line;
-
-       local_irq_save(flags);
-       base_addr[CyCAR] = (u_char) channel;
-       base_addr[CyMSVR1] = 0;
-       local_irq_restore(flags);
-}                              /* cy_throttle */
-
-static void cy_unthrottle(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-       unsigned long flags;
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       int channel;
-
-#ifdef SERIAL_DEBUG_THROTTLE
-       char buf[64];
-
-       printk("throttle %s: %d....\n", tty_name(tty, buf),
-              tty->ldisc.chars_in_buffer(tty));
-       printk("cy_unthrottle %s\n", tty->name);
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_nthrottle")) {
-               return;
-       }
-
-       if (I_IXOFF(tty)) {
-               info->x_char = START_CHAR(tty);
-               /* Should use the "Send Special Character" feature!!! */
-       }
-
-       channel = info->line;
-
-       local_irq_save(flags);
-       base_addr[CyCAR] = (u_char) channel;
-       base_addr[CyMSVR1] = CyRTS;
-       local_irq_restore(flags);
-}                              /* cy_unthrottle */
-
-static int
-get_serial_info(struct cyclades_port *info,
-               struct serial_struct __user * retinfo)
-{
-       struct serial_struct tmp;
-
-/* CP('g'); */
-       if (!retinfo)
-               return -EFAULT;
-       memset(&tmp, 0, sizeof(tmp));
-       tmp.type = info->type;
-       tmp.line = info->line;
-       tmp.port = info->line;
-       tmp.irq = 0;
-       tmp.flags = info->flags;
-       tmp.baud_base = 0;      /*!!! */
-       tmp.close_delay = info->close_delay;
-       tmp.custom_divisor = 0; /*!!! */
-       tmp.hub6 = 0;           /*!!! */
-       return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;
-}                              /* get_serial_info */
-
-static int
-set_serial_info(struct cyclades_port *info,
-               struct serial_struct __user * new_info)
-{
-       struct serial_struct new_serial;
-       struct cyclades_port old_info;
-
-/* CP('s'); */
-       if (!new_info)
-               return -EFAULT;
-       if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
-               return -EFAULT;
-       old_info = *info;
-
-       if (!capable(CAP_SYS_ADMIN)) {
-               if ((new_serial.close_delay != info->close_delay) ||
-                   ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) !=
-                    (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)))
-                       return -EPERM;
-               info->flags = ((info->flags & ~ASYNC_USR_MASK) |
-                              (new_serial.flags & ASYNC_USR_MASK));
-               goto check_and_exit;
-       }
-
-       /*
-        * OK, past this point, all the error checking has been done.
-        * At this point, we start making changes.....
-        */
-
-       info->flags = ((info->flags & ~ASYNC_FLAGS) |
-                      (new_serial.flags & ASYNC_FLAGS));
-       info->close_delay = new_serial.close_delay;
-
-check_and_exit:
-       if (info->flags & ASYNC_INITIALIZED) {
-               config_setup(info);
-               return 0;
-       }
-       return startup(info);
-}                              /* set_serial_info */
-
-static int cy_tiocmget(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-       int channel;
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       unsigned long flags;
-       unsigned char status;
-
-       channel = info->line;
-
-       local_irq_save(flags);
-       base_addr[CyCAR] = (u_char) channel;
-       status = base_addr[CyMSVR1] | base_addr[CyMSVR2];
-       local_irq_restore(flags);
-
-       return ((status & CyRTS) ? TIOCM_RTS : 0)
-           | ((status & CyDTR) ? TIOCM_DTR : 0)
-           | ((status & CyDCD) ? TIOCM_CAR : 0)
-           | ((status & CyDSR) ? TIOCM_DSR : 0)
-           | ((status & CyCTS) ? TIOCM_CTS : 0);
-}                              /* cy_tiocmget */
-
-static int
-cy_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear)
-{
-       struct cyclades_port *info = tty->driver_data;
-       int channel;
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       unsigned long flags;
-
-       channel = info->line;
-
-       if (set & TIOCM_RTS) {
-               local_irq_save(flags);
-               base_addr[CyCAR] = (u_char) channel;
-               base_addr[CyMSVR1] = CyRTS;
-               local_irq_restore(flags);
-       }
-       if (set & TIOCM_DTR) {
-               local_irq_save(flags);
-               base_addr[CyCAR] = (u_char) channel;
-/* CP('S');CP('2'); */
-               base_addr[CyMSVR2] = CyDTR;
-#ifdef SERIAL_DEBUG_DTR
-               printk("cyc: %d: raising DTR\n", __LINE__);
-               printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
-                      base_addr[CyMSVR2]);
-#endif
-               local_irq_restore(flags);
-       }
-
-       if (clear & TIOCM_RTS) {
-               local_irq_save(flags);
-               base_addr[CyCAR] = (u_char) channel;
-               base_addr[CyMSVR1] = 0;
-               local_irq_restore(flags);
-       }
-       if (clear & TIOCM_DTR) {
-               local_irq_save(flags);
-               base_addr[CyCAR] = (u_char) channel;
-/* CP('C');CP('2'); */
-               base_addr[CyMSVR2] = 0;
-#ifdef SERIAL_DEBUG_DTR
-               printk("cyc: %d: dropping DTR\n", __LINE__);
-               printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
-                      base_addr[CyMSVR2]);
-#endif
-               local_irq_restore(flags);
-       }
-
-       return 0;
-}                              /* set_modem_info */
-
-static void send_break(struct cyclades_port *info, int duration)
-{                              /* Let the transmit ISR take care of this (since it
-                                  requires stuffing characters into the output stream).
-                                */
-       info->x_break = duration;
-       if (!info->xmit_cnt) {
-               start_xmit(info);
-       }
-}                              /* send_break */
-
-static int
-get_mon_info(struct cyclades_port *info, struct cyclades_monitor __user * mon)
-{
-
-       if (copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor)))
-               return -EFAULT;
-       info->mon.int_count = 0;
-       info->mon.char_count = 0;
-       info->mon.char_max = 0;
-       info->mon.char_last = 0;
-       return 0;
-}
-
-static int set_threshold(struct cyclades_port *info, unsigned long __user * arg)
-{
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       unsigned long value;
-       int channel;
-
-       if (get_user(value, arg))
-               return -EFAULT;
-
-       channel = info->line;
-       info->cor4 &= ~CyREC_FIFO;
-       info->cor4 |= value & CyREC_FIFO;
-       base_addr[CyCOR4] = info->cor4;
-       return 0;
-}
-
-static int
-get_threshold(struct cyclades_port *info, unsigned long __user * value)
-{
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       int channel;
-       unsigned long tmp;
-
-       channel = info->line;
-
-       tmp = base_addr[CyCOR4] & CyREC_FIFO;
-       return put_user(tmp, value);
-}
-
-static int
-set_default_threshold(struct cyclades_port *info, unsigned long __user * arg)
-{
-       unsigned long value;
-
-       if (get_user(value, arg))
-               return -EFAULT;
-
-       info->default_threshold = value & 0x0f;
-       return 0;
-}
-
-static int
-get_default_threshold(struct cyclades_port *info, unsigned long __user * value)
-{
-       return put_user(info->default_threshold, value);
-}
-
-static int set_timeout(struct cyclades_port *info, unsigned long __user * arg)
-{
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       int channel;
-       unsigned long value;
-
-       if (get_user(value, arg))
-               return -EFAULT;
-
-       channel = info->line;
-
-       base_addr[CyRTPRL] = value & 0xff;
-       base_addr[CyRTPRH] = (value >> 8) & 0xff;
-       return 0;
-}
-
-static int get_timeout(struct cyclades_port *info, unsigned long __user * value)
-{
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       int channel;
-       unsigned long tmp;
-
-       channel = info->line;
-
-       tmp = base_addr[CyRTPRL];
-       return put_user(tmp, value);
-}
-
-static int set_default_timeout(struct cyclades_port *info, unsigned long value)
-{
-       info->default_timeout = value & 0xff;
-       return 0;
-}
-
-static int
-get_default_timeout(struct cyclades_port *info, unsigned long __user * value)
-{
-       return put_user(info->default_timeout, value);
-}
-
-static int
-cy_ioctl(struct tty_struct *tty,
-        unsigned int cmd, unsigned long arg)
-{
-       struct cyclades_port *info = tty->driver_data;
-       int ret_val = 0;
-       void __user *argp = (void __user *)arg;
-
-#ifdef SERIAL_DEBUG_OTHER
-       printk("cy_ioctl %s, cmd = %x arg = %lx\n", tty->name, cmd, arg);       /* */
-#endif
-
-       tty_lock();
-
-       switch (cmd) {
-       case CYGETMON:
-               ret_val = get_mon_info(info, argp);
-               break;
-       case CYGETTHRESH:
-               ret_val = get_threshold(info, argp);
-               break;
-       case CYSETTHRESH:
-               ret_val = set_threshold(info, argp);
-               break;
-       case CYGETDEFTHRESH:
-               ret_val = get_default_threshold(info, argp);
-               break;
-       case CYSETDEFTHRESH:
-               ret_val = set_default_threshold(info, argp);
-               break;
-       case CYGETTIMEOUT:
-               ret_val = get_timeout(info, argp);
-               break;
-       case CYSETTIMEOUT:
-               ret_val = set_timeout(info, argp);
-               break;
-       case CYGETDEFTIMEOUT:
-               ret_val = get_default_timeout(info, argp);
-               break;
-       case CYSETDEFTIMEOUT:
-               ret_val = set_default_timeout(info, (unsigned long)arg);
-               break;
-       case TCSBRK:            /* SVID version: non-zero arg --> no break */
-               ret_val = tty_check_change(tty);
-               if (ret_val)
-                       break;
-               tty_wait_until_sent(tty, 0);
-               if (!arg)
-                       send_break(info, HZ / 4);       /* 1/4 second */
-               break;
-       case TCSBRKP:           /* support for POSIX tcsendbreak() */
-               ret_val = tty_check_change(tty);
-               if (ret_val)
-                       break;
-               tty_wait_until_sent(tty, 0);
-               send_break(info, arg ? arg * (HZ / 10) : HZ / 4);
-               break;
-
-/* The following commands are incompletely implemented!!! */
-       case TIOCGSERIAL:
-               ret_val = get_serial_info(info, argp);
-               break;
-       case TIOCSSERIAL:
-               ret_val = set_serial_info(info, argp);
-               break;
-       default:
-               ret_val = -ENOIOCTLCMD;
-       }
-       tty_unlock();
-
-#ifdef SERIAL_DEBUG_OTHER
-       printk("cy_ioctl done\n");
-#endif
-
-       return ret_val;
-}                              /* cy_ioctl */
-
-static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
-{
-       struct cyclades_port *info = tty->driver_data;
-
-#ifdef SERIAL_DEBUG_OTHER
-       printk("cy_set_termios %s\n", tty->name);
-#endif
-
-       if (tty->termios->c_cflag == old_termios->c_cflag)
-               return;
-       config_setup(info);
-
-       if ((old_termios->c_cflag & CRTSCTS) &&
-           !(tty->termios->c_cflag & CRTSCTS)) {
-               tty->stopped = 0;
-               cy_start(tty);
-       }
-#ifdef tytso_patch_94Nov25_1726
-       if (!(old_termios->c_cflag & CLOCAL) &&
-           (tty->termios->c_cflag & CLOCAL))
-               wake_up_interruptible(&info->open_wait);
-#endif
-}                              /* cy_set_termios */
-
-static void cy_close(struct tty_struct *tty, struct file *filp)
-{
-       struct cyclades_port *info = tty->driver_data;
-
-/* CP('C'); */
-#ifdef SERIAL_DEBUG_OTHER
-       printk("cy_close %s\n", tty->name);
-#endif
-
-       if (!info || serial_paranoia_check(info, tty->name, "cy_close")) {
-               return;
-       }
-#ifdef SERIAL_DEBUG_OPEN
-       printk("cy_close %s, count = %d\n", tty->name, info->count);
-#endif
-
-       if ((tty->count == 1) && (info->count != 1)) {
-               /*
-                * Uh, oh.  tty->count is 1, which means that the tty
-                * structure will be freed.  Info->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("cy_close: bad serial port count; tty->count is 1, "
-                      "info->count is %d\n", info->count);
-               info->count = 1;
-       }
-#ifdef SERIAL_DEBUG_COUNT
-       printk("cyc: %d: decrementing count to %d\n", __LINE__,
-              info->count - 1);
-#endif
-       if (--info->count < 0) {
-               printk("cy_close: bad serial port count for ttys%d: %d\n",
-                      info->line, info->count);
-#ifdef SERIAL_DEBUG_COUNT
-               printk("cyc: %d: setting count to 0\n", __LINE__);
-#endif
-               info->count = 0;
-       }
-       if (info->count)
-               return;
-       info->flags |= ASYNC_CLOSING;
-       if (info->flags & ASYNC_INITIALIZED)
-               tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
-       shutdown(info);
-       cy_flush_buffer(tty);
-       tty_ldisc_flush(tty);
-       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);
-
-#ifdef SERIAL_DEBUG_OTHER
-       printk("cy_close done\n");
-#endif
-}                              /* cy_close */
-
-/*
- * cy_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-void cy_hangup(struct tty_struct *tty)
-{
-       struct cyclades_port *info = tty->driver_data;
-
-#ifdef SERIAL_DEBUG_OTHER
-       printk("cy_hangup %s\n", tty->name);    /* */
-#endif
-
-       if (serial_paranoia_check(info, tty->name, "cy_hangup"))
-               return;
-
-       shutdown(info);
-#if 0
-       info->event = 0;
-       info->count = 0;
-#ifdef SERIAL_DEBUG_COUNT
-       printk("cyc: %d: setting count to 0\n", __LINE__);
-#endif
-       info->tty = 0;
-#endif
-       info->flags &= ~ASYNC_NORMAL_ACTIVE;
-       wake_up_interruptible(&info->open_wait);
-}                              /* cy_hangup */
-
-/*
- * ------------------------------------------------------------
- * cy_open() and friends
- * ------------------------------------------------------------
- */
-
-static int
-block_til_ready(struct tty_struct *tty, struct file *filp,
-               struct cyclades_port *info)
-{
-       DECLARE_WAITQUEUE(wait, current);
-       unsigned long flags;
-       int channel;
-       int retval;
-       volatile u_char *base_addr = (u_char *) BASE_ADDR;
-
-       /*
-        * If the device is in the middle of being closed, then block
-        * until it's done, and then try again.
-        */
-       if (info->flags & ASYNC_CLOSING) {
-               interruptible_sleep_on(&info->close_wait);
-               if (info->flags & ASYNC_HUP_NOTIFY) {
-                       return -EAGAIN;
-               } else {
-                       return -ERESTARTSYS;
-               }
-       }
-
-       /*
-        * If non-blocking mode is set, then make the check up front
-        * and then exit.
-        */
-       if (filp->f_flags & O_NONBLOCK) {
-               info->flags |= ASYNC_NORMAL_ACTIVE;
-               return 0;
-       }
-
-       /*
-        * Block waiting for the carrier detect and the line to become
-        * free (i.e., not in use by the callout).  While we are in
-        * this loop, info->count is dropped by one, so that
-        * cy_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: %s, count = %d\n",
-              tty->name, info->count);
-       /**/
-#endif
-           info->count--;
-#ifdef SERIAL_DEBUG_COUNT
-       printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count);
-#endif
-       info->blocked_open++;
-
-       channel = info->line;
-
-       while (1) {
-               local_irq_save(flags);
-               base_addr[CyCAR] = (u_char) channel;
-               base_addr[CyMSVR1] = CyRTS;
-/* CP('S');CP('4'); */
-               base_addr[CyMSVR2] = CyDTR;
-#ifdef SERIAL_DEBUG_DTR
-               printk("cyc: %d: raising DTR\n", __LINE__);
-               printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
-                      base_addr[CyMSVR2]);
-#endif
-               local_irq_restore(flags);
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (tty_hung_up_p(filp)
-                   || !(info->flags & ASYNC_INITIALIZED)) {
-                       if (info->flags & ASYNC_HUP_NOTIFY) {
-                               retval = -EAGAIN;
-                       } else {
-                               retval = -ERESTARTSYS;
-                       }
-                       break;
-               }
-               local_irq_save(flags);
-               base_addr[CyCAR] = (u_char) channel;
-/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */
-               if (!(info->flags & ASYNC_CLOSING)
-                   && (C_CLOCAL(tty)
-                       || (base_addr[CyMSVR1] & CyDCD))) {
-                       local_irq_restore(flags);
-                       break;
-               }
-               local_irq_restore(flags);
-               if (signal_pending(current)) {
-                       retval = -ERESTARTSYS;
-                       break;
-               }
-#ifdef SERIAL_DEBUG_OPEN
-               printk("block_til_ready blocking: %s, count = %d\n",
-                      tty->name, info->count);
-               /**/
-#endif
-               tty_unlock();
-               schedule();
-               tty_lock();
-       }
-       __set_current_state(TASK_RUNNING);
-       remove_wait_queue(&info->open_wait, &wait);
-       if (!tty_hung_up_p(filp)) {
-               info->count++;
-#ifdef SERIAL_DEBUG_COUNT
-               printk("cyc: %d: incrementing count to %d\n", __LINE__,
-                      info->count);
-#endif
-       }
-       info->blocked_open--;
-#ifdef SERIAL_DEBUG_OPEN
-       printk("block_til_ready after blocking: %s, count = %d\n",
-              tty->name, info->count);
-       /**/
-#endif
-           if (retval)
-               return retval;
-       info->flags |= ASYNC_NORMAL_ACTIVE;
-       return 0;
-}                              /* block_til_ready */
-
-/*
- * This routine is called whenever a serial port is opened.  It
- * performs the serial-specific initialization for the tty structure.
- */
-int cy_open(struct tty_struct *tty, struct file *filp)
-{
-       struct cyclades_port *info;
-       int retval, line;
-
-/* CP('O'); */
-       line = tty->index;
-       if ((line < 0) || (NR_PORTS <= line)) {
-               return -ENODEV;
-       }
-       info = &cy_port[line];
-       if (info->line < 0) {
-               return -ENODEV;
-       }
-#ifdef SERIAL_DEBUG_OTHER
-       printk("cy_open %s\n", tty->name);      /* */
-#endif
-       if (serial_paranoia_check(info, tty->name, "cy_open")) {
-               return -ENODEV;
-       }
-#ifdef SERIAL_DEBUG_OPEN
-       printk("cy_open %s, count = %d\n", tty->name, info->count);
-       /**/
-#endif
-           info->count++;
-#ifdef SERIAL_DEBUG_COUNT
-       printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count);
-#endif
-       tty->driver_data = info;
-       info->tty = tty;
-
-       /*
-        * 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("cy_open returning after block_til_ready with %d\n",
-                      retval);
-#endif
-               return retval;
-       }
-#ifdef SERIAL_DEBUG_OPEN
-       printk("cy_open done\n");
-       /**/
-#endif
-           return 0;
-}                              /* cy_open */
-
-/*
- * ---------------------------------------------------------------------
- * serial167_init() and friends
- *
- * serial167_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_version(void)
-{
-       printk("MVME166/167 cd2401 driver\n");
-}                              /* show_version */
-
-/* initialize chips on card -- return number of valid
-   chips (which is number of ports/4) */
-
-/*
- * This initialises the hardware to a reasonable state.  It should
- * probe the chip first so as to copy 166-Bug setup as a default for
- * port 0.  It initialises CMR to CyASYNC; that is never done again, so
- * as to limit the number of CyINIT_CHAN commands in normal running.
- *
- * ... I wonder what I should do if this fails ...
- */
-
-void mvme167_serial_console_setup(int cflag)
-{
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       int ch;
-       u_char spd;
-       u_char rcor, rbpr, badspeed = 0;
-       unsigned long flags;
-
-       local_irq_save(flags);
-
-       /*
-        * First probe channel zero of the chip, to see what speed has
-        * been selected.
-        */
-
-       base_addr[CyCAR] = 0;
-
-       rcor = base_addr[CyRCOR] << 5;
-       rbpr = base_addr[CyRBPR];
-
-       for (spd = 0; spd < sizeof(baud_bpr); spd++)
-               if (rbpr == baud_bpr[spd] && rcor == baud_co[spd])
-                       break;
-       if (spd >= sizeof(baud_bpr)) {
-               spd = 14;       /* 19200 */
-               badspeed = 1;   /* Failed to identify speed */
-       }
-       initial_console_speed = spd;
-
-       /* OK, we have chosen a speed, now reset and reinitialise */
-
-       my_udelay(20000L);      /* Allow time for any active o/p to complete */
-       if (base_addr[CyCCR] != 0x00) {
-               local_irq_restore(flags);
-               /* printk(" chip is never idle (CCR != 0)\n"); */
-               return;
-       }
-
-       base_addr[CyCCR] = CyCHIP_RESET;        /* Reset the chip */
-       my_udelay(1000L);
-
-       if (base_addr[CyGFRCR] == 0x00) {
-               local_irq_restore(flags);
-               /* printk(" chip is not responding (GFRCR stayed 0)\n"); */
-               return;
-       }
-
-       /*
-        * System clock is 20Mhz, divided by 2048, so divide by 10 for a 1.0ms
-        * tick
-        */
-
-       base_addr[CyTPR] = 10;
-
-       base_addr[CyPILR1] = 0x01;      /* Interrupt level for modem change */
-       base_addr[CyPILR2] = 0x02;      /* Interrupt level for tx ints */
-       base_addr[CyPILR3] = 0x03;      /* Interrupt level for rx ints */
-
-       /*
-        * Attempt to set up all channels to something reasonable, and
-        * bang out a INIT_CHAN command.  We should then be able to limit
-        * the amount of fiddling we have to do in normal running.
-        */
-
-       for (ch = 3; ch >= 0; ch--) {
-               base_addr[CyCAR] = (u_char) ch;
-               base_addr[CyIER] = 0;
-               base_addr[CyCMR] = CyASYNC;
-               base_addr[CyLICR] = (u_char) ch << 2;
-               base_addr[CyLIVR] = 0x5c;
-               base_addr[CyTCOR] = baud_co[spd];
-               base_addr[CyTBPR] = baud_bpr[spd];
-               base_addr[CyRCOR] = baud_co[spd] >> 5;
-               base_addr[CyRBPR] = baud_bpr[spd];
-               base_addr[CySCHR1] = 'Q' & 0x1f;
-               base_addr[CySCHR2] = 'X' & 0x1f;
-               base_addr[CySCRL] = 0;
-               base_addr[CySCRH] = 0;
-               base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE;
-               base_addr[CyCOR2] = 0;
-               base_addr[CyCOR3] = Cy_1_STOP;
-               base_addr[CyCOR4] = baud_cor4[spd];
-               base_addr[CyCOR5] = 0;
-               base_addr[CyCOR6] = 0;
-               base_addr[CyCOR7] = 0;
-               base_addr[CyRTPRL] = 2;
-               base_addr[CyRTPRH] = 0;
-               base_addr[CyMSVR1] = 0;
-               base_addr[CyMSVR2] = 0;
-               write_cy_cmd(base_addr, CyINIT_CHAN | CyDIS_RCVR | CyDIS_XMTR);
-       }
-
-       /*
-        * Now do specials for channel zero....
-        */
-
-       base_addr[CyMSVR1] = CyRTS;
-       base_addr[CyMSVR2] = CyDTR;
-       base_addr[CyIER] = CyRxData;
-       write_cy_cmd(base_addr, CyENB_RCVR | CyENB_XMTR);
-
-       local_irq_restore(flags);
-
-       my_udelay(20000L);      /* Let it all settle down */
-
-       printk("CD2401 initialised,  chip is rev 0x%02x\n", base_addr[CyGFRCR]);
-       if (badspeed)
-               printk
-                   ("  WARNING:  Failed to identify line speed, rcor=%02x,rbpr=%02x\n",
-                    rcor >> 5, rbpr);
-}                              /* serial_console_init */
-
-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,
-       .tiocmget = cy_tiocmget,
-       .tiocmset = cy_tiocmset,
-};
-
-/* 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 64 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 int __init serial167_init(void)
-{
-       struct cyclades_port *info;
-       int ret = 0;
-       int good_ports = 0;
-       int port_num = 0;
-       int index;
-       int DefSpeed;
-#ifdef notyet
-       struct sigaction sa;
-#endif
-
-       if (!(mvme16x_config & MVME16x_CONFIG_GOT_CD2401))
-               return 0;
-
-       cy_serial_driver = alloc_tty_driver(NR_PORTS);
-       if (!cy_serial_driver)
-               return -ENOMEM;
-
-#if 0
-       scrn[1] = '\0';
-#endif
-
-       show_version();
-
-       /* Has "console=0,9600n8" been used in bootinfo to change speed? */
-       if (serial_console_cflag)
-               DefSpeed = serial_console_cflag & 0017;
-       else {
-               DefSpeed = initial_console_speed;
-               serial_console_info = &cy_port[0];
-               serial_console_cflag = DefSpeed | CS8;
-#if 0
-               serial_console = 64;    /*callout_driver.minor_start */
-#endif
-       }
-
-       /* Initialize the tty_driver structure */
-
-       cy_serial_driver->owner = THIS_MODULE;
-       cy_serial_driver->name = "ttyS";
-       cy_serial_driver->major = TTY_MAJOR;
-       cy_serial_driver->minor_start = 64;
-       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_set_operations(cy_serial_driver, &cy_ops);
-
-       ret = tty_register_driver(cy_serial_driver);
-       if (ret) {
-               printk(KERN_ERR "Couldn't register MVME166/7 serial driver\n");
-               put_tty_driver(cy_serial_driver);
-               return ret;
-       }
-
-       port_num = 0;
-       info = cy_port;
-       for (index = 0; index < 1; index++) {
-
-               good_ports = 4;
-
-               if (port_num < NR_PORTS) {
-                       while (good_ports-- && port_num < NR_PORTS) {
-               /*** initialize port ***/
-                               info->magic = CYCLADES_MAGIC;
-                               info->type = PORT_CIRRUS;
-                               info->card = index;
-                               info->line = port_num;
-                               info->flags = STD_COM_FLAGS;
-                               info->tty = NULL;
-                               info->xmit_fifo_size = 12;
-                               info->cor1 = CyPARITY_NONE | Cy_8_BITS;
-                               info->cor2 = CyETC;
-                               info->cor3 = Cy_1_STOP;
-                               info->cor4 = 0x08;      /* _very_ small receive threshold */
-                               info->cor5 = 0;
-                               info->cor6 = 0;
-                               info->cor7 = 0;
-                               info->tbpr = baud_bpr[DefSpeed];        /* Tx BPR */
-                               info->tco = baud_co[DefSpeed];  /* Tx CO */
-                               info->rbpr = baud_bpr[DefSpeed];        /* Rx BPR */
-                               info->rco = baud_co[DefSpeed] >> 5;     /* Rx CO */
-                               info->close_delay = 0;
-                               info->x_char = 0;
-                               info->count = 0;
-#ifdef SERIAL_DEBUG_COUNT
-                               printk("cyc: %d: setting count to 0\n",
-                                      __LINE__);
-#endif
-                               info->blocked_open = 0;
-                               info->default_threshold = 0;
-                               info->default_timeout = 0;
-                               init_waitqueue_head(&info->open_wait);
-                               init_waitqueue_head(&info->close_wait);
-                               /* info->session */
-                               /* info->pgrp */
-/*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/
-                               info->read_status_mask =
-                                   CyTIMEOUT | CySPECHAR | CyBREAK | CyPARITY |
-                                   CyFRAME | CyOVERRUN;
-                               /* info->timeout */
-
-                               printk("ttyS%d ", info->line);
-                               port_num++;
-                               info++;
-                               if (!(port_num & 7)) {
-                                       printk("\n               ");
-                               }
-                       }
-               }
-               printk("\n");
-       }
-       while (port_num < NR_PORTS) {
-               info->line = -1;
-               port_num++;
-               info++;
-       }
-
-       ret = request_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt, 0,
-                         "cd2401_errors", cd2401_rxerr_interrupt);
-       if (ret) {
-               printk(KERN_ERR "Could't get cd2401_errors IRQ");
-               goto cleanup_serial_driver;
-       }
-
-       ret = request_irq(MVME167_IRQ_SER_MODEM, cd2401_modem_interrupt, 0,
-                         "cd2401_modem", cd2401_modem_interrupt);
-       if (ret) {
-               printk(KERN_ERR "Could't get cd2401_modem IRQ");
-               goto cleanup_irq_cd2401_errors;
-       }
-
-       ret = request_irq(MVME167_IRQ_SER_TX, cd2401_tx_interrupt, 0,
-                         "cd2401_txints", cd2401_tx_interrupt);
-       if (ret) {
-               printk(KERN_ERR "Could't get cd2401_txints IRQ");
-               goto cleanup_irq_cd2401_modem;
-       }
-
-       ret = request_irq(MVME167_IRQ_SER_RX, cd2401_rx_interrupt, 0,
-                         "cd2401_rxints", cd2401_rx_interrupt);
-       if (ret) {
-               printk(KERN_ERR "Could't get cd2401_rxints IRQ");
-               goto cleanup_irq_cd2401_txints;
-       }
-
-       /* Now we have registered the interrupt handlers, allow the interrupts */
-
-       pcc2chip[PccSCCMICR] = 0x15;    /* Serial ints are level 5 */
-       pcc2chip[PccSCCTICR] = 0x15;
-       pcc2chip[PccSCCRICR] = 0x15;
-
-       pcc2chip[PccIMLR] = 3;  /* Allow PCC2 ints above 3!? */
-
-       return 0;
-cleanup_irq_cd2401_txints:
-       free_irq(MVME167_IRQ_SER_TX, cd2401_tx_interrupt);
-cleanup_irq_cd2401_modem:
-       free_irq(MVME167_IRQ_SER_MODEM, cd2401_modem_interrupt);
-cleanup_irq_cd2401_errors:
-       free_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt);
-cleanup_serial_driver:
-       if (tty_unregister_driver(cy_serial_driver))
-               printk(KERN_ERR
-                      "Couldn't unregister MVME166/7 serial driver\n");
-       put_tty_driver(cy_serial_driver);
-       return ret;
-}                              /* serial167_init */
-
-module_init(serial167_init);
-
-#ifdef CYCLOM_SHOW_STATUS
-static void show_status(int line_num)
-{
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       int channel;
-       struct cyclades_port *info;
-       unsigned long flags;
-
-       info = &cy_port[line_num];
-       channel = info->line;
-       printk("  channel %d\n", channel);
-       /**/ printk(" cy_port\n");
-       printk("  card line flags = %d %d %x\n",
-              info->card, info->line, info->flags);
-       printk
-           ("  *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n",
-            (long)info->tty, info->read_status_mask, info->timeout,
-            info->xmit_fifo_size);
-       printk("  cor1,cor2,cor3,cor4,cor5,cor6,cor7 = %x %x %x %x %x %x %x\n",
-              info->cor1, info->cor2, info->cor3, info->cor4, info->cor5,
-              info->cor6, info->cor7);
-       printk("  tbpr,tco,rbpr,rco = %d %d %d %d\n", info->tbpr, info->tco,
-              info->rbpr, info->rco);
-       printk("  close_delay event count = %d %d %d\n", info->close_delay,
-              info->event, info->count);
-       printk("  x_char blocked_open = %x %x\n", info->x_char,
-              info->blocked_open);
-       printk("  open_wait = %lx %lx %lx\n", (long)info->open_wait);
-
-       local_irq_save(flags);
-
-/* Global Registers */
-
-       printk(" CyGFRCR %x\n", base_addr[CyGFRCR]);
-       printk(" CyCAR %x\n", base_addr[CyCAR]);
-       printk(" CyRISR %x\n", base_addr[CyRISR]);
-       printk(" CyTISR %x\n", base_addr[CyTISR]);
-       printk(" CyMISR %x\n", base_addr[CyMISR]);
-       printk(" CyRIR %x\n", base_addr[CyRIR]);
-       printk(" CyTIR %x\n", base_addr[CyTIR]);
-       printk(" CyMIR %x\n", base_addr[CyMIR]);
-       printk(" CyTPR %x\n", base_addr[CyTPR]);
-
-       base_addr[CyCAR] = (u_char) channel;
-
-/* Virtual Registers */
-
-#if 0
-       printk(" CyRIVR %x\n", base_addr[CyRIVR]);
-       printk(" CyTIVR %x\n", base_addr[CyTIVR]);
-       printk(" CyMIVR %x\n", base_addr[CyMIVR]);
-       printk(" CyMISR %x\n", base_addr[CyMISR]);
-#endif
-
-/* Channel Registers */
-
-       printk(" CyCCR %x\n", base_addr[CyCCR]);
-       printk(" CyIER %x\n", base_addr[CyIER]);
-       printk(" CyCOR1 %x\n", base_addr[CyCOR1]);
-       printk(" CyCOR2 %x\n", base_addr[CyCOR2]);
-       printk(" CyCOR3 %x\n", base_addr[CyCOR3]);
-       printk(" CyCOR4 %x\n", base_addr[CyCOR4]);
-       printk(" CyCOR5 %x\n", base_addr[CyCOR5]);
-#if 0
-       printk(" CyCCSR %x\n", base_addr[CyCCSR]);
-       printk(" CyRDCR %x\n", base_addr[CyRDCR]);
-#endif
-       printk(" CySCHR1 %x\n", base_addr[CySCHR1]);
-       printk(" CySCHR2 %x\n", base_addr[CySCHR2]);
-#if 0
-       printk(" CySCHR3 %x\n", base_addr[CySCHR3]);
-       printk(" CySCHR4 %x\n", base_addr[CySCHR4]);
-       printk(" CySCRL %x\n", base_addr[CySCRL]);
-       printk(" CySCRH %x\n", base_addr[CySCRH]);
-       printk(" CyLNC %x\n", base_addr[CyLNC]);
-       printk(" CyMCOR1 %x\n", base_addr[CyMCOR1]);
-       printk(" CyMCOR2 %x\n", base_addr[CyMCOR2]);
-#endif
-       printk(" CyRTPRL %x\n", base_addr[CyRTPRL]);
-       printk(" CyRTPRH %x\n", base_addr[CyRTPRH]);
-       printk(" CyMSVR1 %x\n", base_addr[CyMSVR1]);
-       printk(" CyMSVR2 %x\n", base_addr[CyMSVR2]);
-       printk(" CyRBPR %x\n", base_addr[CyRBPR]);
-       printk(" CyRCOR %x\n", base_addr[CyRCOR]);
-       printk(" CyTBPR %x\n", base_addr[CyTBPR]);
-       printk(" CyTCOR %x\n", base_addr[CyTCOR]);
-
-       local_irq_restore(flags);
-}                              /* show_status */
-#endif
-
-#if 0
-/* Dummy routine in mvme16x/config.c for now */
-
-/* Serial console setup. Called from linux/init/main.c */
-
-void console_setup(char *str, int *ints)
-{
-       char *s;
-       int baud, bits, parity;
-       int cflag = 0;
-
-       /* Sanity check. */
-       if (ints[0] > 3 || ints[1] > 3)
-               return;
-
-       /* Get baud, bits and parity */
-       baud = 2400;
-       bits = 8;
-       parity = 'n';
-       if (ints[2])
-               baud = ints[2];
-       if ((s = strchr(str, ','))) {
-               do {
-                       s++;
-               } while (*s >= '0' && *s <= '9');
-               if (*s)
-                       parity = *s++;
-               if (*s)
-                       bits = *s - '0';
-       }
-
-       /* Now construct a cflag setting. */
-       switch (baud) {
-       case 1200:
-               cflag |= B1200;
-               break;
-       case 9600:
-               cflag |= B9600;
-               break;
-       case 19200:
-               cflag |= B19200;
-               break;
-       case 38400:
-               cflag |= B38400;
-               break;
-       case 2400:
-       default:
-               cflag |= B2400;
-               break;
-       }
-       switch (bits) {
-       case 7:
-               cflag |= CS7;
-               break;
-       default:
-       case 8:
-               cflag |= CS8;
-               break;
-       }
-       switch (parity) {
-       case 'o':
-       case 'O':
-               cflag |= PARODD;
-               break;
-       case 'e':
-       case 'E':
-               cflag |= PARENB;
-               break;
-       }
-
-       serial_console_info = &cy_port[ints[1]];
-       serial_console_cflag = cflag;
-       serial_console = ints[1] + 64;  /*callout_driver.minor_start */
-}
-#endif
-
-/*
- * The following is probably out of date for 2.1.x serial console stuff.
- *
- * The console is registered early on from arch/m68k/kernel/setup.c, and
- * it therefore relies on the chip being setup correctly by 166-Bug.  This
- * seems reasonable, as the serial port has been used to invoke the system
- * boot.  It also means that this function must not rely on any data
- * initialisation performed by serial167_init() etc.
- *
- * Of course, once the console has been registered, we had better ensure
- * that serial167_init() doesn't leave the chip non-functional.
- *
- * The console must be locked when we get here.
- */
-
-void serial167_console_write(struct console *co, const char *str,
-                            unsigned count)
-{
-       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
-       unsigned long flags;
-       volatile u_char sink;
-       u_char ier;
-       int port;
-       u_char do_lf = 0;
-       int i = 0;
-
-       local_irq_save(flags);
-
-       /* Ensure transmitter is enabled! */
-
-       port = 0;
-       base_addr[CyCAR] = (u_char) port;
-       while (base_addr[CyCCR])
-               ;
-       base_addr[CyCCR] = CyENB_XMTR;
-
-       ier = base_addr[CyIER];
-       base_addr[CyIER] = CyTxMpty;
-
-       while (1) {
-               if (pcc2chip[PccSCCTICR] & 0x20) {
-                       /* We have a Tx int. Acknowledge it */
-                       sink = pcc2chip[PccTPIACKR];
-                       if ((base_addr[CyLICR] >> 2) == port) {
-                               if (i == count) {
-                                       /* Last char of string is now output */
-                                       base_addr[CyTEOIR] = CyNOTRANS;
-                                       break;
-                               }
-                               if (do_lf) {
-                                       base_addr[CyTDR] = '\n';
-                                       str++;
-                                       i++;
-                                       do_lf = 0;
-                               } else if (*str == '\n') {
-                                       base_addr[CyTDR] = '\r';
-                                       do_lf = 1;
-                               } else {
-                                       base_addr[CyTDR] = *str++;
-                                       i++;
-                               }
-                               base_addr[CyTEOIR] = 0;
-                       } else
-                               base_addr[CyTEOIR] = CyNOTRANS;
-               }
-       }
-
-       base_addr[CyIER] = ier;
-
-       local_irq_restore(flags);
-}
-
-static struct tty_driver *serial167_console_device(struct console *c,
-                                                  int *index)
-{
-       *index = c->index;
-       return cy_serial_driver;
-}
-
-static struct console sercons = {
-       .name = "ttyS",
-       .write = serial167_console_write,
-       .device = serial167_console_device,
-       .flags = CON_PRINTBUFFER,
-       .index = -1,
-};
-
-static int __init serial167_console_init(void)
-{
-       if (vme_brdtype == VME_TYPE_MVME166 ||
-           vme_brdtype == VME_TYPE_MVME167 ||
-           vme_brdtype == VME_TYPE_MVME177) {
-               mvme167_serial_console_setup(0);
-               register_console(&sercons);
-       }
-       return 0;
-}
-
-console_initcall(serial167_console_init);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c
deleted file mode 100644 (file)
index 47e5753..0000000
+++ /dev/null
@@ -1,2368 +0,0 @@
-/*
- *      specialix.c  -- specialix IO8+ multiport serial driver.
- *
- *      Copyright (C) 1997  Roger Wolff (R.E.Wolff@BitWizard.nl)
- *      Copyright (C) 1994-1996  Dmitry Gorodchanin (pgmdsg@ibi.com)
- *
- *      Specialix pays for the development and support of this driver.
- *      Please DO contact io8-linux@specialix.co.uk if you require
- *      support. But please read the documentation (specialix.txt)
- *      first.
- *
- *      This driver was developped in the BitWizard linux device
- *      driver service. If you require a linux device driver for your
- *      product, please contact devices@BitWizard.nl for a quote.
- *
- *      This code is firmly based on the riscom/8 serial driver,
- *      written by Dmitry Gorodchanin. The specialix IO8+ card
- *      programming information was obtained from the CL-CD1865 Data
- *      Book, and Specialix document number 6200059: IO8+ Hardware
- *      Functional Specification.
- *
- *      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.
- *
- * Revision history:
- *
- * Revision 1.0:  April 1st 1997.
- *                Initial release for alpha testing.
- * Revision 1.1:  April 14th 1997.
- *                Incorporated Richard Hudsons suggestions,
- *                removed some debugging printk's.
- * Revision 1.2:  April 15th 1997.
- *                Ported to 2.1.x kernels.
- * Revision 1.3:  April 17th 1997
- *                Backported to 2.0. (Compatibility macros).
- * Revision 1.4:  April 18th 1997
- *                Fixed DTR/RTS bug that caused the card to indicate
- *                "don't send data" to a modem after the password prompt.
- *                Fixed bug for premature (fake) interrupts.
- * Revision 1.5:  April 19th 1997
- *                fixed a minor typo in the header file, cleanup a little.
- *                performance warnings are now MAXed at once per minute.
- * Revision 1.6:  May 23 1997
- *                Changed the specialix=... format to include interrupt.
- * Revision 1.7:  May 27 1997
- *                Made many more debug printk's a compile time option.
- * Revision 1.8:  Jul 1  1997
- *                port to linux-2.1.43 kernel.
- * Revision 1.9:  Oct 9  1998
- *                Added stuff for the IO8+/PCI version.
- * Revision 1.10: Oct 22  1999 / Jan 21 2000.
- *                Added stuff for setserial.
- *                Nicolas Mailhot (Nicolas.Mailhot@email.enst.fr)
- *
- */
-
-#define VERSION "1.11"
-
-
-/*
- * There is a bunch of documentation about the card, jumpers, config
- * settings, restrictions, cables, device names and numbers in
- * Documentation/serial/specialix.txt
- */
-
-#include <linux/module.h>
-
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/errno.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/mm.h>
-#include <linux/serial.h>
-#include <linux/fcntl.h>
-#include <linux/major.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/uaccess.h>
-#include <linux/gfp.h>
-
-#include "specialix_io8.h"
-#include "cd1865.h"
-
-
-/*
-   This driver can spew a whole lot of debugging output at you. If you
-   need maximum performance, you should disable the DEBUG define. To
-   aid in debugging in the field, I'm leaving the compile-time debug
-   features enabled, and disable them "runtime". That allows me to
-   instruct people with problems to enable debugging without requiring
-   them to recompile...
-*/
-#define DEBUG
-
-static int sx_debug;
-static int sx_rxfifo = SPECIALIX_RXFIFO;
-static int sx_rtscts;
-
-#ifdef DEBUG
-#define dprintk(f, str...) if (sx_debug & f) printk(str)
-#else
-#define dprintk(f, str...) /* nothing */
-#endif
-
-#define SX_DEBUG_FLOW    0x0001
-#define SX_DEBUG_DATA    0x0002
-#define SX_DEBUG_PROBE   0x0004
-#define SX_DEBUG_CHAN    0x0008
-#define SX_DEBUG_INIT    0x0010
-#define SX_DEBUG_RX      0x0020
-#define SX_DEBUG_TX      0x0040
-#define SX_DEBUG_IRQ     0x0080
-#define SX_DEBUG_OPEN    0x0100
-#define SX_DEBUG_TERMIOS 0x0200
-#define SX_DEBUG_SIGNALS 0x0400
-#define SX_DEBUG_FIFO    0x0800
-
-
-#define func_enter() dprintk(SX_DEBUG_FLOW, "io8: enter %s\n", __func__)
-#define func_exit()  dprintk(SX_DEBUG_FLOW, "io8: exit  %s\n", __func__)
-
-
-/* Configurable options: */
-
-/* Am I paranoid or not ? ;-) */
-#define SPECIALIX_PARANOIA_CHECK
-
-/*
- * The following defines are mostly for testing purposes. But if you need
- * some nice reporting in your syslog, you can define them also.
- */
-#undef SX_REPORT_FIFO
-#undef SX_REPORT_OVERRUN
-
-
-
-
-#define SPECIALIX_LEGAL_FLAGS \
-       (ASYNC_HUP_NOTIFY   | ASYNC_SAK          | ASYNC_SPLIT_TERMIOS   | \
-        ASYNC_SPD_HI       | ASYNC_SPEED_VHI    | ASYNC_SESSION_LOCKOUT | \
-        ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
-
-static struct tty_driver *specialix_driver;
-
-static struct specialix_board sx_board[SX_NBOARD] =  {
-       { 0, SX_IOBASE1,  9, },
-       { 0, SX_IOBASE2, 11, },
-       { 0, SX_IOBASE3, 12, },
-       { 0, SX_IOBASE4, 15, },
-};
-
-static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
-
-
-static int sx_paranoia_check(struct specialix_port const *port,
-                                   char *name, const char *routine)
-{
-#ifdef SPECIALIX_PARANOIA_CHECK
-       static const char *badmagic = KERN_ERR
-         "sx: Warning: bad specialix port magic number for device %s in %s\n";
-       static const char *badinfo = KERN_ERR
-         "sx: Warning: null specialix port for device %s in %s\n";
-
-       if (!port) {
-               printk(badinfo, name, routine);
-               return 1;
-       }
-       if (port->magic != SPECIALIX_MAGIC) {
-               printk(badmagic, name, routine);
-               return 1;
-       }
-#endif
-       return 0;
-}
-
-
-/*
- *
- *  Service functions for specialix IO8+ driver.
- *
- */
-
-/* Get board number from pointer */
-static inline int board_No(struct specialix_board *bp)
-{
-       return bp - sx_board;
-}
-
-
-/* Get port number from pointer */
-static inline int port_No(struct specialix_port const *port)
-{
-       return SX_PORT(port - sx_port);
-}
-
-
-/* Get pointer to board from pointer to port */
-static inline struct specialix_board *port_Board(
-                                       struct specialix_port const *port)
-{
-       return &sx_board[SX_BOARD(port - sx_port)];
-}
-
-
-/* Input Byte from CL CD186x register */
-static inline unsigned char sx_in(struct specialix_board *bp,
-                                                       unsigned short reg)
-{
-       bp->reg = reg | 0x80;
-       outb(reg | 0x80, bp->base + SX_ADDR_REG);
-       return inb(bp->base + SX_DATA_REG);
-}
-
-
-/* Output Byte to CL CD186x register */
-static inline void sx_out(struct specialix_board *bp, unsigned short reg,
-                         unsigned char val)
-{
-       bp->reg = reg | 0x80;
-       outb(reg | 0x80, bp->base + SX_ADDR_REG);
-       outb(val, bp->base + SX_DATA_REG);
-}
-
-
-/* Input Byte from CL CD186x register */
-static inline unsigned char sx_in_off(struct specialix_board *bp,
-                               unsigned short reg)
-{
-       bp->reg = reg;
-       outb(reg, bp->base + SX_ADDR_REG);
-       return inb(bp->base + SX_DATA_REG);
-}
-
-
-/* Output Byte to CL CD186x register */
-static inline void sx_out_off(struct specialix_board  *bp,
-                               unsigned short reg, unsigned char val)
-{
-       bp->reg = reg;
-       outb(reg, bp->base + SX_ADDR_REG);
-       outb(val, bp->base + SX_DATA_REG);
-}
-
-
-/* Wait for Channel Command Register ready */
-static void sx_wait_CCR(struct specialix_board  *bp)
-{
-       unsigned long delay, flags;
-       unsigned char ccr;
-
-       for (delay = SX_CCR_TIMEOUT; delay; delay--) {
-               spin_lock_irqsave(&bp->lock, flags);
-               ccr = sx_in(bp, CD186x_CCR);
-               spin_unlock_irqrestore(&bp->lock, flags);
-               if (!ccr)
-                       return;
-               udelay(1);
-       }
-
-       printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
-}
-
-
-/* Wait for Channel Command Register ready */
-static void sx_wait_CCR_off(struct specialix_board  *bp)
-{
-       unsigned long delay;
-       unsigned char crr;
-       unsigned long flags;
-
-       for (delay = SX_CCR_TIMEOUT; delay; delay--) {
-               spin_lock_irqsave(&bp->lock, flags);
-               crr = sx_in_off(bp, CD186x_CCR);
-               spin_unlock_irqrestore(&bp->lock, flags);
-               if (!crr)
-                       return;
-               udelay(1);
-       }
-
-       printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
-}
-
-
-/*
- *  specialix IO8+ IO range functions.
- */
-
-static int sx_request_io_range(struct specialix_board *bp)
-{
-       return request_region(bp->base,
-               bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
-               "specialix IO8+") == NULL;
-}
-
-
-static void sx_release_io_range(struct specialix_board *bp)
-{
-       release_region(bp->base, bp->flags & SX_BOARD_IS_PCI ?
-                                       SX_PCI_IO_SPACE : SX_IO_SPACE);
-}
-
-
-/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
-static int sx_set_irq(struct specialix_board *bp)
-{
-       int virq;
-       int i;
-       unsigned long flags;
-
-       if (bp->flags & SX_BOARD_IS_PCI)
-               return 1;
-       switch (bp->irq) {
-       /* In the same order as in the docs... */
-       case 15:
-               virq = 0;
-               break;
-       case 12:
-               virq = 1;
-               break;
-       case 11:
-               virq = 2;
-               break;
-       case 9:
-               virq = 3;
-               break;
-       default:printk(KERN_ERR
-                           "Speclialix: cannot set irq to %d.\n", bp->irq);
-               return 0;
-       }
-       spin_lock_irqsave(&bp->lock, flags);
-       for (i = 0; i < 2; i++) {
-               sx_out(bp, CD186x_CAR, i);
-               sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
-       }
-       spin_unlock_irqrestore(&bp->lock, flags);
-       return 1;
-}
-
-
-/* Reset and setup CD186x chip */
-static int sx_init_CD186x(struct specialix_board  *bp)
-{
-       unsigned long flags;
-       int scaler;
-       int rv = 1;
-
-       func_enter();
-       sx_wait_CCR_off(bp);                       /* Wait for CCR ready        */
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out_off(bp, CD186x_CCR, CCR_HARDRESET);      /* Reset CD186x chip          */
-       spin_unlock_irqrestore(&bp->lock, flags);
-       msleep(50);                                     /* Delay 0.05 sec            */
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out_off(bp, CD186x_GIVR, SX_ID);             /* Set ID for this chip      */
-       sx_out_off(bp, CD186x_GICR, 0);                 /* Clear all bits            */
-       sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT);      /* Prio for modem intr       */
-       sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT);      /* Prio for transmitter intr */
-       sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT);      /* Prio for receiver intr    */
-       /* Set RegAckEn */
-       sx_out_off(bp, CD186x_SRCR, sx_in(bp, CD186x_SRCR) | SRCR_REGACKEN);
-
-       /* Setting up prescaler. We need 4 ticks per 1 ms */
-       scaler =  SX_OSCFREQ/SPECIALIX_TPS;
-
-       sx_out_off(bp, CD186x_PPRH, scaler >> 8);
-       sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
-       spin_unlock_irqrestore(&bp->lock, flags);
-
-       if (!sx_set_irq(bp)) {
-               /* Figure out how to pass this along... */
-               printk(KERN_ERR "Cannot set irq to %d.\n", bp->irq);
-               rv = 0;
-       }
-
-       func_exit();
-       return rv;
-}
-
-
-static int read_cross_byte(struct specialix_board *bp, int reg, int bit)
-{
-       int i;
-       int t;
-       unsigned long flags;
-
-       spin_lock_irqsave(&bp->lock, flags);
-       for (i = 0, t = 0; i < 8; i++) {
-               sx_out_off(bp, CD186x_CAR, i);
-               if (sx_in_off(bp, reg) & bit)
-                       t |= 1 << i;
-       }
-       spin_unlock_irqrestore(&bp->lock, flags);
-
-       return t;
-}
-
-
-/* Main probing routine, also sets irq. */
-static int sx_probe(struct specialix_board *bp)
-{
-       unsigned char val1, val2;
-       int rev;
-       int chip;
-
-       func_enter();
-
-       if (sx_request_io_range(bp)) {
-               func_exit();
-               return 1;
-       }
-
-       /* Are the I/O ports here ? */
-       sx_out_off(bp, CD186x_PPRL, 0x5a);
-       udelay(1);
-       val1 = sx_in_off(bp, CD186x_PPRL);
-
-       sx_out_off(bp, CD186x_PPRL, 0xa5);
-       udelay(1);
-       val2 = sx_in_off(bp, CD186x_PPRL);
-
-
-       if (val1 != 0x5a || val2 != 0xa5) {
-               printk(KERN_INFO
-                       "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
-                                               board_No(bp), bp->base);
-               sx_release_io_range(bp);
-               func_exit();
-               return 1;
-       }
-
-       /* Check the DSR lines that Specialix uses as board
-          identification */
-       val1 = read_cross_byte(bp, CD186x_MSVR, MSVR_DSR);
-       val2 = read_cross_byte(bp, CD186x_MSVR, MSVR_RTS);
-       dprintk(SX_DEBUG_INIT,
-                       "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
-                                       board_No(bp), val1, val2);
-
-       /* They managed to switch the bit order between the docs and
-          the IO8+ card. The new PCI card now conforms to old docs.
-          They changed the PCI docs to reflect the situation on the
-          old card. */
-       val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
-       if (val1 != val2) {
-               printk(KERN_INFO
-                 "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
-                      board_No(bp), val2, bp->base, val1);
-               sx_release_io_range(bp);
-               func_exit();
-               return 1;
-       }
-
-
-       /* Reset CD186x again  */
-       if (!sx_init_CD186x(bp)) {
-               sx_release_io_range(bp);
-               func_exit();
-               return 1;
-       }
-
-       sx_request_io_range(bp);
-       bp->flags |= SX_BOARD_PRESENT;
-
-       /* Chip           revcode   pkgtype
-                         GFRCR     SRCR bit 7
-          CD180 rev B    0x81      0
-          CD180 rev C    0x82      0
-          CD1864 rev A   0x82      1
-          CD1865 rev A   0x83      1  -- Do not use!!! Does not work.
-          CD1865 rev B   0x84      1
-        -- Thanks to Gwen Wang, Cirrus Logic.
-        */
-
-       switch (sx_in_off(bp, CD186x_GFRCR)) {
-       case 0x82:
-               chip = 1864;
-               rev = 'A';
-               break;
-       case 0x83:
-               chip = 1865;
-               rev = 'A';
-               break;
-       case 0x84:
-               chip = 1865;
-               rev = 'B';
-               break;
-       case 0x85:
-               chip = 1865;
-               rev = 'C';
-               break; /* Does not exist at this time */
-       default:
-               chip = -1;
-               rev = 'x';
-       }
-
-       dprintk(SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR));
-
-       printk(KERN_INFO
-    "sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
-                               board_No(bp), bp->base, bp->irq, chip, rev);
-
-       func_exit();
-       return 0;
-}
-
-/*
- *
- *  Interrupt processing routines.
- * */
-
-static struct specialix_port *sx_get_port(struct specialix_board *bp,
-                                              unsigned char const *what)
-{
-       unsigned char channel;
-       struct specialix_port *port = NULL;
-
-       channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
-       dprintk(SX_DEBUG_CHAN, "channel: %d\n", channel);
-       if (channel < CD186x_NCH) {
-               port = &sx_port[board_No(bp) * SX_NPORT + channel];
-               dprintk(SX_DEBUG_CHAN, "port: %d %p flags: 0x%lx\n",
-                       board_No(bp) * SX_NPORT + channel,  port,
-                       port->port.flags & ASYNC_INITIALIZED);
-
-               if (port->port.flags & ASYNC_INITIALIZED) {
-                       dprintk(SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
-                       func_exit();
-                       return port;
-               }
-       }
-       printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
-              board_No(bp), what, channel);
-       return NULL;
-}
-
-
-static void sx_receive_exc(struct specialix_board *bp)
-{
-       struct specialix_port *port;
-       struct tty_struct *tty;
-       unsigned char status;
-       unsigned char ch, flag;
-
-       func_enter();
-
-       port = sx_get_port(bp, "Receive");
-       if (!port) {
-               dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
-               func_exit();
-               return;
-       }
-       tty = port->port.tty;
-
-       status = sx_in(bp, CD186x_RCSR);
-
-       dprintk(SX_DEBUG_RX, "status: 0x%x\n", status);
-       if (status & RCSR_OE) {
-               port->overrun++;
-               dprintk(SX_DEBUG_FIFO,
-                       "sx%d: port %d: Overrun. Total %ld overruns.\n",
-                               board_No(bp), port_No(port), port->overrun);
-       }
-       status &= port->mark_mask;
-
-       /* This flip buffer check needs to be below the reading of the
-          status register to reset the chip's IRQ.... */
-       if (tty_buffer_request_room(tty, 1) == 0) {
-               dprintk(SX_DEBUG_FIFO,
-                   "sx%d: port %d: Working around flip buffer overflow.\n",
-                                       board_No(bp), port_No(port));
-               func_exit();
-               return;
-       }
-
-       ch = sx_in(bp, CD186x_RDR);
-       if (!status) {
-               func_exit();
-               return;
-       }
-       if (status & RCSR_TOUT) {
-               printk(KERN_INFO
-                   "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
-                                       board_No(bp), port_No(port));
-               func_exit();
-               return;
-
-       } else if (status & RCSR_BREAK) {
-               dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
-                      board_No(bp), port_No(port));
-               flag = TTY_BREAK;
-               if (port->port.flags & ASYNC_SAK)
-                       do_SAK(tty);
-
-       } else if (status & RCSR_PE)
-               flag = TTY_PARITY;
-
-       else if (status & RCSR_FE)
-               flag = TTY_FRAME;
-
-       else if (status & RCSR_OE)
-               flag = TTY_OVERRUN;
-
-       else
-               flag = TTY_NORMAL;
-
-       if (tty_insert_flip_char(tty, ch, flag))
-               tty_flip_buffer_push(tty);
-       func_exit();
-}
-
-
-static void sx_receive(struct specialix_board *bp)
-{
-       struct specialix_port *port;
-       struct tty_struct *tty;
-       unsigned char count;
-
-       func_enter();
-
-       port = sx_get_port(bp, "Receive");
-       if (port == NULL) {
-               dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
-               func_exit();
-               return;
-       }
-       tty = port->port.tty;
-
-       count = sx_in(bp, CD186x_RDCR);
-       dprintk(SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
-       port->hits[count > 8 ? 9 : count]++;
-
-       while (count--)
-               tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
-       tty_flip_buffer_push(tty);
-       func_exit();
-}
-
-
-static void sx_transmit(struct specialix_board *bp)
-{
-       struct specialix_port *port;
-       struct tty_struct *tty;
-       unsigned char count;
-
-       func_enter();
-       port = sx_get_port(bp, "Transmit");
-       if (port == NULL) {
-               func_exit();
-               return;
-       }
-       dprintk(SX_DEBUG_TX, "port: %p\n", port);
-       tty = port->port.tty;
-
-       if (port->IER & IER_TXEMPTY) {
-               /* FIFO drained */
-               sx_out(bp, CD186x_CAR, port_No(port));
-               port->IER &= ~IER_TXEMPTY;
-               sx_out(bp, CD186x_IER, port->IER);
-               func_exit();
-               return;
-       }
-
-       if ((port->xmit_cnt <= 0 && !port->break_length)
-           || tty->stopped || tty->hw_stopped) {
-               sx_out(bp, CD186x_CAR, port_No(port));
-               port->IER &= ~IER_TXRDY;
-               sx_out(bp, CD186x_IER, port->IER);
-               func_exit();
-               return;
-       }
-
-       if (port->break_length) {
-               if (port->break_length > 0) {
-                       if (port->COR2 & COR2_ETC) {
-                               sx_out(bp, CD186x_TDR, CD186x_C_ESC);
-                               sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
-                               port->COR2 &= ~COR2_ETC;
-                       }
-                       count = min_t(int, port->break_length, 0xff);
-                       sx_out(bp, CD186x_TDR, CD186x_C_ESC);
-                       sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
-                       sx_out(bp, CD186x_TDR, count);
-                       port->break_length -= count;
-                       if (port->break_length == 0)
-                               port->break_length--;
-               } else {
-                       sx_out(bp, CD186x_TDR, CD186x_C_ESC);
-                       sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
-                       sx_out(bp, CD186x_COR2, port->COR2);
-                       sx_wait_CCR(bp);
-                       sx_out(bp, CD186x_CCR, CCR_CORCHG2);
-                       port->break_length = 0;
-               }
-
-               func_exit();
-               return;
-       }
-
-       count = CD186x_NFIFO;
-       do {
-               sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
-               port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
-               if (--port->xmit_cnt <= 0)
-                       break;
-       } while (--count > 0);
-
-       if (port->xmit_cnt <= 0) {
-               sx_out(bp, CD186x_CAR, port_No(port));
-               port->IER &= ~IER_TXRDY;
-               sx_out(bp, CD186x_IER, port->IER);
-       }
-       if (port->xmit_cnt <= port->wakeup_chars)
-               tty_wakeup(tty);
-
-       func_exit();
-}
-
-
-static void sx_check_modem(struct specialix_board *bp)
-{
-       struct specialix_port *port;
-       struct tty_struct *tty;
-       unsigned char mcr;
-       int msvr_cd;
-
-       dprintk(SX_DEBUG_SIGNALS, "Modem intr. ");
-       port = sx_get_port(bp, "Modem");
-       if (port == NULL)
-               return;
-
-       tty = port->port.tty;
-
-       mcr = sx_in(bp, CD186x_MCR);
-
-       if ((mcr & MCR_CDCHG)) {
-               dprintk(SX_DEBUG_SIGNALS, "CD just changed... ");
-               msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
-               if (msvr_cd) {
-                       dprintk(SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
-                       wake_up_interruptible(&port->port.open_wait);
-               } else {
-                       dprintk(SX_DEBUG_SIGNALS, "Sending HUP.\n");
-                       tty_hangup(tty);
-               }
-       }
-
-#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
-       if (mcr & MCR_CTSCHG) {
-               if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
-                       tty->hw_stopped = 0;
-                       port->IER |= IER_TXRDY;
-                       if (port->xmit_cnt <= port->wakeup_chars)
-                               tty_wakeup(tty);
-               } else {
-                       tty->hw_stopped = 1;
-                       port->IER &= ~IER_TXRDY;
-               }
-               sx_out(bp, CD186x_IER, port->IER);
-       }
-       if (mcr & MCR_DSSXHG) {
-               if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
-                       tty->hw_stopped = 0;
-                       port->IER |= IER_TXRDY;
-                       if (port->xmit_cnt <= port->wakeup_chars)
-                               tty_wakeup(tty);
-               } else {
-                       tty->hw_stopped = 1;
-                       port->IER &= ~IER_TXRDY;
-               }
-               sx_out(bp, CD186x_IER, port->IER);
-       }
-#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
-
-       /* Clear change bits */
-       sx_out(bp, CD186x_MCR, 0);
-}
-
-
-/* The main interrupt processing routine */
-static irqreturn_t sx_interrupt(int dummy, void *dev_id)
-{
-       unsigned char status;
-       unsigned char ack;
-       struct specialix_board *bp = dev_id;
-       unsigned long loop = 0;
-       int saved_reg;
-       unsigned long flags;
-
-       func_enter();
-
-       spin_lock_irqsave(&bp->lock, flags);
-
-       dprintk(SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __func__,
-               port_No(sx_get_port(bp, "INT")),
-               SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1);
-       if (!(bp->flags & SX_BOARD_ACTIVE)) {
-               dprintk(SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n",
-                                                               bp->irq);
-               spin_unlock_irqrestore(&bp->lock, flags);
-               func_exit();
-               return IRQ_NONE;
-       }
-
-       saved_reg = bp->reg;
-
-       while (++loop < 16) {
-               status = sx_in(bp, CD186x_SRSR) &
-                               (SRSR_RREQint | SRSR_TREQint | SRSR_MREQint);
-               if (status == 0)
-                       break;
-               if (status & SRSR_RREQint) {
-                       ack = sx_in(bp, CD186x_RRAR);
-
-                       if (ack == (SX_ID | GIVR_IT_RCV))
-                               sx_receive(bp);
-                       else if (ack == (SX_ID | GIVR_IT_REXC))
-                               sx_receive_exc(bp);
-                       else
-                               printk(KERN_ERR
-                               "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
-                                               board_No(bp), status, ack);
-
-               } else if (status & SRSR_TREQint) {
-                       ack = sx_in(bp, CD186x_TRAR);
-
-                       if (ack == (SX_ID | GIVR_IT_TX))
-                               sx_transmit(bp);
-                       else
-                               printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
-                                       board_No(bp), status, ack,
-                                       port_No(sx_get_port(bp, "Int")));
-               } else if (status & SRSR_MREQint) {
-                       ack = sx_in(bp, CD186x_MRAR);
-
-                       if (ack == (SX_ID | GIVR_IT_MODEM))
-                               sx_check_modem(bp);
-                       else
-                               printk(KERN_ERR
-                                 "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
-                                      board_No(bp), status, ack);
-
-               }
-
-               sx_out(bp, CD186x_EOIR, 0);   /* Mark end of interrupt */
-       }
-       bp->reg = saved_reg;
-       outb(bp->reg, bp->base + SX_ADDR_REG);
-       spin_unlock_irqrestore(&bp->lock, flags);
-       func_exit();
-       return IRQ_HANDLED;
-}
-
-
-/*
- *  Routines for open & close processing.
- */
-
-static void turn_ints_off(struct specialix_board *bp)
-{
-       unsigned long flags;
-
-       func_enter();
-       spin_lock_irqsave(&bp->lock, flags);
-       (void) sx_in_off(bp, 0); /* Turn off interrupts. */
-       spin_unlock_irqrestore(&bp->lock, flags);
-
-       func_exit();
-}
-
-static void turn_ints_on(struct specialix_board *bp)
-{
-       unsigned long flags;
-
-       func_enter();
-
-       spin_lock_irqsave(&bp->lock, flags);
-       (void) sx_in(bp, 0); /* Turn ON interrupts. */
-       spin_unlock_irqrestore(&bp->lock, flags);
-
-       func_exit();
-}
-
-
-/* Called with disabled interrupts */
-static int sx_setup_board(struct specialix_board *bp)
-{
-       int error;
-
-       if (bp->flags & SX_BOARD_ACTIVE)
-               return 0;
-
-       if (bp->flags & SX_BOARD_IS_PCI)
-               error = request_irq(bp->irq, sx_interrupt,
-                       IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
-       else
-               error = request_irq(bp->irq, sx_interrupt,
-                       IRQF_DISABLED, "specialix IO8+", bp);
-
-       if (error)
-               return error;
-
-       turn_ints_on(bp);
-       bp->flags |= SX_BOARD_ACTIVE;
-
-       return 0;
-}
-
-
-/* Called with disabled interrupts */
-static void sx_shutdown_board(struct specialix_board *bp)
-{
-       func_enter();
-
-       if (!(bp->flags & SX_BOARD_ACTIVE)) {
-               func_exit();
-               return;
-       }
-
-       bp->flags &= ~SX_BOARD_ACTIVE;
-
-       dprintk(SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
-                bp->irq, board_No(bp));
-       free_irq(bp->irq, bp);
-       turn_ints_off(bp);
-       func_exit();
-}
-
-static unsigned int sx_crtscts(struct tty_struct *tty)
-{
-       if (sx_rtscts)
-               return C_CRTSCTS(tty);
-       return 1;
-}
-
-/*
- * Setting up port characteristics.
- * Must be called with disabled interrupts
- */
-static void sx_change_speed(struct specialix_board *bp,
-                                               struct specialix_port *port)
-{
-       struct tty_struct *tty;
-       unsigned long baud;
-       long tmp;
-       unsigned char cor1 = 0, cor3 = 0;
-       unsigned char mcor1 = 0, mcor2 = 0;
-       static unsigned long again;
-       unsigned long flags;
-
-       func_enter();
-
-       tty = port->port.tty;
-       if (!tty || !tty->termios) {
-               func_exit();
-               return;
-       }
-
-       port->IER  = 0;
-       port->COR2 = 0;
-       /* Select port on the board */
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out(bp, CD186x_CAR, port_No(port));
-
-       /* The Specialix board doens't implement the RTS lines.
-          They are used to set the IRQ level. Don't touch them. */
-       if (sx_crtscts(tty))
-               port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
-       else
-               port->MSVR =  (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
-       spin_unlock_irqrestore(&bp->lock, flags);
-       dprintk(SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
-       baud = tty_get_baud_rate(tty);
-
-       if (baud == 38400) {
-               if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                       baud = 57600;
-               if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                       baud = 115200;
-       }
-
-       if (!baud) {
-               /* Drop DTR & exit */
-               dprintk(SX_DEBUG_TERMIOS, "Dropping DTR...  Hmm....\n");
-               if (!sx_crtscts(tty)) {
-                       port->MSVR &= ~MSVR_DTR;
-                       spin_lock_irqsave(&bp->lock, flags);
-                       sx_out(bp, CD186x_MSVR, port->MSVR);
-                       spin_unlock_irqrestore(&bp->lock, flags);
-               } else
-                       dprintk(SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
-               return;
-       } else {
-               /* Set DTR on */
-               if (!sx_crtscts(tty))
-                       port->MSVR |= MSVR_DTR;
-       }
-
-       /*
-        * Now we must calculate some speed depended things
-        */
-
-       /* Set baud rate for port */
-       tmp = port->custom_divisor ;
-       if (tmp)
-               printk(KERN_INFO
-                       "sx%d: Using custom baud rate divisor %ld. \n"
-                       "This is an untested option, please be careful.\n",
-                                                       port_No(port), tmp);
-       else
-               tmp = (((SX_OSCFREQ + baud/2) / baud + CD186x_TPC/2) /
-                                                               CD186x_TPC);
-
-       if (tmp < 0x10 && time_before(again, jiffies)) {
-               again = jiffies + HZ * 60;
-               /* Page 48 of version 2.0 of the CL-CD1865 databook */
-               if (tmp >= 12) {
-                       printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
-                               "Performance degradation is possible.\n"
-                               "Read specialix.txt for more info.\n",
-                                               port_No(port), tmp);
-               } else {
-                       printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
-               "Warning: overstressing Cirrus chip. This might not work.\n"
-               "Read specialix.txt for more info.\n", port_No(port), tmp);
-               }
-       }
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
-       sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
-       sx_out(bp, CD186x_RBPRL, tmp & 0xff);
-       sx_out(bp, CD186x_TBPRL, tmp & 0xff);
-       spin_unlock_irqrestore(&bp->lock, flags);
-       if (port->custom_divisor)
-               baud = (SX_OSCFREQ + port->custom_divisor/2) /
-                                                       port->custom_divisor;
-       baud = (baud + 5) / 10;         /* Estimated CPS */
-
-       /* Two timer ticks seems enough to wakeup something like SLIP driver */
-       tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
-       port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
-                                             SERIAL_XMIT_SIZE - 1 : tmp);
-
-       /* Receiver timeout will be transmission time for 1.5 chars */
-       tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
-       tmp = (tmp > 0xff) ? 0xff : tmp;
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out(bp, CD186x_RTPR, tmp);
-       spin_unlock_irqrestore(&bp->lock, flags);
-       switch (C_CSIZE(tty)) {
-       case CS5:
-               cor1 |= COR1_5BITS;
-               break;
-       case CS6:
-               cor1 |= COR1_6BITS;
-               break;
-       case CS7:
-               cor1 |= COR1_7BITS;
-               break;
-       case CS8:
-               cor1 |= COR1_8BITS;
-               break;
-       }
-
-       if (C_CSTOPB(tty))
-               cor1 |= COR1_2SB;
-
-       cor1 |= COR1_IGNORE;
-       if (C_PARENB(tty)) {
-               cor1 |= COR1_NORMPAR;
-               if (C_PARODD(tty))
-                       cor1 |= COR1_ODDP;
-               if (I_INPCK(tty))
-                       cor1 &= ~COR1_IGNORE;
-       }
-       /* Set marking of some errors */
-       port->mark_mask = RCSR_OE | RCSR_TOUT;
-       if (I_INPCK(tty))
-               port->mark_mask |= RCSR_FE | RCSR_PE;
-       if (I_BRKINT(tty) || I_PARMRK(tty))
-               port->mark_mask |= RCSR_BREAK;
-       if (I_IGNPAR(tty))
-               port->mark_mask &= ~(RCSR_FE | RCSR_PE);
-       if (I_IGNBRK(tty)) {
-               port->mark_mask &= ~RCSR_BREAK;
-               if (I_IGNPAR(tty))
-                       /* Real raw mode. Ignore all */
-                       port->mark_mask &= ~RCSR_OE;
-       }
-       /* Enable Hardware Flow Control */
-       if (C_CRTSCTS(tty)) {
-#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
-               port->IER |= IER_DSR | IER_CTS;
-               mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
-               mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
-               spin_lock_irqsave(&bp->lock, flags);
-               tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) &
-                                                       (MSVR_CTS|MSVR_DSR));
-               spin_unlock_irqrestore(&bp->lock, flags);
-#else
-               port->COR2 |= COR2_CTSAE;
-#endif
-       }
-       /* Enable Software Flow Control. FIXME: I'm not sure about this */
-       /* Some people reported that it works, but I still doubt it */
-       if (I_IXON(tty)) {
-               port->COR2 |= COR2_TXIBE;
-               cor3 |= (COR3_FCT | COR3_SCDE);
-               if (I_IXANY(tty))
-                       port->COR2 |= COR2_IXM;
-               spin_lock_irqsave(&bp->lock, flags);
-               sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
-               sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
-               sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
-               sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
-               spin_unlock_irqrestore(&bp->lock, flags);
-       }
-       if (!C_CLOCAL(tty)) {
-               /* Enable CD check */
-               port->IER |= IER_CD;
-               mcor1 |= MCOR1_CDZD;
-               mcor2 |= MCOR2_CDOD;
-       }
-
-       if (C_CREAD(tty))
-               /* Enable receiver */
-               port->IER |= IER_RXD;
-
-       /* Set input FIFO size (1-8 bytes) */
-       cor3 |= sx_rxfifo;
-       /* Setting up CD186x channel registers */
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out(bp, CD186x_COR1, cor1);
-       sx_out(bp, CD186x_COR2, port->COR2);
-       sx_out(bp, CD186x_COR3, cor3);
-       spin_unlock_irqrestore(&bp->lock, flags);
-       /* Make CD186x know about registers change */
-       sx_wait_CCR(bp);
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
-       /* Setting up modem option registers */
-       dprintk(SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n",
-                                                               mcor1, mcor2);
-       sx_out(bp, CD186x_MCOR1, mcor1);
-       sx_out(bp, CD186x_MCOR2, mcor2);
-       spin_unlock_irqrestore(&bp->lock, flags);
-       /* Enable CD186x transmitter & receiver */
-       sx_wait_CCR(bp);
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
-       /* Enable interrupts */
-       sx_out(bp, CD186x_IER, port->IER);
-       /* And finally set the modem lines... */
-       sx_out(bp, CD186x_MSVR, port->MSVR);
-       spin_unlock_irqrestore(&bp->lock, flags);
-
-       func_exit();
-}
-
-
-/* Must be called with interrupts enabled */
-static int sx_setup_port(struct specialix_board *bp,
-                                               struct specialix_port *port)
-{
-       unsigned long flags;
-
-       func_enter();
-
-       if (port->port.flags & ASYNC_INITIALIZED) {
-               func_exit();
-               return 0;
-       }
-
-       if (!port->xmit_buf) {
-               /* We may sleep in get_zeroed_page() */
-               unsigned long tmp;
-
-               tmp = get_zeroed_page(GFP_KERNEL);
-               if (tmp == 0L) {
-                       func_exit();
-                       return -ENOMEM;
-               }
-
-               if (port->xmit_buf) {
-                       free_page(tmp);
-                       func_exit();
-                       return -ERESTARTSYS;
-               }
-               port->xmit_buf = (unsigned char *) tmp;
-       }
-
-       spin_lock_irqsave(&port->lock, flags);
-
-       if (port->port.tty)
-               clear_bit(TTY_IO_ERROR, &port->port.tty->flags);
-
-       port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
-       sx_change_speed(bp, port);
-       port->port.flags |= ASYNC_INITIALIZED;
-
-       spin_unlock_irqrestore(&port->lock, flags);
-
-
-       func_exit();
-       return 0;
-}
-
-
-/* Must be called with interrupts disabled */
-static void sx_shutdown_port(struct specialix_board *bp,
-                                               struct specialix_port *port)
-{
-       struct tty_struct *tty;
-       int i;
-       unsigned long flags;
-
-       func_enter();
-
-       if (!(port->port.flags & ASYNC_INITIALIZED)) {
-               func_exit();
-               return;
-       }
-
-       if (sx_debug & SX_DEBUG_FIFO) {
-               dprintk(SX_DEBUG_FIFO,
-                       "sx%d: port %d: %ld overruns, FIFO hits [ ",
-                               board_No(bp), port_No(port), port->overrun);
-               for (i = 0; i < 10; i++)
-                       dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
-               dprintk(SX_DEBUG_FIFO, "].\n");
-       }
-
-       if (port->xmit_buf) {
-               free_page((unsigned long) port->xmit_buf);
-               port->xmit_buf = NULL;
-       }
-
-       /* Select port */
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out(bp, CD186x_CAR, port_No(port));
-
-       tty = port->port.tty;
-       if (tty == NULL || C_HUPCL(tty)) {
-               /* Drop DTR */
-               sx_out(bp, CD186x_MSVDTR, 0);
-       }
-       spin_unlock_irqrestore(&bp->lock, flags);
-       /* Reset port */
-       sx_wait_CCR(bp);
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
-       /* Disable all interrupts from this port */
-       port->IER = 0;
-       sx_out(bp, CD186x_IER, port->IER);
-       spin_unlock_irqrestore(&bp->lock, flags);
-       if (tty)
-               set_bit(TTY_IO_ERROR, &tty->flags);
-       port->port.flags &= ~ASYNC_INITIALIZED;
-
-       if (!bp->count)
-               sx_shutdown_board(bp);
-       func_exit();
-}
-
-
-static int block_til_ready(struct tty_struct *tty, struct file *filp,
-                                               struct specialix_port *port)
-{
-       DECLARE_WAITQUEUE(wait,  current);
-       struct specialix_board *bp = port_Board(port);
-       int    retval;
-       int    do_clocal = 0;
-       int    CD;
-       unsigned long flags;
-
-       func_enter();
-
-       /*
-        * 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) || port->port.flags & ASYNC_CLOSING) {
-               interruptible_sleep_on(&port->port.close_wait);
-               if (port->port.flags & ASYNC_HUP_NOTIFY) {
-                       func_exit();
-                       return -EAGAIN;
-               } else {
-                       func_exit();
-                       return -ERESTARTSYS;
-               }
-       }
-
-       /*
-        * 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))) {
-               port->port.flags |= ASYNC_NORMAL_ACTIVE;
-               func_exit();
-               return 0;
-       }
-
-       if (C_CLOCAL(tty))
-               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, info->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(&port->port.open_wait, &wait);
-       spin_lock_irqsave(&port->lock, flags);
-       if (!tty_hung_up_p(filp))
-               port->port.count--;
-       spin_unlock_irqrestore(&port->lock, flags);
-       port->port.blocked_open++;
-       while (1) {
-               spin_lock_irqsave(&bp->lock, flags);
-               sx_out(bp, CD186x_CAR, port_No(port));
-               CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
-               if (sx_crtscts(tty)) {
-                       /* Activate RTS */
-                       port->MSVR |= MSVR_DTR;         /* WTF? */
-                       sx_out(bp, CD186x_MSVR, port->MSVR);
-               } else {
-                       /* Activate DTR */
-                       port->MSVR |= MSVR_DTR;
-                       sx_out(bp, CD186x_MSVR, port->MSVR);
-               }
-               spin_unlock_irqrestore(&bp->lock, flags);
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (tty_hung_up_p(filp) ||
-                   !(port->port.flags & ASYNC_INITIALIZED)) {
-                       if (port->port.flags & ASYNC_HUP_NOTIFY)
-                               retval = -EAGAIN;
-                       else
-                               retval = -ERESTARTSYS;
-                       break;
-               }
-               if (!(port->port.flags & ASYNC_CLOSING) &&
-                   (do_clocal || CD))
-                       break;
-               if (signal_pending(current)) {
-                       retval = -ERESTARTSYS;
-                       break;
-               }
-               tty_unlock();
-               schedule();
-               tty_lock();
-       }
-
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&port->port.open_wait, &wait);
-       spin_lock_irqsave(&port->lock, flags);
-       if (!tty_hung_up_p(filp))
-               port->port.count++;
-       port->port.blocked_open--;
-       spin_unlock_irqrestore(&port->lock, flags);
-       if (retval) {
-               func_exit();
-               return retval;
-       }
-
-       port->port.flags |= ASYNC_NORMAL_ACTIVE;
-       func_exit();
-       return 0;
-}
-
-
-static int sx_open(struct tty_struct *tty, struct file *filp)
-{
-       int board;
-       int error;
-       struct specialix_port *port;
-       struct specialix_board *bp;
-       int i;
-       unsigned long flags;
-
-       func_enter();
-
-       board = SX_BOARD(tty->index);
-
-       if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
-               func_exit();
-               return -ENODEV;
-       }
-
-       bp = &sx_board[board];
-       port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
-       port->overrun = 0;
-       for (i = 0; i < 10; i++)
-               port->hits[i] = 0;
-
-       dprintk(SX_DEBUG_OPEN,
-                       "Board = %d, bp = %p, port = %p, portno = %d.\n",
-                                board, bp, port, SX_PORT(tty->index));
-
-       if (sx_paranoia_check(port, tty->name, "sx_open")) {
-               func_enter();
-               return -ENODEV;
-       }
-
-       error = sx_setup_board(bp);
-       if (error) {
-               func_exit();
-               return error;
-       }
-
-       spin_lock_irqsave(&bp->lock, flags);
-       port->port.count++;
-       bp->count++;
-       tty->driver_data = port;
-       port->port.tty = tty;
-       spin_unlock_irqrestore(&bp->lock, flags);
-
-       error = sx_setup_port(bp, port);
-       if (error) {
-               func_enter();
-               return error;
-       }
-
-       error = block_til_ready(tty, filp, port);
-       if (error) {
-               func_enter();
-               return error;
-       }
-
-       func_exit();
-       return 0;
-}
-
-static void sx_flush_buffer(struct tty_struct *tty)
-{
-       struct specialix_port *port = tty->driver_data;
-       unsigned long flags;
-       struct specialix_board  *bp;
-
-       func_enter();
-
-       if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
-               func_exit();
-               return;
-       }
-
-       bp = port_Board(port);
-       spin_lock_irqsave(&port->lock, flags);
-       port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
-       spin_unlock_irqrestore(&port->lock, flags);
-       tty_wakeup(tty);
-
-       func_exit();
-}
-
-static void sx_close(struct tty_struct *tty, struct file *filp)
-{
-       struct specialix_port *port = tty->driver_data;
-       struct specialix_board *bp;
-       unsigned long flags;
-       unsigned long timeout;
-
-       func_enter();
-       if (!port || sx_paranoia_check(port, tty->name, "close")) {
-               func_exit();
-               return;
-       }
-       spin_lock_irqsave(&port->lock, flags);
-
-       if (tty_hung_up_p(filp)) {
-               spin_unlock_irqrestore(&port->lock, flags);
-               func_exit();
-               return;
-       }
-
-       bp = port_Board(port);
-       if (tty->count == 1 && port->port.count != 1) {
-               printk(KERN_ERR "sx%d: sx_close: bad port count;"
-                      " tty->count is 1, port count is %d\n",
-                      board_No(bp), port->port.count);
-               port->port.count = 1;
-       }
-
-       if (port->port.count > 1) {
-               port->port.count--;
-               bp->count--;
-
-               spin_unlock_irqrestore(&port->lock, flags);
-
-               func_exit();
-               return;
-       }
-       port->port.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;
-       spin_unlock_irqrestore(&port->lock, flags);
-       dprintk(SX_DEBUG_OPEN, "Closing\n");
-       if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
-               tty_wait_until_sent(tty, port->port.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.
-        */
-       dprintk(SX_DEBUG_OPEN, "Closed\n");
-       port->IER &= ~IER_RXD;
-       if (port->port.flags & ASYNC_INITIALIZED) {
-               port->IER &= ~IER_TXRDY;
-               port->IER |= IER_TXEMPTY;
-               spin_lock_irqsave(&bp->lock, flags);
-               sx_out(bp, CD186x_CAR, port_No(port));
-               sx_out(bp, CD186x_IER, port->IER);
-               spin_unlock_irqrestore(&bp->lock, flags);
-               /*
-                * 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 (port->IER & IER_TXEMPTY) {
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       msleep_interruptible(jiffies_to_msecs(port->timeout));
-                       if (time_after(jiffies, timeout)) {
-                               printk(KERN_INFO "Timeout waiting for close\n");
-                               break;
-                       }
-               }
-
-       }
-
-       if (--bp->count < 0) {
-               printk(KERN_ERR
-                   "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
-                               board_No(bp), bp->count, tty->index);
-               bp->count = 0;
-       }
-       if (--port->port.count < 0) {
-               printk(KERN_ERR
-                       "sx%d: sx_close: bad port count for tty%d: %d\n",
-                               board_No(bp), port_No(port), port->port.count);
-               port->port.count = 0;
-       }
-
-       sx_shutdown_port(bp, port);
-       sx_flush_buffer(tty);
-       tty_ldisc_flush(tty);
-       spin_lock_irqsave(&port->lock, flags);
-       tty->closing = 0;
-       port->port.tty = NULL;
-       spin_unlock_irqrestore(&port->lock, flags);
-       if (port->port.blocked_open) {
-               if (port->port.close_delay)
-                       msleep_interruptible(
-                               jiffies_to_msecs(port->port.close_delay));
-               wake_up_interruptible(&port->port.open_wait);
-       }
-       port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
-       wake_up_interruptible(&port->port.close_wait);
-
-       func_exit();
-}
-
-
-static int sx_write(struct tty_struct *tty,
-                                       const unsigned char *buf, int count)
-{
-       struct specialix_port *port = tty->driver_data;
-       struct specialix_board *bp;
-       int c, total = 0;
-       unsigned long flags;
-
-       func_enter();
-       if (sx_paranoia_check(port, tty->name, "sx_write")) {
-               func_exit();
-               return 0;
-       }
-
-       bp = port_Board(port);
-
-       if (!port->xmit_buf) {
-               func_exit();
-               return 0;
-       }
-
-       while (1) {
-               spin_lock_irqsave(&port->lock, flags);
-               c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
-                                  SERIAL_XMIT_SIZE - port->xmit_head));
-               if (c <= 0) {
-                       spin_unlock_irqrestore(&port->lock, flags);
-                       break;
-               }
-               memcpy(port->xmit_buf + port->xmit_head, buf, c);
-               port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
-               port->xmit_cnt += c;
-               spin_unlock_irqrestore(&port->lock, flags);
-
-               buf += c;
-               count -= c;
-               total += c;
-       }
-
-       spin_lock_irqsave(&bp->lock, flags);
-       if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
-           !(port->IER & IER_TXRDY)) {
-               port->IER |= IER_TXRDY;
-               sx_out(bp, CD186x_CAR, port_No(port));
-               sx_out(bp, CD186x_IER, port->IER);
-       }
-       spin_unlock_irqrestore(&bp->lock, flags);
-       func_exit();
-
-       return total;
-}
-
-
-static int sx_put_char(struct tty_struct *tty, unsigned char ch)
-{
-       struct specialix_port *port = tty->driver_data;
-       unsigned long flags;
-       struct specialix_board  *bp;
-
-       func_enter();
-
-       if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
-               func_exit();
-               return 0;
-       }
-       dprintk(SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
-       if (!port->xmit_buf) {
-               func_exit();
-               return 0;
-       }
-       bp = port_Board(port);
-       spin_lock_irqsave(&port->lock, flags);
-
-       dprintk(SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n",
-                                       port->xmit_cnt, port->xmit_buf);
-       if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1 || !port->xmit_buf) {
-               spin_unlock_irqrestore(&port->lock, flags);
-               dprintk(SX_DEBUG_TX, "Exit size\n");
-               func_exit();
-               return 0;
-       }
-       dprintk(SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
-       port->xmit_buf[port->xmit_head++] = ch;
-       port->xmit_head &= SERIAL_XMIT_SIZE - 1;
-       port->xmit_cnt++;
-       spin_unlock_irqrestore(&port->lock, flags);
-
-       func_exit();
-       return 1;
-}
-
-
-static void sx_flush_chars(struct tty_struct *tty)
-{
-       struct specialix_port *port = tty->driver_data;
-       unsigned long flags;
-       struct specialix_board  *bp = port_Board(port);
-
-       func_enter();
-
-       if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
-               func_exit();
-               return;
-       }
-       if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
-           !port->xmit_buf) {
-               func_exit();
-               return;
-       }
-       spin_lock_irqsave(&bp->lock, flags);
-       port->IER |= IER_TXRDY;
-       sx_out(port_Board(port), CD186x_CAR, port_No(port));
-       sx_out(port_Board(port), CD186x_IER, port->IER);
-       spin_unlock_irqrestore(&bp->lock, flags);
-
-       func_exit();
-}
-
-
-static int sx_write_room(struct tty_struct *tty)
-{
-       struct specialix_port *port = tty->driver_data;
-       int     ret;
-
-       func_enter();
-
-       if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
-               func_exit();
-               return 0;
-       }
-
-       ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
-       if (ret < 0)
-               ret = 0;
-
-       func_exit();
-       return ret;
-}
-
-
-static int sx_chars_in_buffer(struct tty_struct *tty)
-{
-       struct specialix_port *port = tty->driver_data;
-
-       func_enter();
-
-       if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
-               func_exit();
-               return 0;
-       }
-       func_exit();
-       return port->xmit_cnt;
-}
-
-static int sx_tiocmget(struct tty_struct *tty)
-{
-       struct specialix_port *port = tty->driver_data;
-       struct specialix_board *bp;
-       unsigned char status;
-       unsigned int result;
-       unsigned long flags;
-
-       func_enter();
-
-       if (sx_paranoia_check(port, tty->name, __func__)) {
-               func_exit();
-               return -ENODEV;
-       }
-
-       bp = port_Board(port);
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out(bp, CD186x_CAR, port_No(port));
-       status = sx_in(bp, CD186x_MSVR);
-       spin_unlock_irqrestore(&bp->lock, flags);
-       dprintk(SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
-                       port_No(port), status, sx_in(bp, CD186x_CAR));
-       dprintk(SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
-       if (sx_crtscts(port->port.tty)) {
-               result  = TIOCM_DTR | TIOCM_DSR
-                         |   ((status & MSVR_DTR) ? TIOCM_RTS : 0)
-                         |   ((status & MSVR_CD)  ? TIOCM_CAR : 0)
-                         |   ((status & MSVR_CTS) ? TIOCM_CTS : 0);
-       } else {
-               result  = TIOCM_RTS | TIOCM_DSR
-                         |   ((status & MSVR_DTR) ? TIOCM_DTR : 0)
-                         |   ((status & MSVR_CD)  ? TIOCM_CAR : 0)
-                         |   ((status & MSVR_CTS) ? TIOCM_CTS : 0);
-       }
-
-       func_exit();
-
-       return result;
-}
-
-
-static int sx_tiocmset(struct tty_struct *tty,
-                      unsigned int set, unsigned int clear)
-{
-       struct specialix_port *port = tty->driver_data;
-       unsigned long flags;
-       struct specialix_board *bp;
-
-       func_enter();
-
-       if (sx_paranoia_check(port, tty->name, __func__)) {
-               func_exit();
-               return -ENODEV;
-       }
-
-       bp = port_Board(port);
-
-       spin_lock_irqsave(&port->lock, flags);
-       if (sx_crtscts(port->port.tty)) {
-               if (set & TIOCM_RTS)
-                       port->MSVR |= MSVR_DTR;
-       } else {
-               if (set & TIOCM_DTR)
-                       port->MSVR |= MSVR_DTR;
-       }
-       if (sx_crtscts(port->port.tty)) {
-               if (clear & TIOCM_RTS)
-                       port->MSVR &= ~MSVR_DTR;
-       } else {
-               if (clear & TIOCM_DTR)
-                       port->MSVR &= ~MSVR_DTR;
-       }
-       spin_lock(&bp->lock);
-       sx_out(bp, CD186x_CAR, port_No(port));
-       sx_out(bp, CD186x_MSVR, port->MSVR);
-       spin_unlock(&bp->lock);
-       spin_unlock_irqrestore(&port->lock, flags);
-       func_exit();
-       return 0;
-}
-
-
-static int sx_send_break(struct tty_struct *tty, int length)
-{
-       struct specialix_port *port = tty->driver_data;
-       struct specialix_board *bp = port_Board(port);
-       unsigned long flags;
-
-       func_enter();
-       if (length == 0 || length == -1)
-               return -EOPNOTSUPP;
-
-       spin_lock_irqsave(&port->lock, flags);
-       port->break_length = SPECIALIX_TPS / HZ * length;
-       port->COR2 |= COR2_ETC;
-       port->IER  |= IER_TXRDY;
-       spin_lock(&bp->lock);
-       sx_out(bp, CD186x_CAR, port_No(port));
-       sx_out(bp, CD186x_COR2, port->COR2);
-       sx_out(bp, CD186x_IER, port->IER);
-       spin_unlock(&bp->lock);
-       spin_unlock_irqrestore(&port->lock, flags);
-       sx_wait_CCR(bp);
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out(bp, CD186x_CCR, CCR_CORCHG2);
-       spin_unlock_irqrestore(&bp->lock, flags);
-       sx_wait_CCR(bp);
-
-       func_exit();
-       return 0;
-}
-
-
-static int sx_set_serial_info(struct specialix_port *port,
-                                       struct serial_struct __user *newinfo)
-{
-       struct serial_struct tmp;
-       struct specialix_board *bp = port_Board(port);
-       int change_speed;
-
-       func_enter();
-
-       if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
-               func_enter();
-               return -EFAULT;
-       }
-
-       mutex_lock(&port->port.mutex);
-       change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
-                       (tmp.flags & ASYNC_SPD_MASK));
-       change_speed |= (tmp.custom_divisor != port->custom_divisor);
-
-       if (!capable(CAP_SYS_ADMIN)) {
-               if ((tmp.close_delay != port->port.close_delay) ||
-                   (tmp.closing_wait != port->port.closing_wait) ||
-                   ((tmp.flags & ~ASYNC_USR_MASK) !=
-                    (port->port.flags & ~ASYNC_USR_MASK))) {
-                       func_exit();
-                       mutex_unlock(&port->port.mutex);
-                       return -EPERM;
-               }
-               port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
-                                               (tmp.flags & ASYNC_USR_MASK));
-               port->custom_divisor = tmp.custom_divisor;
-       } else {
-               port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
-                                               (tmp.flags & ASYNC_FLAGS));
-               port->port.close_delay = tmp.close_delay;
-               port->port.closing_wait = tmp.closing_wait;
-               port->custom_divisor = tmp.custom_divisor;
-       }
-       if (change_speed)
-               sx_change_speed(bp, port);
-
-       func_exit();
-       mutex_unlock(&port->port.mutex);
-       return 0;
-}
-
-
-static int sx_get_serial_info(struct specialix_port *port,
-                                    struct serial_struct __user *retinfo)
-{
-       struct serial_struct tmp;
-       struct specialix_board *bp = port_Board(port);
-
-       func_enter();
-
-       memset(&tmp, 0, sizeof(tmp));
-       mutex_lock(&port->port.mutex);
-       tmp.type = PORT_CIRRUS;
-       tmp.line = port - sx_port;
-       tmp.port = bp->base;
-       tmp.irq  = bp->irq;
-       tmp.flags = port->port.flags;
-       tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
-       tmp.close_delay = port->port.close_delay * HZ/100;
-       tmp.closing_wait = port->port.closing_wait * HZ/100;
-       tmp.custom_divisor =  port->custom_divisor;
-       tmp.xmit_fifo_size = CD186x_NFIFO;
-       mutex_unlock(&port->port.mutex);
-       if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
-               func_exit();
-               return -EFAULT;
-       }
-
-       func_exit();
-       return 0;
-}
-
-
-static int sx_ioctl(struct tty_struct *tty,
-                               unsigned int cmd, unsigned long arg)
-{
-       struct specialix_port *port = tty->driver_data;
-       void __user *argp = (void __user *)arg;
-
-       func_enter();
-
-       if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
-               func_exit();
-               return -ENODEV;
-       }
-
-       switch (cmd) {
-       case TIOCGSERIAL:
-               func_exit();
-               return sx_get_serial_info(port, argp);
-       case TIOCSSERIAL:
-               func_exit();
-               return sx_set_serial_info(port, argp);
-       default:
-               func_exit();
-               return -ENOIOCTLCMD;
-       }
-       func_exit();
-       return 0;
-}
-
-
-static void sx_throttle(struct tty_struct *tty)
-{
-       struct specialix_port *port = tty->driver_data;
-       struct specialix_board *bp;
-       unsigned long flags;
-
-       func_enter();
-
-       if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
-               func_exit();
-               return;
-       }
-
-       bp = port_Board(port);
-
-       /* Use DTR instead of RTS ! */
-       if (sx_crtscts(tty))
-               port->MSVR &= ~MSVR_DTR;
-       else {
-               /* Auch!!! I think the system shouldn't call this then. */
-               /* Or maybe we're supposed (allowed?) to do our side of hw
-                  handshake anyway, even when hardware handshake is off.
-                  When you see this in your logs, please report.... */
-               printk(KERN_ERR
-                  "sx%d: Need to throttle, but can't (hardware hs is off)\n",
-                                                       port_No(port));
-       }
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out(bp, CD186x_CAR, port_No(port));
-       spin_unlock_irqrestore(&bp->lock, flags);
-       if (I_IXOFF(tty)) {
-               sx_wait_CCR(bp);
-               spin_lock_irqsave(&bp->lock, flags);
-               sx_out(bp, CD186x_CCR, CCR_SSCH2);
-               spin_unlock_irqrestore(&bp->lock, flags);
-               sx_wait_CCR(bp);
-       }
-       spin_lock_irqsave(&bp->lock, flags);
-       sx_out(bp, CD186x_MSVR, port->MSVR);
-       spin_unlock_irqrestore(&bp->lock, flags);
-
-       func_exit();
-}
-
-
-static void sx_unthrottle(struct tty_struct *tty)
-{
-       struct specialix_port *port = tty->driver_data;
-       struct specialix_board *bp;
-       unsigned long flags;
-
-       func_enter();
-
-       if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
-               func_exit();
-               return;
-       }
-
-       bp = port_Board(port);
-
-       spin_lock_irqsave(&port->lock, flags);
-       /* XXXX Use DTR INSTEAD???? */
-       if (sx_crtscts(tty))
-               port->MSVR |= MSVR_DTR;
-       /* Else clause: see remark in "sx_throttle"... */
-       spin_lock(&bp->lock);
-       sx_out(bp, CD186x_CAR, port_No(port));
-       spin_unlock(&bp->lock);
-       if (I_IXOFF(tty)) {
-               spin_unlock_irqrestore(&port->lock, flags);
-               sx_wait_CCR(bp);
-               spin_lock_irqsave(&bp->lock, flags);
-               sx_out(bp, CD186x_CCR, CCR_SSCH1);
-               spin_unlock_irqrestore(&bp->lock, flags);
-               sx_wait_CCR(bp);
-               spin_lock_irqsave(&port->lock, flags);
-       }
-       spin_lock(&bp->lock);
-       sx_out(bp, CD186x_MSVR, port->MSVR);
-       spin_unlock(&bp->lock);
-       spin_unlock_irqrestore(&port->lock, flags);
-
-       func_exit();
-}
-
-
-static void sx_stop(struct tty_struct *tty)
-{
-       struct specialix_port *port = tty->driver_data;
-       struct specialix_board *bp;
-       unsigned long flags;
-
-       func_enter();
-
-       if (sx_paranoia_check(port, tty->name, "sx_stop")) {
-               func_exit();
-               return;
-       }
-
-       bp = port_Board(port);
-
-       spin_lock_irqsave(&port->lock, flags);
-       port->IER &= ~IER_TXRDY;
-       spin_lock(&bp->lock);
-       sx_out(bp, CD186x_CAR, port_No(port));
-       sx_out(bp, CD186x_IER, port->IER);
-       spin_unlock(&bp->lock);
-       spin_unlock_irqrestore(&port->lock, flags);
-
-       func_exit();
-}
-
-
-static void sx_start(struct tty_struct *tty)
-{
-       struct specialix_port *port = tty->driver_data;
-       struct specialix_board *bp;
-       unsigned long flags;
-
-       func_enter();
-
-       if (sx_paranoia_check(port, tty->name, "sx_start")) {
-               func_exit();
-               return;
-       }
-
-       bp = port_Board(port);
-
-       spin_lock_irqsave(&port->lock, flags);
-       if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
-               port->IER |= IER_TXRDY;
-               spin_lock(&bp->lock);
-               sx_out(bp, CD186x_CAR, port_No(port));
-               sx_out(bp, CD186x_IER, port->IER);
-               spin_unlock(&bp->lock);
-       }
-       spin_unlock_irqrestore(&port->lock, flags);
-
-       func_exit();
-}
-
-static void sx_hangup(struct tty_struct *tty)
-{
-       struct specialix_port *port = tty->driver_data;
-       struct specialix_board *bp;
-       unsigned long flags;
-
-       func_enter();
-
-       if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
-               func_exit();
-               return;
-       }
-
-       bp = port_Board(port);
-
-       sx_shutdown_port(bp, port);
-       spin_lock_irqsave(&port->lock, flags);
-       bp->count -= port->port.count;
-       if (bp->count < 0) {
-               printk(KERN_ERR
-                       "sx%d: sx_hangup: bad board count: %d port: %d\n",
-                                       board_No(bp), bp->count, tty->index);
-               bp->count = 0;
-       }
-       port->port.count = 0;
-       port->port.flags &= ~ASYNC_NORMAL_ACTIVE;
-       port->port.tty = NULL;
-       spin_unlock_irqrestore(&port->lock, flags);
-       wake_up_interruptible(&port->port.open_wait);
-
-       func_exit();
-}
-
-
-static void sx_set_termios(struct tty_struct *tty,
-                                       struct ktermios *old_termios)
-{
-       struct specialix_port *port = tty->driver_data;
-       unsigned long flags;
-       struct specialix_board  *bp;
-
-       if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
-               return;
-
-       bp = port_Board(port);
-       spin_lock_irqsave(&port->lock, flags);
-       sx_change_speed(port_Board(port), port);
-       spin_unlock_irqrestore(&port->lock, flags);
-
-       if ((old_termios->c_cflag & CRTSCTS) &&
-           !(tty->termios->c_cflag & CRTSCTS)) {
-               tty->hw_stopped = 0;
-               sx_start(tty);
-       }
-}
-
-static const struct tty_operations sx_ops = {
-       .open  = sx_open,
-       .close = sx_close,
-       .write = sx_write,
-       .put_char = sx_put_char,
-       .flush_chars = sx_flush_chars,
-       .write_room = sx_write_room,
-       .chars_in_buffer = sx_chars_in_buffer,
-       .flush_buffer = sx_flush_buffer,
-       .ioctl = sx_ioctl,
-       .throttle = sx_throttle,
-       .unthrottle = sx_unthrottle,
-       .set_termios = sx_set_termios,
-       .stop = sx_stop,
-       .start = sx_start,
-       .hangup = sx_hangup,
-       .tiocmget = sx_tiocmget,
-       .tiocmset = sx_tiocmset,
-       .break_ctl = sx_send_break,
-};
-
-static int sx_init_drivers(void)
-{
-       int error;
-       int i;
-
-       func_enter();
-
-       specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
-       if (!specialix_driver) {
-               printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
-               func_exit();
-               return 1;
-       }
-
-       specialix_driver->owner = THIS_MODULE;
-       specialix_driver->name = "ttyW";
-       specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
-       specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
-       specialix_driver->subtype = SERIAL_TYPE_NORMAL;
-       specialix_driver->init_termios = tty_std_termios;
-       specialix_driver->init_termios.c_cflag =
-               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-       specialix_driver->init_termios.c_ispeed = 9600;
-       specialix_driver->init_termios.c_ospeed = 9600;
-       specialix_driver->flags = TTY_DRIVER_REAL_RAW |
-                                               TTY_DRIVER_HARDWARE_BREAK;
-       tty_set_operations(specialix_driver, &sx_ops);
-
-       error = tty_register_driver(specialix_driver);
-       if (error) {
-               put_tty_driver(specialix_driver);
-               printk(KERN_ERR
-                 "sx: Couldn't register specialix IO8+ driver, error = %d\n",
-                                                               error);
-               func_exit();
-               return 1;
-       }
-       memset(sx_port, 0, sizeof(sx_port));
-       for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
-               sx_port[i].magic = SPECIALIX_MAGIC;
-               tty_port_init(&sx_port[i].port);
-               spin_lock_init(&sx_port[i].lock);
-       }
-
-       func_exit();
-       return 0;
-}
-
-static void sx_release_drivers(void)
-{
-       func_enter();
-
-       tty_unregister_driver(specialix_driver);
-       put_tty_driver(specialix_driver);
-       func_exit();
-}
-
-/*
- * This routine must be called by kernel at boot time
- */
-static int __init specialix_init(void)
-{
-       int i;
-       int found = 0;
-
-       func_enter();
-
-       printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
-       printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
-       if (sx_rtscts)
-               printk(KERN_INFO
-                       "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
-       else
-               printk(KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
-
-       for (i = 0; i < SX_NBOARD; i++)
-               spin_lock_init(&sx_board[i].lock);
-
-       if (sx_init_drivers()) {
-               func_exit();
-               return -EIO;
-       }
-
-       for (i = 0; i < SX_NBOARD; i++)
-               if (sx_board[i].base && !sx_probe(&sx_board[i]))
-                       found++;
-
-#ifdef CONFIG_PCI
-       {
-               struct pci_dev *pdev = NULL;
-
-               i = 0;
-               while (i < SX_NBOARD) {
-                       if (sx_board[i].flags & SX_BOARD_PRESENT) {
-                               i++;
-                               continue;
-                       }
-                       pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX,
-                                       PCI_DEVICE_ID_SPECIALIX_IO8, pdev);
-                       if (!pdev)
-                               break;
-
-                       if (pci_enable_device(pdev))
-                               continue;
-
-                       sx_board[i].irq = pdev->irq;
-
-                       sx_board[i].base = pci_resource_start(pdev, 2);
-
-                       sx_board[i].flags |= SX_BOARD_IS_PCI;
-                       if (!sx_probe(&sx_board[i]))
-                               found++;
-               }
-               /* May exit pci_get sequence early with lots of boards */
-               if (pdev != NULL)
-                       pci_dev_put(pdev);
-       }
-#endif
-
-       if (!found) {
-               sx_release_drivers();
-               printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
-               func_exit();
-               return -EIO;
-       }
-
-       func_exit();
-       return 0;
-}
-
-static int iobase[SX_NBOARD]  = {0,};
-static int irq[SX_NBOARD] = {0,};
-
-module_param_array(iobase, int, NULL, 0);
-module_param_array(irq, int, NULL, 0);
-module_param(sx_debug, int, 0);
-module_param(sx_rtscts, int, 0);
-module_param(sx_rxfifo, int, 0);
-
-/*
- * You can setup up to 4 boards.
- * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
- * You should specify the IRQs too in that case "irq=....,...".
- *
- * More than 4 boards in one computer is not possible, as the card can
- * only use 4 different interrupts.
- *
- */
-static int __init specialix_init_module(void)
-{
-       int i;
-
-       func_enter();
-
-       if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
-               for (i = 0; i < SX_NBOARD; i++) {
-                       sx_board[i].base = iobase[i];
-                       sx_board[i].irq = irq[i];
-                       sx_board[i].count = 0;
-               }
-       }
-
-       func_exit();
-
-       return specialix_init();
-}
-
-static void __exit specialix_exit_module(void)
-{
-       int i;
-
-       func_enter();
-
-       sx_release_drivers();
-       for (i = 0; i < SX_NBOARD; i++)
-               if (sx_board[i].flags & SX_BOARD_PRESENT)
-                       sx_release_io_range(&sx_board[i]);
-       func_exit();
-}
-
-static struct pci_device_id specialx_pci_tbl[] __devinitdata __used = {
-       { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
-       { }
-};
-MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
-
-module_init(specialix_init_module);
-module_exit(specialix_exit_module);
-
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV_MAJOR(SPECIALIX_NORMAL_MAJOR);
diff --git a/drivers/char/specialix_io8.h b/drivers/char/specialix_io8.h
deleted file mode 100644 (file)
index c630052..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- *      linux/drivers/char/specialix_io8.h  -- 
- *                                   Specialix IO8+ multiport serial driver.
- *
- *      Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
- *      Copyright (C) 1994-1996  Dmitry Gorodchanin (pgmdsg@ibi.com)
- *
- *
- *      Specialix pays for the development and support of this driver.
- *      Please DO contact io8-linux@specialix.co.uk if you require
- *      support.
- *
- *      This driver was developped in the BitWizard linux device
- *      driver service. If you require a linux device driver for your
- *      product, please contact devices@BitWizard.nl for a quote.
- *
- *      This code is firmly based on the riscom/8 serial driver,
- *      written by Dmitry Gorodchanin. The specialix IO8+ card
- *      programming information was obtained from the CL-CD1865 Data
- *      Book, and Specialix document number 6200059: IO8+ Hardware
- *      Functional Specification.
- *
- *      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.
- * */
-
-#ifndef __LINUX_SPECIALIX_H
-#define __LINUX_SPECIALIX_H
-
-#include <linux/serial.h>
-
-#ifdef __KERNEL__
-
-/* You can have max 4 ISA cards in one PC, and I recommend not much 
-more than a few  PCI versions of the card. */
-
-#define SX_NBOARD              8
-
-/* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */
-#define SX_IO_SPACE             4
-/* The PCI version decodes 8 addresses, but still only 2 are used. */
-#define SX_PCI_IO_SPACE         8
-
-/* eight ports per board. */
-#define SX_NPORT               8
-#define SX_BOARD(line)         ((line) / SX_NPORT)
-#define SX_PORT(line)          ((line) & (SX_NPORT - 1))
-
-
-#define SX_DATA_REG 0     /* Base+0 : Data register */
-#define SX_ADDR_REG 1     /* base+1 : Address register. */
-
-#define MHz *1000000   /* I'm ashamed of myself. */
-
-/* On-board oscillator frequency */
-#define SX_OSCFREQ      (25 MHz/2)
-/* There is a 25MHz crystal on the board, but the chip is in /2 mode */
-
-
-/* Ticks per sec. Used for setting receiver timeout and break length */
-#define SPECIALIX_TPS          4000
-
-/* Yeah, after heavy testing I decided it must be 6.
- * Sure, You can change it if needed.
- */
-#define SPECIALIX_RXFIFO       6       /* Max. receiver FIFO size (1-8) */
-
-#define SPECIALIX_MAGIC                0x0907
-
-#define SX_CCR_TIMEOUT 10000   /* CCR timeout. You may need to wait upto
-                                  10 milliseconds before the internal
-                                  processor is available again after
-                                  you give it a command */
-
-#define SX_IOBASE1     0x100
-#define SX_IOBASE2     0x180
-#define SX_IOBASE3     0x250
-#define SX_IOBASE4     0x260
-
-struct specialix_board {
-       unsigned long   flags;
-       unsigned short  base;
-       unsigned char   irq;
-       //signed   char count;
-       int count;
-       unsigned char   DTR;
-        int reg;
-       spinlock_t lock;
-};
-
-#define SX_BOARD_PRESENT       0x00000001
-#define SX_BOARD_ACTIVE                0x00000002
-#define SX_BOARD_IS_PCI                0x00000004
-
-
-struct specialix_port {
-       int                     magic;
-       struct tty_port         port;
-       int                     baud_base;
-       int                     flags;
-       int                     timeout;
-       unsigned char           * xmit_buf;
-       int                     custom_divisor;
-       int                     xmit_head;
-       int                     xmit_tail;
-       int                     xmit_cnt;
-       short                   wakeup_chars;
-       short                   break_length;
-       unsigned char           mark_mask;
-       unsigned char           IER;
-       unsigned char           MSVR;
-       unsigned char           COR2;
-       unsigned long           overrun;
-       unsigned long           hits[10];
-       spinlock_t lock;
-};
-
-#endif /* __KERNEL__ */
-#endif /* __LINUX_SPECIALIX_H */
-
-
-
-
-
-
-
-
-
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
deleted file mode 100644 (file)
index 4fff5cd..0000000
+++ /dev/null
@@ -1,4651 +0,0 @@
-/*****************************************************************************/
-
-/*
- *     stallion.c  -- stallion multiport serial driver.
- *
- *     Copyright (C) 1996-1999  Stallion Technologies
- *     Copyright (C) 1994-1996  Greg Ungerer.
- *
- *     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.
- *
- *     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.
- */
-
-/*****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/seq_file.h>
-#include <linux/cd1400.h>
-#include <linux/sc26198.h>
-#include <linux/comstats.h>
-#include <linux/stallion.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/ctype.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-
-#include <linux/pci.h>
-
-/*****************************************************************************/
-
-/*
- *     Define different board types. Use the standard Stallion "assigned"
- *     board numbers. Boards supported in this driver are abbreviated as
- *     EIO = EasyIO and ECH = EasyConnection 8/32.
- */
-#define        BRD_EASYIO      20
-#define        BRD_ECH         21
-#define        BRD_ECHMC       22
-#define        BRD_ECHPCI      26
-#define        BRD_ECH64PCI    27
-#define        BRD_EASYIOPCI   28
-
-struct stlconf {
-       unsigned int    brdtype;
-       int             ioaddr1;
-       int             ioaddr2;
-       unsigned long   memaddr;
-       int             irq;
-       int             irqtype;
-};
-
-static unsigned int stl_nrbrds;
-
-/*****************************************************************************/
-
-/*
- *     Define some important driver characteristics. Device major numbers
- *     allocated as per Linux Device Registry.
- */
-#ifndef        STL_SIOMEMMAJOR
-#define        STL_SIOMEMMAJOR         28
-#endif
-#ifndef        STL_SERIALMAJOR
-#define        STL_SERIALMAJOR         24
-#endif
-#ifndef        STL_CALLOUTMAJOR
-#define        STL_CALLOUTMAJOR        25
-#endif
-
-/*
- *     Set the TX buffer size. Bigger is better, but we don't want
- *     to chew too much memory with buffers!
- */
-#define        STL_TXBUFLOW            512
-#define        STL_TXBUFSIZE           4096
-
-/*****************************************************************************/
-
-/*
- *     Define our local driver identity first. Set up stuff to deal with
- *     all the local structures required by a serial tty driver.
- */
-static char    *stl_drvtitle = "Stallion Multiport Serial Driver";
-static char    *stl_drvname = "stallion";
-static char    *stl_drvversion = "5.6.0";
-
-static struct tty_driver       *stl_serial;
-
-/*
- *     Define a local default termios struct. All ports will be created
- *     with this termios initially. Basically all it defines is a raw port
- *     at 9600, 8 data bits, 1 stop bit.
- */
-static struct ktermios         stl_deftermios = {
-       .c_cflag        = (B9600 | CS8 | CREAD | HUPCL | CLOCAL),
-       .c_cc           = INIT_C_CC,
-       .c_ispeed       = 9600,
-       .c_ospeed       = 9600,
-};
-
-/*
- *     Define global place to put buffer overflow characters.
- */
-static char            stl_unwanted[SC26198_RXFIFOSIZE];
-
-/*****************************************************************************/
-
-static DEFINE_MUTEX(stl_brdslock);
-static struct stlbrd           *stl_brds[STL_MAXBRDS];
-
-static const struct tty_port_operations stl_port_ops;
-
-/*
- *     Per board state flags. Used with the state field of the board struct.
- *     Not really much here!
- */
-#define        BRD_FOUND       0x1
-#define STL_PROBED     0x2
-
-
-/*
- *     Define the port structure istate flags. These set of flags are
- *     modified at interrupt time - so setting and reseting them needs
- *     to be atomic. Use the bit clear/setting routines for this.
- */
-#define        ASYI_TXBUSY     1
-#define        ASYI_TXLOW      2
-#define        ASYI_TXFLOWED   3
-
-/*
- *     Define an array of board names as printable strings. Handy for
- *     referencing boards when printing trace and stuff.
- */
-static char    *stl_brdnames[] = {
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       "EasyIO",
-       "EC8/32-AT",
-       "EC8/32-MC",
-       NULL,
-       NULL,
-       NULL,
-       "EC8/32-PCI",
-       "EC8/64-PCI",
-       "EasyIO-PCI",
-};
-
-/*****************************************************************************/
-
-/*
- *     Define some string labels for arguments passed from the module
- *     load line. These allow for easy board definitions, and easy
- *     modification of the io, memory and irq resoucres.
- */
-static unsigned int stl_nargs;
-static char    *board0[4];
-static char    *board1[4];
-static char    *board2[4];
-static char    *board3[4];
-
-static char    **stl_brdsp[] = {
-       (char **) &board0,
-       (char **) &board1,
-       (char **) &board2,
-       (char **) &board3
-};
-
-/*
- *     Define a set of common board names, and types. This is used to
- *     parse any module arguments.
- */
-
-static struct {
-       char    *name;
-       int     type;
-} stl_brdstr[] = {
-       { "easyio", BRD_EASYIO },
-       { "eio", BRD_EASYIO },
-       { "20", BRD_EASYIO },
-       { "ec8/32", BRD_ECH },
-       { "ec8/32-at", BRD_ECH },
-       { "ec8/32-isa", BRD_ECH },
-       { "ech", BRD_ECH },
-       { "echat", BRD_ECH },
-       { "21", BRD_ECH },
-       { "ec8/32-mc", BRD_ECHMC },
-       { "ec8/32-mca", BRD_ECHMC },
-       { "echmc", BRD_ECHMC },
-       { "echmca", BRD_ECHMC },
-       { "22", BRD_ECHMC },
-       { "ec8/32-pc", BRD_ECHPCI },
-       { "ec8/32-pci", BRD_ECHPCI },
-       { "26", BRD_ECHPCI },
-       { "ec8/64-pc", BRD_ECH64PCI },
-       { "ec8/64-pci", BRD_ECH64PCI },
-       { "ech-pci", BRD_ECH64PCI },
-       { "echpci", BRD_ECH64PCI },
-       { "echpc", BRD_ECH64PCI },
-       { "27", BRD_ECH64PCI },
-       { "easyio-pc", BRD_EASYIOPCI },
-       { "easyio-pci", BRD_EASYIOPCI },
-       { "eio-pci", BRD_EASYIOPCI },
-       { "eiopci", BRD_EASYIOPCI },
-       { "28", BRD_EASYIOPCI },
-};
-
-/*
- *     Define the module agruments.
- */
-
-module_param_array(board0, charp, &stl_nargs, 0);
-MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,ioaddr2][,irq]]");
-module_param_array(board1, charp, &stl_nargs, 0);
-MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,ioaddr2][,irq]]");
-module_param_array(board2, charp, &stl_nargs, 0);
-MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,ioaddr2][,irq]]");
-module_param_array(board3, charp, &stl_nargs, 0);
-MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,ioaddr2][,irq]]");
-
-/*****************************************************************************/
-
-/*
- *     Hardware ID bits for the EasyIO and ECH boards. These defines apply
- *     to the directly accessible io ports of these boards (not the uarts -
- *     they are in cd1400.h and sc26198.h).
- */
-#define        EIO_8PORTRS     0x04
-#define        EIO_4PORTRS     0x05
-#define        EIO_8PORTDI     0x00
-#define        EIO_8PORTM      0x06
-#define        EIO_MK3         0x03
-#define        EIO_IDBITMASK   0x07
-
-#define        EIO_BRDMASK     0xf0
-#define        ID_BRD4         0x10
-#define        ID_BRD8         0x20
-#define        ID_BRD16        0x30
-
-#define        EIO_INTRPEND    0x08
-#define        EIO_INTEDGE     0x00
-#define        EIO_INTLEVEL    0x08
-#define        EIO_0WS         0x10
-
-#define        ECH_ID          0xa0
-#define        ECH_IDBITMASK   0xe0
-#define        ECH_BRDENABLE   0x08
-#define        ECH_BRDDISABLE  0x00
-#define        ECH_INTENABLE   0x01
-#define        ECH_INTDISABLE  0x00
-#define        ECH_INTLEVEL    0x02
-#define        ECH_INTEDGE     0x00
-#define        ECH_INTRPEND    0x01
-#define        ECH_BRDRESET    0x01
-
-#define        ECHMC_INTENABLE 0x01
-#define        ECHMC_BRDRESET  0x02
-
-#define        ECH_PNLSTATUS   2
-#define        ECH_PNL16PORT   0x20
-#define        ECH_PNLIDMASK   0x07
-#define        ECH_PNLXPID     0x40
-#define        ECH_PNLINTRPEND 0x80
-
-#define        ECH_ADDR2MASK   0x1e0
-
-/*
- *     Define the vector mapping bits for the programmable interrupt board
- *     hardware. These bits encode the interrupt for the board to use - it
- *     is software selectable (except the EIO-8M).
- */
-static unsigned char   stl_vecmap[] = {
-       0xff, 0xff, 0xff, 0x04, 0x06, 0x05, 0xff, 0x07,
-       0xff, 0xff, 0x00, 0x02, 0x01, 0xff, 0xff, 0x03
-};
-
-/*
- *     Lock ordering is that you may not take stallion_lock holding
- *     brd_lock.
- */
-
-static spinlock_t brd_lock;            /* Guard the board mapping */
-static spinlock_t stallion_lock;       /* Guard the tty driver */
-
-/*
- *     Set up enable and disable macros for the ECH boards. They require
- *     the secondary io address space to be activated and deactivated.
- *     This way all ECH boards can share their secondary io region.
- *     If this is an ECH-PCI board then also need to set the page pointer
- *     to point to the correct page.
- */
-#define        BRDENABLE(brdnr,pagenr)                                         \
-       if (stl_brds[(brdnr)]->brdtype == BRD_ECH)                      \
-               outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDENABLE),    \
-                       stl_brds[(brdnr)]->ioctrl);                     \
-       else if (stl_brds[(brdnr)]->brdtype == BRD_ECHPCI)              \
-               outb((pagenr), stl_brds[(brdnr)]->ioctrl);
-
-#define        BRDDISABLE(brdnr)                                               \
-       if (stl_brds[(brdnr)]->brdtype == BRD_ECH)                      \
-               outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE),   \
-                       stl_brds[(brdnr)]->ioctrl);
-
-#define        STL_CD1400MAXBAUD       230400
-#define        STL_SC26198MAXBAUD      460800
-
-#define        STL_BAUDBASE            115200
-#define        STL_CLOSEDELAY          (5 * HZ / 10)
-
-/*****************************************************************************/
-
-/*
- *     Define the Stallion PCI vendor and device IDs.
- */
-#ifndef        PCI_VENDOR_ID_STALLION
-#define        PCI_VENDOR_ID_STALLION          0x124d
-#endif
-#ifndef PCI_DEVICE_ID_ECHPCI832
-#define        PCI_DEVICE_ID_ECHPCI832         0x0000
-#endif
-#ifndef PCI_DEVICE_ID_ECHPCI864
-#define        PCI_DEVICE_ID_ECHPCI864         0x0002
-#endif
-#ifndef PCI_DEVICE_ID_EIOPCI
-#define        PCI_DEVICE_ID_EIOPCI            0x0003
-#endif
-
-/*
- *     Define structure to hold all Stallion PCI boards.
- */
-
-static struct pci_device_id stl_pcibrds[] = {
-       { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI864),
-               .driver_data = BRD_ECH64PCI },
-       { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_EIOPCI),
-               .driver_data = BRD_EASYIOPCI },
-       { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832),
-               .driver_data = BRD_ECHPCI },
-       { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410),
-               .driver_data = BRD_ECHPCI },
-       { }
-};
-MODULE_DEVICE_TABLE(pci, stl_pcibrds);
-
-/*****************************************************************************/
-
-/*
- *     Define macros to extract a brd/port number from a minor number.
- */
-#define        MINOR2BRD(min)          (((min) & 0xc0) >> 6)
-#define        MINOR2PORT(min)         ((min) & 0x3f)
-
-/*
- *     Define a baud rate table that converts termios baud rate selector
- *     into the actual baud rate value. All baud rate calculations are
- *     based on the actual baud rate required.
- */
-static unsigned int    stl_baudrates[] = {
-       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-       9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
-};
-
-/*****************************************************************************/
-
-/*
- *     Declare all those functions in this driver!
- */
-
-static long    stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg);
-static int     stl_brdinit(struct stlbrd *brdp);
-static int     stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp);
-static int     stl_clrportstats(struct stlport *portp, comstats_t __user *cp);
-
-/*
- *     CD1400 uart specific handling functions.
- */
-static void    stl_cd1400setreg(struct stlport *portp, int regnr, int value);
-static int     stl_cd1400getreg(struct stlport *portp, int regnr);
-static int     stl_cd1400updatereg(struct stlport *portp, int regnr, int value);
-static int     stl_cd1400panelinit(struct stlbrd *brdp, struct stlpanel *panelp);
-static void    stl_cd1400portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp);
-static void    stl_cd1400setport(struct stlport *portp, struct ktermios *tiosp);
-static int     stl_cd1400getsignals(struct stlport *portp);
-static void    stl_cd1400setsignals(struct stlport *portp, int dtr, int rts);
-static void    stl_cd1400ccrwait(struct stlport *portp);
-static void    stl_cd1400enablerxtx(struct stlport *portp, int rx, int tx);
-static void    stl_cd1400startrxtx(struct stlport *portp, int rx, int tx);
-static void    stl_cd1400disableintrs(struct stlport *portp);
-static void    stl_cd1400sendbreak(struct stlport *portp, int len);
-static void    stl_cd1400flowctrl(struct stlport *portp, int state);
-static void    stl_cd1400sendflow(struct stlport *portp, int state);
-static void    stl_cd1400flush(struct stlport *portp);
-static int     stl_cd1400datastate(struct stlport *portp);
-static void    stl_cd1400eiointr(struct stlpanel *panelp, unsigned int iobase);
-static void    stl_cd1400echintr(struct stlpanel *panelp, unsigned int iobase);
-static void    stl_cd1400txisr(struct stlpanel *panelp, int ioaddr);
-static void    stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr);
-static void    stl_cd1400mdmisr(struct stlpanel *panelp, int ioaddr);
-
-static inline int      stl_cd1400breakisr(struct stlport *portp, int ioaddr);
-
-/*
- *     SC26198 uart specific handling functions.
- */
-static void    stl_sc26198setreg(struct stlport *portp, int regnr, int value);
-static int     stl_sc26198getreg(struct stlport *portp, int regnr);
-static int     stl_sc26198updatereg(struct stlport *portp, int regnr, int value);
-static int     stl_sc26198getglobreg(struct stlport *portp, int regnr);
-static int     stl_sc26198panelinit(struct stlbrd *brdp, struct stlpanel *panelp);
-static void    stl_sc26198portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp);
-static void    stl_sc26198setport(struct stlport *portp, struct ktermios *tiosp);
-static int     stl_sc26198getsignals(struct stlport *portp);
-static void    stl_sc26198setsignals(struct stlport *portp, int dtr, int rts);
-static void    stl_sc26198enablerxtx(struct stlport *portp, int rx, int tx);
-static void    stl_sc26198startrxtx(struct stlport *portp, int rx, int tx);
-static void    stl_sc26198disableintrs(struct stlport *portp);
-static void    stl_sc26198sendbreak(struct stlport *portp, int len);
-static void    stl_sc26198flowctrl(struct stlport *portp, int state);
-static void    stl_sc26198sendflow(struct stlport *portp, int state);
-static void    stl_sc26198flush(struct stlport *portp);
-static int     stl_sc26198datastate(struct stlport *portp);
-static void    stl_sc26198wait(struct stlport *portp);
-static void    stl_sc26198txunflow(struct stlport *portp, struct tty_struct *tty);
-static void    stl_sc26198intr(struct stlpanel *panelp, unsigned int iobase);
-static void    stl_sc26198txisr(struct stlport *port);
-static void    stl_sc26198rxisr(struct stlport *port, unsigned int iack);
-static void    stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char ch);
-static void    stl_sc26198rxbadchars(struct stlport *portp);
-static void    stl_sc26198otherisr(struct stlport *port, unsigned int iack);
-
-/*****************************************************************************/
-
-/*
- *     Generic UART support structure.
- */
-typedef struct uart {
-       int     (*panelinit)(struct stlbrd *brdp, struct stlpanel *panelp);
-       void    (*portinit)(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp);
-       void    (*setport)(struct stlport *portp, struct ktermios *tiosp);
-       int     (*getsignals)(struct stlport *portp);
-       void    (*setsignals)(struct stlport *portp, int dtr, int rts);
-       void    (*enablerxtx)(struct stlport *portp, int rx, int tx);
-       void    (*startrxtx)(struct stlport *portp, int rx, int tx);
-       void    (*disableintrs)(struct stlport *portp);
-       void    (*sendbreak)(struct stlport *portp, int len);
-       void    (*flowctrl)(struct stlport *portp, int state);
-       void    (*sendflow)(struct stlport *portp, int state);
-       void    (*flush)(struct stlport *portp);
-       int     (*datastate)(struct stlport *portp);
-       void    (*intr)(struct stlpanel *panelp, unsigned int iobase);
-} uart_t;
-
-/*
- *     Define some macros to make calling these functions nice and clean.
- */
-#define        stl_panelinit           (* ((uart_t *) panelp->uartp)->panelinit)
-#define        stl_portinit            (* ((uart_t *) portp->uartp)->portinit)
-#define        stl_setport             (* ((uart_t *) portp->uartp)->setport)
-#define        stl_getsignals          (* ((uart_t *) portp->uartp)->getsignals)
-#define        stl_setsignals          (* ((uart_t *) portp->uartp)->setsignals)
-#define        stl_enablerxtx          (* ((uart_t *) portp->uartp)->enablerxtx)
-#define        stl_startrxtx           (* ((uart_t *) portp->uartp)->startrxtx)
-#define        stl_disableintrs        (* ((uart_t *) portp->uartp)->disableintrs)
-#define        stl_sendbreak           (* ((uart_t *) portp->uartp)->sendbreak)
-#define        stl_flowctrl            (* ((uart_t *) portp->uartp)->flowctrl)
-#define        stl_sendflow            (* ((uart_t *) portp->uartp)->sendflow)
-#define        stl_flush               (* ((uart_t *) portp->uartp)->flush)
-#define        stl_datastate           (* ((uart_t *) portp->uartp)->datastate)
-
-/*****************************************************************************/
-
-/*
- *     CD1400 UART specific data initialization.
- */
-static uart_t stl_cd1400uart = {
-       stl_cd1400panelinit,
-       stl_cd1400portinit,
-       stl_cd1400setport,
-       stl_cd1400getsignals,
-       stl_cd1400setsignals,
-       stl_cd1400enablerxtx,
-       stl_cd1400startrxtx,
-       stl_cd1400disableintrs,
-       stl_cd1400sendbreak,
-       stl_cd1400flowctrl,
-       stl_cd1400sendflow,
-       stl_cd1400flush,
-       stl_cd1400datastate,
-       stl_cd1400eiointr
-};
-
-/*
- *     Define the offsets within the register bank of a cd1400 based panel.
- *     These io address offsets are common to the EasyIO board as well.
- */
-#define        EREG_ADDR       0
-#define        EREG_DATA       4
-#define        EREG_RXACK      5
-#define        EREG_TXACK      6
-#define        EREG_MDACK      7
-
-#define        EREG_BANKSIZE   8
-
-#define        CD1400_CLK      25000000
-#define        CD1400_CLK8M    20000000
-
-/*
- *     Define the cd1400 baud rate clocks. These are used when calculating
- *     what clock and divisor to use for the required baud rate. Also
- *     define the maximum baud rate allowed, and the default base baud.
- */
-static int     stl_cd1400clkdivs[] = {
-       CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4
-};
-
-/*****************************************************************************/
-
-/*
- *     SC26198 UART specific data initization.
- */
-static uart_t stl_sc26198uart = {
-       stl_sc26198panelinit,
-       stl_sc26198portinit,
-       stl_sc26198setport,
-       stl_sc26198getsignals,
-       stl_sc26198setsignals,
-       stl_sc26198enablerxtx,
-       stl_sc26198startrxtx,
-       stl_sc26198disableintrs,
-       stl_sc26198sendbreak,
-       stl_sc26198flowctrl,
-       stl_sc26198sendflow,
-       stl_sc26198flush,
-       stl_sc26198datastate,
-       stl_sc26198intr
-};
-
-/*
- *     Define the offsets within the register bank of a sc26198 based panel.
- */
-#define        XP_DATA         0
-#define        XP_ADDR         1
-#define        XP_MODID        2
-#define        XP_STATUS       2
-#define        XP_IACK         3
-
-#define        XP_BANKSIZE     4
-
-/*
- *     Define the sc26198 baud rate table. Offsets within the table
- *     represent the actual baud rate selector of sc26198 registers.
- */
-static unsigned int    sc26198_baudtable[] = {
-       50, 75, 150, 200, 300, 450, 600, 900, 1200, 1800, 2400, 3600,
-       4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200,
-       230400, 460800, 921600
-};
-
-#define        SC26198_NRBAUDS         ARRAY_SIZE(sc26198_baudtable)
-
-/*****************************************************************************/
-
-/*
- *     Define the driver info for a user level control device. Used mainly
- *     to get at port stats - only not using the port device itself.
- */
-static const struct file_operations    stl_fsiomem = {
-       .owner          = THIS_MODULE,
-       .unlocked_ioctl = stl_memioctl,
-       .llseek         = noop_llseek,
-};
-
-static struct class *stallion_class;
-
-static void stl_cd_change(struct stlport *portp)
-{
-       unsigned int oldsigs = portp->sigs;
-       struct tty_struct *tty = tty_port_tty_get(&portp->port);
-
-       if (!tty)
-               return;
-
-       portp->sigs = stl_getsignals(portp);
-
-       if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0))
-               wake_up_interruptible(&portp->port.open_wait);
-
-       if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0))
-               if (portp->port.flags & ASYNC_CHECK_CD)
-                       tty_hangup(tty);
-       tty_kref_put(tty);
-}
-
-/*
- *     Check for any arguments passed in on the module load command line.
- */
-
-/*****************************************************************************/
-
-/*
- *     Parse the supplied argument string, into the board conf struct.
- */
-
-static int __init stl_parsebrd(struct stlconf *confp, char **argp)
-{
-       char    *sp;
-       unsigned int i;
-
-       pr_debug("stl_parsebrd(confp=%p,argp=%p)\n", confp, argp);
-
-       if ((argp[0] == NULL) || (*argp[0] == 0))
-               return 0;
-
-       for (sp = argp[0], i = 0; (*sp != 0) && (i < 25); sp++, i++)
-               *sp = tolower(*sp);
-
-       for (i = 0; i < ARRAY_SIZE(stl_brdstr); i++)
-               if (strcmp(stl_brdstr[i].name, argp[0]) == 0)
-                       break;
-
-       if (i == ARRAY_SIZE(stl_brdstr)) {
-               printk("STALLION: unknown board name, %s?\n", argp[0]);
-               return 0;
-       }
-
-       confp->brdtype = stl_brdstr[i].type;
-
-       i = 1;
-       if ((argp[i] != NULL) && (*argp[i] != 0))
-               confp->ioaddr1 = simple_strtoul(argp[i], NULL, 0);
-       i++;
-       if (confp->brdtype == BRD_ECH) {
-               if ((argp[i] != NULL) && (*argp[i] != 0))
-                       confp->ioaddr2 = simple_strtoul(argp[i], NULL, 0);
-               i++;
-       }
-       if ((argp[i] != NULL) && (*argp[i] != 0))
-               confp->irq = simple_strtoul(argp[i], NULL, 0);
-       return 1;
-}
-
-/*****************************************************************************/
-
-/*
- *     Allocate a new board structure. Fill out the basic info in it.
- */
-
-static struct stlbrd *stl_allocbrd(void)
-{
-       struct stlbrd   *brdp;
-
-       brdp = kzalloc(sizeof(struct stlbrd), GFP_KERNEL);
-       if (!brdp) {
-               printk("STALLION: failed to allocate memory (size=%Zd)\n",
-                       sizeof(struct stlbrd));
-               return NULL;
-       }
-
-       brdp->magic = STL_BOARDMAGIC;
-       return brdp;
-}
-
-/*****************************************************************************/
-
-static int stl_activate(struct tty_port *port, struct tty_struct *tty)
-{
-       struct stlport *portp = container_of(port, struct stlport, port);
-       if (!portp->tx.buf) {
-               portp->tx.buf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL);
-               if (!portp->tx.buf)
-                       return -ENOMEM;
-               portp->tx.head = portp->tx.buf;
-               portp->tx.tail = portp->tx.buf;
-       }
-       stl_setport(portp, tty->termios);
-       portp->sigs = stl_getsignals(portp);
-       stl_setsignals(portp, 1, 1);
-       stl_enablerxtx(portp, 1, 1);
-       stl_startrxtx(portp, 1, 0);
-       return 0;
-}
-
-static int stl_open(struct tty_struct *tty, struct file *filp)
-{
-       struct stlport  *portp;
-       struct stlbrd   *brdp;
-       unsigned int    minordev, brdnr, panelnr;
-       int             portnr;
-
-       pr_debug("stl_open(tty=%p,filp=%p): device=%s\n", tty, filp, tty->name);
-
-       minordev = tty->index;
-       brdnr = MINOR2BRD(minordev);
-       if (brdnr >= stl_nrbrds)
-               return -ENODEV;
-       brdp = stl_brds[brdnr];
-       if (brdp == NULL)
-               return -ENODEV;
-
-       minordev = MINOR2PORT(minordev);
-       for (portnr = -1, panelnr = 0; panelnr < STL_MAXPANELS; panelnr++) {
-               if (brdp->panels[panelnr] == NULL)
-                       break;
-               if (minordev < brdp->panels[panelnr]->nrports) {
-                       portnr = minordev;
-                       break;
-               }
-               minordev -= brdp->panels[panelnr]->nrports;
-       }
-       if (portnr < 0)
-               return -ENODEV;
-
-       portp = brdp->panels[panelnr]->ports[portnr];
-       if (portp == NULL)
-               return -ENODEV;
-
-       tty->driver_data = portp;
-       return tty_port_open(&portp->port, tty, filp);
-
-}
-
-/*****************************************************************************/
-
-static int stl_carrier_raised(struct tty_port *port)
-{
-       struct stlport *portp = container_of(port, struct stlport, port);
-       return (portp->sigs & TIOCM_CD) ? 1 : 0;
-}
-
-static void stl_dtr_rts(struct tty_port *port, int on)
-{
-       struct stlport *portp = container_of(port, struct stlport, port);
-       /* Takes brd_lock internally */
-       stl_setsignals(portp, on, on);
-}
-
-/*****************************************************************************/
-
-static void stl_flushbuffer(struct tty_struct *tty)
-{
-       struct stlport  *portp;
-
-       pr_debug("stl_flushbuffer(tty=%p)\n", tty);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-
-       stl_flush(portp);
-       tty_wakeup(tty);
-}
-
-/*****************************************************************************/
-
-static void stl_waituntilsent(struct tty_struct *tty, int timeout)
-{
-       struct stlport  *portp;
-       unsigned long   tend;
-
-       pr_debug("stl_waituntilsent(tty=%p,timeout=%d)\n", tty, timeout);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-
-       if (timeout == 0)
-               timeout = HZ;
-       tend = jiffies + timeout;
-
-       while (stl_datastate(portp)) {
-               if (signal_pending(current))
-                       break;
-               msleep_interruptible(20);
-               if (time_after_eq(jiffies, tend))
-                       break;
-       }
-}
-
-/*****************************************************************************/
-
-static void stl_shutdown(struct tty_port *port)
-{
-       struct stlport *portp = container_of(port, struct stlport, port);
-       stl_disableintrs(portp);
-       stl_enablerxtx(portp, 0, 0);
-       stl_flush(portp);
-       portp->istate = 0;
-       if (portp->tx.buf != NULL) {
-               kfree(portp->tx.buf);
-               portp->tx.buf = NULL;
-               portp->tx.head = NULL;
-               portp->tx.tail = NULL;
-       }
-}
-
-static void stl_close(struct tty_struct *tty, struct file *filp)
-{
-       struct stlport*portp;
-       pr_debug("stl_close(tty=%p,filp=%p)\n", tty, filp);
-
-       portp = tty->driver_data;
-       if(portp == NULL)
-               return;
-       tty_port_close(&portp->port, tty, filp);
-}
-
-/*****************************************************************************/
-
-/*
- *     Write routine. Take data and stuff it in to the TX ring queue.
- *     If transmit interrupts are not running then start them.
- */
-
-static int stl_write(struct tty_struct *tty, const unsigned char *buf, int count)
-{
-       struct stlport  *portp;
-       unsigned int    len, stlen;
-       unsigned char   *chbuf;
-       char            *head, *tail;
-
-       pr_debug("stl_write(tty=%p,buf=%p,count=%d)\n", tty, buf, count);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return 0;
-       if (portp->tx.buf == NULL)
-               return 0;
-
-/*
- *     If copying direct from user space we must cater for page faults,
- *     causing us to "sleep" here for a while. To handle this copy in all
- *     the data we need now, into a local buffer. Then when we got it all
- *     copy it into the TX buffer.
- */
-       chbuf = (unsigned char *) buf;
-
-       head = portp->tx.head;
-       tail = portp->tx.tail;
-       if (head >= tail) {
-               len = STL_TXBUFSIZE - (head - tail) - 1;
-               stlen = STL_TXBUFSIZE - (head - portp->tx.buf);
-       } else {
-               len = tail - head - 1;
-               stlen = len;
-       }
-
-       len = min(len, (unsigned int)count);
-       count = 0;
-       while (len > 0) {
-               stlen = min(len, stlen);
-               memcpy(head, chbuf, stlen);
-               len -= stlen;
-               chbuf += stlen;
-               count += stlen;
-               head += stlen;
-               if (head >= (portp->tx.buf + STL_TXBUFSIZE)) {
-                       head = portp->tx.buf;
-                       stlen = tail - head;
-               }
-       }
-       portp->tx.head = head;
-
-       clear_bit(ASYI_TXLOW, &portp->istate);
-       stl_startrxtx(portp, -1, 1);
-
-       return count;
-}
-
-/*****************************************************************************/
-
-static int stl_putchar(struct tty_struct *tty, unsigned char ch)
-{
-       struct stlport  *portp;
-       unsigned int    len;
-       char            *head, *tail;
-
-       pr_debug("stl_putchar(tty=%p,ch=%x)\n", tty, ch);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return -EINVAL;
-       if (portp->tx.buf == NULL)
-               return -EINVAL;
-
-       head = portp->tx.head;
-       tail = portp->tx.tail;
-
-       len = (head >= tail) ? (STL_TXBUFSIZE - (head - tail)) : (tail - head);
-       len--;
-
-       if (len > 0) {
-               *head++ = ch;
-               if (head >= (portp->tx.buf + STL_TXBUFSIZE))
-                       head = portp->tx.buf;
-       }       
-       portp->tx.head = head;
-       return 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     If there are any characters in the buffer then make sure that TX
- *     interrupts are on and get'em out. Normally used after the putchar
- *     routine has been called.
- */
-
-static void stl_flushchars(struct tty_struct *tty)
-{
-       struct stlport  *portp;
-
-       pr_debug("stl_flushchars(tty=%p)\n", tty);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-       if (portp->tx.buf == NULL)
-               return;
-
-       stl_startrxtx(portp, -1, 1);
-}
-
-/*****************************************************************************/
-
-static int stl_writeroom(struct tty_struct *tty)
-{
-       struct stlport  *portp;
-       char            *head, *tail;
-
-       pr_debug("stl_writeroom(tty=%p)\n", tty);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return 0;
-       if (portp->tx.buf == NULL)
-               return 0;
-
-       head = portp->tx.head;
-       tail = portp->tx.tail;
-       return (head >= tail) ? (STL_TXBUFSIZE - (head - tail) - 1) : (tail - head - 1);
-}
-
-/*****************************************************************************/
-
-/*
- *     Return number of chars in the TX buffer. Normally we would just
- *     calculate the number of chars in the buffer and return that, but if
- *     the buffer is empty and TX interrupts are still on then we return
- *     that the buffer still has 1 char in it. This way whoever called us
- *     will not think that ALL chars have drained - since the UART still
- *     must have some chars in it (we are busy after all).
- */
-
-static int stl_charsinbuffer(struct tty_struct *tty)
-{
-       struct stlport  *portp;
-       unsigned int    size;
-       char            *head, *tail;
-
-       pr_debug("stl_charsinbuffer(tty=%p)\n", tty);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return 0;
-       if (portp->tx.buf == NULL)
-               return 0;
-
-       head = portp->tx.head;
-       tail = portp->tx.tail;
-       size = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
-       if ((size == 0) && test_bit(ASYI_TXBUSY, &portp->istate))
-               size = 1;
-       return size;
-}
-
-/*****************************************************************************/
-
-/*
- *     Generate the serial struct info.
- */
-
-static int stl_getserial(struct stlport *portp, struct serial_struct __user *sp)
-{
-       struct serial_struct    sio;
-       struct stlbrd           *brdp;
-
-       pr_debug("stl_getserial(portp=%p,sp=%p)\n", portp, sp);
-
-       memset(&sio, 0, sizeof(struct serial_struct));
-
-       mutex_lock(&portp->port.mutex);
-       sio.line = portp->portnr;
-       sio.port = portp->ioaddr;
-       sio.flags = portp->port.flags;
-       sio.baud_base = portp->baud_base;
-       sio.close_delay = portp->close_delay;
-       sio.closing_wait = portp->closing_wait;
-       sio.custom_divisor = portp->custom_divisor;
-       sio.hub6 = 0;
-       if (portp->uartp == &stl_cd1400uart) {
-               sio.type = PORT_CIRRUS;
-               sio.xmit_fifo_size = CD1400_TXFIFOSIZE;
-       } else {
-               sio.type = PORT_UNKNOWN;
-               sio.xmit_fifo_size = SC26198_TXFIFOSIZE;
-       }
-
-       brdp = stl_brds[portp->brdnr];
-       if (brdp != NULL)
-               sio.irq = brdp->irq;
-       mutex_unlock(&portp->port.mutex);
-
-       return copy_to_user(sp, &sio, sizeof(struct serial_struct)) ? -EFAULT : 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Set port according to the serial struct info.
- *     At this point we do not do any auto-configure stuff, so we will
- *     just quietly ignore any requests to change irq, etc.
- */
-
-static int stl_setserial(struct tty_struct *tty, struct serial_struct __user *sp)
-{
-       struct stlport *        portp = tty->driver_data;
-       struct serial_struct    sio;
-
-       pr_debug("stl_setserial(portp=%p,sp=%p)\n", portp, sp);
-
-       if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
-               return -EFAULT;
-       mutex_lock(&portp->port.mutex);
-       if (!capable(CAP_SYS_ADMIN)) {
-               if ((sio.baud_base != portp->baud_base) ||
-                   (sio.close_delay != portp->close_delay) ||
-                   ((sio.flags & ~ASYNC_USR_MASK) !=
-                   (portp->port.flags & ~ASYNC_USR_MASK))) {
-                       mutex_unlock(&portp->port.mutex);
-                       return -EPERM;
-               }
-       } 
-
-       portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) |
-               (sio.flags & ASYNC_USR_MASK);
-       portp->baud_base = sio.baud_base;
-       portp->close_delay = sio.close_delay;
-       portp->closing_wait = sio.closing_wait;
-       portp->custom_divisor = sio.custom_divisor;
-       mutex_unlock(&portp->port.mutex);
-       stl_setport(portp, tty->termios);
-       return 0;
-}
-
-/*****************************************************************************/
-
-static int stl_tiocmget(struct tty_struct *tty)
-{
-       struct stlport  *portp;
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return -ENODEV;
-       if (tty->flags & (1 << TTY_IO_ERROR))
-               return -EIO;
-
-       return stl_getsignals(portp);
-}
-
-static int stl_tiocmset(struct tty_struct *tty,
-                       unsigned int set, unsigned int clear)
-{
-       struct stlport  *portp;
-       int rts = -1, dtr = -1;
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return -ENODEV;
-       if (tty->flags & (1 << TTY_IO_ERROR))
-               return -EIO;
-
-       if (set & TIOCM_RTS)
-               rts = 1;
-       if (set & TIOCM_DTR)
-               dtr = 1;
-       if (clear & TIOCM_RTS)
-               rts = 0;
-       if (clear & TIOCM_DTR)
-               dtr = 0;
-
-       stl_setsignals(portp, dtr, rts);
-       return 0;
-}
-
-static int stl_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
-{
-       struct stlport  *portp;
-       int             rc;
-       void __user *argp = (void __user *)arg;
-
-       pr_debug("stl_ioctl(tty=%p,cmd=%x,arg=%lx)\n", tty, cmd, arg);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return -ENODEV;
-
-       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-           (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS))
-               if (tty->flags & (1 << TTY_IO_ERROR))
-                       return -EIO;
-
-       rc = 0;
-
-       switch (cmd) {
-       case TIOCGSERIAL:
-               rc = stl_getserial(portp, argp);
-               break;
-       case TIOCSSERIAL:
-               rc = stl_setserial(tty, argp);
-               break;
-       case COM_GETPORTSTATS:
-               rc = stl_getportstats(tty, portp, argp);
-               break;
-       case COM_CLRPORTSTATS:
-               rc = stl_clrportstats(portp, argp);
-               break;
-       case TIOCSERCONFIG:
-       case TIOCSERGWILD:
-       case TIOCSERSWILD:
-       case TIOCSERGETLSR:
-       case TIOCSERGSTRUCT:
-       case TIOCSERGETMULTI:
-       case TIOCSERSETMULTI:
-       default:
-               rc = -ENOIOCTLCMD;
-               break;
-       }
-       return rc;
-}
-
-/*****************************************************************************/
-
-/*
- *     Start the transmitter again. Just turn TX interrupts back on.
- */
-
-static void stl_start(struct tty_struct *tty)
-{
-       struct stlport  *portp;
-
-       pr_debug("stl_start(tty=%p)\n", tty);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-       stl_startrxtx(portp, -1, 1);
-}
-
-/*****************************************************************************/
-
-static void stl_settermios(struct tty_struct *tty, struct ktermios *old)
-{
-       struct stlport  *portp;
-       struct ktermios *tiosp;
-
-       pr_debug("stl_settermios(tty=%p,old=%p)\n", tty, old);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-
-       tiosp = tty->termios;
-       if ((tiosp->c_cflag == old->c_cflag) &&
-           (tiosp->c_iflag == old->c_iflag))
-               return;
-
-       stl_setport(portp, tiosp);
-       stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0),
-               -1);
-       if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) {
-               tty->hw_stopped = 0;
-               stl_start(tty);
-       }
-       if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL))
-               wake_up_interruptible(&portp->port.open_wait);
-}
-
-/*****************************************************************************/
-
-/*
- *     Attempt to flow control who ever is sending us data. Based on termios
- *     settings use software or/and hardware flow control.
- */
-
-static void stl_throttle(struct tty_struct *tty)
-{
-       struct stlport  *portp;
-
-       pr_debug("stl_throttle(tty=%p)\n", tty);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-       stl_flowctrl(portp, 0);
-}
-
-/*****************************************************************************/
-
-/*
- *     Unflow control the device sending us data...
- */
-
-static void stl_unthrottle(struct tty_struct *tty)
-{
-       struct stlport  *portp;
-
-       pr_debug("stl_unthrottle(tty=%p)\n", tty);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-       stl_flowctrl(portp, 1);
-}
-
-/*****************************************************************************/
-
-/*
- *     Stop the transmitter. Basically to do this we will just turn TX
- *     interrupts off.
- */
-
-static void stl_stop(struct tty_struct *tty)
-{
-       struct stlport  *portp;
-
-       pr_debug("stl_stop(tty=%p)\n", tty);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-       stl_startrxtx(portp, -1, 0);
-}
-
-/*****************************************************************************/
-
-/*
- *     Hangup this port. This is pretty much like closing the port, only
- *     a little more brutal. No waiting for data to drain. Shutdown the
- *     port and maybe drop signals.
- */
-
-static void stl_hangup(struct tty_struct *tty)
-{
-       struct stlport  *portp = tty->driver_data;
-       pr_debug("stl_hangup(tty=%p)\n", tty);
-
-       if (portp == NULL)
-               return;
-       tty_port_hangup(&portp->port);
-}
-
-/*****************************************************************************/
-
-static int stl_breakctl(struct tty_struct *tty, int state)
-{
-       struct stlport  *portp;
-
-       pr_debug("stl_breakctl(tty=%p,state=%d)\n", tty, state);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return -EINVAL;
-
-       stl_sendbreak(portp, ((state == -1) ? 1 : 2));
-       return 0;
-}
-
-/*****************************************************************************/
-
-static void stl_sendxchar(struct tty_struct *tty, char ch)
-{
-       struct stlport  *portp;
-
-       pr_debug("stl_sendxchar(tty=%p,ch=%x)\n", tty, ch);
-
-       portp = tty->driver_data;
-       if (portp == NULL)
-               return;
-
-       if (ch == STOP_CHAR(tty))
-               stl_sendflow(portp, 0);
-       else if (ch == START_CHAR(tty))
-               stl_sendflow(portp, 1);
-       else
-               stl_putchar(tty, ch);
-}
-
-static void stl_portinfo(struct seq_file *m, struct stlport *portp, int portnr)
-{
-       int     sigs;
-       char sep;
-
-       seq_printf(m, "%d: uart:%s tx:%d rx:%d",
-               portnr, (portp->hwid == 1) ? "SC26198" : "CD1400",
-               (int) portp->stats.txtotal, (int) portp->stats.rxtotal);
-
-       if (portp->stats.rxframing)
-               seq_printf(m, " fe:%d", (int) portp->stats.rxframing);
-       if (portp->stats.rxparity)
-               seq_printf(m, " pe:%d", (int) portp->stats.rxparity);
-       if (portp->stats.rxbreaks)
-               seq_printf(m, " brk:%d", (int) portp->stats.rxbreaks);
-       if (portp->stats.rxoverrun)
-               seq_printf(m, " oe:%d", (int) portp->stats.rxoverrun);
-
-       sigs = stl_getsignals(portp);
-       sep = ' ';
-       if (sigs & TIOCM_RTS) {
-               seq_printf(m, "%c%s", sep, "RTS");
-               sep = '|';
-       }
-       if (sigs & TIOCM_CTS) {
-               seq_printf(m, "%c%s", sep, "CTS");
-               sep = '|';
-       }
-       if (sigs & TIOCM_DTR) {
-               seq_printf(m, "%c%s", sep, "DTR");
-               sep = '|';
-       }
-       if (sigs & TIOCM_CD) {
-               seq_printf(m, "%c%s", sep, "DCD");
-               sep = '|';
-       }
-       if (sigs & TIOCM_DSR) {
-               seq_printf(m, "%c%s", sep, "DSR");
-               sep = '|';
-       }
-       seq_putc(m, '\n');
-}
-
-/*****************************************************************************/
-
-/*
- *     Port info, read from the /proc file system.
- */
-
-static int stl_proc_show(struct seq_file *m, void *v)
-{
-       struct stlbrd   *brdp;
-       struct stlpanel *panelp;
-       struct stlport  *portp;
-       unsigned int    brdnr, panelnr, portnr;
-       int             totalport;
-
-       totalport = 0;
-
-       seq_printf(m, "%s: version %s\n", stl_drvtitle, stl_drvversion);
-
-/*
- *     We scan through for each board, panel and port. The offset is
- *     calculated on the fly, and irrelevant ports are skipped.
- */
-       for (brdnr = 0; brdnr < stl_nrbrds; brdnr++) {
-               brdp = stl_brds[brdnr];
-               if (brdp == NULL)
-                       continue;
-               if (brdp->state == 0)
-                       continue;
-
-               totalport = brdnr * STL_MAXPORTS;
-               for (panelnr = 0; panelnr < brdp->nrpanels; panelnr++) {
-                       panelp = brdp->panels[panelnr];
-                       if (panelp == NULL)
-                               continue;
-
-                       for (portnr = 0; portnr < panelp->nrports; portnr++,
-                           totalport++) {
-                               portp = panelp->ports[portnr];
-                               if (portp == NULL)
-                                       continue;
-                               stl_portinfo(m, portp, totalport);
-                       }
-               }
-       }
-       return 0;
-}
-
-static int stl_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, stl_proc_show, NULL);
-}
-
-static const struct file_operations stl_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = stl_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-/*****************************************************************************/
-
-/*
- *     All board interrupts are vectored through here first. This code then
- *     calls off to the approrpriate board interrupt handlers.
- */
-
-static irqreturn_t stl_intr(int irq, void *dev_id)
-{
-       struct stlbrd *brdp = dev_id;
-
-       pr_debug("stl_intr(brdp=%p,irq=%d)\n", brdp, brdp->irq);
-
-       return IRQ_RETVAL((* brdp->isr)(brdp));
-}
-
-/*****************************************************************************/
-
-/*
- *     Interrupt service routine for EasyIO board types.
- */
-
-static int stl_eiointr(struct stlbrd *brdp)
-{
-       struct stlpanel *panelp;
-       unsigned int    iobase;
-       int             handled = 0;
-
-       spin_lock(&brd_lock);
-       panelp = brdp->panels[0];
-       iobase = panelp->iobase;
-       while (inb(brdp->iostatus) & EIO_INTRPEND) {
-               handled = 1;
-               (* panelp->isr)(panelp, iobase);
-       }
-       spin_unlock(&brd_lock);
-       return handled;
-}
-
-/*****************************************************************************/
-
-/*
- *     Interrupt service routine for ECH-AT board types.
- */
-
-static int stl_echatintr(struct stlbrd *brdp)
-{
-       struct stlpanel *panelp;
-       unsigned int    ioaddr, bnknr;
-       int             handled = 0;
-
-       outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
-
-       while (inb(brdp->iostatus) & ECH_INTRPEND) {
-               handled = 1;
-               for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) {
-                       ioaddr = brdp->bnkstataddr[bnknr];
-                       if (inb(ioaddr) & ECH_PNLINTRPEND) {
-                               panelp = brdp->bnk2panel[bnknr];
-                               (* panelp->isr)(panelp, (ioaddr & 0xfffc));
-                       }
-               }
-       }
-
-       outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
-
-       return handled;
-}
-
-/*****************************************************************************/
-
-/*
- *     Interrupt service routine for ECH-MCA board types.
- */
-
-static int stl_echmcaintr(struct stlbrd *brdp)
-{
-       struct stlpanel *panelp;
-       unsigned int    ioaddr, bnknr;
-       int             handled = 0;
-
-       while (inb(brdp->iostatus) & ECH_INTRPEND) {
-               handled = 1;
-               for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) {
-                       ioaddr = brdp->bnkstataddr[bnknr];
-                       if (inb(ioaddr) & ECH_PNLINTRPEND) {
-                               panelp = brdp->bnk2panel[bnknr];
-                               (* panelp->isr)(panelp, (ioaddr & 0xfffc));
-                       }
-               }
-       }
-       return handled;
-}
-
-/*****************************************************************************/
-
-/*
- *     Interrupt service routine for ECH-PCI board types.
- */
-
-static int stl_echpciintr(struct stlbrd *brdp)
-{
-       struct stlpanel *panelp;
-       unsigned int    ioaddr, bnknr, recheck;
-       int             handled = 0;
-
-       while (1) {
-               recheck = 0;
-               for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) {
-                       outb(brdp->bnkpageaddr[bnknr], brdp->ioctrl);
-                       ioaddr = brdp->bnkstataddr[bnknr];
-                       if (inb(ioaddr) & ECH_PNLINTRPEND) {
-                               panelp = brdp->bnk2panel[bnknr];
-                               (* panelp->isr)(panelp, (ioaddr & 0xfffc));
-                               recheck++;
-                               handled = 1;
-                       }
-               }
-               if (! recheck)
-                       break;
-       }
-       return handled;
-}
-
-/*****************************************************************************/
-
-/*
- *     Interrupt service routine for ECH-8/64-PCI board types.
- */
-
-static int stl_echpci64intr(struct stlbrd *brdp)
-{
-       struct stlpanel *panelp;
-       unsigned int    ioaddr, bnknr;
-       int             handled = 0;
-
-       while (inb(brdp->ioctrl) & 0x1) {
-               handled = 1;
-               for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) {
-                       ioaddr = brdp->bnkstataddr[bnknr];
-                       if (inb(ioaddr) & ECH_PNLINTRPEND) {
-                               panelp = brdp->bnk2panel[bnknr];
-                               (* panelp->isr)(panelp, (ioaddr & 0xfffc));
-                       }
-               }
-       }
-
-       return handled;
-}
-
-/*****************************************************************************/
-
-/*
- *     Initialize all the ports on a panel.
- */
-
-static int __devinit stl_initports(struct stlbrd *brdp, struct stlpanel *panelp)
-{
-       struct stlport *portp;
-       unsigned int i;
-       int chipmask;
-
-       pr_debug("stl_initports(brdp=%p,panelp=%p)\n", brdp, panelp);
-
-       chipmask = stl_panelinit(brdp, panelp);
-
-/*
- *     All UART's are initialized (if found!). Now go through and setup
- *     each ports data structures.
- */
-       for (i = 0; i < panelp->nrports; i++) {
-               portp = kzalloc(sizeof(struct stlport), GFP_KERNEL);
-               if (!portp) {
-                       printk("STALLION: failed to allocate memory "
-                               "(size=%Zd)\n", sizeof(struct stlport));
-                       break;
-               }
-               tty_port_init(&portp->port);
-               portp->port.ops = &stl_port_ops;
-               portp->magic = STL_PORTMAGIC;
-               portp->portnr = i;
-               portp->brdnr = panelp->brdnr;
-               portp->panelnr = panelp->panelnr;
-               portp->uartp = panelp->uartp;
-               portp->clk = brdp->clk;
-               portp->baud_base = STL_BAUDBASE;
-               portp->close_delay = STL_CLOSEDELAY;
-               portp->closing_wait = 30 * HZ;
-               init_waitqueue_head(&portp->port.open_wait);
-               init_waitqueue_head(&portp->port.close_wait);
-               portp->stats.brd = portp->brdnr;
-               portp->stats.panel = portp->panelnr;
-               portp->stats.port = portp->portnr;
-               panelp->ports[i] = portp;
-               stl_portinit(brdp, panelp, portp);
-       }
-
-       return 0;
-}
-
-static void stl_cleanup_panels(struct stlbrd *brdp)
-{
-       struct stlpanel *panelp;
-       struct stlport *portp;
-       unsigned int j, k;
-       struct tty_struct *tty;
-
-       for (j = 0; j < STL_MAXPANELS; j++) {
-               panelp = brdp->panels[j];
-               if (panelp == NULL)
-                       continue;
-               for (k = 0; k < STL_PORTSPERPANEL; k++) {
-                       portp = panelp->ports[k];
-                       if (portp == NULL)
-                               continue;
-                       tty = tty_port_tty_get(&portp->port);
-                       if (tty != NULL) {
-                               stl_hangup(tty);
-                               tty_kref_put(tty);
-                       }
-                       kfree(portp->tx.buf);
-                       kfree(portp);
-               }
-               kfree(panelp);
-       }
-}
-
-/*****************************************************************************/
-
-/*
- *     Try to find and initialize an EasyIO board.
- */
-
-static int __devinit stl_initeio(struct stlbrd *brdp)
-{
-       struct stlpanel *panelp;
-       unsigned int    status;
-       char            *name;
-       int             retval;
-
-       pr_debug("stl_initeio(brdp=%p)\n", brdp);
-
-       brdp->ioctrl = brdp->ioaddr1 + 1;
-       brdp->iostatus = brdp->ioaddr1 + 2;
-
-       status = inb(brdp->iostatus);
-       if ((status & EIO_IDBITMASK) == EIO_MK3)
-               brdp->ioctrl++;
-
-/*
- *     Handle board specific stuff now. The real difference is PCI
- *     or not PCI.
- */
-       if (brdp->brdtype == BRD_EASYIOPCI) {
-               brdp->iosize1 = 0x80;
-               brdp->iosize2 = 0x80;
-               name = "serial(EIO-PCI)";
-               outb(0x41, (brdp->ioaddr2 + 0x4c));
-       } else {
-               brdp->iosize1 = 8;
-               name = "serial(EIO)";
-               if ((brdp->irq < 0) || (brdp->irq > 15) ||
-                   (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
-                       printk("STALLION: invalid irq=%d for brd=%d\n",
-                               brdp->irq, brdp->brdnr);
-                       retval = -EINVAL;
-                       goto err;
-               }
-               outb((stl_vecmap[brdp->irq] | EIO_0WS |
-                       ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)),
-                       brdp->ioctrl);
-       }
-
-       retval = -EBUSY;
-       if (!request_region(brdp->ioaddr1, brdp->iosize1, name)) {
-               printk(KERN_WARNING "STALLION: Warning, board %d I/O address "
-                       "%x conflicts with another device\n", brdp->brdnr, 
-                       brdp->ioaddr1);
-               goto err;
-       }
-       
-       if (brdp->iosize2 > 0)
-               if (!request_region(brdp->ioaddr2, brdp->iosize2, name)) {
-                       printk(KERN_WARNING "STALLION: Warning, board %d I/O "
-                               "address %x conflicts with another device\n",
-                               brdp->brdnr, brdp->ioaddr2);
-                       printk(KERN_WARNING "STALLION: Warning, also "
-                               "releasing board %d I/O address %x \n", 
-                               brdp->brdnr, brdp->ioaddr1);
-                       goto err_rel1;
-               }
-
-/*
- *     Everything looks OK, so let's go ahead and probe for the hardware.
- */
-       brdp->clk = CD1400_CLK;
-       brdp->isr = stl_eiointr;
-
-       retval = -ENODEV;
-       switch (status & EIO_IDBITMASK) {
-       case EIO_8PORTM:
-               brdp->clk = CD1400_CLK8M;
-               /* fall thru */
-       case EIO_8PORTRS:
-       case EIO_8PORTDI:
-               brdp->nrports = 8;
-               break;
-       case EIO_4PORTRS:
-               brdp->nrports = 4;
-               break;
-       case EIO_MK3:
-               switch (status & EIO_BRDMASK) {
-               case ID_BRD4:
-                       brdp->nrports = 4;
-                       break;
-               case ID_BRD8:
-                       brdp->nrports = 8;
-                       break;
-               case ID_BRD16:
-                       brdp->nrports = 16;
-                       break;
-               default:
-                       goto err_rel2;
-               }
-               break;
-       default:
-               goto err_rel2;
-       }
-
-/*
- *     We have verified that the board is actually present, so now we
- *     can complete the setup.
- */
-
-       panelp = kzalloc(sizeof(struct stlpanel), GFP_KERNEL);
-       if (!panelp) {
-               printk(KERN_WARNING "STALLION: failed to allocate memory "
-                       "(size=%Zd)\n", sizeof(struct stlpanel));
-               retval = -ENOMEM;
-               goto err_rel2;
-       }
-
-       panelp->magic = STL_PANELMAGIC;
-       panelp->brdnr = brdp->brdnr;
-       panelp->panelnr = 0;
-       panelp->nrports = brdp->nrports;
-       panelp->iobase = brdp->ioaddr1;
-       panelp->hwid = status;
-       if ((status & EIO_IDBITMASK) == EIO_MK3) {
-               panelp->uartp = &stl_sc26198uart;
-               panelp->isr = stl_sc26198intr;
-       } else {
-               panelp->uartp = &stl_cd1400uart;
-               panelp->isr = stl_cd1400eiointr;
-       }
-
-       brdp->panels[0] = panelp;
-       brdp->nrpanels = 1;
-       brdp->state |= BRD_FOUND;
-       brdp->hwid = status;
-       if (request_irq(brdp->irq, stl_intr, IRQF_SHARED, name, brdp) != 0) {
-               printk("STALLION: failed to register interrupt "
-                   "routine for %s irq=%d\n", name, brdp->irq);
-               retval = -ENODEV;
-               goto err_fr;
-       }
-
-       return 0;
-err_fr:
-       stl_cleanup_panels(brdp);
-err_rel2:
-       if (brdp->iosize2 > 0)
-               release_region(brdp->ioaddr2, brdp->iosize2);
-err_rel1:
-       release_region(brdp->ioaddr1, brdp->iosize1);
-err:
-       return retval;
-}
-
-/*****************************************************************************/
-
-/*
- *     Try to find an ECH board and initialize it. This code is capable of
- *     dealing with all types of ECH board.
- */
-
-static int __devinit stl_initech(struct stlbrd *brdp)
-{
-       struct stlpanel *panelp;
-       unsigned int    status, nxtid, ioaddr, conflict, panelnr, banknr, i;
-       int             retval;
-       char            *name;
-
-       pr_debug("stl_initech(brdp=%p)\n", brdp);
-
-       status = 0;
-       conflict = 0;
-
-/*
- *     Set up the initial board register contents for boards. This varies a
- *     bit between the different board types. So we need to handle each
- *     separately. Also do a check that the supplied IRQ is good.
- */
-       switch (brdp->brdtype) {
-
-       case BRD_ECH:
-               brdp->isr = stl_echatintr;
-               brdp->ioctrl = brdp->ioaddr1 + 1;
-               brdp->iostatus = brdp->ioaddr1 + 1;
-               status = inb(brdp->iostatus);
-               if ((status & ECH_IDBITMASK) != ECH_ID) {
-                       retval = -ENODEV;
-                       goto err;
-               }
-               if ((brdp->irq < 0) || (brdp->irq > 15) ||
-                   (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
-                       printk("STALLION: invalid irq=%d for brd=%d\n",
-                               brdp->irq, brdp->brdnr);
-                       retval = -EINVAL;
-                       goto err;
-               }
-               status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1);
-               status |= (stl_vecmap[brdp->irq] << 1);
-               outb((status | ECH_BRDRESET), brdp->ioaddr1);
-               brdp->ioctrlval = ECH_INTENABLE |
-                       ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE);
-               for (i = 0; i < 10; i++)
-                       outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
-               brdp->iosize1 = 2;
-               brdp->iosize2 = 32;
-               name = "serial(EC8/32)";
-               outb(status, brdp->ioaddr1);
-               break;
-
-       case BRD_ECHMC:
-               brdp->isr = stl_echmcaintr;
-               brdp->ioctrl = brdp->ioaddr1 + 0x20;
-               brdp->iostatus = brdp->ioctrl;
-               status = inb(brdp->iostatus);
-               if ((status & ECH_IDBITMASK) != ECH_ID) {
-                       retval = -ENODEV;
-                       goto err;
-               }
-               if ((brdp->irq < 0) || (brdp->irq > 15) ||
-                   (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
-                       printk("STALLION: invalid irq=%d for brd=%d\n",
-                               brdp->irq, brdp->brdnr);
-                       retval = -EINVAL;
-                       goto err;
-               }
-               outb(ECHMC_BRDRESET, brdp->ioctrl);
-               outb(ECHMC_INTENABLE, brdp->ioctrl);
-               brdp->iosize1 = 64;
-               name = "serial(EC8/32-MC)";
-               break;
-
-       case BRD_ECHPCI:
-               brdp->isr = stl_echpciintr;
-               brdp->ioctrl = brdp->ioaddr1 + 2;
-               brdp->iosize1 = 4;
-               brdp->iosize2 = 8;
-               name = "serial(EC8/32-PCI)";
-               break;
-
-       case BRD_ECH64PCI:
-               brdp->isr = stl_echpci64intr;
-               brdp->ioctrl = brdp->ioaddr2 + 0x40;
-               outb(0x43, (brdp->ioaddr1 + 0x4c));
-               brdp->iosize1 = 0x80;
-               brdp->iosize2 = 0x80;
-               name = "serial(EC8/64-PCI)";
-               break;
-
-       default:
-               printk("STALLION: unknown board type=%d\n", brdp->brdtype);
-               retval = -EINVAL;
-               goto err;
-       }
-
-/*
- *     Check boards for possible IO address conflicts and return fail status 
- *     if an IO conflict found.
- */
-       retval = -EBUSY;
-       if (!request_region(brdp->ioaddr1, brdp->iosize1, name)) {
-               printk(KERN_WARNING "STALLION: Warning, board %d I/O address "
-                       "%x conflicts with another device\n", brdp->brdnr, 
-                       brdp->ioaddr1);
-               goto err;
-       }
-       
-       if (brdp->iosize2 > 0)
-               if (!request_region(brdp->ioaddr2, brdp->iosize2, name)) {
-                       printk(KERN_WARNING "STALLION: Warning, board %d I/O "
-                               "address %x conflicts with another device\n",
-                               brdp->brdnr, brdp->ioaddr2);
-                       printk(KERN_WARNING "STALLION: Warning, also "
-                               "releasing board %d I/O address %x \n", 
-                               brdp->brdnr, brdp->ioaddr1);
-                       goto err_rel1;
-               }
-
-/*
- *     Scan through the secondary io address space looking for panels.
- *     As we find'em allocate and initialize panel structures for each.
- */
-       brdp->clk = CD1400_CLK;
-       brdp->hwid = status;
-
-       ioaddr = brdp->ioaddr2;
-       banknr = 0;
-       panelnr = 0;
-       nxtid = 0;
-
-       for (i = 0; i < STL_MAXPANELS; i++) {
-               if (brdp->brdtype == BRD_ECHPCI) {
-                       outb(nxtid, brdp->ioctrl);
-                       ioaddr = brdp->ioaddr2;
-               }
-               status = inb(ioaddr + ECH_PNLSTATUS);
-               if ((status & ECH_PNLIDMASK) != nxtid)
-                       break;
-               panelp = kzalloc(sizeof(struct stlpanel), GFP_KERNEL);
-               if (!panelp) {
-                       printk("STALLION: failed to allocate memory "
-                               "(size=%Zd)\n", sizeof(struct stlpanel));
-                       retval = -ENOMEM;
-                       goto err_fr;
-               }
-               panelp->magic = STL_PANELMAGIC;
-               panelp->brdnr = brdp->brdnr;
-               panelp->panelnr = panelnr;
-               panelp->iobase = ioaddr;
-               panelp->pagenr = nxtid;
-               panelp->hwid = status;
-               brdp->bnk2panel[banknr] = panelp;
-               brdp->bnkpageaddr[banknr] = nxtid;
-               brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS;
-
-               if (status & ECH_PNLXPID) {
-                       panelp->uartp = &stl_sc26198uart;
-                       panelp->isr = stl_sc26198intr;
-                       if (status & ECH_PNL16PORT) {
-                               panelp->nrports = 16;
-                               brdp->bnk2panel[banknr] = panelp;
-                               brdp->bnkpageaddr[banknr] = nxtid;
-                               brdp->bnkstataddr[banknr++] = ioaddr + 4 +
-                                       ECH_PNLSTATUS;
-                       } else
-                               panelp->nrports = 8;
-               } else {
-                       panelp->uartp = &stl_cd1400uart;
-                       panelp->isr = stl_cd1400echintr;
-                       if (status & ECH_PNL16PORT) {
-                               panelp->nrports = 16;
-                               panelp->ackmask = 0x80;
-                               if (brdp->brdtype != BRD_ECHPCI)
-                                       ioaddr += EREG_BANKSIZE;
-                               brdp->bnk2panel[banknr] = panelp;
-                               brdp->bnkpageaddr[banknr] = ++nxtid;
-                               brdp->bnkstataddr[banknr++] = ioaddr +
-                                       ECH_PNLSTATUS;
-                       } else {
-                               panelp->nrports = 8;
-                               panelp->ackmask = 0xc0;
-                       }
-               }
-
-               nxtid++;
-               ioaddr += EREG_BANKSIZE;
-               brdp->nrports += panelp->nrports;
-               brdp->panels[panelnr++] = panelp;
-               if ((brdp->brdtype != BRD_ECHPCI) &&
-                   (ioaddr >= (brdp->ioaddr2 + brdp->iosize2))) {
-                       retval = -EINVAL;
-                       goto err_fr;
-               }
-       }
-
-       brdp->nrpanels = panelnr;
-       brdp->nrbnks = banknr;
-       if (brdp->brdtype == BRD_ECH)
-               outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
-
-       brdp->state |= BRD_FOUND;
-       if (request_irq(brdp->irq, stl_intr, IRQF_SHARED, name, brdp) != 0) {
-               printk("STALLION: failed to register interrupt "
-                   "routine for %s irq=%d\n", name, brdp->irq);
-               retval = -ENODEV;
-               goto err_fr;
-       }
-
-       return 0;
-err_fr:
-       stl_cleanup_panels(brdp);
-       if (brdp->iosize2 > 0)
-               release_region(brdp->ioaddr2, brdp->iosize2);
-err_rel1:
-       release_region(brdp->ioaddr1, brdp->iosize1);
-err:
-       return retval;
-}
-
-/*****************************************************************************/
-
-/*
- *     Initialize and configure the specified board.
- *     Scan through all the boards in the configuration and see what we
- *     can find. Handle EIO and the ECH boards a little differently here
- *     since the initial search and setup is very different.
- */
-
-static int __devinit stl_brdinit(struct stlbrd *brdp)
-{
-       int i, retval;
-
-       pr_debug("stl_brdinit(brdp=%p)\n", brdp);
-
-       switch (brdp->brdtype) {
-       case BRD_EASYIO:
-       case BRD_EASYIOPCI:
-               retval = stl_initeio(brdp);
-               if (retval)
-                       goto err;
-               break;
-       case BRD_ECH:
-       case BRD_ECHMC:
-       case BRD_ECHPCI:
-       case BRD_ECH64PCI:
-               retval = stl_initech(brdp);
-               if (retval)
-                       goto err;
-               break;
-       default:
-               printk("STALLION: board=%d is unknown board type=%d\n",
-                       brdp->brdnr, brdp->brdtype);
-               retval = -ENODEV;
-               goto err;
-       }
-
-       if ((brdp->state & BRD_FOUND) == 0) {
-               printk("STALLION: %s board not found, board=%d io=%x irq=%d\n",
-                       stl_brdnames[brdp->brdtype], brdp->brdnr,
-                       brdp->ioaddr1, brdp->irq);
-               goto err_free;
-       }
-
-       for (i = 0; i < STL_MAXPANELS; i++)
-               if (brdp->panels[i] != NULL)
-                       stl_initports(brdp, brdp->panels[i]);
-
-       printk("STALLION: %s found, board=%d io=%x irq=%d "
-               "nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype],
-               brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels,
-               brdp->nrports);
-
-       return 0;
-err_free:
-       free_irq(brdp->irq, brdp);
-
-       stl_cleanup_panels(brdp);
-
-       release_region(brdp->ioaddr1, brdp->iosize1);
-       if (brdp->iosize2 > 0)
-               release_region(brdp->ioaddr2, brdp->iosize2);
-err:
-       return retval;
-}
-
-/*****************************************************************************/
-
-/*
- *     Find the next available board number that is free.
- */
-
-static int __devinit stl_getbrdnr(void)
-{
-       unsigned int i;
-
-       for (i = 0; i < STL_MAXBRDS; i++)
-               if (stl_brds[i] == NULL) {
-                       if (i >= stl_nrbrds)
-                               stl_nrbrds = i + 1;
-                       return i;
-               }
-
-       return -1;
-}
-
-/*****************************************************************************/
-/*
- *     We have a Stallion board. Allocate a board structure and
- *     initialize it. Read its IO and IRQ resources from PCI
- *     configuration space.
- */
-
-static int __devinit stl_pciprobe(struct pci_dev *pdev,
-               const struct pci_device_id *ent)
-{
-       struct stlbrd *brdp;
-       unsigned int i, brdtype = ent->driver_data;
-       int brdnr, retval = -ENODEV;
-
-       if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE)
-               goto err;
-
-       retval = pci_enable_device(pdev);
-       if (retval)
-               goto err;
-       brdp = stl_allocbrd();
-       if (brdp == NULL) {
-               retval = -ENOMEM;
-               goto err;
-       }
-       mutex_lock(&stl_brdslock);
-       brdnr = stl_getbrdnr();
-       if (brdnr < 0) {
-               dev_err(&pdev->dev, "too many boards found, "
-                       "maximum supported %d\n", STL_MAXBRDS);
-               mutex_unlock(&stl_brdslock);
-               retval = -ENODEV;
-               goto err_fr;
-       }
-       brdp->brdnr = (unsigned int)brdnr;
-       stl_brds[brdp->brdnr] = brdp;
-       mutex_unlock(&stl_brdslock);
-
-       brdp->brdtype = brdtype;
-       brdp->state |= STL_PROBED;
-
-/*
- *     We have all resources from the board, so let's setup the actual
- *     board structure now.
- */
-       switch (brdtype) {
-       case BRD_ECHPCI:
-               brdp->ioaddr2 = pci_resource_start(pdev, 0);
-               brdp->ioaddr1 = pci_resource_start(pdev, 1);
-               break;
-       case BRD_ECH64PCI:
-               brdp->ioaddr2 = pci_resource_start(pdev, 2);
-               brdp->ioaddr1 = pci_resource_start(pdev, 1);
-               break;
-       case BRD_EASYIOPCI:
-               brdp->ioaddr1 = pci_resource_start(pdev, 2);
-               brdp->ioaddr2 = pci_resource_start(pdev, 1);
-               break;
-       default:
-               dev_err(&pdev->dev, "unknown PCI board type=%u\n", brdtype);
-               break;
-       }
-
-       brdp->irq = pdev->irq;
-       retval = stl_brdinit(brdp);
-       if (retval)
-               goto err_null;
-
-       pci_set_drvdata(pdev, brdp);
-
-       for (i = 0; i < brdp->nrports; i++)
-               tty_register_device(stl_serial,
-                               brdp->brdnr * STL_MAXPORTS + i, &pdev->dev);
-
-       return 0;
-err_null:
-       stl_brds[brdp->brdnr] = NULL;
-err_fr:
-       kfree(brdp);
-err:
-       return retval;
-}
-
-static void __devexit stl_pciremove(struct pci_dev *pdev)
-{
-       struct stlbrd *brdp = pci_get_drvdata(pdev);
-       unsigned int i;
-
-       free_irq(brdp->irq, brdp);
-
-       stl_cleanup_panels(brdp);
-
-       release_region(brdp->ioaddr1, brdp->iosize1);
-       if (brdp->iosize2 > 0)
-               release_region(brdp->ioaddr2, brdp->iosize2);
-
-       for (i = 0; i < brdp->nrports; i++)
-               tty_unregister_device(stl_serial,
-                               brdp->brdnr * STL_MAXPORTS + i);
-
-       stl_brds[brdp->brdnr] = NULL;
-       kfree(brdp);
-}
-
-static struct pci_driver stl_pcidriver = {
-       .name = "stallion",
-       .id_table = stl_pcibrds,
-       .probe = stl_pciprobe,
-       .remove = __devexit_p(stl_pciremove)
-};
-
-/*****************************************************************************/
-
-/*
- *     Return the board stats structure to user app.
- */
-
-static int stl_getbrdstats(combrd_t __user *bp)
-{
-       combrd_t        stl_brdstats;
-       struct stlbrd   *brdp;
-       struct stlpanel *panelp;
-       unsigned int i;
-
-       if (copy_from_user(&stl_brdstats, bp, sizeof(combrd_t)))
-               return -EFAULT;
-       if (stl_brdstats.brd >= STL_MAXBRDS)
-               return -ENODEV;
-       brdp = stl_brds[stl_brdstats.brd];
-       if (brdp == NULL)
-               return -ENODEV;
-
-       memset(&stl_brdstats, 0, sizeof(combrd_t));
-       stl_brdstats.brd = brdp->brdnr;
-       stl_brdstats.type = brdp->brdtype;
-       stl_brdstats.hwid = brdp->hwid;
-       stl_brdstats.state = brdp->state;
-       stl_brdstats.ioaddr = brdp->ioaddr1;
-       stl_brdstats.ioaddr2 = brdp->ioaddr2;
-       stl_brdstats.irq = brdp->irq;
-       stl_brdstats.nrpanels = brdp->nrpanels;
-       stl_brdstats.nrports = brdp->nrports;
-       for (i = 0; i < brdp->nrpanels; i++) {
-               panelp = brdp->panels[i];
-               stl_brdstats.panels[i].panel = i;
-               stl_brdstats.panels[i].hwid = panelp->hwid;
-               stl_brdstats.panels[i].nrports = panelp->nrports;
-       }
-
-       return copy_to_user(bp, &stl_brdstats, sizeof(combrd_t)) ? -EFAULT : 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Resolve the referenced port number into a port struct pointer.
- */
-
-static struct stlport *stl_getport(int brdnr, int panelnr, int portnr)
-{
-       struct stlbrd   *brdp;
-       struct stlpanel *panelp;
-
-       if (brdnr < 0 || brdnr >= STL_MAXBRDS)
-               return NULL;
-       brdp = stl_brds[brdnr];
-       if (brdp == NULL)
-               return NULL;
-       if (panelnr < 0 || (unsigned int)panelnr >= brdp->nrpanels)
-               return NULL;
-       panelp = brdp->panels[panelnr];
-       if (panelp == NULL)
-               return NULL;
-       if (portnr < 0 || (unsigned int)portnr >= panelp->nrports)
-               return NULL;
-       return panelp->ports[portnr];
-}
-
-/*****************************************************************************/
-
-/*
- *     Return the port stats structure to user app. A NULL port struct
- *     pointer passed in means that we need to find out from the app
- *     what port to get stats for (used through board control device).
- */
-
-static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp)
-{
-       comstats_t      stl_comstats;
-       unsigned char   *head, *tail;
-       unsigned long   flags;
-
-       if (!portp) {
-               if (copy_from_user(&stl_comstats, cp, sizeof(comstats_t)))
-                       return -EFAULT;
-               portp = stl_getport(stl_comstats.brd, stl_comstats.panel,
-                       stl_comstats.port);
-               if (portp == NULL)
-                       return -ENODEV;
-       }
-
-       mutex_lock(&portp->port.mutex);
-       portp->stats.state = portp->istate;
-       portp->stats.flags = portp->port.flags;
-       portp->stats.hwid = portp->hwid;
-
-       portp->stats.ttystate = 0;
-       portp->stats.cflags = 0;
-       portp->stats.iflags = 0;
-       portp->stats.oflags = 0;
-       portp->stats.lflags = 0;
-       portp->stats.rxbuffered = 0;
-
-       spin_lock_irqsave(&stallion_lock, flags);
-       if (tty != NULL && portp->port.tty == tty) {
-               portp->stats.ttystate = tty->flags;
-               /* No longer available as a statistic */
-               portp->stats.rxbuffered = 1; /*tty->flip.count; */
-               if (tty->termios != NULL) {
-                       portp->stats.cflags = tty->termios->c_cflag;
-                       portp->stats.iflags = tty->termios->c_iflag;
-                       portp->stats.oflags = tty->termios->c_oflag;
-                       portp->stats.lflags = tty->termios->c_lflag;
-               }
-       }
-       spin_unlock_irqrestore(&stallion_lock, flags);
-
-       head = portp->tx.head;
-       tail = portp->tx.tail;
-       portp->stats.txbuffered = (head >= tail) ? (head - tail) :
-               (STL_TXBUFSIZE - (tail - head));
-
-       portp->stats.signals = (unsigned long) stl_getsignals(portp);
-       mutex_unlock(&portp->port.mutex);
-
-       return copy_to_user(cp, &portp->stats,
-                           sizeof(comstats_t)) ? -EFAULT : 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Clear the port stats structure. We also return it zeroed out...
- */
-
-static int stl_clrportstats(struct stlport *portp, comstats_t __user *cp)
-{
-       comstats_t      stl_comstats;
-
-       if (!portp) {
-               if (copy_from_user(&stl_comstats, cp, sizeof(comstats_t)))
-                       return -EFAULT;
-               portp = stl_getport(stl_comstats.brd, stl_comstats.panel,
-                       stl_comstats.port);
-               if (portp == NULL)
-                       return -ENODEV;
-       }
-
-       mutex_lock(&portp->port.mutex);
-       memset(&portp->stats, 0, sizeof(comstats_t));
-       portp->stats.brd = portp->brdnr;
-       portp->stats.panel = portp->panelnr;
-       portp->stats.port = portp->portnr;
-       mutex_unlock(&portp->port.mutex);
-       return copy_to_user(cp, &portp->stats,
-                           sizeof(comstats_t)) ? -EFAULT : 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Return the entire driver ports structure to a user app.
- */
-
-static int stl_getportstruct(struct stlport __user *arg)
-{
-       struct stlport  stl_dummyport;
-       struct stlport  *portp;
-
-       if (copy_from_user(&stl_dummyport, arg, sizeof(struct stlport)))
-               return -EFAULT;
-       portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr,
-                stl_dummyport.portnr);
-       if (!portp)
-               return -ENODEV;
-       return copy_to_user(arg, portp, sizeof(struct stlport)) ? -EFAULT : 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Return the entire driver board structure to a user app.
- */
-
-static int stl_getbrdstruct(struct stlbrd __user *arg)
-{
-       struct stlbrd   stl_dummybrd;
-       struct stlbrd   *brdp;
-
-       if (copy_from_user(&stl_dummybrd, arg, sizeof(struct stlbrd)))
-               return -EFAULT;
-       if (stl_dummybrd.brdnr >= STL_MAXBRDS)
-               return -ENODEV;
-       brdp = stl_brds[stl_dummybrd.brdnr];
-       if (!brdp)
-               return -ENODEV;
-       return copy_to_user(arg, brdp, sizeof(struct stlbrd)) ? -EFAULT : 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     The "staliomem" device is also required to do some special operations
- *     on the board and/or ports. In this driver it is mostly used for stats
- *     collection.
- */
-
-static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
-{
-       int     brdnr, rc;
-       void __user *argp = (void __user *)arg;
-
-       pr_debug("stl_memioctl(fp=%p,cmd=%x,arg=%lx)\n", fp, cmd,arg);
-
-       brdnr = iminor(fp->f_dentry->d_inode);
-       if (brdnr >= STL_MAXBRDS)
-               return -ENODEV;
-       rc = 0;
-
-       switch (cmd) {
-       case COM_GETPORTSTATS:
-               rc = stl_getportstats(NULL, NULL, argp);
-               break;
-       case COM_CLRPORTSTATS:
-               rc = stl_clrportstats(NULL, argp);
-               break;
-       case COM_GETBRDSTATS:
-               rc = stl_getbrdstats(argp);
-               break;
-       case COM_READPORT:
-               rc = stl_getportstruct(argp);
-               break;
-       case COM_READBOARD:
-               rc = stl_getbrdstruct(argp);
-               break;
-       default:
-               rc = -ENOIOCTLCMD;
-               break;
-       }
-       return rc;
-}
-
-static const struct tty_operations stl_ops = {
-       .open = stl_open,
-       .close = stl_close,
-       .write = stl_write,
-       .put_char = stl_putchar,
-       .flush_chars = stl_flushchars,
-       .write_room = stl_writeroom,
-       .chars_in_buffer = stl_charsinbuffer,
-       .ioctl = stl_ioctl,
-       .set_termios = stl_settermios,
-       .throttle = stl_throttle,
-       .unthrottle = stl_unthrottle,
-       .stop = stl_stop,
-       .start = stl_start,
-       .hangup = stl_hangup,
-       .flush_buffer = stl_flushbuffer,
-       .break_ctl = stl_breakctl,
-       .wait_until_sent = stl_waituntilsent,
-       .send_xchar = stl_sendxchar,
-       .tiocmget = stl_tiocmget,
-       .tiocmset = stl_tiocmset,
-       .proc_fops = &stl_proc_fops,
-};
-
-static const struct tty_port_operations stl_port_ops = {
-       .carrier_raised = stl_carrier_raised,
-       .dtr_rts = stl_dtr_rts,
-       .activate = stl_activate,
-       .shutdown = stl_shutdown,
-};
-
-/*****************************************************************************/
-/*                       CD1400 HARDWARE FUNCTIONS                           */
-/*****************************************************************************/
-
-/*
- *     These functions get/set/update the registers of the cd1400 UARTs.
- *     Access to the cd1400 registers is via an address/data io port pair.
- *     (Maybe should make this inline...)
- */
-
-static int stl_cd1400getreg(struct stlport *portp, int regnr)
-{
-       outb((regnr + portp->uartaddr), portp->ioaddr);
-       return inb(portp->ioaddr + EREG_DATA);
-}
-
-static void stl_cd1400setreg(struct stlport *portp, int regnr, int value)
-{
-       outb(regnr + portp->uartaddr, portp->ioaddr);
-       outb(value, portp->ioaddr + EREG_DATA);
-}
-
-static int stl_cd1400updatereg(struct stlport *portp, int regnr, int value)
-{
-       outb(regnr + portp->uartaddr, portp->ioaddr);
-       if (inb(portp->ioaddr + EREG_DATA) != value) {
-               outb(value, portp->ioaddr + EREG_DATA);
-               return 1;
-       }
-       return 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Inbitialize the UARTs in a panel. We don't care what sort of board
- *     these ports are on - since the port io registers are almost
- *     identical when dealing with ports.
- */
-
-static int stl_cd1400panelinit(struct stlbrd *brdp, struct stlpanel *panelp)
-{
-       unsigned int    gfrcr;
-       int             chipmask, i, j;
-       int             nrchips, uartaddr, ioaddr;
-       unsigned long   flags;
-
-       pr_debug("stl_panelinit(brdp=%p,panelp=%p)\n", brdp, panelp);
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(panelp->brdnr, panelp->pagenr);
-
-/*
- *     Check that each chip is present and started up OK.
- */
-       chipmask = 0;
-       nrchips = panelp->nrports / CD1400_PORTS;
-       for (i = 0; i < nrchips; i++) {
-               if (brdp->brdtype == BRD_ECHPCI) {
-                       outb((panelp->pagenr + (i >> 1)), brdp->ioctrl);
-                       ioaddr = panelp->iobase;
-               } else
-                       ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1));
-               uartaddr = (i & 0x01) ? 0x080 : 0;
-               outb((GFRCR + uartaddr), ioaddr);
-               outb(0, (ioaddr + EREG_DATA));
-               outb((CCR + uartaddr), ioaddr);
-               outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
-               outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
-               outb((GFRCR + uartaddr), ioaddr);
-               for (j = 0; j < CCR_MAXWAIT; j++)
-                       if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0)
-                               break;
-
-               if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) {
-                       printk("STALLION: cd1400 not responding, "
-                               "brd=%d panel=%d chip=%d\n",
-                               panelp->brdnr, panelp->panelnr, i);
-                       continue;
-               }
-               chipmask |= (0x1 << i);
-               outb((PPR + uartaddr), ioaddr);
-               outb(PPR_SCALAR, (ioaddr + EREG_DATA));
-       }
-
-       BRDDISABLE(panelp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-       return chipmask;
-}
-
-/*****************************************************************************/
-
-/*
- *     Initialize hardware specific port registers.
- */
-
-static void stl_cd1400portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp)
-{
-       unsigned long flags;
-       pr_debug("stl_cd1400portinit(brdp=%p,panelp=%p,portp=%p)\n", brdp,
-                       panelp, portp);
-
-       if ((brdp == NULL) || (panelp == NULL) ||
-           (portp == NULL))
-               return;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) ||
-               (portp->portnr < 8)) ? 0 : EREG_BANKSIZE);
-       portp->uartaddr = (portp->portnr & 0x04) << 5;
-       portp->pagenr = panelp->pagenr + (portp->portnr >> 3);
-
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
-       stl_cd1400setreg(portp, LIVR, (portp->portnr << 3));
-       portp->hwid = stl_cd1400getreg(portp, GFRCR);
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Wait for the command register to be ready. We will poll this,
- *     since it won't usually take too long to be ready.
- */
-
-static void stl_cd1400ccrwait(struct stlport *portp)
-{
-       int     i;
-
-       for (i = 0; i < CCR_MAXWAIT; i++)
-               if (stl_cd1400getreg(portp, CCR) == 0)
-                       return;
-
-       printk("STALLION: cd1400 not responding, port=%d panel=%d brd=%d\n",
-               portp->portnr, portp->panelnr, portp->brdnr);
-}
-
-/*****************************************************************************/
-
-/*
- *     Set up the cd1400 registers for a port based on the termios port
- *     settings.
- */
-
-static void stl_cd1400setport(struct stlport *portp, struct ktermios *tiosp)
-{
-       struct stlbrd   *brdp;
-       unsigned long   flags;
-       unsigned int    clkdiv, baudrate;
-       unsigned char   cor1, cor2, cor3;
-       unsigned char   cor4, cor5, ccr;
-       unsigned char   srer, sreron, sreroff;
-       unsigned char   mcor1, mcor2, rtpr;
-       unsigned char   clk, div;
-
-       cor1 = 0;
-       cor2 = 0;
-       cor3 = 0;
-       cor4 = 0;
-       cor5 = 0;
-       ccr = 0;
-       rtpr = 0;
-       clk = 0;
-       div = 0;
-       mcor1 = 0;
-       mcor2 = 0;
-       sreron = 0;
-       sreroff = 0;
-
-       brdp = stl_brds[portp->brdnr];
-       if (brdp == NULL)
-               return;
-
-/*
- *     Set up the RX char ignore mask with those RX error types we
- *     can ignore. We can get the cd1400 to help us out a little here,
- *     it will ignore parity errors and breaks for us.
- */
-       portp->rxignoremsk = 0;
-       if (tiosp->c_iflag & IGNPAR) {
-               portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN);
-               cor1 |= COR1_PARIGNORE;
-       }
-       if (tiosp->c_iflag & IGNBRK) {
-               portp->rxignoremsk |= ST_BREAK;
-               cor4 |= COR4_IGNBRK;
-       }
-
-       portp->rxmarkmsk = ST_OVERRUN;
-       if (tiosp->c_iflag & (INPCK | PARMRK))
-               portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING);
-       if (tiosp->c_iflag & BRKINT)
-               portp->rxmarkmsk |= ST_BREAK;
-
-/*
- *     Go through the char size, parity and stop bits and set all the
- *     option register appropriately.
- */
-       switch (tiosp->c_cflag & CSIZE) {
-       case CS5:
-               cor1 |= COR1_CHL5;
-               break;
-       case CS6:
-               cor1 |= COR1_CHL6;
-               break;
-       case CS7:
-               cor1 |= COR1_CHL7;
-               break;
-       default:
-               cor1 |= COR1_CHL8;
-               break;
-       }
-
-       if (tiosp->c_cflag & CSTOPB)
-               cor1 |= COR1_STOP2;
-       else
-               cor1 |= COR1_STOP1;
-
-       if (tiosp->c_cflag & PARENB) {
-               if (tiosp->c_cflag & PARODD)
-                       cor1 |= (COR1_PARENB | COR1_PARODD);
-               else
-                       cor1 |= (COR1_PARENB | COR1_PAREVEN);
-       } else {
-               cor1 |= COR1_PARNONE;
-       }
-
-/*
- *     Set the RX FIFO threshold at 6 chars. This gives a bit of breathing
- *     space for hardware flow control and the like. This should be set to
- *     VMIN. Also here we will set the RX data timeout to 10ms - this should
- *     really be based on VTIME.
- */
-       cor3 |= FIFO_RXTHRESHOLD;
-       rtpr = 2;
-
-/*
- *     Calculate the baud rate timers. For now we will just assume that
- *     the input and output baud are the same. Could have used a baud
- *     table here, but this way we can generate virtually any baud rate
- *     we like!
- */
-       baudrate = tiosp->c_cflag & CBAUD;
-       if (baudrate & CBAUDEX) {
-               baudrate &= ~CBAUDEX;
-               if ((baudrate < 1) || (baudrate > 4))
-                       tiosp->c_cflag &= ~CBAUDEX;
-               else
-                       baudrate += 15;
-       }
-       baudrate = stl_baudrates[baudrate];
-       if ((tiosp->c_cflag & CBAUD) == B38400) {
-               if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                       baudrate = 57600;
-               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                       baudrate = 115200;
-               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-                       baudrate = 230400;
-               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-                       baudrate = 460800;
-               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
-                       baudrate = (portp->baud_base / portp->custom_divisor);
-       }
-       if (baudrate > STL_CD1400MAXBAUD)
-               baudrate = STL_CD1400MAXBAUD;
-
-       if (baudrate > 0) {
-               for (clk = 0; clk < CD1400_NUMCLKS; clk++) {
-                       clkdiv = (portp->clk / stl_cd1400clkdivs[clk]) / baudrate;
-                       if (clkdiv < 0x100)
-                               break;
-               }
-               div = (unsigned char) clkdiv;
-       }
-
-/*
- *     Check what form of modem signaling is required and set it up.
- */
-       if ((tiosp->c_cflag & CLOCAL) == 0) {
-               mcor1 |= MCOR1_DCD;
-               mcor2 |= MCOR2_DCD;
-               sreron |= SRER_MODEM;
-               portp->port.flags |= ASYNC_CHECK_CD;
-       } else
-               portp->port.flags &= ~ASYNC_CHECK_CD;
-
-/*
- *     Setup cd1400 enhanced modes if we can. In particular we want to
- *     handle as much of the flow control as possible automatically. As
- *     well as saving a few CPU cycles it will also greatly improve flow
- *     control reliability.
- */
-       if (tiosp->c_iflag & IXON) {
-               cor2 |= COR2_TXIBE;
-               cor3 |= COR3_SCD12;
-               if (tiosp->c_iflag & IXANY)
-                       cor2 |= COR2_IXM;
-       }
-
-       if (tiosp->c_cflag & CRTSCTS) {
-               cor2 |= COR2_CTSAE;
-               mcor1 |= FIFO_RTSTHRESHOLD;
-       }
-
-/*
- *     All cd1400 register values calculated so go through and set
- *     them all up.
- */
-
-       pr_debug("SETPORT: portnr=%d panelnr=%d brdnr=%d\n",
-               portp->portnr, portp->panelnr, portp->brdnr);
-       pr_debug("    cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n",
-               cor1, cor2, cor3, cor4, cor5);
-       pr_debug("    mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n",
-               mcor1, mcor2, rtpr, sreron, sreroff);
-       pr_debug("    tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div);
-       pr_debug("    schr1=%x schr2=%x schr3=%x schr4=%x\n",
-               tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP],
-               tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x3));
-       srer = stl_cd1400getreg(portp, SRER);
-       stl_cd1400setreg(portp, SRER, 0);
-       if (stl_cd1400updatereg(portp, COR1, cor1))
-               ccr = 1;
-       if (stl_cd1400updatereg(portp, COR2, cor2))
-               ccr = 1;
-       if (stl_cd1400updatereg(portp, COR3, cor3))
-               ccr = 1;
-       if (ccr) {
-               stl_cd1400ccrwait(portp);
-               stl_cd1400setreg(portp, CCR, CCR_CORCHANGE);
-       }
-       stl_cd1400setreg(portp, COR4, cor4);
-       stl_cd1400setreg(portp, COR5, cor5);
-       stl_cd1400setreg(portp, MCOR1, mcor1);
-       stl_cd1400setreg(portp, MCOR2, mcor2);
-       if (baudrate > 0) {
-               stl_cd1400setreg(portp, TCOR, clk);
-               stl_cd1400setreg(portp, TBPR, div);
-               stl_cd1400setreg(portp, RCOR, clk);
-               stl_cd1400setreg(portp, RBPR, div);
-       }
-       stl_cd1400setreg(portp, SCHR1, tiosp->c_cc[VSTART]);
-       stl_cd1400setreg(portp, SCHR2, tiosp->c_cc[VSTOP]);
-       stl_cd1400setreg(portp, SCHR3, tiosp->c_cc[VSTART]);
-       stl_cd1400setreg(portp, SCHR4, tiosp->c_cc[VSTOP]);
-       stl_cd1400setreg(portp, RTPR, rtpr);
-       mcor1 = stl_cd1400getreg(portp, MSVR1);
-       if (mcor1 & MSVR1_DCD)
-               portp->sigs |= TIOCM_CD;
-       else
-               portp->sigs &= ~TIOCM_CD;
-       stl_cd1400setreg(portp, SRER, ((srer & ~sreroff) | sreron));
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Set the state of the DTR and RTS signals.
- */
-
-static void stl_cd1400setsignals(struct stlport *portp, int dtr, int rts)
-{
-       unsigned char   msvr1, msvr2;
-       unsigned long   flags;
-
-       pr_debug("stl_cd1400setsignals(portp=%p,dtr=%d,rts=%d)\n",
-                       portp, dtr, rts);
-
-       msvr1 = 0;
-       msvr2 = 0;
-       if (dtr > 0)
-               msvr1 = MSVR1_DTR;
-       if (rts > 0)
-               msvr2 = MSVR2_RTS;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
-       if (rts >= 0)
-               stl_cd1400setreg(portp, MSVR2, msvr2);
-       if (dtr >= 0)
-               stl_cd1400setreg(portp, MSVR1, msvr1);
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Return the state of the signals.
- */
-
-static int stl_cd1400getsignals(struct stlport *portp)
-{
-       unsigned char   msvr1, msvr2;
-       unsigned long   flags;
-       int             sigs;
-
-       pr_debug("stl_cd1400getsignals(portp=%p)\n", portp);
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
-       msvr1 = stl_cd1400getreg(portp, MSVR1);
-       msvr2 = stl_cd1400getreg(portp, MSVR2);
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-
-       sigs = 0;
-       sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0;
-       sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0;
-       sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0;
-       sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0;
-#if 0
-       sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0;
-       sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0;
-#else
-       sigs |= TIOCM_DSR;
-#endif
-       return sigs;
-}
-
-/*****************************************************************************/
-
-/*
- *     Enable/Disable the Transmitter and/or Receiver.
- */
-
-static void stl_cd1400enablerxtx(struct stlport *portp, int rx, int tx)
-{
-       unsigned char   ccr;
-       unsigned long   flags;
-
-       pr_debug("stl_cd1400enablerxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx, tx);
-
-       ccr = 0;
-
-       if (tx == 0)
-               ccr |= CCR_TXDISABLE;
-       else if (tx > 0)
-               ccr |= CCR_TXENABLE;
-       if (rx == 0)
-               ccr |= CCR_RXDISABLE;
-       else if (rx > 0)
-               ccr |= CCR_RXENABLE;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
-       stl_cd1400ccrwait(portp);
-       stl_cd1400setreg(portp, CCR, ccr);
-       stl_cd1400ccrwait(portp);
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Start/stop the Transmitter and/or Receiver.
- */
-
-static void stl_cd1400startrxtx(struct stlport *portp, int rx, int tx)
-{
-       unsigned char   sreron, sreroff;
-       unsigned long   flags;
-
-       pr_debug("stl_cd1400startrxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx, tx);
-
-       sreron = 0;
-       sreroff = 0;
-       if (tx == 0)
-               sreroff |= (SRER_TXDATA | SRER_TXEMPTY);
-       else if (tx == 1)
-               sreron |= SRER_TXDATA;
-       else if (tx >= 2)
-               sreron |= SRER_TXEMPTY;
-       if (rx == 0)
-               sreroff |= SRER_RXDATA;
-       else if (rx > 0)
-               sreron |= SRER_RXDATA;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
-       stl_cd1400setreg(portp, SRER,
-               ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron));
-       BRDDISABLE(portp->brdnr);
-       if (tx > 0)
-               set_bit(ASYI_TXBUSY, &portp->istate);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Disable all interrupts from this port.
- */
-
-static void stl_cd1400disableintrs(struct stlport *portp)
-{
-       unsigned long   flags;
-
-       pr_debug("stl_cd1400disableintrs(portp=%p)\n", portp);
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
-       stl_cd1400setreg(portp, SRER, 0);
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-static void stl_cd1400sendbreak(struct stlport *portp, int len)
-{
-       unsigned long   flags;
-
-       pr_debug("stl_cd1400sendbreak(portp=%p,len=%d)\n", portp, len);
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
-       stl_cd1400setreg(portp, SRER,
-               ((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) |
-               SRER_TXEMPTY));
-       BRDDISABLE(portp->brdnr);
-       portp->brklen = len;
-       if (len == 1)
-               portp->stats.txbreaks++;
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Take flow control actions...
- */
-
-static void stl_cd1400flowctrl(struct stlport *portp, int state)
-{
-       struct tty_struct       *tty;
-       unsigned long           flags;
-
-       pr_debug("stl_cd1400flowctrl(portp=%p,state=%x)\n", portp, state);
-
-       if (portp == NULL)
-               return;
-       tty = tty_port_tty_get(&portp->port);
-       if (tty == NULL)
-               return;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
-
-       if (state) {
-               if (tty->termios->c_iflag & IXOFF) {
-                       stl_cd1400ccrwait(portp);
-                       stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1);
-                       portp->stats.rxxon++;
-                       stl_cd1400ccrwait(portp);
-               }
-/*
- *             Question: should we return RTS to what it was before? It may
- *             have been set by an ioctl... Suppose not, since if you have
- *             hardware flow control set then it is pretty silly to go and
- *             set the RTS line by hand.
- */
-               if (tty->termios->c_cflag & CRTSCTS) {
-                       stl_cd1400setreg(portp, MCOR1,
-                               (stl_cd1400getreg(portp, MCOR1) |
-                               FIFO_RTSTHRESHOLD));
-                       stl_cd1400setreg(portp, MSVR2, MSVR2_RTS);
-                       portp->stats.rxrtson++;
-               }
-       } else {
-               if (tty->termios->c_iflag & IXOFF) {
-                       stl_cd1400ccrwait(portp);
-                       stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2);
-                       portp->stats.rxxoff++;
-                       stl_cd1400ccrwait(portp);
-               }
-               if (tty->termios->c_cflag & CRTSCTS) {
-                       stl_cd1400setreg(portp, MCOR1,
-                               (stl_cd1400getreg(portp, MCOR1) & 0xf0));
-                       stl_cd1400setreg(portp, MSVR2, 0);
-                       portp->stats.rxrtsoff++;
-               }
-       }
-
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-       tty_kref_put(tty);
-}
-
-/*****************************************************************************/
-
-/*
- *     Send a flow control character...
- */
-
-static void stl_cd1400sendflow(struct stlport *portp, int state)
-{
-       struct tty_struct       *tty;
-       unsigned long           flags;
-
-       pr_debug("stl_cd1400sendflow(portp=%p,state=%x)\n", portp, state);
-
-       if (portp == NULL)
-               return;
-       tty = tty_port_tty_get(&portp->port);
-       if (tty == NULL)
-               return;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
-       if (state) {
-               stl_cd1400ccrwait(portp);
-               stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1);
-               portp->stats.rxxon++;
-               stl_cd1400ccrwait(portp);
-       } else {
-               stl_cd1400ccrwait(portp);
-               stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2);
-               portp->stats.rxxoff++;
-               stl_cd1400ccrwait(portp);
-       }
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-       tty_kref_put(tty);
-}
-
-/*****************************************************************************/
-
-static void stl_cd1400flush(struct stlport *portp)
-{
-       unsigned long   flags;
-
-       pr_debug("stl_cd1400flush(portp=%p)\n", portp);
-
-       if (portp == NULL)
-               return;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
-       stl_cd1400ccrwait(portp);
-       stl_cd1400setreg(portp, CCR, CCR_TXFLUSHFIFO);
-       stl_cd1400ccrwait(portp);
-       portp->tx.tail = portp->tx.head;
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Return the current state of data flow on this port. This is only
- *     really interesting when determining if data has fully completed
- *     transmission or not... This is easy for the cd1400, it accurately
- *     maintains the busy port flag.
- */
-
-static int stl_cd1400datastate(struct stlport *portp)
-{
-       pr_debug("stl_cd1400datastate(portp=%p)\n", portp);
-
-       if (portp == NULL)
-               return 0;
-
-       return test_bit(ASYI_TXBUSY, &portp->istate) ? 1 : 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Interrupt service routine for cd1400 EasyIO boards.
- */
-
-static void stl_cd1400eiointr(struct stlpanel *panelp, unsigned int iobase)
-{
-       unsigned char   svrtype;
-
-       pr_debug("stl_cd1400eiointr(panelp=%p,iobase=%x)\n", panelp, iobase);
-
-       spin_lock(&brd_lock);
-       outb(SVRR, iobase);
-       svrtype = inb(iobase + EREG_DATA);
-       if (panelp->nrports > 4) {
-               outb((SVRR + 0x80), iobase);
-               svrtype |= inb(iobase + EREG_DATA);
-       }
-
-       if (svrtype & SVRR_RX)
-               stl_cd1400rxisr(panelp, iobase);
-       else if (svrtype & SVRR_TX)
-               stl_cd1400txisr(panelp, iobase);
-       else if (svrtype & SVRR_MDM)
-               stl_cd1400mdmisr(panelp, iobase);
-
-       spin_unlock(&brd_lock);
-}
-
-/*****************************************************************************/
-
-/*
- *     Interrupt service routine for cd1400 panels.
- */
-
-static void stl_cd1400echintr(struct stlpanel *panelp, unsigned int iobase)
-{
-       unsigned char   svrtype;
-
-       pr_debug("stl_cd1400echintr(panelp=%p,iobase=%x)\n", panelp, iobase);
-
-       outb(SVRR, iobase);
-       svrtype = inb(iobase + EREG_DATA);
-       outb((SVRR + 0x80), iobase);
-       svrtype |= inb(iobase + EREG_DATA);
-       if (svrtype & SVRR_RX)
-               stl_cd1400rxisr(panelp, iobase);
-       else if (svrtype & SVRR_TX)
-               stl_cd1400txisr(panelp, iobase);
-       else if (svrtype & SVRR_MDM)
-               stl_cd1400mdmisr(panelp, iobase);
-}
-
-
-/*****************************************************************************/
-
-/*
- *     Unfortunately we need to handle breaks in the TX data stream, since
- *     this is the only way to generate them on the cd1400.
- */
-
-static int stl_cd1400breakisr(struct stlport *portp, int ioaddr)
-{
-       if (portp->brklen == 1) {
-               outb((COR2 + portp->uartaddr), ioaddr);
-               outb((inb(ioaddr + EREG_DATA) | COR2_ETC),
-                       (ioaddr + EREG_DATA));
-               outb((TDR + portp->uartaddr), ioaddr);
-               outb(ETC_CMD, (ioaddr + EREG_DATA));
-               outb(ETC_STARTBREAK, (ioaddr + EREG_DATA));
-               outb((SRER + portp->uartaddr), ioaddr);
-               outb((inb(ioaddr + EREG_DATA) & ~(SRER_TXDATA | SRER_TXEMPTY)),
-                       (ioaddr + EREG_DATA));
-               return 1;
-       } else if (portp->brklen > 1) {
-               outb((TDR + portp->uartaddr), ioaddr);
-               outb(ETC_CMD, (ioaddr + EREG_DATA));
-               outb(ETC_STOPBREAK, (ioaddr + EREG_DATA));
-               portp->brklen = -1;
-               return 1;
-       } else {
-               outb((COR2 + portp->uartaddr), ioaddr);
-               outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC),
-                       (ioaddr + EREG_DATA));
-               portp->brklen = 0;
-       }
-       return 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Transmit interrupt handler. This has gotta be fast!  Handling TX
- *     chars is pretty simple, stuff as many as possible from the TX buffer
- *     into the cd1400 FIFO. Must also handle TX breaks here, since they
- *     are embedded as commands in the data stream. Oh no, had to use a goto!
- *     This could be optimized more, will do when I get time...
- *     In practice it is possible that interrupts are enabled but that the
- *     port has been hung up. Need to handle not having any TX buffer here,
- *     this is done by using the side effect that head and tail will also
- *     be NULL if the buffer has been freed.
- */
-
-static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr)
-{
-       struct stlport  *portp;
-       int             len, stlen;
-       char            *head, *tail;
-       unsigned char   ioack, srer;
-       struct tty_struct *tty;
-
-       pr_debug("stl_cd1400txisr(panelp=%p,ioaddr=%x)\n", panelp, ioaddr);
-
-       ioack = inb(ioaddr + EREG_TXACK);
-       if (((ioack & panelp->ackmask) != 0) ||
-           ((ioack & ACK_TYPMASK) != ACK_TYPTX)) {
-               printk("STALLION: bad TX interrupt ack value=%x\n", ioack);
-               return;
-       }
-       portp = panelp->ports[(ioack >> 3)];
-
-/*
- *     Unfortunately we need to handle breaks in the data stream, since
- *     this is the only way to generate them on the cd1400. Do it now if
- *     a break is to be sent.
- */
-       if (portp->brklen != 0)
-               if (stl_cd1400breakisr(portp, ioaddr))
-                       goto stl_txalldone;
-
-       head = portp->tx.head;
-       tail = portp->tx.tail;
-       len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
-       if ((len == 0) || ((len < STL_TXBUFLOW) &&
-           (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
-               set_bit(ASYI_TXLOW, &portp->istate);
-               tty = tty_port_tty_get(&portp->port);
-               if (tty) {
-                       tty_wakeup(tty);
-                       tty_kref_put(tty);
-               }
-       }
-
-       if (len == 0) {
-               outb((SRER + portp->uartaddr), ioaddr);
-               srer = inb(ioaddr + EREG_DATA);
-               if (srer & SRER_TXDATA) {
-                       srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY;
-               } else {
-                       srer &= ~(SRER_TXDATA | SRER_TXEMPTY);
-                       clear_bit(ASYI_TXBUSY, &portp->istate);
-               }
-               outb(srer, (ioaddr + EREG_DATA));
-       } else {
-               len = min(len, CD1400_TXFIFOSIZE);
-               portp->stats.txtotal += len;
-               stlen = min_t(unsigned int, len,
-                               (portp->tx.buf + STL_TXBUFSIZE) - tail);
-               outb((TDR + portp->uartaddr), ioaddr);
-               outsb((ioaddr + EREG_DATA), tail, stlen);
-               len -= stlen;
-               tail += stlen;
-               if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
-                       tail = portp->tx.buf;
-               if (len > 0) {
-                       outsb((ioaddr + EREG_DATA), tail, len);
-                       tail += len;
-               }
-               portp->tx.tail = tail;
-       }
-
-stl_txalldone:
-       outb((EOSRR + portp->uartaddr), ioaddr);
-       outb(0, (ioaddr + EREG_DATA));
-}
-
-/*****************************************************************************/
-
-/*
- *     Receive character interrupt handler. Determine if we have good chars
- *     or bad chars and then process appropriately. Good chars are easy
- *     just shove the lot into the RX buffer and set all status byte to 0.
- *     If a bad RX char then process as required. This routine needs to be
- *     fast!  In practice it is possible that we get an interrupt on a port
- *     that is closed. This can happen on hangups - since they completely
- *     shutdown a port not in user context. Need to handle this case.
- */
-
-static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr)
-{
-       struct stlport          *portp;
-       struct tty_struct       *tty;
-       unsigned int            ioack, len, buflen;
-       unsigned char           status;
-       char                    ch;
-
-       pr_debug("stl_cd1400rxisr(panelp=%p,ioaddr=%x)\n", panelp, ioaddr);
-
-       ioack = inb(ioaddr + EREG_RXACK);
-       if ((ioack & panelp->ackmask) != 0) {
-               printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
-               return;
-       }
-       portp = panelp->ports[(ioack >> 3)];
-       tty = tty_port_tty_get(&portp->port);
-
-       if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) {
-               outb((RDCR + portp->uartaddr), ioaddr);
-               len = inb(ioaddr + EREG_DATA);
-               if (tty == NULL || (buflen = tty_buffer_request_room(tty, len)) == 0) {
-                       len = min_t(unsigned int, len, sizeof(stl_unwanted));
-                       outb((RDSR + portp->uartaddr), ioaddr);
-                       insb((ioaddr + EREG_DATA), &stl_unwanted[0], len);
-                       portp->stats.rxlost += len;
-                       portp->stats.rxtotal += len;
-               } else {
-                       len = min(len, buflen);
-                       if (len > 0) {
-                               unsigned char *ptr;
-                               outb((RDSR + portp->uartaddr), ioaddr);
-                               tty_prepare_flip_string(tty, &ptr, len);
-                               insb((ioaddr + EREG_DATA), ptr, len);
-                               tty_schedule_flip(tty);
-                               portp->stats.rxtotal += len;
-                       }
-               }
-       } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) {
-               outb((RDSR + portp->uartaddr), ioaddr);
-               status = inb(ioaddr + EREG_DATA);
-               ch = inb(ioaddr + EREG_DATA);
-               if (status & ST_PARITY)
-                       portp->stats.rxparity++;
-               if (status & ST_FRAMING)
-                       portp->stats.rxframing++;
-               if (status & ST_OVERRUN)
-                       portp->stats.rxoverrun++;
-               if (status & ST_BREAK)
-                       portp->stats.rxbreaks++;
-               if (status & ST_SCHARMASK) {
-                       if ((status & ST_SCHARMASK) == ST_SCHAR1)
-                               portp->stats.txxon++;
-                       if ((status & ST_SCHARMASK) == ST_SCHAR2)
-                               portp->stats.txxoff++;
-                       goto stl_rxalldone;
-               }
-               if (tty != NULL && (portp->rxignoremsk & status) == 0) {
-                       if (portp->rxmarkmsk & status) {
-                               if (status & ST_BREAK) {
-                                       status = TTY_BREAK;
-                                       if (portp->port.flags & ASYNC_SAK) {
-                                               do_SAK(tty);
-                                               BRDENABLE(portp->brdnr, portp->pagenr);
-                                       }
-                               } else if (status & ST_PARITY)
-                                       status = TTY_PARITY;
-                               else if (status & ST_FRAMING)
-                                       status = TTY_FRAME;
-                               else if(status & ST_OVERRUN)
-                                       status = TTY_OVERRUN;
-                               else
-                                       status = 0;
-                       } else
-                               status = 0;
-                       tty_insert_flip_char(tty, ch, status);
-                       tty_schedule_flip(tty);
-               }
-       } else {
-               printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
-               tty_kref_put(tty);
-               return;
-       }
-
-stl_rxalldone:
-       tty_kref_put(tty);
-       outb((EOSRR + portp->uartaddr), ioaddr);
-       outb(0, (ioaddr + EREG_DATA));
-}
-
-/*****************************************************************************/
-
-/*
- *     Modem interrupt handler. The is called when the modem signal line
- *     (DCD) has changed state. Leave most of the work to the off-level
- *     processing routine.
- */
-
-static void stl_cd1400mdmisr(struct stlpanel *panelp, int ioaddr)
-{
-       struct stlport  *portp;
-       unsigned int    ioack;
-       unsigned char   misr;
-
-       pr_debug("stl_cd1400mdmisr(panelp=%p)\n", panelp);
-
-       ioack = inb(ioaddr + EREG_MDACK);
-       if (((ioack & panelp->ackmask) != 0) ||
-           ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) {
-               printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack);
-               return;
-       }
-       portp = panelp->ports[(ioack >> 3)];
-
-       outb((MISR + portp->uartaddr), ioaddr);
-       misr = inb(ioaddr + EREG_DATA);
-       if (misr & MISR_DCD) {
-               stl_cd_change(portp);
-               portp->stats.modem++;
-       }
-
-       outb((EOSRR + portp->uartaddr), ioaddr);
-       outb(0, (ioaddr + EREG_DATA));
-}
-
-/*****************************************************************************/
-/*                      SC26198 HARDWARE FUNCTIONS                           */
-/*****************************************************************************/
-
-/*
- *     These functions get/set/update the registers of the sc26198 UARTs.
- *     Access to the sc26198 registers is via an address/data io port pair.
- *     (Maybe should make this inline...)
- */
-
-static int stl_sc26198getreg(struct stlport *portp, int regnr)
-{
-       outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
-       return inb(portp->ioaddr + XP_DATA);
-}
-
-static void stl_sc26198setreg(struct stlport *portp, int regnr, int value)
-{
-       outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
-       outb(value, (portp->ioaddr + XP_DATA));
-}
-
-static int stl_sc26198updatereg(struct stlport *portp, int regnr, int value)
-{
-       outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
-       if (inb(portp->ioaddr + XP_DATA) != value) {
-               outb(value, (portp->ioaddr + XP_DATA));
-               return 1;
-       }
-       return 0;
-}
-
-/*****************************************************************************/
-
-/*
- *     Functions to get and set the sc26198 global registers.
- */
-
-static int stl_sc26198getglobreg(struct stlport *portp, int regnr)
-{
-       outb(regnr, (portp->ioaddr + XP_ADDR));
-       return inb(portp->ioaddr + XP_DATA);
-}
-
-#if 0
-static void stl_sc26198setglobreg(struct stlport *portp, int regnr, int value)
-{
-       outb(regnr, (portp->ioaddr + XP_ADDR));
-       outb(value, (portp->ioaddr + XP_DATA));
-}
-#endif
-
-/*****************************************************************************/
-
-/*
- *     Inbitialize the UARTs in a panel. We don't care what sort of board
- *     these ports are on - since the port io registers are almost
- *     identical when dealing with ports.
- */
-
-static int stl_sc26198panelinit(struct stlbrd *brdp, struct stlpanel *panelp)
-{
-       int     chipmask, i;
-       int     nrchips, ioaddr;
-
-       pr_debug("stl_sc26198panelinit(brdp=%p,panelp=%p)\n", brdp, panelp);
-
-       BRDENABLE(panelp->brdnr, panelp->pagenr);
-
-/*
- *     Check that each chip is present and started up OK.
- */
-       chipmask = 0;
-       nrchips = (panelp->nrports + 4) / SC26198_PORTS;
-       if (brdp->brdtype == BRD_ECHPCI)
-               outb(panelp->pagenr, brdp->ioctrl);
-
-       for (i = 0; i < nrchips; i++) {
-               ioaddr = panelp->iobase + (i * 4); 
-               outb(SCCR, (ioaddr + XP_ADDR));
-               outb(CR_RESETALL, (ioaddr + XP_DATA));
-               outb(TSTR, (ioaddr + XP_ADDR));
-               if (inb(ioaddr + XP_DATA) != 0) {
-                       printk("STALLION: sc26198 not responding, "
-                               "brd=%d panel=%d chip=%d\n",
-                               panelp->brdnr, panelp->panelnr, i);
-                       continue;
-               }
-               chipmask |= (0x1 << i);
-               outb(GCCR, (ioaddr + XP_ADDR));
-               outb(GCCR_IVRTYPCHANACK, (ioaddr + XP_DATA));
-               outb(WDTRCR, (ioaddr + XP_ADDR));
-               outb(0xff, (ioaddr + XP_DATA));
-       }
-
-       BRDDISABLE(panelp->brdnr);
-       return chipmask;
-}
-
-/*****************************************************************************/
-
-/*
- *     Initialize hardware specific port registers.
- */
-
-static void stl_sc26198portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp)
-{
-       pr_debug("stl_sc26198portinit(brdp=%p,panelp=%p,portp=%p)\n", brdp,
-                       panelp, portp);
-
-       if ((brdp == NULL) || (panelp == NULL) ||
-           (portp == NULL))
-               return;
-
-       portp->ioaddr = panelp->iobase + ((portp->portnr < 8) ? 0 : 4);
-       portp->uartaddr = (portp->portnr & 0x07) << 4;
-       portp->pagenr = panelp->pagenr;
-       portp->hwid = 0x1;
-
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_sc26198setreg(portp, IOPCR, IOPCR_SETSIGS);
-       BRDDISABLE(portp->brdnr);
-}
-
-/*****************************************************************************/
-
-/*
- *     Set up the sc26198 registers for a port based on the termios port
- *     settings.
- */
-
-static void stl_sc26198setport(struct stlport *portp, struct ktermios *tiosp)
-{
-       struct stlbrd   *brdp;
-       unsigned long   flags;
-       unsigned int    baudrate;
-       unsigned char   mr0, mr1, mr2, clk;
-       unsigned char   imron, imroff, iopr, ipr;
-
-       mr0 = 0;
-       mr1 = 0;
-       mr2 = 0;
-       clk = 0;
-       iopr = 0;
-       imron = 0;
-       imroff = 0;
-
-       brdp = stl_brds[portp->brdnr];
-       if (brdp == NULL)
-               return;
-
-/*
- *     Set up the RX char ignore mask with those RX error types we
- *     can ignore.
- */
-       portp->rxignoremsk = 0;
-       if (tiosp->c_iflag & IGNPAR)
-               portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING |
-                       SR_RXOVERRUN);
-       if (tiosp->c_iflag & IGNBRK)
-               portp->rxignoremsk |= SR_RXBREAK;
-
-       portp->rxmarkmsk = SR_RXOVERRUN;
-       if (tiosp->c_iflag & (INPCK | PARMRK))
-               portp->rxmarkmsk |= (SR_RXPARITY | SR_RXFRAMING);
-       if (tiosp->c_iflag & BRKINT)
-               portp->rxmarkmsk |= SR_RXBREAK;
-
-/*
- *     Go through the char size, parity and stop bits and set all the
- *     option register appropriately.
- */
-       switch (tiosp->c_cflag & CSIZE) {
-       case CS5:
-               mr1 |= MR1_CS5;
-               break;
-       case CS6:
-               mr1 |= MR1_CS6;
-               break;
-       case CS7:
-               mr1 |= MR1_CS7;
-               break;
-       default:
-               mr1 |= MR1_CS8;
-               break;
-       }
-
-       if (tiosp->c_cflag & CSTOPB)
-               mr2 |= MR2_STOP2;
-       else
-               mr2 |= MR2_STOP1;
-
-       if (tiosp->c_cflag & PARENB) {
-               if (tiosp->c_cflag & PARODD)
-                       mr1 |= (MR1_PARENB | MR1_PARODD);
-               else
-                       mr1 |= (MR1_PARENB | MR1_PAREVEN);
-       } else
-               mr1 |= MR1_PARNONE;
-
-       mr1 |= MR1_ERRBLOCK;
-
-/*
- *     Set the RX FIFO threshold at 8 chars. This gives a bit of breathing
- *     space for hardware flow control and the like. This should be set to
- *     VMIN.
- */
-       mr2 |= MR2_RXFIFOHALF;
-
-/*
- *     Calculate the baud rate timers. For now we will just assume that
- *     the input and output baud are the same. The sc26198 has a fixed
- *     baud rate table, so only discrete baud rates possible.
- */
-       baudrate = tiosp->c_cflag & CBAUD;
-       if (baudrate & CBAUDEX) {
-               baudrate &= ~CBAUDEX;
-               if ((baudrate < 1) || (baudrate > 4))
-                       tiosp->c_cflag &= ~CBAUDEX;
-               else
-                       baudrate += 15;
-       }
-       baudrate = stl_baudrates[baudrate];
-       if ((tiosp->c_cflag & CBAUD) == B38400) {
-               if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                       baudrate = 57600;
-               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                       baudrate = 115200;
-               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-                       baudrate = 230400;
-               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-                       baudrate = 460800;
-               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
-                       baudrate = (portp->baud_base / portp->custom_divisor);
-       }
-       if (baudrate > STL_SC26198MAXBAUD)
-               baudrate = STL_SC26198MAXBAUD;
-
-       if (baudrate > 0)
-               for (clk = 0; clk < SC26198_NRBAUDS; clk++)
-                       if (baudrate <= sc26198_baudtable[clk])
-                               break;
-
-/*
- *     Check what form of modem signaling is required and set it up.
- */
-       if (tiosp->c_cflag & CLOCAL) {
-               portp->port.flags &= ~ASYNC_CHECK_CD;
-       } else {
-               iopr |= IOPR_DCDCOS;
-               imron |= IR_IOPORT;
-               portp->port.flags |= ASYNC_CHECK_CD;
-       }
-
-/*
- *     Setup sc26198 enhanced modes if we can. In particular we want to
- *     handle as much of the flow control as possible automatically. As
- *     well as saving a few CPU cycles it will also greatly improve flow
- *     control reliability.
- */
-       if (tiosp->c_iflag & IXON) {
-               mr0 |= MR0_SWFTX | MR0_SWFT;
-               imron |= IR_XONXOFF;
-       } else
-               imroff |= IR_XONXOFF;
-
-       if (tiosp->c_iflag & IXOFF)
-               mr0 |= MR0_SWFRX;
-
-       if (tiosp->c_cflag & CRTSCTS) {
-               mr2 |= MR2_AUTOCTS;
-               mr1 |= MR1_AUTORTS;
-       }
-
-/*
- *     All sc26198 register values calculated so go through and set
- *     them all up.
- */
-
-       pr_debug("SETPORT: portnr=%d panelnr=%d brdnr=%d\n",
-               portp->portnr, portp->panelnr, portp->brdnr);
-       pr_debug("    mr0=%x mr1=%x mr2=%x clk=%x\n", mr0, mr1, mr2, clk);
-       pr_debug("    iopr=%x imron=%x imroff=%x\n", iopr, imron, imroff);
-       pr_debug("    schr1=%x schr2=%x schr3=%x schr4=%x\n",
-               tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP],
-               tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_sc26198setreg(portp, IMR, 0);
-       stl_sc26198updatereg(portp, MR0, mr0);
-       stl_sc26198updatereg(portp, MR1, mr1);
-       stl_sc26198setreg(portp, SCCR, CR_RXERRBLOCK);
-       stl_sc26198updatereg(portp, MR2, mr2);
-       stl_sc26198updatereg(portp, IOPIOR,
-               ((stl_sc26198getreg(portp, IOPIOR) & ~IPR_CHANGEMASK) | iopr));
-
-       if (baudrate > 0) {
-               stl_sc26198setreg(portp, TXCSR, clk);
-               stl_sc26198setreg(portp, RXCSR, clk);
-       }
-
-       stl_sc26198setreg(portp, XONCR, tiosp->c_cc[VSTART]);
-       stl_sc26198setreg(portp, XOFFCR, tiosp->c_cc[VSTOP]);
-
-       ipr = stl_sc26198getreg(portp, IPR);
-       if (ipr & IPR_DCD)
-               portp->sigs &= ~TIOCM_CD;
-       else
-               portp->sigs |= TIOCM_CD;
-
-       portp->imr = (portp->imr & ~imroff) | imron;
-       stl_sc26198setreg(portp, IMR, portp->imr);
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Set the state of the DTR and RTS signals.
- */
-
-static void stl_sc26198setsignals(struct stlport *portp, int dtr, int rts)
-{
-       unsigned char   iopioron, iopioroff;
-       unsigned long   flags;
-
-       pr_debug("stl_sc26198setsignals(portp=%p,dtr=%d,rts=%d)\n", portp,
-                       dtr, rts);
-
-       iopioron = 0;
-       iopioroff = 0;
-       if (dtr == 0)
-               iopioroff |= IPR_DTR;
-       else if (dtr > 0)
-               iopioron |= IPR_DTR;
-       if (rts == 0)
-               iopioroff |= IPR_RTS;
-       else if (rts > 0)
-               iopioron |= IPR_RTS;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_sc26198setreg(portp, IOPIOR,
-               ((stl_sc26198getreg(portp, IOPIOR) & ~iopioroff) | iopioron));
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Return the state of the signals.
- */
-
-static int stl_sc26198getsignals(struct stlport *portp)
-{
-       unsigned char   ipr;
-       unsigned long   flags;
-       int             sigs;
-
-       pr_debug("stl_sc26198getsignals(portp=%p)\n", portp);
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       ipr = stl_sc26198getreg(portp, IPR);
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-
-       sigs = 0;
-       sigs |= (ipr & IPR_DCD) ? 0 : TIOCM_CD;
-       sigs |= (ipr & IPR_CTS) ? 0 : TIOCM_CTS;
-       sigs |= (ipr & IPR_DTR) ? 0: TIOCM_DTR;
-       sigs |= (ipr & IPR_RTS) ? 0: TIOCM_RTS;
-       sigs |= TIOCM_DSR;
-       return sigs;
-}
-
-/*****************************************************************************/
-
-/*
- *     Enable/Disable the Transmitter and/or Receiver.
- */
-
-static void stl_sc26198enablerxtx(struct stlport *portp, int rx, int tx)
-{
-       unsigned char   ccr;
-       unsigned long   flags;
-
-       pr_debug("stl_sc26198enablerxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx,tx);
-
-       ccr = portp->crenable;
-       if (tx == 0)
-               ccr &= ~CR_TXENABLE;
-       else if (tx > 0)
-               ccr |= CR_TXENABLE;
-       if (rx == 0)
-               ccr &= ~CR_RXENABLE;
-       else if (rx > 0)
-               ccr |= CR_RXENABLE;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_sc26198setreg(portp, SCCR, ccr);
-       BRDDISABLE(portp->brdnr);
-       portp->crenable = ccr;
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Start/stop the Transmitter and/or Receiver.
- */
-
-static void stl_sc26198startrxtx(struct stlport *portp, int rx, int tx)
-{
-       unsigned char   imr;
-       unsigned long   flags;
-
-       pr_debug("stl_sc26198startrxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx, tx);
-
-       imr = portp->imr;
-       if (tx == 0)
-               imr &= ~IR_TXRDY;
-       else if (tx == 1)
-               imr |= IR_TXRDY;
-       if (rx == 0)
-               imr &= ~(IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG);
-       else if (rx > 0)
-               imr |= IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_sc26198setreg(portp, IMR, imr);
-       BRDDISABLE(portp->brdnr);
-       portp->imr = imr;
-       if (tx > 0)
-               set_bit(ASYI_TXBUSY, &portp->istate);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Disable all interrupts from this port.
- */
-
-static void stl_sc26198disableintrs(struct stlport *portp)
-{
-       unsigned long   flags;
-
-       pr_debug("stl_sc26198disableintrs(portp=%p)\n", portp);
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       portp->imr = 0;
-       stl_sc26198setreg(portp, IMR, 0);
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-static void stl_sc26198sendbreak(struct stlport *portp, int len)
-{
-       unsigned long   flags;
-
-       pr_debug("stl_sc26198sendbreak(portp=%p,len=%d)\n", portp, len);
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       if (len == 1) {
-               stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK);
-               portp->stats.txbreaks++;
-       } else
-               stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK);
-
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Take flow control actions...
- */
-
-static void stl_sc26198flowctrl(struct stlport *portp, int state)
-{
-       struct tty_struct       *tty;
-       unsigned long           flags;
-       unsigned char           mr0;
-
-       pr_debug("stl_sc26198flowctrl(portp=%p,state=%x)\n", portp, state);
-
-       if (portp == NULL)
-               return;
-       tty = tty_port_tty_get(&portp->port);
-       if (tty == NULL)
-               return;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-
-       if (state) {
-               if (tty->termios->c_iflag & IXOFF) {
-                       mr0 = stl_sc26198getreg(portp, MR0);
-                       stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
-                       stl_sc26198setreg(portp, SCCR, CR_TXSENDXON);
-                       mr0 |= MR0_SWFRX;
-                       portp->stats.rxxon++;
-                       stl_sc26198wait(portp);
-                       stl_sc26198setreg(portp, MR0, mr0);
-               }
-/*
- *             Question: should we return RTS to what it was before? It may
- *             have been set by an ioctl... Suppose not, since if you have
- *             hardware flow control set then it is pretty silly to go and
- *             set the RTS line by hand.
- */
-               if (tty->termios->c_cflag & CRTSCTS) {
-                       stl_sc26198setreg(portp, MR1,
-                               (stl_sc26198getreg(portp, MR1) | MR1_AUTORTS));
-                       stl_sc26198setreg(portp, IOPIOR,
-                               (stl_sc26198getreg(portp, IOPIOR) | IOPR_RTS));
-                       portp->stats.rxrtson++;
-               }
-       } else {
-               if (tty->termios->c_iflag & IXOFF) {
-                       mr0 = stl_sc26198getreg(portp, MR0);
-                       stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
-                       stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF);
-                       mr0 &= ~MR0_SWFRX;
-                       portp->stats.rxxoff++;
-                       stl_sc26198wait(portp);
-                       stl_sc26198setreg(portp, MR0, mr0);
-               }
-               if (tty->termios->c_cflag & CRTSCTS) {
-                       stl_sc26198setreg(portp, MR1,
-                               (stl_sc26198getreg(portp, MR1) & ~MR1_AUTORTS));
-                       stl_sc26198setreg(portp, IOPIOR,
-                               (stl_sc26198getreg(portp, IOPIOR) & ~IOPR_RTS));
-                       portp->stats.rxrtsoff++;
-               }
-       }
-
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-       tty_kref_put(tty);
-}
-
-/*****************************************************************************/
-
-/*
- *     Send a flow control character.
- */
-
-static void stl_sc26198sendflow(struct stlport *portp, int state)
-{
-       struct tty_struct       *tty;
-       unsigned long           flags;
-       unsigned char           mr0;
-
-       pr_debug("stl_sc26198sendflow(portp=%p,state=%x)\n", portp, state);
-
-       if (portp == NULL)
-               return;
-       tty = tty_port_tty_get(&portp->port);
-       if (tty == NULL)
-               return;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       if (state) {
-               mr0 = stl_sc26198getreg(portp, MR0);
-               stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
-               stl_sc26198setreg(portp, SCCR, CR_TXSENDXON);
-               mr0 |= MR0_SWFRX;
-               portp->stats.rxxon++;
-               stl_sc26198wait(portp);
-               stl_sc26198setreg(portp, MR0, mr0);
-       } else {
-               mr0 = stl_sc26198getreg(portp, MR0);
-               stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
-               stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF);
-               mr0 &= ~MR0_SWFRX;
-               portp->stats.rxxoff++;
-               stl_sc26198wait(portp);
-               stl_sc26198setreg(portp, MR0, mr0);
-       }
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-       tty_kref_put(tty);
-}
-
-/*****************************************************************************/
-
-static void stl_sc26198flush(struct stlport *portp)
-{
-       unsigned long   flags;
-
-       pr_debug("stl_sc26198flush(portp=%p)\n", portp);
-
-       if (portp == NULL)
-               return;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       stl_sc26198setreg(portp, SCCR, CR_TXRESET);
-       stl_sc26198setreg(portp, SCCR, portp->crenable);
-       BRDDISABLE(portp->brdnr);
-       portp->tx.tail = portp->tx.head;
-       spin_unlock_irqrestore(&brd_lock, flags);
-}
-
-/*****************************************************************************/
-
-/*
- *     Return the current state of data flow on this port. This is only
- *     really interesting when determining if data has fully completed
- *     transmission or not... The sc26198 interrupt scheme cannot
- *     determine when all data has actually drained, so we need to
- *     check the port statusy register to be sure.
- */
-
-static int stl_sc26198datastate(struct stlport *portp)
-{
-       unsigned long   flags;
-       unsigned char   sr;
-
-       pr_debug("stl_sc26198datastate(portp=%p)\n", portp);
-
-       if (portp == NULL)
-               return 0;
-       if (test_bit(ASYI_TXBUSY, &portp->istate))
-               return 1;
-
-       spin_lock_irqsave(&brd_lock, flags);
-       BRDENABLE(portp->brdnr, portp->pagenr);
-       sr = stl_sc26198getreg(portp, SR);
-       BRDDISABLE(portp->brdnr);
-       spin_unlock_irqrestore(&brd_lock, flags);
-
-       return (sr & SR_TXEMPTY) ? 0 : 1;
-}
-
-/*****************************************************************************/
-
-/*
- *     Delay for a small amount of time, to give the sc26198 a chance
- *     to process a command...
- */
-
-static void stl_sc26198wait(struct stlport *portp)
-{
-       int     i;
-
-       pr_debug("stl_sc26198wait(portp=%p)\n", portp);
-
-       if (portp == NULL)
-               return;
-
-       for (i = 0; i < 20; i++)
-               stl_sc26198getglobreg(portp, TSTR);
-}
-
-/*****************************************************************************/
-
-/*
- *     If we are TX flow controlled and in IXANY mode then we may
- *     need to unflow control here. We gotta do this because of the
- *     automatic flow control modes of the sc26198.
- */
-
-static void stl_sc26198txunflow(struct stlport *portp, struct tty_struct *tty)
-{
-       unsigned char   mr0;
-
-       mr0 = stl_sc26198getreg(portp, MR0);
-       stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
-       stl_sc26198setreg(portp, SCCR, CR_HOSTXON);
-       stl_sc26198wait(portp);
-       stl_sc26198setreg(portp, MR0, mr0);
-       clear_bit(ASYI_TXFLOWED, &portp->istate);
-}
-
-/*****************************************************************************/
-
-/*
- *     Interrupt service routine for sc26198 panels.
- */
-
-static void stl_sc26198intr(struct stlpanel *panelp, unsigned int iobase)
-{
-       struct stlport  *portp;
-       unsigned int    iack;
-
-       spin_lock(&brd_lock);
-
-/* 
- *     Work around bug in sc26198 chip... Cannot have A6 address
- *     line of UART high, else iack will be returned as 0.
- */
-       outb(0, (iobase + 1));
-
-       iack = inb(iobase + XP_IACK);
-       portp = panelp->ports[(iack & IVR_CHANMASK) + ((iobase & 0x4) << 1)];
-
-       if (iack & IVR_RXDATA)
-               stl_sc26198rxisr(portp, iack);
-       else if (iack & IVR_TXDATA)
-               stl_sc26198txisr(portp);
-       else
-               stl_sc26198otherisr(portp, iack);
-
-       spin_unlock(&brd_lock);
-}
-
-/*****************************************************************************/
-
-/*
- *     Transmit interrupt handler. This has gotta be fast!  Handling TX
- *     chars is pretty simple, stuff as many as possible from the TX buffer
- *     into the sc26198 FIFO.
- *     In practice it is possible that interrupts are enabled but that the
- *     port has been hung up. Need to handle not having any TX buffer here,
- *     this is done by using the side effect that head and tail will also
- *     be NULL if the buffer has been freed.
- */
-
-static void stl_sc26198txisr(struct stlport *portp)
-{
-       struct tty_struct *tty;
-       unsigned int    ioaddr;
-       unsigned char   mr0;
-       int             len, stlen;
-       char            *head, *tail;
-
-       pr_debug("stl_sc26198txisr(portp=%p)\n", portp);
-
-       ioaddr = portp->ioaddr;
-       head = portp->tx.head;
-       tail = portp->tx.tail;
-       len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
-       if ((len == 0) || ((len < STL_TXBUFLOW) &&
-           (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
-               set_bit(ASYI_TXLOW, &portp->istate);
-               tty = tty_port_tty_get(&portp->port);
-               if (tty) {
-                       tty_wakeup(tty);
-                       tty_kref_put(tty);
-               }
-       }
-
-       if (len == 0) {
-               outb((MR0 | portp->uartaddr), (ioaddr + XP_ADDR));
-               mr0 = inb(ioaddr + XP_DATA);
-               if ((mr0 & MR0_TXMASK) == MR0_TXEMPTY) {
-                       portp->imr &= ~IR_TXRDY;
-                       outb((IMR | portp->uartaddr), (ioaddr + XP_ADDR));
-                       outb(portp->imr, (ioaddr + XP_DATA));
-                       clear_bit(ASYI_TXBUSY, &portp->istate);
-               } else {
-                       mr0 |= ((mr0 & ~MR0_TXMASK) | MR0_TXEMPTY);
-                       outb(mr0, (ioaddr + XP_DATA));
-               }
-       } else {
-               len = min(len, SC26198_TXFIFOSIZE);
-               portp->stats.txtotal += len;
-               stlen = min_t(unsigned int, len,
-                               (portp->tx.buf + STL_TXBUFSIZE) - tail);
-               outb(GTXFIFO, (ioaddr + XP_ADDR));
-               outsb((ioaddr + XP_DATA), tail, stlen);
-               len -= stlen;
-               tail += stlen;
-               if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
-                       tail = portp->tx.buf;
-               if (len > 0) {
-                       outsb((ioaddr + XP_DATA), tail, len);
-                       tail += len;
-               }
-               portp->tx.tail = tail;
-       }
-}
-
-/*****************************************************************************/
-
-/*
- *     Receive character interrupt handler. Determine if we have good chars
- *     or bad chars and then process appropriately. Good chars are easy
- *     just shove the lot into the RX buffer and set all status byte to 0.
- *     If a bad RX char then process as required. This routine needs to be
- *     fast!  In practice it is possible that we get an interrupt on a port
- *     that is closed. This can happen on hangups - since they completely
- *     shutdown a port not in user context. Need to handle this case.
- */
-
-static void stl_sc26198rxisr(struct stlport *portp, unsigned int iack)
-{
-       struct tty_struct       *tty;
-       unsigned int            len, buflen, ioaddr;
-
-       pr_debug("stl_sc26198rxisr(portp=%p,iack=%x)\n", portp, iack);
-
-       tty = tty_port_tty_get(&portp->port);
-       ioaddr = portp->ioaddr;
-       outb(GIBCR, (ioaddr + XP_ADDR));
-       len = inb(ioaddr + XP_DATA) + 1;
-
-       if ((iack & IVR_TYPEMASK) == IVR_RXDATA) {
-               if (tty == NULL || (buflen = tty_buffer_request_room(tty, len)) == 0) {
-                       len = min_t(unsigned int, len, sizeof(stl_unwanted));
-                       outb(GRXFIFO, (ioaddr + XP_ADDR));
-                       insb((ioaddr + XP_DATA), &stl_unwanted[0], len);
-                       portp->stats.rxlost += len;
-                       portp->stats.rxtotal += len;
-               } else {
-                       len = min(len, buflen);
-                       if (len > 0) {
-                               unsigned char *ptr;
-                               outb(GRXFIFO, (ioaddr + XP_ADDR));
-                               tty_prepare_flip_string(tty, &ptr, len);
-                               insb((ioaddr + XP_DATA), ptr, len);
-                               tty_schedule_flip(tty);
-                               portp->stats.rxtotal += len;
-                       }
-               }
-       } else {
-               stl_sc26198rxbadchars(portp);
-       }
-
-/*
- *     If we are TX flow controlled and in IXANY mode then we may need
- *     to unflow control here. We gotta do this because of the automatic
- *     flow control modes of the sc26198.
- */
-       if (test_bit(ASYI_TXFLOWED, &portp->istate)) {
-               if ((tty != NULL) &&
-                   (tty->termios != NULL) &&
-                   (tty->termios->c_iflag & IXANY)) {
-                       stl_sc26198txunflow(portp, tty);
-               }
-       }
-       tty_kref_put(tty);
-}
-
-/*****************************************************************************/
-
-/*
- *     Process an RX bad character.
- */
-
-static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char ch)
-{
-       struct tty_struct       *tty;
-       unsigned int            ioaddr;
-
-       tty = tty_port_tty_get(&portp->port);
-       ioaddr = portp->ioaddr;
-
-       if (status & SR_RXPARITY)
-               portp->stats.rxparity++;
-       if (status & SR_RXFRAMING)
-               portp->stats.rxframing++;
-       if (status & SR_RXOVERRUN)
-               portp->stats.rxoverrun++;
-       if (status & SR_RXBREAK)
-               portp->stats.rxbreaks++;
-
-       if ((tty != NULL) &&
-           ((portp->rxignoremsk & status) == 0)) {
-               if (portp->rxmarkmsk & status) {
-                       if (status & SR_RXBREAK) {
-                               status = TTY_BREAK;
-                               if (portp->port.flags & ASYNC_SAK) {
-                                       do_SAK(tty);
-                                       BRDENABLE(portp->brdnr, portp->pagenr);
-                               }
-                       } else if (status & SR_RXPARITY)
-                               status = TTY_PARITY;
-                       else if (status & SR_RXFRAMING)
-                               status = TTY_FRAME;
-                       else if(status & SR_RXOVERRUN)
-                               status = TTY_OVERRUN;
-                       else
-                               status = 0;
-               } else
-                       status = 0;
-
-               tty_insert_flip_char(tty, ch, status);
-               tty_schedule_flip(tty);
-
-               if (status == 0)
-                       portp->stats.rxtotal++;
-       }
-       tty_kref_put(tty);
-}
-
-/*****************************************************************************/
-
-/*
- *     Process all characters in the RX FIFO of the UART. Check all char
- *     status bytes as well, and process as required. We need to check
- *     all bytes in the FIFO, in case some more enter the FIFO while we
- *     are here. To get the exact character error type we need to switch
- *     into CHAR error mode (that is why we need to make sure we empty
- *     the FIFO).
- */
-
-static void stl_sc26198rxbadchars(struct stlport *portp)
-{
-       unsigned char   status, mr1;
-       char            ch;
-
-/*
- *     To get the precise error type for each character we must switch
- *     back into CHAR error mode.
- */
-       mr1 = stl_sc26198getreg(portp, MR1);
-       stl_sc26198setreg(portp, MR1, (mr1 & ~MR1_ERRBLOCK));
-
-       while ((status = stl_sc26198getreg(portp, SR)) & SR_RXRDY) {
-               stl_sc26198setreg(portp, SCCR, CR_CLEARRXERR);
-               ch = stl_sc26198getreg(portp, RXFIFO);
-               stl_sc26198rxbadch(portp, status, ch);
-       }
-
-/*
- *     To get correct interrupt class we must switch back into BLOCK
- *     error mode.
- */
-       stl_sc26198setreg(portp, MR1, mr1);
-}
-
-/*****************************************************************************/
-
-/*
- *     Other interrupt handler. This includes modem signals, flow
- *     control actions, etc. Most stuff is left to off-level interrupt
- *     processing time.
- */
-
-static void stl_sc26198otherisr(struct stlport *portp, unsigned int iack)
-{
-       unsigned char   cir, ipr, xisr;
-
-       pr_debug("stl_sc26198otherisr(portp=%p,iack=%x)\n", portp, iack);
-
-       cir = stl_sc26198getglobreg(portp, CIR);
-
-       switch (cir & CIR_SUBTYPEMASK) {
-       case CIR_SUBCOS:
-               ipr = stl_sc26198getreg(portp, IPR);
-               if (ipr & IPR_DCDCHANGE) {
-                       stl_cd_change(portp);
-                       portp->stats.modem++;
-               }
-               break;
-       case CIR_SUBXONXOFF:
-               xisr = stl_sc26198getreg(portp, XISR);
-               if (xisr & XISR_RXXONGOT) {
-                       set_bit(ASYI_TXFLOWED, &portp->istate);
-                       portp->stats.txxoff++;
-               }
-               if (xisr & XISR_RXXOFFGOT) {
-                       clear_bit(ASYI_TXFLOWED, &portp->istate);
-                       portp->stats.txxon++;
-               }
-               break;
-       case CIR_SUBBREAK:
-               stl_sc26198setreg(portp, SCCR, CR_BREAKRESET);
-               stl_sc26198rxbadchars(portp);
-               break;
-       default:
-               break;
-       }
-}
-
-static void stl_free_isabrds(void)
-{
-       struct stlbrd *brdp;
-       unsigned int i;
-
-       for (i = 0; i < stl_nrbrds; i++) {
-               if ((brdp = stl_brds[i]) == NULL || (brdp->state & STL_PROBED))
-                       continue;
-
-               free_irq(brdp->irq, brdp);
-
-               stl_cleanup_panels(brdp);
-
-               release_region(brdp->ioaddr1, brdp->iosize1);
-               if (brdp->iosize2 > 0)
-                       release_region(brdp->ioaddr2, brdp->iosize2);
-
-               kfree(brdp);
-               stl_brds[i] = NULL;
-       }
-}
-
-/*
- *     Loadable module initialization stuff.
- */
-static int __init stallion_module_init(void)
-{
-       struct stlbrd   *brdp;
-       struct stlconf  conf;
-       unsigned int i, j;
-       int retval;
-
-       printk(KERN_INFO "%s: version %s\n", stl_drvtitle, stl_drvversion);
-
-       spin_lock_init(&stallion_lock);
-       spin_lock_init(&brd_lock);
-
-       stl_serial = alloc_tty_driver(STL_MAXBRDS * STL_MAXPORTS);
-       if (!stl_serial) {
-               retval = -ENOMEM;
-               goto err;
-       }
-
-       stl_serial->owner = THIS_MODULE;
-       stl_serial->driver_name = stl_drvname;
-       stl_serial->name = "ttyE";
-       stl_serial->major = STL_SERIALMAJOR;
-       stl_serial->minor_start = 0;
-       stl_serial->type = TTY_DRIVER_TYPE_SERIAL;
-       stl_serial->subtype = SERIAL_TYPE_NORMAL;
-       stl_serial->init_termios = stl_deftermios;
-       stl_serial->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
-       tty_set_operations(stl_serial, &stl_ops);
-
-       retval = tty_register_driver(stl_serial);
-       if (retval) {
-               printk("STALLION: failed to register serial driver\n");
-               goto err_frtty;
-       }
-
-/*
- *     Find any dynamically supported boards. That is via module load
- *     line options.
- */
-       for (i = stl_nrbrds; i < stl_nargs; i++) {
-               memset(&conf, 0, sizeof(conf));
-               if (stl_parsebrd(&conf, stl_brdsp[i]) == 0)
-                       continue;
-               if ((brdp = stl_allocbrd()) == NULL)
-                       continue;
-               brdp->brdnr = i;
-               brdp->brdtype = conf.brdtype;
-               brdp->ioaddr1 = conf.ioaddr1;
-               brdp->ioaddr2 = conf.ioaddr2;
-               brdp->irq = conf.irq;
-               brdp->irqtype = conf.irqtype;
-               stl_brds[brdp->brdnr] = brdp;
-               if (stl_brdinit(brdp)) {
-                       stl_brds[brdp->brdnr] = NULL;
-                       kfree(brdp);
-               } else {
-                       for (j = 0; j < brdp->nrports; j++)
-                               tty_register_device(stl_serial,
-                                       brdp->brdnr * STL_MAXPORTS + j, NULL);
-                       stl_nrbrds = i + 1;
-               }
-       }
-
-       /* this has to be _after_ isa finding because of locking */
-       retval = pci_register_driver(&stl_pcidriver);
-       if (retval && stl_nrbrds == 0) {
-               printk(KERN_ERR "STALLION: can't register pci driver\n");
-               goto err_unrtty;
-       }
-
-/*
- *     Set up a character driver for per board stuff. This is mainly used
- *     to do stats ioctls on the ports.
- */
-       if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem))
-               printk("STALLION: failed to register serial board device\n");
-
-       stallion_class = class_create(THIS_MODULE, "staliomem");
-       if (IS_ERR(stallion_class))
-               printk("STALLION: failed to create class\n");
-       for (i = 0; i < 4; i++)
-               device_create(stallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i),
-                             NULL, "staliomem%d", i);
-
-       return 0;
-err_unrtty:
-       tty_unregister_driver(stl_serial);
-err_frtty:
-       put_tty_driver(stl_serial);
-err:
-       return retval;
-}
-
-static void __exit stallion_module_exit(void)
-{
-       struct stlbrd *brdp;
-       unsigned int i, j;
-
-       pr_debug("cleanup_module()\n");
-
-       printk(KERN_INFO "Unloading %s: version %s\n", stl_drvtitle,
-               stl_drvversion);
-
-/*
- *     Free up all allocated resources used by the ports. This includes
- *     memory and interrupts. As part of this process we will also do
- *     a hangup on every open port - to try to flush out any processes
- *     hanging onto ports.
- */
-       for (i = 0; i < stl_nrbrds; i++) {
-               if ((brdp = stl_brds[i]) == NULL || (brdp->state & STL_PROBED))
-                       continue;
-               for (j = 0; j < brdp->nrports; j++)
-                       tty_unregister_device(stl_serial,
-                               brdp->brdnr * STL_MAXPORTS + j);
-       }
-
-       for (i = 0; i < 4; i++)
-               device_destroy(stallion_class, MKDEV(STL_SIOMEMMAJOR, i));
-       unregister_chrdev(STL_SIOMEMMAJOR, "staliomem");
-       class_destroy(stallion_class);
-
-       pci_unregister_driver(&stl_pcidriver);
-
-       stl_free_isabrds();
-
-       tty_unregister_driver(stl_serial);
-       put_tty_driver(stl_serial);
-}
-
-module_init(stallion_module_init);
-module_exit(stallion_module_exit);
-
-MODULE_AUTHOR("Greg Ungerer");
-MODULE_DESCRIPTION("Stallion Multiport Serial Driver");
-MODULE_LICENSE("GPL");
index 5c8fcfc..fb1fc4e 100644 (file)
@@ -41,6 +41,8 @@ config STAGING_EXCLUDE_BUILD
 
 if !STAGING_EXCLUDE_BUILD
 
+source "drivers/staging/tty/Kconfig"
+
 source "drivers/staging/et131x/Kconfig"
 
 source "drivers/staging/slicoss/Kconfig"
index d538863..f498e34 100644 (file)
@@ -3,6 +3,7 @@
 # fix for build system bug...
 obj-$(CONFIG_STAGING)          += staging.o
 
+obj-y += tty/
 obj-$(CONFIG_ET131X)           += et131x/
 obj-$(CONFIG_SLICOSS)          += slicoss/
 obj-$(CONFIG_VIDEO_GO7007)     += go7007/
diff --git a/drivers/staging/tty/Kconfig b/drivers/staging/tty/Kconfig
new file mode 100644 (file)
index 0000000..77103a0
--- /dev/null
@@ -0,0 +1,87 @@
+config STALLION
+       tristate "Stallion EasyIO or EC8/32 support"
+       depends on STALDRV && (ISA || EISA || PCI)
+       help
+         If you have an EasyIO or EasyConnection 8/32 multiport Stallion
+         card, then this is for you; say Y.  Make sure to read
+         <file:Documentation/serial/stallion.txt>.
+
+         To compile this driver as a module, choose M here: the
+         module will be called stallion.
+
+config ISTALLION
+       tristate "Stallion EC8/64, ONboard, Brumby support"
+       depends on STALDRV && (ISA || EISA || PCI)
+       help
+         If you have an EasyConnection 8/64, ONboard, Brumby or Stallion
+         serial multiport card, say Y here. Make sure to read
+         <file:Documentation/serial/stallion.txt>.
+
+         To compile this driver as a module, choose M here: the
+         module will be called istallion.
+
+config DIGIEPCA
+       tristate "Digiboard Intelligent Async Support"
+       depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
+       ---help---
+         This is a driver for Digi International's Xx, Xeve, and Xem series
+         of cards which provide multiple serial ports. 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. This driver
+         supports the original PC (ISA) boards as well as PCI, and EISA. If
+         you have a card like this, say Y here and read the file
+         <file:Documentation/serial/digiepca.txt>.
+
+         To compile this driver as a module, choose M here: the
+         module will be called epca.
+
+config RISCOM8
+       tristate "SDL RISCom/8 card support"
+       depends on SERIAL_NONSTANDARD
+       help
+         This is a driver for the SDL Communications RISCom/8 multiport card,
+         which gives you many serial ports. 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. If you have a card like that,
+         say Y here and read the file <file:Documentation/serial/riscom8.txt>.
+
+         Also it's possible to say M here and compile this driver as kernel
+         loadable module; the module will be called riscom8.
+
+config SPECIALIX
+       tristate "Specialix IO8+ card support"
+       depends on SERIAL_NONSTANDARD
+       help
+         This is a driver for the Specialix IO8+ multiport card (both the
+         ISA and the PCI version) which gives you many serial ports. 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.
+
+         If you have a card like that, say Y here and read the file
+         <file:Documentation/serial/specialix.txt>. Also it's possible to say
+         M here and compile this driver as kernel loadable module which will be
+         called specialix.
+
+config COMPUTONE
+       tristate "Computone IntelliPort Plus serial support"
+       depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
+       ---help---
+         This driver supports the entire family of Intelliport II/Plus
+         controllers with the exception of the MicroChannel controllers and
+         products previous to the Intelliport II. These are multiport cards,
+         which give you many serial ports. 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. If you have a card like that, say
+         Y here and read <file:Documentation/serial/computone.txt>.
+
+         To compile this driver as module, choose M here: the
+         module will be called ip2.
+
+config SERIAL167
+       bool "CD2401 support for MVME166/7 serial ports"
+       depends on MVME16x
+       help
+         This is the driver for the serial ports on the Motorola MVME166,
+         167, and 172 boards.  Everyone using one of these boards should say
+         Y here.
+
diff --git a/drivers/staging/tty/Makefile b/drivers/staging/tty/Makefile
new file mode 100644 (file)
index 0000000..ac57c10
--- /dev/null
@@ -0,0 +1,7 @@
+obj-$(CONFIG_STALLION)         += stallion.o
+obj-$(CONFIG_ISTALLION)                += istallion.o
+obj-$(CONFIG_DIGIEPCA)         += epca.o
+obj-$(CONFIG_SERIAL167)                += serial167.o
+obj-$(CONFIG_SPECIALIX)                += specialix.o
+obj-$(CONFIG_RISCOM8)          += riscom8.o
+obj-$(CONFIG_COMPUTONE)                += ip2/
diff --git a/drivers/staging/tty/TODO b/drivers/staging/tty/TODO
new file mode 100644 (file)
index 0000000..8875645
--- /dev/null
@@ -0,0 +1,6 @@
+These are a few tty/serial drivers that either do not build,
+or work if they do build, or if they seem to work, are for obsolete
+hardware, or are full of unfixable races and no one uses them anymore.
+
+If no one steps up to adopt any of these drivers, they will be removed
+in the 2.6.41 release.
diff --git a/drivers/staging/tty/epca.c b/drivers/staging/tty/epca.c
new file mode 100644 (file)
index 0000000..7ad3638
--- /dev/null
@@ -0,0 +1,2784 @@
+/*
+       Copyright (C) 1996  Digi International.
+
+       For technical support please email digiLinux@dgii.com or
+       call Digi tech support at (612) 912-3456
+
+       ** This driver is no longer supported by Digi **
+
+       Much of this design and code came from epca.c which was
+       copyright (C) 1994, 1995 Troy De Jongh, and subsquently
+       modified by David Nugent, Christoph Lameter, Mike McLagan.
+
+       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.
+*/
+/* See README.epca for change history --DAT*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/serial.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+#include "digiPCI.h"
+
+
+#include "digi1.h"
+#include "digiFep1.h"
+#include "epca.h"
+#include "epcaconfig.h"
+
+#define VERSION            "1.3.0.1-LK2.6"
+
+/* This major needs to be submitted to Linux to join the majors list */
+#define DIGIINFOMAJOR       35  /* For Digi specific ioctl */
+
+
+#define MAXCARDS 7
+#define epcaassert(x, msg)  if (!(x)) epca_error(__LINE__, msg)
+
+#define PFX "epca: "
+
+static int nbdevs, num_cards, liloconfig;
+static int digi_poller_inhibited = 1 ;
+
+static int setup_error_code;
+static int invalid_lilo_config;
+
+/*
+ * The ISA boards do window flipping into the same spaces so its only sane with
+ * a single lock. It's still pretty efficient. This lock guards the hardware
+ * and the tty_port lock guards the kernel side stuff like use counts. Take
+ * this lock inside the port lock if you must take both.
+ */
+static DEFINE_SPINLOCK(epca_lock);
+
+/* MAXBOARDS is typically 12, but ISA and EISA cards are restricted
+   to 7 below. */
+static struct board_info boards[MAXBOARDS];
+
+static struct tty_driver *pc_driver;
+static struct tty_driver *pc_info;
+
+/* ------------------ Begin Digi specific structures -------------------- */
+
+/*
+ * digi_channels represents an array of structures that keep track of each
+ * channel of the Digi product. Information such as transmit and receive
+ * pointers, termio data, and signal definitions (DTR, CTS, etc ...) are stored
+ * here. This structure is NOT used to overlay the cards physical channel
+ * structure.
+ */
+static struct channel digi_channels[MAX_ALLOC];
+
+/*
+ * card_ptr is an array used to hold the address of the first channel structure
+ * of each card. This array will hold the addresses of various channels located
+ * in digi_channels.
+ */
+static struct channel *card_ptr[MAXCARDS];
+
+static struct timer_list epca_timer;
+
+/*
+ * Begin generic memory functions. These functions will be alias (point at)
+ * more specific functions dependent on the board being configured.
+ */
+static void memwinon(struct board_info *b, unsigned int win);
+static void memwinoff(struct board_info *b, unsigned int win);
+static void globalwinon(struct channel *ch);
+static void rxwinon(struct channel *ch);
+static void txwinon(struct channel *ch);
+static void memoff(struct channel *ch);
+static void assertgwinon(struct channel *ch);
+static void assertmemoff(struct channel *ch);
+
+/* ---- Begin more 'specific' memory functions for cx_like products --- */
+
+static void pcxem_memwinon(struct board_info *b, unsigned int win);
+static void pcxem_memwinoff(struct board_info *b, unsigned int win);
+static void pcxem_globalwinon(struct channel *ch);
+static void pcxem_rxwinon(struct channel *ch);
+static void pcxem_txwinon(struct channel *ch);
+static void pcxem_memoff(struct channel *ch);
+
+/* ------ Begin more 'specific' memory functions for the pcxe ------- */
+
+static void pcxe_memwinon(struct board_info *b, unsigned int win);
+static void pcxe_memwinoff(struct board_info *b, unsigned int win);
+static void pcxe_globalwinon(struct channel *ch);
+static void pcxe_rxwinon(struct channel *ch);
+static void pcxe_txwinon(struct channel *ch);
+static void pcxe_memoff(struct channel *ch);
+
+/* ---- Begin more 'specific' memory functions for the pc64xe and pcxi ---- */
+/* Note : pc64xe and pcxi share the same windowing routines */
+
+static void pcxi_memwinon(struct board_info *b, unsigned int win);
+static void pcxi_memwinoff(struct board_info *b, unsigned int win);
+static void pcxi_globalwinon(struct channel *ch);
+static void pcxi_rxwinon(struct channel *ch);
+static void pcxi_txwinon(struct channel *ch);
+static void pcxi_memoff(struct channel *ch);
+
+/* - Begin 'specific' do nothing memory functions needed for some cards - */
+
+static void dummy_memwinon(struct board_info *b, unsigned int win);
+static void dummy_memwinoff(struct board_info *b, unsigned int win);
+static void dummy_globalwinon(struct channel *ch);
+static void dummy_rxwinon(struct channel *ch);
+static void dummy_txwinon(struct channel *ch);
+static void dummy_memoff(struct channel *ch);
+static void dummy_assertgwinon(struct channel *ch);
+static void dummy_assertmemoff(struct channel *ch);
+
+static struct channel *verifyChannel(struct tty_struct *);
+static void pc_sched_event(struct channel *, int);
+static void epca_error(int, char *);
+static void pc_close(struct tty_struct *, struct file *);
+static void shutdown(struct channel *, struct tty_struct *tty);
+static void pc_hangup(struct tty_struct *);
+static int pc_write_room(struct tty_struct *);
+static int pc_chars_in_buffer(struct tty_struct *);
+static void pc_flush_buffer(struct tty_struct *);
+static void pc_flush_chars(struct tty_struct *);
+static int pc_open(struct tty_struct *, struct file *);
+static void post_fep_init(unsigned int crd);
+static void epcapoll(unsigned long);
+static void doevent(int);
+static void fepcmd(struct channel *, int, int, int, int, int);
+static unsigned termios2digi_h(struct channel *ch, unsigned);
+static unsigned termios2digi_i(struct channel *ch, unsigned);
+static unsigned termios2digi_c(struct channel *ch, unsigned);
+static void epcaparam(struct tty_struct *, struct channel *);
+static void receive_data(struct channel *, struct tty_struct *tty);
+static int pc_ioctl(struct tty_struct *,
+                       unsigned int, unsigned long);
+static int info_ioctl(struct tty_struct *,
+                       unsigned int, unsigned long);
+static void pc_set_termios(struct tty_struct *, struct ktermios *);
+static void do_softint(struct work_struct *work);
+static void pc_stop(struct tty_struct *);
+static void pc_start(struct tty_struct *);
+static void pc_throttle(struct tty_struct *tty);
+static void pc_unthrottle(struct tty_struct *tty);
+static int pc_send_break(struct tty_struct *tty, int msec);
+static void setup_empty_event(struct tty_struct *tty, struct channel *ch);
+
+static int pc_write(struct tty_struct *, const unsigned char *, int);
+static int pc_init(void);
+static int init_PCI(void);
+
+/*
+ * Table of functions for each board to handle memory. Mantaining parallelism
+ * is a *very* good idea here. The idea is for the runtime code to blindly call
+ * these functions, not knowing/caring about the underlying hardware. This
+ * stuff should contain no conditionals; if more functionality is needed a
+ * different entry should be established. These calls are the interface calls
+ * and are the only functions that should be accessed. Anyone caught making
+ * direct calls deserves what they get.
+ */
+static void memwinon(struct board_info *b, unsigned int win)
+{
+       b->memwinon(b, win);
+}
+
+static void memwinoff(struct board_info *b, unsigned int win)
+{
+       b->memwinoff(b, win);
+}
+
+static void globalwinon(struct channel *ch)
+{
+       ch->board->globalwinon(ch);
+}
+
+static void rxwinon(struct channel *ch)
+{
+       ch->board->rxwinon(ch);
+}
+
+static void txwinon(struct channel *ch)
+{
+       ch->board->txwinon(ch);
+}
+
+static void memoff(struct channel *ch)
+{
+       ch->board->memoff(ch);
+}
+static void assertgwinon(struct channel *ch)
+{
+       ch->board->assertgwinon(ch);
+}
+
+static void assertmemoff(struct channel *ch)
+{
+       ch->board->assertmemoff(ch);
+}
+
+/* PCXEM windowing is the same as that used in the PCXR and CX series cards. */
+static void pcxem_memwinon(struct board_info *b, unsigned int win)
+{
+       outb_p(FEPWIN | win, b->port + 1);
+}
+
+static void pcxem_memwinoff(struct board_info *b, unsigned int win)
+{
+       outb_p(0, b->port + 1);
+}
+
+static void pcxem_globalwinon(struct channel *ch)
+{
+       outb_p(FEPWIN, (int)ch->board->port + 1);
+}
+
+static void pcxem_rxwinon(struct channel *ch)
+{
+       outb_p(ch->rxwin, (int)ch->board->port + 1);
+}
+
+static void pcxem_txwinon(struct channel *ch)
+{
+       outb_p(ch->txwin, (int)ch->board->port + 1);
+}
+
+static void pcxem_memoff(struct channel *ch)
+{
+       outb_p(0, (int)ch->board->port + 1);
+}
+
+/* ----------------- Begin pcxe memory window stuff ------------------ */
+static void pcxe_memwinon(struct board_info *b, unsigned int win)
+{
+       outb_p(FEPWIN | win, b->port + 1);
+}
+
+static void pcxe_memwinoff(struct board_info *b, unsigned int win)
+{
+       outb_p(inb(b->port) & ~FEPMEM, b->port + 1);
+       outb_p(0, b->port + 1);
+}
+
+static void pcxe_globalwinon(struct channel *ch)
+{
+       outb_p(FEPWIN, (int)ch->board->port + 1);
+}
+
+static void pcxe_rxwinon(struct channel *ch)
+{
+       outb_p(ch->rxwin, (int)ch->board->port + 1);
+}
+
+static void pcxe_txwinon(struct channel *ch)
+{
+       outb_p(ch->txwin, (int)ch->board->port + 1);
+}
+
+static void pcxe_memoff(struct channel *ch)
+{
+       outb_p(0, (int)ch->board->port);
+       outb_p(0, (int)ch->board->port + 1);
+}
+
+/* ------------- Begin pc64xe and pcxi memory window stuff -------------- */
+static void pcxi_memwinon(struct board_info *b, unsigned int win)
+{
+       outb_p(inb(b->port) | FEPMEM, b->port);
+}
+
+static void pcxi_memwinoff(struct board_info *b, unsigned int win)
+{
+       outb_p(inb(b->port) & ~FEPMEM, b->port);
+}
+
+static void pcxi_globalwinon(struct channel *ch)
+{
+       outb_p(FEPMEM, ch->board->port);
+}
+
+static void pcxi_rxwinon(struct channel *ch)
+{
+       outb_p(FEPMEM, ch->board->port);
+}
+
+static void pcxi_txwinon(struct channel *ch)
+{
+       outb_p(FEPMEM, ch->board->port);
+}
+
+static void pcxi_memoff(struct channel *ch)
+{
+       outb_p(0, ch->board->port);
+}
+
+static void pcxi_assertgwinon(struct channel *ch)
+{
+       epcaassert(inb(ch->board->port) & FEPMEM, "Global memory off");
+}
+
+static void pcxi_assertmemoff(struct channel *ch)
+{
+       epcaassert(!(inb(ch->board->port) & FEPMEM), "Memory on");
+}
+
+/*
+ * Not all of the cards need specific memory windowing routines. Some cards
+ * (Such as PCI) needs no windowing routines at all. We provide these do
+ * nothing routines so that the same code base can be used. The driver will
+ * ALWAYS call a windowing routine if it thinks it needs to; regardless of the
+ * card. However, dependent on the card the routine may or may not do anything.
+ */
+static void dummy_memwinon(struct board_info *b, unsigned int win)
+{
+}
+
+static void dummy_memwinoff(struct board_info *b, unsigned int win)
+{
+}
+
+static void dummy_globalwinon(struct channel *ch)
+{
+}
+
+static void dummy_rxwinon(struct channel *ch)
+{
+}
+
+static void dummy_txwinon(struct channel *ch)
+{
+}
+
+static void dummy_memoff(struct channel *ch)
+{
+}
+
+static void dummy_assertgwinon(struct channel *ch)
+{
+}
+
+static void dummy_assertmemoff(struct channel *ch)
+{
+}
+
+static struct channel *verifyChannel(struct tty_struct *tty)
+{
+       /*
+        * This routine basically provides a sanity check. It insures that the
+        * channel returned is within the proper range of addresses as well as
+        * properly initialized. If some bogus info gets passed in
+        * through tty->driver_data this should catch it.
+        */
+       if (tty) {
+               struct channel *ch = tty->driver_data;
+               if (ch >= &digi_channels[0] && ch < &digi_channels[nbdevs]) {
+                       if (ch->magic == EPCA_MAGIC)
+                               return ch;
+               }
+       }
+       return NULL;
+}
+
+static void pc_sched_event(struct channel *ch, int event)
+{
+       /*
+        * We call this to schedule interrupt processing on some event. The
+        * kernel sees our request and calls the related routine in OUR driver.
+        */
+       ch->event |= 1 << event;
+       schedule_work(&ch->tqueue);
+}
+
+static void epca_error(int line, char *msg)
+{
+       printk(KERN_ERR "epca_error (Digi): line = %d %s\n", line, msg);
+}
+
+static void pc_close(struct tty_struct *tty, struct file *filp)
+{
+       struct channel *ch;
+       struct tty_port *port;
+       /*
+        * verifyChannel returns the channel from the tty struct if it is
+        * valid. This serves as a sanity check.
+        */
+       ch = verifyChannel(tty);
+       if (ch == NULL)
+               return;
+       port = &ch->port;
+
+       if (tty_port_close_start(port, tty, filp) == 0)
+               return;
+
+       pc_flush_buffer(tty);
+       shutdown(ch, tty);
+
+       tty_port_close_end(port, tty);
+       ch->event = 0;  /* FIXME: review ch->event locking */
+       tty_port_tty_set(port, NULL);
+}
+
+static void shutdown(struct channel *ch, struct tty_struct *tty)
+{
+       unsigned long flags;
+       struct board_chan __iomem *bc;
+       struct tty_port *port = &ch->port;
+
+       if (!(port->flags & ASYNC_INITIALIZED))
+               return;
+
+       spin_lock_irqsave(&epca_lock, flags);
+
+       globalwinon(ch);
+       bc = ch->brdchan;
+
+       /*
+        * In order for an event to be generated on the receipt of data the
+        * idata flag must be set. Since we are shutting down, this is not
+        * necessary clear this flag.
+        */
+       if (bc)
+               writeb(0, &bc->idata);
+
+       /* If we're a modem control device and HUPCL is on, drop RTS & DTR. */
+       if (tty->termios->c_cflag & HUPCL)  {
+               ch->omodem &= ~(ch->m_rts | ch->m_dtr);
+               fepcmd(ch, SETMODEM, 0, ch->m_dtr | ch->m_rts, 10, 1);
+       }
+       memoff(ch);
+
+       /*
+        * The channel has officialy been closed. The next time it is opened it
+        * will have to reinitialized. Set a flag to indicate this.
+        */
+       /* Prevent future Digi programmed interrupts from coming active */
+       port->flags &= ~ASYNC_INITIALIZED;
+       spin_unlock_irqrestore(&epca_lock, flags);
+}
+
+static void pc_hangup(struct tty_struct *tty)
+{
+       struct channel *ch;
+
+       /*
+        * verifyChannel returns the channel from the tty struct if it is
+        * valid. This serves as a sanity check.
+        */
+       ch = verifyChannel(tty);
+       if (ch != NULL) {
+               pc_flush_buffer(tty);
+               tty_ldisc_flush(tty);
+               shutdown(ch, tty);
+
+               ch->event = 0;  /* FIXME: review locking of ch->event */
+               tty_port_hangup(&ch->port);
+       }
+}
+
+static int pc_write(struct tty_struct *tty,
+                       const unsigned char *buf, int bytesAvailable)
+{
+       unsigned int head, tail;
+       int dataLen;
+       int size;
+       int amountCopied;
+       struct channel *ch;
+       unsigned long flags;
+       int remain;
+       struct board_chan __iomem *bc;
+
+       /*
+        * pc_write is primarily called directly by the kernel routine
+        * tty_write (Though it can also be called by put_char) found in
+        * tty_io.c. pc_write is passed a line discipline buffer where the data
+        * to be written out is stored. The line discipline implementation
+        * itself is done at the kernel level and is not brought into the
+        * driver.
+        */
+
+       /*
+        * verifyChannel returns the channel from the tty struct if it is
+        * valid. This serves as a sanity check.
+        */
+       ch = verifyChannel(tty);
+       if (ch == NULL)
+               return 0;
+
+       /* Make a pointer to the channel data structure found on the board. */
+       bc   = ch->brdchan;
+       size = ch->txbufsize;
+       amountCopied = 0;
+
+       spin_lock_irqsave(&epca_lock, flags);
+       globalwinon(ch);
+
+       head = readw(&bc->tin) & (size - 1);
+       tail = readw(&bc->tout);
+
+       if (tail != readw(&bc->tout))
+               tail = readw(&bc->tout);
+       tail &= (size - 1);
+
+       if (head >= tail) {
+               /* head has not wrapped */
+               /*
+                * remain (much like dataLen above) represents the total amount
+                * of space available on the card for data. Here dataLen
+                * represents the space existing between the head pointer and
+                * the end of buffer. This is important because a memcpy cannot
+                * be told to automatically wrap around when it hits the buffer
+                * end.
+                */
+               dataLen = size - head;
+               remain = size - (head - tail) - 1;
+       } else {
+               /* head has wrapped around */
+               remain = tail - head - 1;
+               dataLen = remain;
+       }
+       /*
+        * Check the space on the card. If we have more data than space; reduce
+        * the amount of data to fit the space.
+        */
+       bytesAvailable = min(remain, bytesAvailable);
+       txwinon(ch);
+       while (bytesAvailable > 0) {
+               /* there is data to copy onto card */
+
+               /*
+                * If head is not wrapped, the below will make sure the first
+                * data copy fills to the end of card buffer.
+                */
+               dataLen = min(bytesAvailable, dataLen);
+               memcpy_toio(ch->txptr + head, buf, dataLen);
+               buf += dataLen;
+               head += dataLen;
+               amountCopied += dataLen;
+               bytesAvailable -= dataLen;
+
+               if (head >= size) {
+                       head = 0;
+                       dataLen = tail;
+               }
+       }
+       ch->statusflags |= TXBUSY;
+       globalwinon(ch);
+       writew(head, &bc->tin);
+
+       if ((ch->statusflags & LOWWAIT) == 0)  {
+               ch->statusflags |= LOWWAIT;
+               writeb(1, &bc->ilow);
+       }
+       memoff(ch);
+       spin_unlock_irqrestore(&epca_lock, flags);
+       return amountCopied;
+}
+
+static int pc_write_room(struct tty_struct *tty)
+{
+       int remain = 0;
+       struct channel *ch;
+       unsigned long flags;
+       unsigned int head, tail;
+       struct board_chan __iomem *bc;
+       /*
+        * verifyChannel returns the channel from the tty struct if it is
+        * valid. This serves as a sanity check.
+        */
+       ch = verifyChannel(tty);
+       if (ch != NULL) {
+               spin_lock_irqsave(&epca_lock, flags);
+               globalwinon(ch);
+
+               bc   = ch->brdchan;
+               head = readw(&bc->tin) & (ch->txbufsize - 1);
+               tail = readw(&bc->tout);
+
+               if (tail != readw(&bc->tout))
+                       tail = readw(&bc->tout);
+               /* Wrap tail if necessary */
+               tail &= (ch->txbufsize - 1);
+               remain = tail - head - 1;
+               if (remain < 0)
+                       remain += ch->txbufsize;
+
+               if (remain && (ch->statusflags & LOWWAIT) == 0) {
+                       ch->statusflags |= LOWWAIT;
+                       writeb(1, &bc->ilow);
+               }
+               memoff(ch);
+               spin_unlock_irqrestore(&epca_lock, flags);
+       }
+       /* Return how much room is left on card */
+       return remain;
+}
+
+static int pc_chars_in_buffer(struct tty_struct *tty)
+{
+       int chars;
+       unsigned int ctail, head, tail;
+       int remain;
+       unsigned long flags;
+       struct channel *ch;
+       struct board_chan __iomem *bc;
+       /*
+        * verifyChannel returns the channel from the tty struct if it is
+        * valid. This serves as a sanity check.
+        */
+       ch = verifyChannel(tty);
+       if (ch == NULL)
+               return 0;
+
+       spin_lock_irqsave(&epca_lock, flags);
+       globalwinon(ch);
+
+       bc = ch->brdchan;
+       tail = readw(&bc->tout);
+       head = readw(&bc->tin);
+       ctail = readw(&ch->mailbox->cout);
+
+       if (tail == head && readw(&ch->mailbox->cin) == ctail &&
+                                               readb(&bc->tbusy) == 0)
+               chars = 0;
+       else  { /* Begin if some space on the card has been used */
+               head = readw(&bc->tin) & (ch->txbufsize - 1);
+               tail &= (ch->txbufsize - 1);
+               /*
+                * The logic here is basically opposite of the above
+                * pc_write_room here we are finding the amount of bytes in the
+                * buffer filled. Not the amount of bytes empty.
+                */
+               remain = tail - head - 1;
+               if (remain < 0)
+                       remain += ch->txbufsize;
+               chars = (int)(ch->txbufsize - remain);
+               /*
+                * Make it possible to wakeup anything waiting for output in
+                * tty_ioctl.c, etc.
+                *
+                * If not already set. Setup an event to indicate when the
+                * transmit buffer empties.
+                */
+               if (!(ch->statusflags & EMPTYWAIT))
+                       setup_empty_event(tty, ch);
+       } /* End if some space on the card has been used */
+       memoff(ch);
+       spin_unlock_irqrestore(&epca_lock, flags);
+       /* Return number of characters residing on card. */
+       return chars;
+}
+
+static void pc_flush_buffer(struct tty_struct *tty)
+{
+       unsigned int tail;
+       unsigned long flags;
+       struct channel *ch;
+       struct board_chan __iomem *bc;
+       /*
+        * verifyChannel returns the channel from the tty struct if it is
+        * valid. This serves as a sanity check.
+        */
+       ch = verifyChannel(tty);
+       if (ch == NULL)
+               return;
+
+       spin_lock_irqsave(&epca_lock, flags);
+       globalwinon(ch);
+       bc   = ch->brdchan;
+       tail = readw(&bc->tout);
+       /* Have FEP move tout pointer; effectively flushing transmit buffer */
+       fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0);
+       memoff(ch);
+       spin_unlock_irqrestore(&epca_lock, flags);
+       tty_wakeup(tty);
+}
+
+static void pc_flush_chars(struct tty_struct *tty)
+{
+       struct channel *ch;
+       /*
+        * verifyChannel returns the channel from the tty struct if it is
+        * valid. This serves as a sanity check.
+        */
+       ch = verifyChannel(tty);
+       if (ch != NULL) {
+               unsigned long flags;
+               spin_lock_irqsave(&epca_lock, flags);
+               /*
+                * If not already set and the transmitter is busy setup an
+                * event to indicate when the transmit empties.
+                */
+               if ((ch->statusflags & TXBUSY) &&
+                               !(ch->statusflags & EMPTYWAIT))
+                       setup_empty_event(tty, ch);
+               spin_unlock_irqrestore(&epca_lock, flags);
+       }
+}
+
+static int epca_carrier_raised(struct tty_port *port)
+{
+       struct channel *ch = container_of(port, struct channel, port);
+       if (ch->imodem & ch->dcd)
+               return 1;
+       return 0;
+}
+
+static void epca_dtr_rts(struct tty_port *port, int onoff)
+{
+}
+
+static int pc_open(struct tty_struct *tty, struct file *filp)
+{
+       struct channel *ch;
+       struct tty_port *port;
+       unsigned long flags;
+       int line, retval, boardnum;
+       struct board_chan __iomem *bc;
+       unsigned int head;
+
+       line = tty->index;
+       if (line < 0 || line >= nbdevs)
+               return -ENODEV;
+
+       ch = &digi_channels[line];
+       port = &ch->port;
+       boardnum = ch->boardnum;
+
+       /* Check status of board configured in system.  */
+
+       /*
+        * I check to see if the epca_setup routine detected a user error. It
+        * might be better to put this in pc_init, but for the moment it goes
+        * here.
+        */
+       if (invalid_lilo_config) {
+               if (setup_error_code & INVALID_BOARD_TYPE)
+                       printk(KERN_ERR "epca: pc_open: Invalid board type specified in kernel options.\n");
+               if (setup_error_code & INVALID_NUM_PORTS)
+                       printk(KERN_ERR "epca: pc_open: Invalid number of ports specified in kernel options.\n");
+               if (setup_error_code & INVALID_MEM_BASE)
+                       printk(KERN_ERR "epca: pc_open: Invalid board memory address specified in kernel options.\n");
+               if (setup_error_code & INVALID_PORT_BASE)
+                       printk(KERN_ERR "epca; pc_open: Invalid board port address specified in kernel options.\n");
+               if (setup_error_code & INVALID_BOARD_STATUS)
+                       printk(KERN_ERR "epca: pc_open: Invalid board status specified in kernel options.\n");
+               if (setup_error_code & INVALID_ALTPIN)
+                       printk(KERN_ERR "epca: pc_open: Invalid board altpin specified in kernel options;\n");
+               tty->driver_data = NULL;   /* Mark this device as 'down' */
+               return -ENODEV;
+       }
+       if (boardnum >= num_cards || boards[boardnum].status == DISABLED)  {
+               tty->driver_data = NULL;   /* Mark this device as 'down' */
+               return(-ENODEV);
+       }
+
+       bc = ch->brdchan;
+       if (bc == NULL) {
+               tty->driver_data = NULL;
+               return -ENODEV;
+       }
+
+       spin_lock_irqsave(&port->lock, flags);
+       /*
+        * Every time a channel is opened, increment a counter. This is
+        * necessary because we do not wish to flush and shutdown the channel
+        * until the last app holding the channel open, closes it.
+        */
+       port->count++;
+       /*
+        * Set a kernel structures pointer to our local channel structure. This
+        * way we can get to it when passed only a tty struct.
+        */
+       tty->driver_data = ch;
+       port->tty = tty;
+       /*
+        * If this is the first time the channel has been opened, initialize
+        * the tty->termios struct otherwise let pc_close handle it.
+        */
+       spin_lock(&epca_lock);
+       globalwinon(ch);
+       ch->statusflags = 0;
+
+       /* Save boards current modem status */
+       ch->imodem = readb(&bc->mstat);
+
+       /*
+        * Set receive head and tail ptrs to each other. This indicates no data
+        * available to read.
+        */
+       head = readw(&bc->rin);
+       writew(head, &bc->rout);
+
+       /* Set the channels associated tty structure */
+
+       /*
+        * The below routine generally sets up parity, baud, flow control
+        * issues, etc.... It effect both control flags and input flags.
+        */
+       epcaparam(tty, ch);
+       memoff(ch);
+       spin_unlock(&epca_lock);
+       port->flags |= ASYNC_INITIALIZED;
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       retval = tty_port_block_til_ready(port, tty, filp);
+       if (retval)
+               return retval;
+       /*
+        * Set this again in case a hangup set it to zero while this open() was
+        * waiting for the line...
+        */
+       spin_lock_irqsave(&port->lock, flags);
+       port->tty = tty;
+       spin_lock(&epca_lock);
+       globalwinon(ch);
+       /* Enable Digi Data events */
+       writeb(1, &bc->idata);
+       memoff(ch);
+       spin_unlock(&epca_lock);
+       spin_unlock_irqrestore(&port->lock, flags);
+       return 0;
+}
+
+static int __init epca_module_init(void)
+{
+       return pc_init();
+}
+module_init(epca_module_init);
+
+static struct pci_driver epca_driver;
+
+static void __exit epca_module_exit(void)
+{
+       int               count, crd;
+       struct board_info *bd;
+       struct channel    *ch;
+
+       del_timer_sync(&epca_timer);
+
+       if (tty_unregister_driver(pc_driver) ||
+                               tty_unregister_driver(pc_info)) {
+               printk(KERN_WARNING "epca: cleanup_module failed to un-register tty driver\n");
+               return;
+       }
+       put_tty_driver(pc_driver);
+       put_tty_driver(pc_info);
+
+       for (crd = 0; crd < num_cards; crd++) {
+               bd = &boards[crd];
+               if (!bd) { /* sanity check */
+                       printk(KERN_ERR "<Error> - Digi : cleanup_module failed\n");
+                       return;
+               }
+               ch = card_ptr[crd];
+               for (count = 0; count < bd->numports; count++, ch++) {
+                       struct tty_struct *tty = tty_port_tty_get(&ch->port);
+                       if (tty) {
+                               tty_hangup(tty);
+                               tty_kref_put(tty);
+                       }
+               }
+       }
+       pci_unregister_driver(&epca_driver);
+}
+module_exit(epca_module_exit);
+
+static const struct tty_operations pc_ops = {
+       .open = pc_open,
+       .close = pc_close,
+       .write = pc_write,
+       .write_room = pc_write_room,
+       .flush_buffer = pc_flush_buffer,
+       .chars_in_buffer = pc_chars_in_buffer,
+       .flush_chars = pc_flush_chars,
+       .ioctl = pc_ioctl,
+       .set_termios = pc_set_termios,
+       .stop = pc_stop,
+       .start = pc_start,
+       .throttle = pc_throttle,
+       .unthrottle = pc_unthrottle,
+       .hangup = pc_hangup,
+       .break_ctl = pc_send_break
+};
+
+static const struct tty_port_operations epca_port_ops = {
+       .carrier_raised = epca_carrier_raised,
+       .dtr_rts = epca_dtr_rts,
+};
+
+static int info_open(struct tty_struct *tty, struct file *filp)
+{
+       return 0;
+}
+
+static const struct tty_operations info_ops = {
+       .open = info_open,
+       .ioctl = info_ioctl,
+};
+
+static int __init pc_init(void)
+{
+       int crd;
+       struct board_info *bd;
+       unsigned char board_id = 0;
+       int err = -ENOMEM;
+
+       int pci_boards_found, pci_count;
+
+       pci_count = 0;
+
+       pc_driver = alloc_tty_driver(MAX_ALLOC);
+       if (!pc_driver)
+               goto out1;
+
+       pc_info = alloc_tty_driver(MAX_ALLOC);
+       if (!pc_info)
+               goto out2;
+
+       /*
+        * If epca_setup has not been ran by LILO set num_cards to defaults;
+        * copy board structure defined by digiConfig into drivers board
+        * structure. Note : If LILO has ran epca_setup then epca_setup will
+        * handle defining num_cards as well as copying the data into the board
+        * structure.
+        */
+       if (!liloconfig) {
+               /* driver has been configured via. epcaconfig */
+               nbdevs = NBDEVS;
+               num_cards = NUMCARDS;
+               memcpy(&boards, &static_boards,
+                      sizeof(struct board_info) * NUMCARDS);
+       }
+
+       /*
+        * Note : If lilo was used to configure the driver and the ignore
+        * epcaconfig option was choosen (digiepca=2) then nbdevs and num_cards
+        * will equal 0 at this point. This is okay; PCI cards will still be
+        * picked up if detected.
+        */
+
+       /*
+        * Set up interrupt, we will worry about memory allocation in
+        * post_fep_init.
+        */
+       printk(KERN_INFO "DIGI epca driver version %s loaded.\n", VERSION);
+
+       /*
+        * NOTE : This code assumes that the number of ports found in the
+        * boards array is correct. This could be wrong if the card in question
+        * is PCI (And therefore has no ports entry in the boards structure.)
+        * The rest of the information will be valid for PCI because the
+        * beginning of pc_init scans for PCI and determines i/o and base
+        * memory addresses. I am not sure if it is possible to read the number
+        * of ports supported by the card prior to it being booted (Since that
+        * is the state it is in when pc_init is run). Because it is not
+        * possible to query the number of supported ports until after the card
+        * has booted; we are required to calculate the card_ptrs as the card
+        * is initialized (Inside post_fep_init). The negative thing about this
+        * approach is that digiDload's call to GET_INFO will have a bad port
+        * value. (Since this is called prior to post_fep_init.)
+        */
+       pci_boards_found = 0;
+       if (num_cards < MAXBOARDS)
+               pci_boards_found += init_PCI();
+       num_cards += pci_boards_found;
+
+       pc_driver->owner = THIS_MODULE;
+       pc_driver->name = "ttyD";
+       pc_driver->major = DIGI_MAJOR;
+       pc_driver->minor_start = 0;
+       pc_driver->type = TTY_DRIVER_TYPE_SERIAL;
+       pc_driver->subtype = SERIAL_TYPE_NORMAL;
+       pc_driver->init_termios = tty_std_termios;
+       pc_driver->init_termios.c_iflag = 0;
+       pc_driver->init_termios.c_oflag = 0;
+       pc_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
+       pc_driver->init_termios.c_lflag = 0;
+       pc_driver->init_termios.c_ispeed = 9600;
+       pc_driver->init_termios.c_ospeed = 9600;
+       pc_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_HARDWARE_BREAK;
+       tty_set_operations(pc_driver, &pc_ops);
+
+       pc_info->owner = THIS_MODULE;
+       pc_info->name = "digi_ctl";
+       pc_info->major = DIGIINFOMAJOR;
+       pc_info->minor_start = 0;
+       pc_info->type = TTY_DRIVER_TYPE_SERIAL;
+       pc_info->subtype = SERIAL_TYPE_INFO;
+       pc_info->init_termios = tty_std_termios;
+       pc_info->init_termios.c_iflag = 0;
+       pc_info->init_termios.c_oflag = 0;
+       pc_info->init_termios.c_lflag = 0;
+       pc_info->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
+       pc_info->init_termios.c_ispeed = 9600;
+       pc_info->init_termios.c_ospeed = 9600;
+       pc_info->flags = TTY_DRIVER_REAL_RAW;
+       tty_set_operations(pc_info, &info_ops);
+
+
+       for (crd = 0; crd < num_cards; crd++) {
+               /*
+                * This is where the appropriate memory handlers for the
+                * hardware is set. Everything at runtime blindly jumps through
+                * these vectors.
+                */
+
+               /* defined in epcaconfig.h */
+               bd = &boards[crd];
+
+               switch (bd->type) {
+               case PCXEM:
+               case EISAXEM:
+                       bd->memwinon     = pcxem_memwinon;
+                       bd->memwinoff    = pcxem_memwinoff;
+                       bd->globalwinon  = pcxem_globalwinon;
+                       bd->txwinon      = pcxem_txwinon;
+                       bd->rxwinon      = pcxem_rxwinon;
+                       bd->memoff       = pcxem_memoff;
+                       bd->assertgwinon = dummy_assertgwinon;
+                       bd->assertmemoff = dummy_assertmemoff;
+                       break;
+
+               case PCIXEM:
+               case PCIXRJ:
+               case PCIXR:
+                       bd->memwinon     = dummy_memwinon;
+                       bd->memwinoff    = dummy_memwinoff;
+                       bd->globalwinon  = dummy_globalwinon;
+                       bd->txwinon      = dummy_txwinon;
+                       bd->rxwinon      = dummy_rxwinon;
+                       bd->memoff       = dummy_memoff;
+                       bd->assertgwinon = dummy_assertgwinon;
+                       bd->assertmemoff = dummy_assertmemoff;
+                       break;
+
+               case PCXE:
+               case PCXEVE:
+                       bd->memwinon     = pcxe_memwinon;
+                       bd->memwinoff    = pcxe_memwinoff;
+                       bd->globalwinon  = pcxe_globalwinon;
+                       bd->txwinon      = pcxe_txwinon;
+                       bd->rxwinon      = pcxe_rxwinon;
+                       bd->memoff       = pcxe_memoff;
+                       bd->assertgwinon = dummy_assertgwinon;
+                       bd->assertmemoff = dummy_assertmemoff;
+                       break;
+
+               case PCXI:
+               case PC64XE:
+                       bd->memwinon     = pcxi_memwinon;
+                       bd->memwinoff    = pcxi_memwinoff;
+                       bd->globalwinon  = pcxi_globalwinon;
+                       bd->txwinon      = pcxi_txwinon;
+                       bd->rxwinon      = pcxi_rxwinon;
+                       bd->memoff       = pcxi_memoff;
+                       bd->assertgwinon = pcxi_assertgwinon;
+                       bd->assertmemoff = pcxi_assertmemoff;
+                       break;
+
+               default:
+                       break;
+               }
+
+               /*
+                * Some cards need a memory segment to be defined for use in
+                * transmit and receive windowing operations. These boards are
+                * listed in the below switch. In the case of the XI the amount
+                * of memory on the board is variable so the memory_seg is also
+                * variable. This code determines what they segment should be.
+                */
+               switch (bd->type) {
+               case PCXE:
+               case PCXEVE:
+               case PC64XE:
+                       bd->memory_seg = 0xf000;
+                       break;
+
+               case PCXI:
+                       board_id = inb((int)bd->port);
+                       if ((board_id & 0x1) == 0x1) {
+                               /* it's an XI card */
+                               /* Is it a 64K board */
+                               if ((board_id & 0x30) == 0)
+                                       bd->memory_seg = 0xf000;
+
+                               /* Is it a 128K board */
+                               if ((board_id & 0x30) == 0x10)
+                                       bd->memory_seg = 0xe000;
+
+                               /* Is is a 256K board */
+                               if ((board_id & 0x30) == 0x20)
+                                       bd->memory_seg = 0xc000;
+
+                               /* Is it a 512K board */
+                               if ((board_id & 0x30) == 0x30)
+                                       bd->memory_seg = 0x8000;
+                       } else
+                               printk(KERN_ERR "epca: Board at 0x%x doesn't appear to be an XI\n", (int)bd->port);
+                       break;
+               }
+       }
+
+       err = tty_register_driver(pc_driver);
+       if (err) {
+               printk(KERN_ERR "Couldn't register Digi PC/ driver");
+               goto out3;
+       }
+
+       err = tty_register_driver(pc_info);
+       if (err) {
+               printk(KERN_ERR "Couldn't register Digi PC/ info ");
+               goto out4;
+       }
+
+       /* Start up the poller to check for events on all enabled boards */
+       init_timer(&epca_timer);
+       epca_timer.function = epcapoll;
+       mod_timer(&epca_timer, jiffies + HZ/25);
+       return 0;
+
+out4:
+       tty_unregister_driver(pc_driver);
+out3:
+       put_tty_driver(pc_info);
+out2:
+       put_tty_driver(pc_driver);
+out1:
+       return err;
+}
+
+static void post_fep_init(unsigned int crd)
+{
+       int i;
+       void __iomem *memaddr;
+       struct global_data __iomem *gd;
+       struct board_info *bd;
+       struct board_chan __iomem *bc;
+       struct channel *ch;
+       int shrinkmem = 0, lowwater;
+
+       /*
+        * This call is made by the user via. the ioctl call DIGI_INIT. It is
+        * responsible for setting up all the card specific stuff.
+        */
+       bd = &boards[crd];
+
+       /*
+        * If this is a PCI board, get the port info. Remember PCI cards do not
+        * have entries into the epcaconfig.h file, so we can't get the number
+        * of ports from it. Unfortunetly, this means that anyone doing a
+        * DIGI_GETINFO before the board has booted will get an invalid number
+        * of ports returned (It should return 0). Calls to DIGI_GETINFO after
+        * DIGI_INIT has been called will return the proper values.
+        */
+       if (bd->type >= PCIXEM) { /* Begin get PCI number of ports */
+               /*
+                * Below we use XEMPORTS as a memory offset regardless of which
+                * PCI card it is. This is because all of the supported PCI
+                * cards have the same memory offset for the channel data. This
+                * will have to be changed if we ever develop a PCI/XE card.
+                * NOTE : The FEP manual states that the port offset is 0xC22
+                * as opposed to 0xC02. This is only true for PC/XE, and PC/XI
+                * cards; not for the XEM, or CX series. On the PCI cards the
+                * number of ports is determined by reading a ID PROM located
+                * in the box attached to the card. The card can then determine
+                * the index the id to determine the number of ports available.
+                * (FYI - The id should be located at 0x1ac (And may use up to
+                * 4 bytes if the box in question is a XEM or CX)).
+                */
+               /* PCI cards are already remapped at this point ISA are not */
+               bd->numports = readw(bd->re_map_membase + XEMPORTS);
+               epcaassert(bd->numports <= 64, "PCI returned a invalid number of ports");
+               nbdevs += (bd->numports);
+       } else {
+               /* Fix up the mappings for ISA/EISA etc */
+               /* FIXME: 64K - can we be smarter ? */
+               bd->re_map_membase = ioremap_nocache(bd->membase, 0x10000);
+       }
+
+       if (crd != 0)
+               card_ptr[crd] = card_ptr[crd-1] + boards[crd-1].numports;
+       else
+               card_ptr[crd] = &digi_channels[crd]; /* <- For card 0 only */
+
+       ch = card_ptr[crd];
+       epcaassert(ch <= &digi_channels[nbdevs - 1], "ch out of range");
+
+       memaddr = bd->re_map_membase;
+
+       /*
+        * The below assignment will set bc to point at the BEGINING of the
+        * cards channel structures. For 1 card there will be between 8 and 64
+        * of these structures.
+        */
+       bc = memaddr + CHANSTRUCT;
+
+       /*
+        * The below assignment will set gd to point at the BEGINING of global
+        * memory address 0xc00. The first data in that global memory actually
+        * starts at address 0xc1a. The command in pointer begins at 0xd10.
+        */
+       gd = memaddr + GLOBAL;
+
+       /*
+        * XEPORTS (address 0xc22) points at the number of channels the card
+        * supports. (For 64XE, XI, XEM, and XR use 0xc02)
+        */
+       if ((bd->type == PCXEVE || bd->type == PCXE) &&
+                                       (readw(memaddr + XEPORTS) < 3))
+               shrinkmem = 1;
+       if (bd->type < PCIXEM)
+               if (!request_region((int)bd->port, 4, board_desc[bd->type]))
+                       return;
+       memwinon(bd, 0);
+
+       /*
+        * Remember ch is the main drivers channels structure, while bc is the
+        * cards channel structure.
+        */
+       for (i = 0; i < bd->numports; i++, ch++, bc++) {
+               unsigned long flags;
+               u16 tseg, rseg;
+
+               tty_port_init(&ch->port);
+               ch->port.ops = &epca_port_ops;
+               ch->brdchan = bc;
+               ch->mailbox = gd;
+               INIT_WORK(&ch->tqueue, do_softint);
+               ch->board = &boards[crd];
+
+               spin_lock_irqsave(&epca_lock, flags);
+               switch (bd->type) {
+               /*
+                * Since some of the boards use different bitmaps for
+                * their control signals we cannot hard code these
+                * values and retain portability. We virtualize this
+                * data here.
+                */
+               case EISAXEM:
+               case PCXEM:
+               case PCIXEM:
+               case PCIXRJ:
+               case PCIXR:
+                       ch->m_rts = 0x02;
+                       ch->m_dcd = 0x80;
+                       ch->m_dsr = 0x20;
+                       ch->m_cts = 0x10;
+                       ch->m_ri  = 0x40;
+                       ch->m_dtr = 0x01;
+                       break;
+
+               case PCXE:
+               case PCXEVE:
+               case PCXI:
+               case PC64XE:
+                       ch->m_rts = 0x02;
+                       ch->m_dcd = 0x08;
+                       ch->m_dsr = 0x10;
+                       ch->m_cts = 0x20;
+                       ch->m_ri  = 0x40;
+                       ch->m_dtr = 0x80;
+                       break;
+               }
+
+               if (boards[crd].altpin) {
+                       ch->dsr = ch->m_dcd;
+                       ch->dcd = ch->m_dsr;
+                       ch->digiext.digi_flags |= DIGI_ALTPIN;
+               } else {
+                       ch->dcd = ch->m_dcd;
+                       ch->dsr = ch->m_dsr;
+               }
+
+               ch->boardnum   = crd;
+               ch->channelnum = i;
+               ch->magic      = EPCA_MAGIC;
+               tty_port_tty_set(&ch->port, NULL);
+
+               if (shrinkmem) {
+                       fepcmd(ch, SETBUFFER, 32, 0, 0, 0);
+                       shrinkmem = 0;
+               }
+
+               tseg = readw(&bc->tseg);
+               rseg = readw(&bc->rseg);
+
+               switch (bd->type) {
+               case PCIXEM:
+               case PCIXRJ:
+               case PCIXR:
+                       /* Cover all the 2MEG cards */
+                       ch->txptr = memaddr + ((tseg << 4) & 0x1fffff);
+                       ch->rxptr = memaddr + ((rseg << 4) & 0x1fffff);
+                       ch->txwin = FEPWIN | (tseg >> 11);
+                       ch->rxwin = FEPWIN | (rseg >> 11);
+                       break;
+
+               case PCXEM:
+               case EISAXEM:
+                       /* Cover all the 32K windowed cards */
+                       /* Mask equal to window size - 1 */
+                       ch->txptr = memaddr + ((tseg << 4) & 0x7fff);
+                       ch->rxptr = memaddr + ((rseg << 4) & 0x7fff);
+                       ch->txwin = FEPWIN | (tseg >> 11);
+                       ch->rxwin = FEPWIN | (rseg >> 11);
+                       break;
+
+               case PCXEVE:
+               case PCXE:
+                       ch->txptr = memaddr + (((tseg - bd->memory_seg) << 4)
+                                                               & 0x1fff);
+                       ch->txwin = FEPWIN | ((tseg - bd->memory_seg) >> 9);
+                       ch->rxptr = memaddr + (((rseg - bd->memory_seg) << 4)
+                                                               & 0x1fff);
+                       ch->rxwin = FEPWIN | ((rseg - bd->memory_seg) >> 9);
+                       break;
+
+               case PCXI:
+               case PC64XE:
+                       ch->txptr = memaddr + ((tseg - bd->memory_seg) << 4);
+                       ch->rxptr = memaddr + ((rseg - bd->memory_seg) << 4);
+                       ch->txwin = ch->rxwin = 0;
+                       break;
+               }
+
+               ch->txbufhead = 0;
+               ch->txbufsize = readw(&bc->tmax) + 1;
+
+               ch->rxbufhead = 0;
+               ch->rxbufsize = readw(&bc->rmax) + 1;
+
+               lowwater = ch->txbufsize >= 2000 ? 1024 : (ch->txbufsize / 2);
+
+               /* Set transmitter low water mark */
+               fepcmd(ch, STXLWATER, lowwater, 0, 10, 0);
+
+               /* Set receiver low water mark */
+               fepcmd(ch, SRXLWATER, (ch->rxbufsize / 4), 0, 10, 0);
+
+               /* Set receiver high water mark */
+               fepcmd(ch, SRXHWATER, (3 * ch->rxbufsize / 4), 0, 10, 0);
+
+               writew(100, &bc->edelay);
+               writeb(1, &bc->idata);
+
+               ch->startc  = readb(&bc->startc);
+               ch->stopc   = readb(&bc->stopc);
+               ch->startca = readb(&bc->startca);
+               ch->stopca  = readb(&bc->stopca);
+
+               ch->fepcflag = 0;
+               ch->fepiflag = 0;
+               ch->fepoflag = 0;
+               ch->fepstartc = 0;
+               ch->fepstopc = 0;
+               ch->fepstartca = 0;
+               ch->fepstopca = 0;
+
+               ch->port.close_delay = 50;
+
+               spin_unlock_irqrestore(&epca_lock, flags);
+       }
+
+       printk(KERN_INFO
+       "Digi PC/Xx Driver V%s:  %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n",
+                               VERSION, board_desc[bd->type], (long)bd->port,
+                                       (long)bd->membase, bd->numports);
+       memwinoff(bd, 0);
+}
+
+static void epcapoll(unsigned long ignored)
+{
+       unsigned long flags;
+       int crd;
+       unsigned int head, tail;
+       struct channel *ch;
+       struct board_info *bd;
+
+       /*
+        * This routine is called upon every timer interrupt. Even though the
+        * Digi series cards are capable of generating interrupts this method
+        * of non-looping polling is more efficient. This routine checks for
+        * card generated events (Such as receive data, are transmit buffer
+        * empty) and acts on those events.
+        */
+       for (crd = 0; crd < num_cards; crd++) {
+               bd = &boards[crd];
+               ch = card_ptr[crd];
+
+               if ((bd->status == DISABLED) || digi_poller_inhibited)
+                       continue;
+
+               /*
+                * assertmemoff is not needed here; indeed it is an empty
+                * subroutine. It is being kept because future boards may need
+                * this as well as some legacy boards.
+                */
+               spin_lock_irqsave(&epca_lock, flags);
+
+               assertmemoff(ch);
+
+               globalwinon(ch);
+
+               /*
+                * In this case head and tail actually refer to the event queue
+                * not the transmit or receive queue.
+                */
+               head = readw(&ch->mailbox->ein);
+               tail = readw(&ch->mailbox->eout);
+
+               /* If head isn't equal to tail we have an event */
+               if (head != tail)
+                       doevent(crd);
+               memoff(ch);
+
+               spin_unlock_irqrestore(&epca_lock, flags);
+       } /* End for each card */
+       mod_timer(&epca_timer, jiffies + (HZ / 25));
+}
+
+static void doevent(int crd)
+{
+       void __iomem *eventbuf;
+       struct channel *ch, *chan0;
+       static struct tty_struct *tty;
+       struct board_info *bd;
+       struct board_chan __iomem *bc;
+       unsigned int tail, head;
+       int event, channel;
+       int mstat, lstat;
+
+       /*
+        * This subroutine is called by epcapoll when an event is detected
+        * in the event queue. This routine responds to those events.
+        */
+       bd = &boards[crd];
+
+       chan0 = card_ptr[crd];
+       epcaassert(chan0 <= &digi_channels[nbdevs - 1], "ch out of range");
+       assertgwinon(chan0);
+       while ((tail = readw(&chan0->mailbox->eout)) !=
+                       (head = readw(&chan0->mailbox->ein))) {
+               /* Begin while something in event queue */
+               assertgwinon(chan0);
+               eventbuf = bd->re_map_membase + tail + ISTART;
+               /* Get the channel the event occurred on */
+               channel = readb(eventbuf);
+               /* Get the actual event code that occurred */
+               event = readb(eventbuf + 1);
+               /*
+                * The two assignments below get the current modem status
+                * (mstat) and the previous modem status (lstat). These are
+                * useful becuase an event could signal a change in modem
+                * signals itself.
+                */
+               mstat = readb(eventbuf + 2);
+               lstat = readb(eventbuf + 3);
+
+               ch = chan0 + channel;
+               if ((unsigned)channel >= bd->numports || !ch)  {
+                       if (channel >= bd->numports)
+                               ch = chan0;
+                       bc = ch->brdchan;
+                       goto next;
+               }
+
+               bc = ch->brdchan;
+               if (bc == NULL)
+                       goto next;
+
+               tty = tty_port_tty_get(&ch->port);
+               if (event & DATA_IND)  { /* Begin DATA_IND */
+                       receive_data(ch, tty);
+                       assertgwinon(ch);
+               } /* End DATA_IND */
+               /* else *//* Fix for DCD transition missed bug */
+               if (event & MODEMCHG_IND) {
+                       /* A modem signal change has been indicated */
+                       ch->imodem = mstat;
+                       if (test_bit(ASYNCB_CHECK_CD, &ch->port.flags)) {
+                               /* We are now receiving dcd */
+                               if (mstat & ch->dcd)
+                                       wake_up_interruptible(&ch->port.open_wait);
+                               else    /* No dcd; hangup */
+                                       pc_sched_event(ch, EPCA_EVENT_HANGUP);
+                       }
+               }
+               if (tty) {
+                       if (event & BREAK_IND) {
+                               /* A break has been indicated */
+                               tty_insert_flip_char(tty, 0, TTY_BREAK);
+                               tty_schedule_flip(tty);
+                       } else if (event & LOWTX_IND)  {
+                               if (ch->statusflags & LOWWAIT) {
+                                       ch->statusflags &= ~LOWWAIT;
+                                       tty_wakeup(tty);
+                               }
+                       } else if (event & EMPTYTX_IND) {
+                               /* This event is generated by
+                                  setup_empty_event */
+                               ch->statusflags &= ~TXBUSY;
+                               if (ch->statusflags & EMPTYWAIT) {
+                                       ch->statusflags &= ~EMPTYWAIT;
+                                       tty_wakeup(tty);
+                               }
+                       }
+                       tty_kref_put(tty);
+               }
+next:
+               globalwinon(ch);
+               BUG_ON(!bc);
+               writew(1, &bc->idata);
+               writew((tail + 4) & (IMAX - ISTART - 4), &chan0->mailbox->eout);
+               globalwinon(chan0);
+       } /* End while something in event queue */
+}
+
+static void fepcmd(struct channel *ch, int cmd, int word_or_byte,
+                                       int byte2, int ncmds, int bytecmd)
+{
+       unchar __iomem *memaddr;
+       unsigned int head, cmdTail, cmdStart, cmdMax;
+       long count;
+       int n;
+
+       /* This is the routine in which commands may be passed to the card. */
+
+       if (ch->board->status == DISABLED)
+               return;
+       assertgwinon(ch);
+       /* Remember head (As well as max) is just an offset not a base addr */
+       head = readw(&ch->mailbox->cin);
+       /* cmdStart is a base address */
+       cmdStart = readw(&ch->mailbox->cstart);
+       /*
+        * We do the addition below because we do not want a max pointer
+        * relative to cmdStart. We want a max pointer that points at the
+        * physical end of the command queue.
+        */
+       cmdMax = (cmdStart + 4 + readw(&ch->mailbox->cmax));
+       memaddr = ch->board->re_map_membase;
+
+       if (head >= (cmdMax - cmdStart) || (head & 03))  {
+               printk(KERN_ERR "line %d: Out of range, cmd = %x, head = %x\n",
+                                               __LINE__,  cmd, head);
+               printk(KERN_ERR "line %d: Out of range, cmdMax = %x, cmdStart = %x\n",
+                                               __LINE__,  cmdMax, cmdStart);
+               return;
+       }
+       if (bytecmd)  {
+               writeb(cmd, memaddr + head + cmdStart + 0);
+               writeb(ch->channelnum,  memaddr + head + cmdStart + 1);
+               /* Below word_or_byte is bits to set */
+               writeb(word_or_byte,  memaddr + head + cmdStart + 2);
+               /* Below byte2 is bits to reset */
+               writeb(byte2, memaddr + head + cmdStart + 3);
+       }  else {
+               writeb(cmd, memaddr + head + cmdStart + 0);
+               writeb(ch->channelnum,  memaddr + head + cmdStart + 1);
+               writeb(word_or_byte,  memaddr + head + cmdStart + 2);
+       }
+       head = (head + 4) & (cmdMax - cmdStart - 4);
+       writew(head, &ch->mailbox->cin);
+       count = FEPTIMEOUT;
+
+       for (;;) {
+               count--;
+               if (count == 0)  {
+                       printk(KERN_ERR "<Error> - Fep not responding in fepcmd()\n");
+                       return;
+               }
+               head = readw(&ch->mailbox->cin);
+               cmdTail = readw(&ch->mailbox->cout);
+               n = (head - cmdTail) & (cmdMax - cmdStart - 4);
+               /*
+                * Basically this will break when the FEP acknowledges the
+                * command by incrementing cmdTail (Making it equal to head).
+                */
+               if (n <= ncmds * (sizeof(short) * 4))
+                       break;
+       }
+}
+
+/*
+ * Digi products use fields in their channels structures that are very similar
+ * to the c_cflag and c_iflag fields typically found in UNIX termios
+ * structures. The below three routines allow mappings between these hardware
+ * "flags" and their respective Linux flags.
+ */
+static unsigned termios2digi_h(struct channel *ch, unsigned cflag)
+{
+       unsigned res = 0;
+
+       if (cflag & CRTSCTS) {
+               ch->digiext.digi_flags |= (RTSPACE | CTSPACE);
+               res |= ((ch->m_cts) | (ch->m_rts));
+       }
+
+       if (ch->digiext.digi_flags & RTSPACE)
+               res |= ch->m_rts;
+
+       if (ch->digiext.digi_flags & DTRPACE)
+               res |= ch->m_dtr;
+
+       if (ch->digiext.digi_flags & CTSPACE)
+               res |= ch->m_cts;
+
+       if (ch->digiext.digi_flags & DSRPACE)
+               res |= ch->dsr;
+
+       if (ch->digiext.digi_flags & DCDPACE)
+               res |= ch->dcd;
+
+       if (res & (ch->m_rts))
+               ch->digiext.digi_flags |= RTSPACE;
+
+       if (res & (ch->m_cts))
+               ch->digiext.digi_flags |= CTSPACE;
+
+       return res;
+}
+
+static unsigned termios2digi_i(struct channel *ch, unsigned iflag)
+{
+       unsigned res = iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK |
+                                       INPCK | ISTRIP | IXON | IXANY | IXOFF);
+       if (ch->digiext.digi_flags & DIGI_AIXON)
+               res |= IAIXON;
+       return res;
+}
+
+static unsigned termios2digi_c(struct channel *ch, unsigned cflag)
+{
+       unsigned res = 0;
+       if (cflag & CBAUDEX) {
+               ch->digiext.digi_flags |= DIGI_FAST;
+               /*
+                * HUPCL bit is used by FEP to indicate fast baud table is to
+                * be used.
+                */
+               res |= FEP_HUPCL;
+       } else
+               ch->digiext.digi_flags &= ~DIGI_FAST;
+       /*
+        * CBAUD has bit position 0x1000 set these days to indicate Linux
+        * baud rate remap. Digi hardware can't handle the bit assignment.
+        * (We use a different bit assignment for high speed.). Clear this
+        * bit out.
+        */
+       res |= cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | CSTOPB | CSIZE);
+       /*
+        * This gets a little confusing. The Digi cards have their own
+        * representation of c_cflags controlling baud rate. For the most part
+        * this is identical to the Linux implementation. However; Digi
+        * supports one rate (76800) that Linux doesn't. This means that the
+        * c_cflag entry that would normally mean 76800 for Digi actually means
+        * 115200 under Linux. Without the below mapping, a stty 115200 would
+        * only drive the board at 76800. Since the rate 230400 is also found
+        * after 76800, the same problem afflicts us when we choose a rate of
+        * 230400. Without the below modificiation stty 230400 would actually
+        * give us 115200.
+        *
+        * There are two additional differences. The Linux value for CLOCAL
+        * (0x800; 0004000) has no meaning to the Digi hardware. Also in later
+        * releases of Linux; the CBAUD define has CBAUDEX (0x1000; 0010000)
+        * ored into it (CBAUD = 0x100f as opposed to 0xf). CBAUDEX should be
+        * checked for a screened out prior to termios2digi_c returning. Since
+        * CLOCAL isn't used by the board this can be ignored as long as the
+        * returned value is used only by Digi hardware.
+        */
+       if (cflag & CBAUDEX) {
+               /*
+                * The below code is trying to guarantee that only baud rates
+                * 115200 and 230400 are remapped. We use exclusive or because
+                * the various baud rates share common bit positions and
+                * therefore can't be tested for easily.
+                */
+               if ((!((cflag & 0x7) ^ (B115200 & ~CBAUDEX))) ||
+                   (!((cflag & 0x7) ^ (B230400 & ~CBAUDEX))))
+                       res += 1;
+       }
+       return res;
+}
+
+/* Caller must hold the locks */
+static void epcaparam(struct tty_struct *tty, struct channel *ch)
+{
+       unsigned int cmdHead;
+       struct ktermios *ts;
+       struct board_chan __iomem *bc;
+       unsigned mval, hflow, cflag, iflag;
+
+       bc = ch->brdchan;
+       epcaassert(bc != NULL, "bc out of range");
+
+       assertgwinon(ch);
+       ts = tty->termios;
+       if ((ts->c_cflag & CBAUD) == 0)  { /* Begin CBAUD detected */
+               cmdHead = readw(&bc->rin);
+               writew(cmdHead, &bc->rout);
+               cmdHead = readw(&bc->tin);
+               /* Changing baud in mid-stream transmission can be wonderful */
+               /*
+                * Flush current transmit buffer by setting cmdTail pointer
+                * (tout) to cmdHead pointer (tin). Hopefully the transmit
+                * buffer is empty.
+                */
+               fepcmd(ch, STOUT, (unsigned) cmdHead, 0, 0, 0);
+               mval = 0;
+       } else { /* Begin CBAUD not detected */
+               /*
+                * c_cflags have changed but that change had nothing to do with
+                * BAUD. Propagate the change to the card.
+                */
+               cflag = termios2digi_c(ch, ts->c_cflag);
+               if (cflag != ch->fepcflag)  {
+                       ch->fepcflag = cflag;
+                       /* Set baud rate, char size, stop bits, parity */
+                       fepcmd(ch, SETCTRLFLAGS, (unsigned) cflag, 0, 0, 0);
+               }
+               /*
+                * If the user has not forced CLOCAL and if the device is not a
+                * CALLOUT device (Which is always CLOCAL) we set flags such
+                * that the driver will wait on carrier detect.
+                */
+               if (ts->c_cflag & CLOCAL)
+                       clear_bit(ASYNCB_CHECK_CD, &ch->port.flags);
+               else
+                       set_bit(ASYNCB_CHECK_CD, &ch->port.flags);
+               mval = ch->m_dtr | ch->m_rts;
+       } /* End CBAUD not detected */
+       iflag = termios2digi_i(ch, ts->c_iflag);
+       /* Check input mode flags */
+       if (iflag != ch->fepiflag)  {
+               ch->fepiflag = iflag;
+               /*
+                * Command sets channels iflag structure on the board. Such
+                * things as input soft flow control, handling of parity
+                * errors, and break handling are all set here.
+                *
+                * break handling, parity handling, input stripping,
+                * flow control chars
+                */
+               fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0);
+       }
+       /*
+        * Set the board mint value for this channel. This will cause hardware
+        * events to be generated each time the DCD signal (Described in mint)
+        * changes.
+        */
+       writeb(ch->dcd, &bc->mint);
+       if ((ts->c_cflag & CLOCAL) || (ch->digiext.digi_flags & DIGI_FORCEDCD))
+               if (ch->digiext.digi_flags & DIGI_FORCEDCD)
+                       writeb(0, &bc->mint);
+       ch->imodem = readb(&bc->mstat);
+       hflow = termios2digi_h(ch, ts->c_cflag);
+       if (hflow != ch->hflow)  {
+               ch->hflow = hflow;
+               /*
+                * Hard flow control has been selected but the board is not
+                * using it. Activate hard flow control now.
+                */
+               fepcmd(ch, SETHFLOW, hflow, 0xff, 0, 1);
+       }
+       mval ^= ch->modemfake & (mval ^ ch->modem);
+
+       if (ch->omodem ^ mval)  {
+               ch->omodem = mval;
+               /*
+                * The below command sets the DTR and RTS mstat structure. If
+                * hard flow control is NOT active these changes will drive the
+                * output of the actual DTR and RTS lines. If hard flow control
+                * is active, the changes will be saved in the mstat structure
+                * and only asserted when hard flow control is turned off.
+                */
+
+               /* First reset DTR & RTS; then set them */
+               fepcmd(ch, SETMODEM, 0, ((ch->m_dtr)|(ch->m_rts)), 0, 1);
+               fepcmd(ch, SETMODEM, mval, 0, 0, 1);
+       }
+       if (ch->startc != ch->fepstartc || ch->stopc != ch->fepstopc)  {
+               ch->fepstartc = ch->startc;
+               ch->fepstopc = ch->stopc;
+               /*
+                * The XON / XOFF characters have changed; propagate these
+                * changes to the card.
+                */
+               fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1);
+       }
+       if (ch->startca != ch->fepstartca || ch->stopca != ch->fepstopca)  {
+               ch->fepstartca = ch->startca;
+               ch->fepstopca = ch->stopca;
+               /*
+                * Similar to the above, this time the auxilarly XON / XOFF
+                * characters have changed; propagate these changes to the card.
+                */
+               fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1);
+       }
+}
+
+/* Caller holds lock */
+static void receive_data(struct channel *ch, struct tty_struct *tty)
+{
+       unchar *rptr;
+       struct ktermios *ts = NULL;
+       struct board_chan __iomem *bc;
+       int dataToRead, wrapgap, bytesAvailable;
+       unsigned int tail, head;
+       unsigned int wrapmask;
+
+       /*
+        * This routine is called by doint when a receive data event has taken
+        * place.
+        */
+       globalwinon(ch);
+       if (ch->statusflags & RXSTOPPED)
+               return;
+       if (tty)
+               ts = tty->termios;
+       bc = ch->brdchan;
+       BUG_ON(!bc);
+       wrapmask = ch->rxbufsize - 1;
+
+       /*
+        * Get the head and tail pointers to the receiver queue. Wrap the head
+        * pointer if it has reached the end of the buffer.
+        */
+       head = readw(&bc->rin);
+       head &= wrapmask;
+       tail = readw(&bc->rout) & wrapmask;
+
+       bytesAvailable = (head - tail) & wrapmask;
+       if (bytesAvailable == 0)
+               return;
+
+       /* If CREAD bit is off or device not open, set TX tail to head */
+       if (!tty || !ts || !(ts->c_cflag & CREAD)) {
+               writew(head, &bc->rout);
+               return;
+       }
+
+       if (tty_buffer_request_room(tty, bytesAvailable + 1) == 0)
+               return;
+
+       if (readb(&bc->orun)) {
+               writeb(0, &bc->orun);
+               printk(KERN_WARNING "epca; overrun! DigiBoard device %s\n",
+                                                               tty->name);
+               tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+       }
+       rxwinon(ch);
+       while (bytesAvailable > 0) {
+               /* Begin while there is data on the card */
+               wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail;
+               /*
+                * Even if head has wrapped around only report the amount of
+                * data to be equal to the size - tail. Remember memcpy can't
+                * automaticly wrap around the receive buffer.
+                */
+               dataToRead = (wrapgap < bytesAvailable) ? wrapgap
+                                                       : bytesAvailable;
+               /* Make sure we don't overflow the buffer */
+               dataToRead = tty_prepare_flip_string(tty, &rptr, dataToRead);
+               if (dataToRead == 0)
+                       break;
+               /*
+                * Move data read from our card into the line disciplines
+                * buffer for translation if necessary.
+                */
+               memcpy_fromio(rptr, ch->rxptr + tail, dataToRead);
+               tail = (tail + dataToRead) & wrapmask;
+               bytesAvailable -= dataToRead;
+       } /* End while there is data on the card */
+       globalwinon(ch);
+       writew(tail, &bc->rout);
+       /* Must be called with global data */
+       tty_schedule_flip(tty);
+}
+
+static int info_ioctl(struct tty_struct *tty,
+                   unsigned int cmd, unsigned long arg)
+{
+       switch (cmd) {
+       case DIGI_GETINFO:
+               {
+                       struct digi_info di;
+                       int brd;
+
+                       if (get_user(brd, (unsigned int __user *)arg))
+                               return -EFAULT;
+                       if (brd < 0 || brd >= num_cards || num_cards == 0)
+                               return -ENODEV;
+
+                       memset(&di, 0, sizeof(di));
+
+                       di.board = brd;
+                       di.status = boards[brd].status;
+                       di.type = boards[brd].type ;
+                       di.numports = boards[brd].numports ;
+                       /* Legacy fixups - just move along nothing to see */
+                       di.port = (unsigned char *)boards[brd].port ;
+                       di.membase = (unsigned char *)boards[brd].membase ;
+
+                       if (copy_to_user((void __user *)arg, &di, sizeof(di)))
+                               return -EFAULT;
+                       break;
+
+               }
+
+       case DIGI_POLLER:
+               {
+                       int brd = arg & 0xff000000 >> 16;
+                       unsigned char state = arg & 0xff;
+
+                       if (brd < 0 || brd >= num_cards) {
+                               printk(KERN_ERR "epca: DIGI POLLER : brd not valid!\n");
+                               return -ENODEV;
+                       }
+                       digi_poller_inhibited = state;
+                       break;
+               }
+
+       case DIGI_INIT:
+               {
+                       /*
+                        * This call is made by the apps to complete the
+                        * initialization of the board(s). This routine is
+                        * responsible for setting the card to its initial
+                        * state and setting the drivers control fields to the
+                        * sutianle settings for the card in question.
+                        */
+                       int crd;
+                       for (crd = 0; crd < num_cards; crd++)
+                               post_fep_init(crd);
+                       break;
+               }
+       default:
+               return -ENOTTY;
+       }
+       return 0;
+}
+
+static int pc_tiocmget(struct tty_struct *tty)
+{
+       struct channel *ch = tty->driver_data;
+       struct board_chan __iomem *bc;
+       unsigned int mstat, mflag = 0;
+       unsigned long flags;
+
+       if (ch)
+               bc = ch->brdchan;
+       else
+               return -EINVAL;
+
+       spin_lock_irqsave(&epca_lock, flags);
+       globalwinon(ch);
+       mstat = readb(&bc->mstat);
+       memoff(ch);
+       spin_unlock_irqrestore(&epca_lock, flags);
+
+       if (mstat & ch->m_dtr)
+               mflag |= TIOCM_DTR;
+       if (mstat & ch->m_rts)
+               mflag |= TIOCM_RTS;
+       if (mstat & ch->m_cts)
+               mflag |= TIOCM_CTS;
+       if (mstat & ch->dsr)
+               mflag |= TIOCM_DSR;
+       if (mstat & ch->m_ri)
+               mflag |= TIOCM_RI;
+       if (mstat & ch->dcd)
+               mflag |= TIOCM_CD;
+       return mflag;
+}
+
+static int pc_tiocmset(struct tty_struct *tty,
+                      unsigned int set, unsigned int clear)
+{
+       struct channel *ch = tty->driver_data;
+       unsigned long flags;
+
+       if (!ch)
+               return -EINVAL;
+
+       spin_lock_irqsave(&epca_lock, flags);
+       /*
+        * I think this modemfake stuff is broken. It doesn't correctly reflect
+        * the behaviour desired by the TIOCM* ioctls. Therefore this is
+        * probably broken.
+        */
+       if (set & TIOCM_RTS) {
+               ch->modemfake |= ch->m_rts;
+               ch->modem |= ch->m_rts;
+       }
+       if (set & TIOCM_DTR) {
+               ch->modemfake |= ch->m_dtr;
+               ch->modem |= ch->m_dtr;
+       }
+       if (clear & TIOCM_RTS) {
+               ch->modemfake |= ch->m_rts;
+               ch->modem &= ~ch->m_rts;
+       }
+       if (clear & TIOCM_DTR) {
+               ch->modemfake |= ch->m_dtr;
+               ch->modem &= ~ch->m_dtr;
+       }
+       globalwinon(ch);
+       /*
+        * The below routine generally sets up parity, baud, flow control
+        * issues, etc.... It effect both control flags and input flags.
+        */
+       epcaparam(tty, ch);
+       memoff(ch);
+       spin_unlock_irqrestore(&epca_lock, flags);
+       return 0;
+}
+
+static int pc_ioctl(struct tty_struct *tty,
+                                       unsigned int cmd, unsigned long arg)
+{
+       digiflow_t dflow;
+       unsigned long flags;
+       unsigned int mflag, mstat;
+       unsigned char startc, stopc;
+       struct board_chan __iomem *bc;
+       struct channel *ch = tty->driver_data;
+       void __user *argp = (void __user *)arg;
+
+       if (ch)
+               bc = ch->brdchan;
+       else
+               return -EINVAL;
+       switch (cmd) {
+       case TIOCMODG:
+               mflag = pc_tiocmget(tty);
+               if (put_user(mflag, (unsigned long __user *)argp))
+                       return -EFAULT;
+               break;
+       case TIOCMODS:
+               if (get_user(mstat, (unsigned __user *)argp))
+                       return -EFAULT;
+               return pc_tiocmset(tty, mstat, ~mstat);
+       case TIOCSDTR:
+               spin_lock_irqsave(&epca_lock, flags);
+               ch->omodem |= ch->m_dtr;
+               globalwinon(ch);
+               fepcmd(ch, SETMODEM, ch->m_dtr, 0, 10, 1);
+               memoff(ch);
+               spin_unlock_irqrestore(&epca_lock, flags);
+               break;
+
+       case TIOCCDTR:
+               spin_lock_irqsave(&epca_lock, flags);
+               ch->omodem &= ~ch->m_dtr;
+               globalwinon(ch);
+               fepcmd(ch, SETMODEM, 0, ch->m_dtr, 10, 1);
+               memoff(ch);
+               spin_unlock_irqrestore(&epca_lock, flags);
+               break;
+       case DIGI_GETA:
+               if (copy_to_user(argp, &ch->digiext, sizeof(digi_t)))
+                       return -EFAULT;
+               break;
+       case DIGI_SETAW:
+       case DIGI_SETAF:
+               if (cmd == DIGI_SETAW) {
+                       /* Setup an event to indicate when the transmit
+                          buffer empties */
+                       spin_lock_irqsave(&epca_lock, flags);
+                       setup_empty_event(tty, ch);
+                       spin_unlock_irqrestore(&epca_lock, flags);
+                       tty_wait_until_sent(tty, 0);
+               } else {
+                       /* ldisc lock already held in ioctl */
+                       if (tty->ldisc->ops->flush_buffer)
+                               tty->ldisc->ops->flush_buffer(tty);
+               }
+               /* Fall Thru */
+       case DIGI_SETA:
+               if (copy_from_user(&ch->digiext, argp, sizeof(digi_t)))
+                       return -EFAULT;
+
+               if (ch->digiext.digi_flags & DIGI_ALTPIN)  {
+                       ch->dcd = ch->m_dsr;
+                       ch->dsr = ch->m_dcd;
+               } else {
+                       ch->dcd = ch->m_dcd;
+                       ch->dsr = ch->m_dsr;
+                       }
+
+               spin_lock_irqsave(&epca_lock, flags);
+               globalwinon(ch);
+
+               /*
+                * The below routine generally sets up parity, baud, flow
+                * control issues, etc.... It effect both control flags and
+                * input flags.
+                */
+               epcaparam(tty, ch);
+               memoff(ch);
+               spin_unlock_irqrestore(&epca_lock, flags);
+               break;
+
+       case DIGI_GETFLOW:
+       case DIGI_GETAFLOW:
+               spin_lock_irqsave(&epca_lock, flags);
+               globalwinon(ch);
+               if (cmd == DIGI_GETFLOW) {
+                       dflow.startc = readb(&bc->startc);
+                       dflow.stopc = readb(&bc->stopc);
+               } else {
+                       dflow.startc = readb(&bc->startca);
+                       dflow.stopc = readb(&bc->stopca);
+               }
+               memoff(ch);
+               spin_unlock_irqrestore(&epca_lock, flags);
+
+               if (copy_to_user(argp, &dflow, sizeof(dflow)))
+                       return -EFAULT;
+               break;
+
+       case DIGI_SETAFLOW:
+       case DIGI_SETFLOW:
+               if (cmd == DIGI_SETFLOW) {
+                       startc = ch->startc;
+                       stopc = ch->stopc;
+               } else {
+                       startc = ch->startca;
+                       stopc = ch->stopca;
+               }
+
+               if (copy_from_user(&dflow, argp, sizeof(dflow)))
+                       return -EFAULT;
+
+               if (dflow.startc != startc || dflow.stopc != stopc) {
+                       /* Begin  if setflow toggled */
+                       spin_lock_irqsave(&epca_lock, flags);
+                       globalwinon(ch);
+
+                       if (cmd == DIGI_SETFLOW) {
+                               ch->fepstartc = ch->startc = dflow.startc;
+                               ch->fepstopc = ch->stopc = dflow.stopc;
+                               fepcmd(ch, SONOFFC, ch->fepstartc,
+                                               ch->fepstopc, 0, 1);
+                       } else {
+                               ch->fepstartca = ch->startca = dflow.startc;
+                               ch->fepstopca  = ch->stopca = dflow.stopc;
+                               fepcmd(ch, SAUXONOFFC, ch->fepstartca,
+                                               ch->fepstopca, 0, 1);
+                       }
+
+                       if (ch->statusflags & TXSTOPPED)
+                               pc_start(tty);
+
+                       memoff(ch);
+                       spin_unlock_irqrestore(&epca_lock, flags);
+               } /* End if setflow toggled */
+               break;
+       default:
+               return -ENOIOCTLCMD;
+       }
+       return 0;
+}
+
+static void pc_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+       struct channel *ch;
+       unsigned long flags;
+       /*
+        * verifyChannel returns the channel from the tty struct if it is
+        * valid. This serves as a sanity check.
+        */
+       ch = verifyChannel(tty);
+
+       if (ch != NULL)  { /* Begin if channel valid */
+               spin_lock_irqsave(&epca_lock, flags);
+               globalwinon(ch);
+               epcaparam(tty, ch);
+               memoff(ch);
+               spin_unlock_irqrestore(&epca_lock, flags);
+
+               if ((old_termios->c_cflag & CRTSCTS) &&
+                        ((tty->termios->c_cflag & CRTSCTS) == 0))
+                       tty->hw_stopped = 0;
+
+               if (!(old_termios->c_cflag & CLOCAL) &&
+                        (tty->termios->c_cflag & CLOCAL))
+                       wake_up_interruptible(&ch->port.open_wait);
+
+       } /* End if channel valid */
+}
+
+static void do_softint(struct work_struct *work)
+{
+       struct channel *ch = container_of(work, struct channel, tqueue);
+       /* Called in response to a modem change event */
+       if (ch && ch->magic == EPCA_MAGIC) {
+               struct tty_struct *tty = tty_port_tty_get(&ch->port);
+
+               if (tty && tty->driver_data) {
+                       if (test_and_clear_bit(EPCA_EVENT_HANGUP, &ch->event)) {
+                               tty_hangup(tty);
+                               wake_up_interruptible(&ch->port.open_wait);
+                               clear_bit(ASYNCB_NORMAL_ACTIVE,
+                                               &ch->port.flags);
+                       }
+               }
+               tty_kref_put(tty);
+       }
+}
+
+/*
+ * pc_stop and pc_start provide software flow control to the routine and the
+ * pc_ioctl routine.
+ */
+static void pc_stop(struct tty_struct *tty)
+{
+       struct channel *ch;
+       unsigned long flags;
+       /*
+        * verifyChannel returns the channel from the tty struct if it is
+        * valid. This serves as a sanity check.
+        */
+       ch = verifyChannel(tty);
+       if (ch != NULL) {
+               spin_lock_irqsave(&epca_lock, flags);
+               if ((ch->statusflags & TXSTOPPED) == 0) {
+                       /* Begin if transmit stop requested */
+                       globalwinon(ch);
+                       /* STOP transmitting now !! */
+                       fepcmd(ch, PAUSETX, 0, 0, 0, 0);
+                       ch->statusflags |= TXSTOPPED;
+                       memoff(ch);
+               } /* End if transmit stop requested */
+               spin_unlock_irqrestore(&epca_lock, flags);
+       }
+}
+
+static void pc_start(struct tty_struct *tty)
+{
+       struct channel *ch;
+       /*
+        * verifyChannel returns the channel from the tty struct if it is
+        * valid. This serves as a sanity check.
+        */
+       ch = verifyChannel(tty);
+       if (ch != NULL) {
+               unsigned long flags;
+               spin_lock_irqsave(&epca_lock, flags);
+               /* Just in case output was resumed because of a change
+                  in Digi-flow */
+               if (ch->statusflags & TXSTOPPED)  {
+                       /* Begin transmit resume requested */
+                       struct board_chan __iomem *bc;
+                       globalwinon(ch);
+                       bc = ch->brdchan;
+                       if (ch->statusflags & LOWWAIT)
+                               writeb(1, &bc->ilow);
+                       /* Okay, you can start transmitting again... */
+                       fepcmd(ch, RESUMETX, 0, 0, 0, 0);
+                       ch->statusflags &= ~TXSTOPPED;
+                       memoff(ch);
+               } /* End transmit resume requested */
+               spin_unlock_irqrestore(&epca_lock, flags);
+       }
+}
+
+/*
+ * The below routines pc_throttle and pc_unthrottle are used to slow (And
+ * resume) the receipt of data into the kernels receive buffers. The exact
+ * occurrence of this depends on the size of the kernels receive buffer and
+ * what the 'watermarks' are set to for that buffer. See the n_ttys.c file for
+ * more details.
+ */
+static void pc_throttle(struct tty_struct *tty)
+{
+       struct channel *ch;
+       unsigned long flags;
+       /*
+        * verifyChannel returns the channel from the tty struct if it is
+        * valid. This serves as a sanity check.
+        */
+       ch = verifyChannel(tty);
+       if (ch != NULL) {
+               spin_lock_irqsave(&epca_lock, flags);
+               if ((ch->statusflags & RXSTOPPED) == 0) {
+                       globalwinon(ch);
+                       fepcmd(ch, PAUSERX, 0, 0, 0, 0);
+                       ch->statusflags |= RXSTOPPED;
+                       memoff(ch);
+               }
+               spin_unlock_irqrestore(&epca_lock, flags);
+       }
+}
+
+static void pc_unthrottle(struct tty_struct *tty)
+{
+       struct channel *ch;
+       unsigned long flags;
+       /*
+        * verifyChannel returns the channel from the tty struct if it is
+        * valid. This serves as a sanity check.
+        */
+       ch = verifyChannel(tty);
+       if (ch != NULL) {
+               /* Just in case output was resumed because of a change
+                  in Digi-flow */
+               spin_lock_irqsave(&epca_lock, flags);
+               if (ch->statusflags & RXSTOPPED) {
+                       globalwinon(ch);
+                       fepcmd(ch, RESUMERX, 0, 0, 0, 0);
+                       ch->statusflags &= ~RXSTOPPED;
+                       memoff(ch);
+               }
+               spin_unlock_irqrestore(&epca_lock, flags);
+       }
+}
+
+static int pc_send_break(struct tty_struct *tty, int msec)
+{
+       struct channel *ch = tty->driver_data;
+       unsigned long flags;
+
+       if (msec == -1)
+               msec = 0xFFFF;
+       else if (msec > 0xFFFE)
+               msec = 0xFFFE;
+       else if (msec < 1)
+               msec = 1;
+
+       spin_lock_irqsave(&epca_lock, flags);
+       globalwinon(ch);
+       /*
+        * Maybe I should send an infinite break here, schedule() for msec
+        * amount of time, and then stop the break. This way, the user can't
+        * screw up the FEP by causing digi_send_break() to be called (i.e. via
+        * an ioctl()) more than once in msec amount of time.
+        * Try this for now...
+        */
+       fepcmd(ch, SENDBREAK, msec, 0, 10, 0);
+       memoff(ch);
+       spin_unlock_irqrestore(&epca_lock, flags);
+       return 0;
+}
+
+/* Caller MUST hold the lock */
+static void setup_empty_event(struct tty_struct *tty, struct channel *ch)
+{
+       struct board_chan __iomem *bc = ch->brdchan;
+
+       globalwinon(ch);
+       ch->statusflags |= EMPTYWAIT;
+       /*
+        * When set the iempty flag request a event to be generated when the
+        * transmit buffer is empty (If there is no BREAK in progress).
+        */
+       writeb(1, &bc->iempty);
+       memoff(ch);
+}
+
+#ifndef MODULE
+static void __init epca_setup(char *str, int *ints)
+{
+       struct board_info board;
+       int               index, loop, last;
+       char              *temp, *t2;
+       unsigned          len;
+
+       /*
+        * If this routine looks a little strange it is because it is only
+        * called if a LILO append command is given to boot the kernel with
+        * parameters. In this way, we can provide the user a method of
+        * changing his board configuration without rebuilding the kernel.
+        */
+       if (!liloconfig)
+               liloconfig = 1;
+
+       memset(&board, 0, sizeof(board));
+
+       /* Assume the data is int first, later we can change it */
+       /* I think that array position 0 of ints holds the number of args */
+       for (last = 0, index = 1; index <= ints[0]; index++)
+               switch (index) { /* Begin parse switch */
+               case 1:
+                       board.status = ints[index];
+                       /*
+                        * We check for 2 (As opposed to 1; because 2 is a flag
+                        * instructing the driver to ignore epcaconfig.) For
+                        * this reason we check for 2.
+                        */
+                       if (board.status == 2) {
+                       /* Begin ignore epcaconfig as well as lilo cmd line */
+                               nbdevs = 0;
+                               num_cards = 0;
+                               return;
+                       } /* End ignore epcaconfig as well as lilo cmd line */
+
+                       if (board.status > 2) {
+                               printk(KERN_ERR "epca_setup: Invalid board status 0x%x\n",
+                                               board.status);
+                               invalid_lilo_config = 1;
+                               setup_error_code |= INVALID_BOARD_STATUS;
+                               return;
+                       }
+                       last = index;
+                       break;
+               case 2:
+                       board.type = ints[index];
+                       if (board.type >= PCIXEM)  {
+                               printk(KERN_ERR "epca_setup: Invalid board type 0x%x\n", board.type);
+                               invalid_lilo_config = 1;
+                               setup_error_code |= INVALID_BOARD_TYPE;
+                               return;
+                       }
+                       last = index;
+                       break;
+               case 3:
+                       board.altpin = ints[index];
+                       if (board.altpin > 1) {
+                               printk(KERN_ERR "epca_setup: Invalid board altpin 0x%x\n", board.altpin);
+                               invalid_lilo_config = 1;
+                               setup_error_code |= INVALID_ALTPIN;
+                               return;
+                       }
+                       last = index;
+                       break;
+
+               case 4:
+                       board.numports = ints[index];
+                       if (board.numports < 2 || board.numports > 256) {
+                               printk(KERN_ERR "epca_setup: Invalid board numports 0x%x\n", board.numports);
+                               invalid_lilo_config = 1;
+                               setup_error_code |= INVALID_NUM_PORTS;
+                               return;
+                       }
+                       nbdevs += board.numports;
+                       last = index;
+                       break;
+
+               case 5:
+                       board.port = ints[index];
+                       if (ints[index] <= 0) {
+                               printk(KERN_ERR "epca_setup: Invalid io port 0x%x\n", (unsigned int)board.port);
+                               invalid_lilo_config = 1;
+                               setup_error_code |= INVALID_PORT_BASE;
+                               return;
+                       }
+                       last = index;
+                       break;
+
+               case 6:
+                       board.membase = ints[index];
+                       if (ints[index] <= 0) {
+                               printk(KERN_ERR "epca_setup: Invalid memory base 0x%x\n",
+                                       (unsigned int)board.membase);
+                               invalid_lilo_config = 1;
+                               setup_error_code |= INVALID_MEM_BASE;
+                               return;
+                       }
+                       last = index;
+                       break;
+
+               default:
+                       printk(KERN_ERR "<Error> - epca_setup: Too many integer parms\n");
+                       return;
+
+               } /* End parse switch */
+
+       while (str && *str)  { /* Begin while there is a string arg */
+               /* find the next comma or terminator */
+               temp = str;
+               /* While string is not null, and a comma hasn't been found */
+               while (*temp && (*temp != ','))
+                       temp++;
+               if (!*temp)
+                       temp = NULL;
+               else
+                       *temp++ = 0;
+               /* Set index to the number of args + 1 */
+               index = last + 1;
+
+               switch (index) {
+               case 1:
+                       len = strlen(str);
+                       if (strncmp("Disable", str, len) == 0)
+                               board.status = 0;
+                       else if (strncmp("Enable", str, len) == 0)
+                               board.status = 1;
+                       else {
+                               printk(KERN_ERR "epca_setup: Invalid status %s\n", str);
+                               invalid_lilo_config = 1;
+                               setup_error_code |= INVALID_BOARD_STATUS;
+                               return;
+                       }
+                       last = index;
+                       break;
+
+               case 2:
+                       for (loop = 0; loop < EPCA_NUM_TYPES; loop++)
+                               if (strcmp(board_desc[loop], str) == 0)
+                                       break;
+                       /*
+                        * If the index incremented above refers to a
+                        * legitamate board type set it here.
+                        */
+                       if (index < EPCA_NUM_TYPES)
+                               board.type = loop;
+                       else {
+                               printk(KERN_ERR "epca_setup: Invalid board type: %s\n", str);
+                               invalid_lilo_config = 1;
+                               setup_error_code |= INVALID_BOARD_TYPE;
+                               return;
+                       }
+                       last = index;
+                       break;
+
+               case 3:
+                       len = strlen(str);
+                       if (strncmp("Disable", str, len) == 0)
+                               board.altpin = 0;
+                       else if (strncmp("Enable", str, len) == 0)
+                               board.altpin = 1;
+                       else {
+                               printk(KERN_ERR "epca_setup: Invalid altpin %s\n", str);
+                               invalid_lilo_config = 1;
+                               setup_error_code |= INVALID_ALTPIN;
+                               return;
+                       }
+                       last = index;
+                       break;
+
+               case 4:
+                       t2 = str;
+                       while (isdigit(*t2))
+                               t2++;
+
+                       if (*t2) {
+                               printk(KERN_ERR "epca_setup: Invalid port count %s\n", str);
+                               invalid_lilo_config = 1;
+                               setup_error_code |= INVALID_NUM_PORTS;
+                               return;
+                       }
+
+                       /*
+                        * There is not a man page for simple_strtoul but the
+                        * code can be found in vsprintf.c. The first argument
+                        * is the string to translate (To an unsigned long
+                        * obviously), the second argument can be the address
+                        * of any character variable or a NULL. If a variable
+                        * is given, the end pointer of the string will be
+                        * stored in that variable; if a NULL is given the end
+                        * pointer will not be returned. The last argument is
+                        * the base to use. If a 0 is indicated, the routine
+                        * will attempt to determine the proper base by looking
+                        * at the values prefix (A '0' for octal, a 'x' for
+                        * hex, etc ... If a value is given it will use that
+                        * value as the base.
+                        */
+                       board.numports = simple_strtoul(str, NULL, 0);
+                       nbdevs += board.numports;
+                       last = index;
+                       break;
+
+               case 5:
+                       t2 = str;
+                       while (isxdigit(*t2))
+                               t2++;
+
+                       if (*t2) {
+                               printk(KERN_ERR "epca_setup: Invalid i/o address %s\n", str);
+                               invalid_lilo_config = 1;
+                               setup_error_code |= INVALID_PORT_BASE;
+                               return;
+                       }
+
+                       board.port = simple_strtoul(str, NULL, 16);
+                       last = index;
+                       break;
+
+               case 6:
+                       t2 = str;
+                       while (isxdigit(*t2))
+                               t2++;
+
+                       if (*t2) {
+                               printk(KERN_ERR "epca_setup: Invalid memory base %s\n", str);
+                               invalid_lilo_config = 1;
+                               setup_error_code |= INVALID_MEM_BASE;
+                               return;
+                       }
+                       board.membase = simple_strtoul(str, NULL, 16);
+                       last = index;
+                       break;
+               default:
+                       printk(KERN_ERR "epca: Too many string parms\n");
+                       return;
+               }
+               str = temp;
+       } /* End while there is a string arg */
+
+       if (last < 6) {
+               printk(KERN_ERR "epca: Insufficient parms specified\n");
+               return;
+       }
+
+       /* I should REALLY validate the stuff here */
+       /* Copies our local copy of board into boards */
+       memcpy((void *)&boards[num_cards], (void *)&board, sizeof(board));
+       /* Does this get called once per lilo arg are what ? */
+       printk(KERN_INFO "PC/Xx: Added board %i, %s %i ports at 0x%4.4X base 0x%6.6X\n",
+               num_cards, board_desc[board.type],
+               board.numports, (int)board.port, (unsigned int) board.membase);
+       num_cards++;
+}
+
+static int __init epca_real_setup(char *str)
+{
+       int ints[11];
+
+       epca_setup(get_options(str, 11, ints), ints);
+       return 1;
+}
+
+__setup("digiepca", epca_real_setup);
+#endif
+
+enum epic_board_types {
+       brd_xr = 0,
+       brd_xem,
+       brd_cx,
+       brd_xrj,
+};
+
+/* indexed directly by epic_board_types enum */
+static struct {
+       unsigned char board_type;
+       unsigned bar_idx;               /* PCI base address region */
+} epca_info_tbl[] = {
+       { PCIXR, 0, },
+       { PCIXEM, 0, },
+       { PCICX, 0, },
+       { PCIXRJ, 2, },
+};
+
+static int __devinit epca_init_one(struct pci_dev *pdev,
+                                const struct pci_device_id *ent)
+{
+       static int board_num = -1;
+       int board_idx, info_idx = ent->driver_data;
+       unsigned long addr;
+
+       if (pci_enable_device(pdev))
+               return -EIO;
+
+       board_num++;
+       board_idx = board_num + num_cards;
+       if (board_idx >= MAXBOARDS)
+               goto err_out;
+
+       addr = pci_resource_start(pdev, epca_info_tbl[info_idx].bar_idx);
+       if (!addr) {
+               printk(KERN_ERR PFX "PCI region #%d not available (size 0)\n",
+                       epca_info_tbl[info_idx].bar_idx);
+               goto err_out;
+       }
+
+       boards[board_idx].status = ENABLED;
+       boards[board_idx].type = epca_info_tbl[info_idx].board_type;
+       boards[board_idx].numports = 0x0;
+       boards[board_idx].port = addr + PCI_IO_OFFSET;
+       boards[board_idx].membase = addr;
+
+       if (!request_mem_region(addr + PCI_IO_OFFSET, 0x200000, "epca")) {
+               printk(KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n",
+                       0x200000, addr + PCI_IO_OFFSET);
+               goto err_out;
+       }
+
+       boards[board_idx].re_map_port = ioremap_nocache(addr + PCI_IO_OFFSET,
+                                                               0x200000);
+       if (!boards[board_idx].re_map_port) {
+               printk(KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n",
+                       0x200000, addr + PCI_IO_OFFSET);
+               goto err_out_free_pciio;
+       }
+
+       if (!request_mem_region(addr, 0x200000, "epca")) {
+               printk(KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n",
+                       0x200000, addr);
+               goto err_out_free_iounmap;
+       }
+
+       boards[board_idx].re_map_membase = ioremap_nocache(addr, 0x200000);
+       if (!boards[board_idx].re_map_membase) {
+               printk(KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n",
+                       0x200000, addr + PCI_IO_OFFSET);
+               goto err_out_free_memregion;
+       }
+
+       /*
+        * I don't know what the below does, but the hardware guys say its
+        * required on everything except PLX (In this case XRJ).
+        */
+       if (info_idx != brd_xrj) {
+               pci_write_config_byte(pdev, 0x40, 0);
+               pci_write_config_byte(pdev, 0x46, 0);
+       }
+
+       return 0;
+
+err_out_free_memregion:
+       release_mem_region(addr, 0x200000);
+err_out_free_iounmap:
+       iounmap(boards[board_idx].re_map_port);
+err_out_free_pciio:
+       release_mem_region(addr + PCI_IO_OFFSET, 0x200000);
+err_out:
+       return -ENODEV;
+}
+
+
+static struct pci_device_id epca_pci_tbl[] = {
+       { PCI_VENDOR_DIGI, PCI_DEVICE_XR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xr },
+       { PCI_VENDOR_DIGI, PCI_DEVICE_XEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xem },
+       { PCI_VENDOR_DIGI, PCI_DEVICE_CX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_cx },
+       { PCI_VENDOR_DIGI, PCI_DEVICE_XRJ, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xrj },
+       { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, epca_pci_tbl);
+
+static int __init init_PCI(void)
+{
+       memset(&epca_driver, 0, sizeof(epca_driver));
+       epca_driver.name = "epca";
+       epca_driver.id_table = epca_pci_tbl;
+       epca_driver.probe = epca_init_one;
+
+       return pci_register_driver(&epca_driver);
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/tty/epca.h b/drivers/staging/tty/epca.h
new file mode 100644 (file)
index 0000000..d414bf2
--- /dev/null
@@ -0,0 +1,158 @@
+#define XEMPORTS    0xC02
+#define XEPORTS     0xC22
+
+#define MAX_ALLOC   0x100
+
+#define MAXBOARDS   12
+#define FEPCODESEG  0x0200L
+#define FEPCODE     0x2000L
+#define BIOSCODE    0xf800L
+
+#define MISCGLOBAL  0x0C00L
+#define NPORT       0x0C22L
+#define MBOX        0x0C40L
+#define PORTBASE    0x0C90L
+
+/* Begin code defines used for epca_setup */
+
+#define INVALID_BOARD_TYPE   0x1
+#define INVALID_NUM_PORTS    0x2
+#define INVALID_MEM_BASE     0x4
+#define INVALID_PORT_BASE    0x8
+#define INVALID_BOARD_STATUS 0x10
+#define INVALID_ALTPIN       0x20
+
+/* End code defines used for epca_setup */
+
+
+#define FEPCLR      0x00
+#define FEPMEM      0x02
+#define FEPRST      0x04
+#define FEPINT      0x08
+#define        FEPMASK     0x0e
+#define        FEPWIN      0x80
+
+#define PCXE    0
+#define PCXEVE  1
+#define PCXEM   2   
+#define EISAXEM 3
+#define PC64XE  4
+#define PCXI    5
+#define PCIXEM  7
+#define PCICX   8
+#define PCIXR   9
+#define PCIXRJ  10
+#define EPCA_NUM_TYPES 6
+
+
+static char *board_desc[] = 
+{
+       "PC/Xe",
+       "PC/Xeve",
+       "PC/Xem",
+       "EISA/Xem",
+       "PC/64Xe",
+       "PC/Xi",
+       "unknown",
+       "PCI/Xem",
+       "PCI/CX",
+       "PCI/Xr",
+       "PCI/Xrj",
+};
+
+#define STARTC      021
+#define STOPC       023
+#define IAIXON      0x2000
+
+
+#define TXSTOPPED  0x1
+#define LOWWAIT    0x2
+#define EMPTYWAIT  0x4
+#define RXSTOPPED  0x8
+#define TXBUSY     0x10
+
+#define DISABLED   0
+#define ENABLED    1
+#define OFF        0
+#define ON         1
+
+#define FEPTIMEOUT 200000  
+#define SERIAL_TYPE_INFO    3
+#define EPCA_EVENT_HANGUP   1
+#define EPCA_MAGIC          0x5c6df104L
+
+struct channel 
+{
+       long   magic;
+       struct tty_port port;
+       unsigned char boardnum;
+       unsigned char channelnum;
+       unsigned char omodem;         /* FEP output modem status     */
+       unsigned char imodem;         /* FEP input modem status      */
+       unsigned char modemfake;      /* Modem values to be forced   */
+       unsigned char modem;          /* Force values                */
+       unsigned char hflow;
+       unsigned char dsr;
+       unsigned char dcd;
+       unsigned char m_rts ;           /* The bits used in whatever FEP */
+       unsigned char m_dcd ;           /* is indiginous to this board to */
+       unsigned char m_dsr ;           /* represent each of the physical */
+       unsigned char m_cts ;           /* handshake lines */
+       unsigned char m_ri ;
+       unsigned char m_dtr ;
+       unsigned char stopc;
+       unsigned char startc;
+       unsigned char stopca;
+       unsigned char startca;
+       unsigned char fepstopc;
+       unsigned char fepstartc;
+       unsigned char fepstopca;
+       unsigned char fepstartca;
+       unsigned char txwin;
+       unsigned char rxwin;
+       unsigned short fepiflag;
+       unsigned short fepcflag;
+       unsigned short fepoflag;
+       unsigned short txbufhead;
+       unsigned short txbufsize;
+       unsigned short rxbufhead;
+       unsigned short rxbufsize;
+       int    close_delay;
+       unsigned long  event;
+       uint   dev;
+       unsigned long  statusflags;
+       unsigned long  c_iflag;
+       unsigned long  c_cflag;
+       unsigned long  c_lflag;
+       unsigned long  c_oflag;
+       unsigned char __iomem *txptr;
+       unsigned char __iomem *rxptr;
+       struct board_info           *board;
+       struct board_chan           __iomem *brdchan;
+       struct digi_struct          digiext;
+       struct work_struct          tqueue;
+       struct global_data          __iomem *mailbox;
+};
+
+struct board_info      
+{
+       unsigned char status;
+       unsigned char type;
+       unsigned char altpin;
+       unsigned short numports;
+       unsigned long port;
+       unsigned long membase;
+       void __iomem *re_map_port;
+       void __iomem *re_map_membase;
+       unsigned long  memory_seg;
+       void ( * memwinon )     (struct board_info *, unsigned int) ;
+       void ( * memwinoff )    (struct board_info *, unsigned int) ;
+       void ( * globalwinon )  (struct channel *) ;
+       void ( * txwinon )      (struct channel *) ;
+       void ( * rxwinon )      (struct channel *) ;
+       void ( * memoff )       (struct channel *) ;
+       void ( * assertgwinon ) (struct channel *) ;
+       void ( * assertmemoff ) (struct channel *) ;
+       unsigned char poller_inhibited ;
+};
+
diff --git a/drivers/staging/tty/epcaconfig.h b/drivers/staging/tty/epcaconfig.h
new file mode 100644 (file)
index 0000000..55dec06
--- /dev/null
@@ -0,0 +1,7 @@
+#define NUMCARDS 0
+#define NBDEVS 0
+
+struct board_info static_boards[NUMCARDS]={
+};
+
+/* DO NOT HAND EDIT THIS FILE! */
diff --git a/drivers/staging/tty/ip2/Makefile b/drivers/staging/tty/ip2/Makefile
new file mode 100644 (file)
index 0000000..7b78e0d
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for the Computone IntelliPort Plus Driver
+#
+
+obj-$(CONFIG_COMPUTONE)         += ip2.o
+
+ip2-y                  := ip2main.o
+
diff --git a/drivers/staging/tty/ip2/i2cmd.c b/drivers/staging/tty/ip2/i2cmd.c
new file mode 100644 (file)
index 0000000..e7af647
--- /dev/null
@@ -0,0 +1,210 @@
+/*******************************************************************************
+*
+*   (c) 1998 by Computone Corporation
+*
+********************************************************************************
+*
+*
+*   PACKAGE:     Linux tty Device Driver for IntelliPort family of multiport
+*                serial I/O controllers.
+*
+*   DESCRIPTION: Definition table for In-line and Bypass commands. Applicable
+*                only when the standard loadware is active. (This is included
+*                source code, not a separate compilation module.)
+*
+*******************************************************************************/
+
+//------------------------------------------------------------------------------
+//
+// Revision History:
+//
+// 10 October 1991   MAG First Draft
+//  7 November 1991  MAG Reflects additional commands.
+// 24 February 1992  MAG Additional commands for 1.4.x loadware
+// 11 March 1992     MAG Additional commands
+// 30 March 1992     MAG Additional command: CMD_DSS_NOW
+// 18 May 1992       MAG Discovered commands 39 & 40 must be at the end of a
+//                       packet: affects implementation.
+//------------------------------------------------------------------------------
+
+//************
+//* Includes *
+//************
+
+#include "i2cmd.h"   /* To get some bit-defines */
+
+//------------------------------------------------------------------------------
+// Here is the table of global arrays which represent each type of command
+// supported in the IntelliPort standard loadware. See also i2cmd.h
+// for a more complete explanation of what is going on.
+//------------------------------------------------------------------------------
+
+// Here are the various globals: note that the names are not used except through
+// the macros defined in i2cmd.h. Also note that although they are character
+// arrays here (for extendability) they are cast to structure pointers in the
+// i2cmd.h macros. See i2cmd.h for flags definitions.
+
+//                     Length Flags Command
+static UCHAR ct02[] = { 1, BTH,     0x02                     }; // DTR UP
+static UCHAR ct03[] = { 1, BTH,     0x03                     }; // DTR DN
+static UCHAR ct04[] = { 1, BTH,     0x04                     }; // RTS UP
+static UCHAR ct05[] = { 1, BTH,     0x05                     }; // RTS DN
+static UCHAR ct06[] = { 1, BYP,     0x06                     }; // START FL
+static UCHAR ct07[] = { 2, BTH,     0x07,0                   }; // BAUD
+static UCHAR ct08[] = { 2, BTH,     0x08,0                   }; // BITS
+static UCHAR ct09[] = { 2, BTH,     0x09,0                   }; // STOP
+static UCHAR ct10[] = { 2, BTH,     0x0A,0                   }; // PARITY
+static UCHAR ct11[] = { 2, BTH,     0x0B,0                   }; // XON
+static UCHAR ct12[] = { 2, BTH,     0x0C,0                   }; // XOFF
+static UCHAR ct13[] = { 1, BTH,     0x0D                     }; // STOP FL
+static UCHAR ct14[] = { 1, BYP|VIP, 0x0E                     }; // ACK HOTK
+//static UCHAR ct15[]={ 2, BTH|VIP, 0x0F,0                   }; // IRQ SET
+static UCHAR ct16[] = { 2, INL,     0x10,0                   }; // IXONOPTS
+static UCHAR ct17[] = { 2, INL,     0x11,0                   }; // OXONOPTS
+static UCHAR ct18[] = { 1, INL,     0x12                     }; // CTSENAB
+static UCHAR ct19[] = { 1, BTH,     0x13                     }; // CTSDSAB
+static UCHAR ct20[] = { 1, INL,     0x14                     }; // DCDENAB
+static UCHAR ct21[] = { 1, BTH,     0x15                     }; // DCDDSAB
+static UCHAR ct22[] = { 1, BTH,     0x16                     }; // DSRENAB
+static UCHAR ct23[] = { 1, BTH,     0x17                     }; // DSRDSAB
+static UCHAR ct24[] = { 1, BTH,     0x18                     }; // RIENAB
+static UCHAR ct25[] = { 1, BTH,     0x19                     }; // RIDSAB
+static UCHAR ct26[] = { 2, BTH,     0x1A,0                   }; // BRKENAB
+static UCHAR ct27[] = { 1, BTH,     0x1B                     }; // BRKDSAB
+//static UCHAR ct28[]={ 2, BTH,     0x1C,0                   }; // MAXBLOKSIZE
+//static UCHAR ct29[]={ 2, 0,       0x1D,0                   }; // reserved
+static UCHAR ct30[] = { 1, INL,     0x1E                     }; // CTSFLOWENAB
+static UCHAR ct31[] = { 1, INL,     0x1F                     }; // CTSFLOWDSAB
+static UCHAR ct32[] = { 1, INL,     0x20                     }; // RTSFLOWENAB
+static UCHAR ct33[] = { 1, INL,     0x21                     }; // RTSFLOWDSAB
+static UCHAR ct34[] = { 2, BTH,     0x22,0                   }; // ISTRIPMODE
+static UCHAR ct35[] = { 2, BTH|END, 0x23,0                   }; // SENDBREAK
+static UCHAR ct36[] = { 2, BTH,     0x24,0                   }; // SETERRMODE
+//static UCHAR ct36a[]={ 3, INL,    0x24,0,0                 }; // SET_REPLACE
+
+// The following is listed for completeness, but should never be sent directly
+// by user-level code. It is sent only by library routines in response to data
+// movement.
+//static UCHAR ct37[]={ 5, BYP|VIP, 0x25,0,0,0,0             }; // FLOW PACKET
+
+// Back to normal
+//static UCHAR ct38[] = {11, BTH|VAR, 0x26,0,0,0,0,0,0,0,0,0,0 }; // DEF KEY SEQ
+//static UCHAR ct39[]={ 3, BTH|END, 0x27,0,0                 }; // OPOSTON
+//static UCHAR ct40[]={ 1, BTH|END, 0x28                     }; // OPOSTOFF
+static UCHAR ct41[] = { 1, BYP,     0x29                     }; // RESUME
+//static UCHAR ct42[]={ 2, BTH,     0x2A,0                   }; // TXBAUD
+//static UCHAR ct43[]={ 2, BTH,     0x2B,0                   }; // RXBAUD
+//static UCHAR ct44[]={ 2, BTH,     0x2C,0                   }; // MS PING
+//static UCHAR ct45[]={ 1, BTH,     0x2D                     }; // HOTENAB
+//static UCHAR ct46[]={ 1, BTH,     0x2E                     }; // HOTDSAB
+//static UCHAR ct47[]={ 7, BTH,     0x2F,0,0,0,0,0,0         }; // UNIX FLAGS
+//static UCHAR ct48[]={ 1, BTH,     0x30                     }; // DSRFLOWENAB
+//static UCHAR ct49[]={ 1, BTH,     0x31                     }; // DSRFLOWDSAB
+//static UCHAR ct50[]={ 1, BTH,     0x32                     }; // DTRFLOWENAB
+//static UCHAR ct51[]={ 1, BTH,     0x33                     }; // DTRFLOWDSAB
+//static UCHAR ct52[]={ 1, BTH,     0x34                     }; // BAUDTABRESET
+//static UCHAR ct53[] = { 3, BTH,     0x35,0,0                 }; // BAUDREMAP
+static UCHAR ct54[] = { 3, BTH,     0x36,0,0                 }; // CUSTOMBAUD1
+static UCHAR ct55[] = { 3, BTH,     0x37,0,0                 }; // CUSTOMBAUD2
+static UCHAR ct56[] = { 2, BTH|END, 0x38,0                   }; // PAUSE
+static UCHAR ct57[] = { 1, BYP,     0x39                     }; // SUSPEND
+static UCHAR ct58[] = { 1, BYP,     0x3A                     }; // UNSUSPEND
+static UCHAR ct59[] = { 2, BTH,     0x3B,0                   }; // PARITYCHK
+static UCHAR ct60[] = { 1, INL|VIP, 0x3C                     }; // BOOKMARKREQ
+//static UCHAR ct61[]={ 2, BTH,     0x3D,0                   }; // INTERNALLOOP
+//static UCHAR ct62[]={ 2, BTH,     0x3E,0                   }; // HOTKTIMEOUT
+static UCHAR ct63[] = { 2, INL,     0x3F,0                   }; // SETTXON
+static UCHAR ct64[] = { 2, INL,     0x40,0                   }; // SETTXOFF
+//static UCHAR ct65[]={ 2, BTH,     0x41,0                   }; // SETAUTORTS
+//static UCHAR ct66[]={ 2, BTH,     0x42,0                   }; // SETHIGHWAT
+//static UCHAR ct67[]={ 2, BYP,     0x43,0                   }; // STARTSELFL
+//static UCHAR ct68[]={ 2, INL,     0x44,0                   }; // ENDSELFL
+//static UCHAR ct69[]={ 1, BYP,     0x45                     }; // HWFLOW_OFF
+//static UCHAR ct70[]={ 1, BTH,     0x46                     }; // ODSRFL_ENAB
+//static UCHAR ct71[]={ 1, BTH,     0x47                     }; // ODSRFL_DSAB
+//static UCHAR ct72[]={ 1, BTH,     0x48                     }; // ODCDFL_ENAB
+//static UCHAR ct73[]={ 1, BTH,     0x49                     }; // ODCDFL_DSAB
+//static UCHAR ct74[]={ 2, BTH,     0x4A,0                   }; // LOADLEVEL
+//static UCHAR ct75[]={ 2, BTH,     0x4B,0                   }; // STATDATA
+//static UCHAR ct76[]={ 1, BYP,     0x4C                     }; // BREAK_ON
+//static UCHAR ct77[]={ 1, BYP,     0x4D                     }; // BREAK_OFF
+//static UCHAR ct78[]={ 1, BYP,     0x4E                     }; // GETFC
+static UCHAR ct79[] = { 2, BYP,     0x4F,0                   }; // XMIT_NOW
+//static UCHAR ct80[]={ 4, BTH,     0x50,0,0,0               }; // DIVISOR_LATCH
+//static UCHAR ct81[]={ 1, BYP,     0x51                     }; // GET_STATUS
+//static UCHAR ct82[]={ 1, BYP,     0x52                     }; // GET_TXCNT
+//static UCHAR ct83[]={ 1, BYP,     0x53                     }; // GET_RXCNT
+//static UCHAR ct84[]={ 1, BYP,     0x54                     }; // GET_BOXIDS
+//static UCHAR ct85[]={10, BYP,     0x55,0,0,0,0,0,0,0,0,0   }; // ENAB_MULT
+//static UCHAR ct86[]={ 2, BTH,     0x56,0                   }; // RCV_ENABLE
+static UCHAR ct87[] = { 1, BYP,     0x57                     }; // HW_TEST
+//static UCHAR ct88[]={ 3, BTH,     0x58,0,0                 }; // RCV_THRESHOLD
+//static UCHAR ct90[]={ 3, BYP,     0x5A,0,0                 }; // Set SILO
+//static UCHAR ct91[]={ 2, BYP,     0x5B,0                   }; // timed break
+
+// Some composite commands as well
+//static UCHAR cc01[]={ 2, BTH,     0x02,0x04                }; // DTR & RTS UP
+//static UCHAR cc02[]={ 2, BTH,     0x03,0x05                }; // DTR & RTS DN
+
+//********
+//* Code *
+//********
+
+//******************************************************************************
+// Function:   i2cmdUnixFlags(iflag, cflag, lflag)
+// Parameters: Unix tty flags
+//
+// Returns:    Pointer to command structure
+//
+// Description:
+//
+// This routine sets the parameters of command 47 and returns a pointer to the
+// appropriate structure.
+//******************************************************************************
+#if 0
+cmdSyntaxPtr
+i2cmdUnixFlags(unsigned short iflag,unsigned short cflag,unsigned short lflag)
+{
+       cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct47;
+
+       pCM->cmd[1] = (unsigned char)  iflag;
+       pCM->cmd[2] = (unsigned char) (iflag >> 8);
+       pCM->cmd[3] = (unsigned char)  cflag;
+       pCM->cmd[4] = (unsigned char) (cflag >> 8);
+       pCM->cmd[5] = (unsigned char)  lflag;
+       pCM->cmd[6] = (unsigned char) (lflag >> 8);
+       return pCM;
+}
+#endif  /*  0  */
+
+//******************************************************************************
+// Function:   i2cmdBaudDef(which, rate)
+// Parameters: ?
+//
+// Returns:    Pointer to command structure
+//
+// Description:
+//
+// This routine sets the parameters of commands 54 or 55 (according to the
+// argument which), and returns a pointer to the appropriate structure.
+//******************************************************************************
+static cmdSyntaxPtr
+i2cmdBaudDef(int which, unsigned short rate)
+{
+       cmdSyntaxPtr pCM;
+
+       switch(which)
+       {
+       case 1:
+               pCM = (cmdSyntaxPtr) ct54;
+               break;
+       default:
+       case 2:
+               pCM = (cmdSyntaxPtr) ct55;
+               break;
+       }
+       pCM->cmd[1] = (unsigned char) rate;
+       pCM->cmd[2] = (unsigned char) (rate >> 8);
+       return pCM;
+}
+
diff --git a/drivers/staging/tty/ip2/i2cmd.h b/drivers/staging/tty/ip2/i2cmd.h
new file mode 100644 (file)
index 0000000..29277ec
--- /dev/null
@@ -0,0 +1,630 @@
+/*******************************************************************************
+*
+*   (c) 1999 by Computone Corporation
+*
+********************************************************************************
+*
+*
+*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
+*                serial I/O controllers.
+*
+*   DESCRIPTION: Definitions and support for In-line and Bypass commands.
+*                Applicable only when the standard loadware is active.
+*
+*******************************************************************************/
+//------------------------------------------------------------------------------
+// Revision History:
+//
+// 10 October 1991   MAG First Draft
+//  7 November 1991  MAG Reflects some new commands
+// 20 February 1992  MAG CMD_HOTACK corrected: no argument.
+// 24 February 1992  MAG Support added for new commands for 1.4.x loadware.
+// 11 March 1992     MAG Additional commands.
+// 16 March 1992     MAG Additional commands.
+// 30 March 1992     MAG Additional command: CMD_DSS_NOW
+// 18 May   1992     MAG Changed CMD_OPOST
+//
+//------------------------------------------------------------------------------
+#ifndef I2CMD_H      // To prevent multiple includes
+#define I2CMD_H   1
+
+#include "ip2types.h"
+
+// This module is designed to provide a uniform method of sending commands to
+// the board through command packets. The difficulty is, some commands take
+// parameters, others do not. Furthermore, it is often useful to send several
+// commands to the same channel as part of the same packet. (See also i2pack.h.)
+//
+// This module is designed so that the caller should not be responsible for
+// remembering the exact syntax of each command, or at least so that the
+// compiler could check things somewhat. I'll explain as we go...
+//
+// First, a structure which can embody the syntax of each type of command.
+//
+typedef struct _cmdSyntax
+{
+       UCHAR length;   // Number of bytes in the command
+       UCHAR flags;    // Information about the command (see below)
+
+       // The command and its parameters, which may be of arbitrary length. Don't
+       // worry yet how the parameters will be initialized; macros later take care
+       // of it. Also, don't worry about the arbitrary length issue; this structure
+       // is never used to allocate space (see i2cmd.c).
+       UCHAR cmd[2];
+} cmdSyntax, *cmdSyntaxPtr;
+
+// Bit assignments for flags
+
+#define INL 1           // Set if suitable for inline commands
+#define BYP 2           // Set if suitable for bypass commands
+#define BTH (INL|BYP)   // suitable for either!
+#define END 4           // Set if this must be the last command in a block
+#define VIP 8           // Set if this command is special in some way and really
+                                               // should only be sent from the library-level and not
+                                               // directly from user-level
+#define VAR 0x10        // This command is of variable length!
+
+// Declarations for the global arrays used to bear the commands and their
+// arguments.
+//
+// Note: Since these are globals and the arguments might change, it is important
+// that the library routine COPY these into buffers from whence they would be
+// sent, rather than merely storing the pointers. In multi-threaded
+// environments, important that the copy should obtain before any context switch
+// is allowed. Also, for parameterized commands, DO NOT ISSUE THE SAME COMMAND
+// MORE THAN ONCE WITH THE SAME PARAMETERS in the same call.
+//
+static UCHAR ct02[];
+static UCHAR ct03[];
+static UCHAR ct04[];
+static UCHAR ct05[];
+static UCHAR ct06[];
+static UCHAR ct07[];
+static UCHAR ct08[];
+static UCHAR ct09[];
+static UCHAR ct10[];
+static UCHAR ct11[];
+static UCHAR ct12[];
+static UCHAR ct13[];
+static UCHAR ct14[];
+static UCHAR ct15[];
+static UCHAR ct16[];
+static UCHAR ct17[];
+static UCHAR ct18[];
+static UCHAR ct19[];
+static UCHAR ct20[];
+static UCHAR ct21[];
+static UCHAR ct22[];
+static UCHAR ct23[];
+static UCHAR ct24[];
+static UCHAR ct25[];
+static UCHAR ct26[];
+static UCHAR ct27[];
+static UCHAR ct28[];
+static UCHAR ct29[];
+static UCHAR ct30[];
+static UCHAR ct31[];
+static UCHAR ct32[];
+static UCHAR ct33[];
+static UCHAR ct34[];
+static UCHAR ct35[];
+static UCHAR ct36[];
+static UCHAR ct36a[];
+static UCHAR ct41[];
+static UCHAR ct42[];
+static UCHAR ct43[];
+static UCHAR ct44[];
+static UCHAR ct45[];
+static UCHAR ct46[];
+static UCHAR ct48[];
+static UCHAR ct49[];
+static UCHAR ct50[];
+static UCHAR ct51[];
+static UCHAR ct52[];
+static UCHAR ct56[];
+static UCHAR ct57[];
+static UCHAR ct58[];
+static UCHAR ct59[];
+static UCHAR ct60[];
+static UCHAR ct61[];
+static UCHAR ct62[];
+static UCHAR ct63[];
+static UCHAR ct64[];
+static UCHAR ct65[];
+static UCHAR ct66[];
+static UCHAR ct67[];
+static UCHAR ct68[];
+static UCHAR ct69[];
+static UCHAR ct70[];
+static UCHAR ct71[];
+static UCHAR ct72[];
+static UCHAR ct73[];
+static UCHAR ct74[];
+static UCHAR ct75[];
+static UCHAR ct76[];
+static UCHAR ct77[];
+static UCHAR ct78[];
+static UCHAR ct79[];
+static UCHAR ct80[];
+static UCHAR ct81[];
+static UCHAR ct82[];
+static UCHAR ct83[];
+static UCHAR ct84[];
+static UCHAR ct85[];
+static UCHAR ct86[];
+static UCHAR ct87[];
+static UCHAR ct88[];
+static UCHAR ct89[];
+static UCHAR ct90[];
+static UCHAR ct91[];
+static UCHAR cc01[];
+static UCHAR cc02[];
+
+// Now, refer to i2cmd.c, and see the character arrays defined there. They are
+// cast here to cmdSyntaxPtr.
+//
+// There are library functions for issuing bypass or inline commands. These
+// functions take one or more arguments of the type cmdSyntaxPtr. The routine
+// then can figure out how long each command is supposed to be and easily add it
+// to the list.
+//
+// For ease of use, we define manifests which return pointers to appropriate
+// cmdSyntaxPtr things. But some commands also take arguments. If a single
+// argument is used, we define a macro which performs the single assignment and
+// (through the expedient of a comma expression) references the appropriate
+// pointer. For commands requiring several arguments, we actually define a
+// function to perform the assignments.
+
+#define CMD_DTRUP      (cmdSyntaxPtr)(ct02)    // Raise DTR
+#define CMD_DTRDN      (cmdSyntaxPtr)(ct03)    // Lower DTR
+#define CMD_RTSUP      (cmdSyntaxPtr)(ct04)    // Raise RTS
+#define CMD_RTSDN      (cmdSyntaxPtr)(ct05)    // Lower RTS
+#define CMD_STARTFL    (cmdSyntaxPtr)(ct06)    // Start Flushing Data
+
+#define CMD_DTRRTS_UP (cmdSyntaxPtr)(cc01)     // Raise DTR and RTS
+#define CMD_DTRRTS_DN (cmdSyntaxPtr)(cc02)     // Lower DTR and RTS
+
+// Set Baud Rate for transmit and receive
+#define CMD_SETBAUD(arg) \
+       (((cmdSyntaxPtr)(ct07))->cmd[1] = (arg),(cmdSyntaxPtr)(ct07))
+
+#define CBR_50       1
+#define CBR_75       2
+#define CBR_110      3
+#define CBR_134      4
+#define CBR_150      5
+#define CBR_200      6
+#define CBR_300      7
+#define CBR_600      8
+#define CBR_1200     9
+#define CBR_1800     10
+#define CBR_2400     11
+#define CBR_4800     12
+#define CBR_9600     13
+#define CBR_19200    14
+#define CBR_38400    15
+#define CBR_2000     16
+#define CBR_3600     17
+#define CBR_7200     18
+#define CBR_56000    19
+#define CBR_57600    20
+#define CBR_64000    21
+#define CBR_76800    22
+#define CBR_115200   23
+#define CBR_C1       24    // Custom baud rate 1
+#define CBR_C2       25    // Custom baud rate 2
+#define CBR_153600   26
+#define CBR_230400   27
+#define CBR_307200   28
+#define CBR_460800   29
+#define CBR_921600   30
+
+// Set Character size
+//
+#define CMD_SETBITS(arg) \
+       (((cmdSyntaxPtr)(ct08))->cmd[1] = (arg),(cmdSyntaxPtr)(ct08))
+
+#define CSZ_5  0
+#define CSZ_6  1
+#define CSZ_7  2
+#define CSZ_8  3
+
+// Set number of stop bits
+//
+#define CMD_SETSTOP(arg) \
+       (((cmdSyntaxPtr)(ct09))->cmd[1] = (arg),(cmdSyntaxPtr)(ct09))
+
+#define CST_1  0
+#define CST_15 1  // 1.5 stop bits
+#define CST_2  2
+
+// Set parity option
+//
+#define CMD_SETPAR(arg) \
+       (((cmdSyntaxPtr)(ct10))->cmd[1] = (arg),(cmdSyntaxPtr)(ct10))
+
+#define CSP_NP 0  // no parity
+#define CSP_OD 1  // odd parity
+#define CSP_EV 2  // Even parity
+#define CSP_SP 3  // Space parity
+#define CSP_MK 4  // Mark parity
+
+// Define xon char for transmitter flow control
+//
+#define CMD_DEF_IXON(arg) \
+       (((cmdSyntaxPtr)(ct11))->cmd[1] = (arg),(cmdSyntaxPtr)(ct11))
+
+// Define xoff char for transmitter flow control
+//
+#define CMD_DEF_IXOFF(arg) \
+       (((cmdSyntaxPtr)(ct12))->cmd[1] = (arg),(cmdSyntaxPtr)(ct12))
+
+#define CMD_STOPFL   (cmdSyntaxPtr)(ct13) // Stop Flushing data
+
+// Acknowledge receipt of hotkey signal
+//
+#define CMD_HOTACK   (cmdSyntaxPtr)(ct14)
+
+// Define irq level to use. Should actually be sent by library-level code, not
+// directly from user...
+//
+#define CMDVALUE_IRQ 15 // For library use at initialization. Until this command
+                                               // is sent, board processing doesn't really start.
+#define CMD_SET_IRQ(arg) \
+       (((cmdSyntaxPtr)(ct15))->cmd[1] = (arg),(cmdSyntaxPtr)(ct15))
+
+#define CIR_POLL  0  // No IRQ - Poll
+#define CIR_3     3  // IRQ 3
+#define CIR_4     4  // IRQ 4
+#define CIR_5     5  // IRQ 5
+#define CIR_7     7  // IRQ 7
+#define CIR_10    10 // IRQ 10
+#define CIR_11    11 // IRQ 11
+#define CIR_12    12 // IRQ 12
+#define CIR_15    15 // IRQ 15
+
+// Select transmit flow xon/xoff options
+//
+#define CMD_IXON_OPT(arg) \
+       (((cmdSyntaxPtr)(ct16))->cmd[1] = (arg),(cmdSyntaxPtr)(ct16))
+
+#define CIX_NONE  0  // Incoming Xon/Xoff characters not special
+#define CIX_XON   1  // Xoff disable, Xon enable
+#define CIX_XANY  2  // Xoff disable, any key enable
+
+// Select receive flow xon/xoff options
+//
+#define CMD_OXON_OPT(arg) \
+       (((cmdSyntaxPtr)(ct17))->cmd[1] = (arg),(cmdSyntaxPtr)(ct17))
+
+#define COX_NONE  0  // Don't send Xon/Xoff
+#define COX_XON   1  // Send xon/xoff to start/stop incoming data
+
+
+#define CMD_CTS_REP  (cmdSyntaxPtr)(ct18) // Enable  CTS reporting
+#define CMD_CTS_NREP (cmdSyntaxPtr)(ct19) // Disable CTS reporting
+
+#define CMD_DCD_REP  (cmdSyntaxPtr)(ct20) // Enable  DCD reporting
+#define CMD_DCD_NREP (cmdSyntaxPtr)(ct21) // Disable DCD reporting
+
+#define CMD_DSR_REP  (cmdSyntaxPtr)(ct22) // Enable  DSR reporting
+#define CMD_DSR_NREP (cmdSyntaxPtr)(ct23) // Disable DSR reporting
+
+#define CMD_RI_REP   (cmdSyntaxPtr)(ct24) // Enable  RI  reporting
+#define CMD_RI_NREP  (cmdSyntaxPtr)(ct25) // Disable RI  reporting
+
+// Enable break reporting and select style
+//
+#define CMD_BRK_REP(arg) \
+       (((cmdSyntaxPtr)(ct26))->cmd[1] = (arg),(cmdSyntaxPtr)(ct26))
+
+#define CBK_STAT     0x00  // Report breaks as a status (exception,irq)
+#define CBK_NULL     0x01  // Report breaks as a good null
+#define CBK_STAT_SEQ 0x02  // Report breaks as a status AND as in-band character
+                           //  sequence FFh, 01h, 10h
+#define CBK_SEQ      0x03  // Report breaks as the in-band 
+                                                  //sequence FFh, 01h, 10h ONLY.
+#define CBK_FLSH     0x04  // if this bit set also flush input data
+#define CBK_POSIX    0x08  // if this bit set report as FF,0,0 sequence
+#define CBK_SINGLE   0x10  // if this bit set with CBK_SEQ or CBK_STAT_SEQ
+                                                  //then reports single null instead of triple
+
+#define CMD_BRK_NREP (cmdSyntaxPtr)(ct27) // Disable break reporting
+
+// Specify maximum block size for received data
+//
+#define CMD_MAX_BLOCK(arg) \
+       (((cmdSyntaxPtr)(ct28))->cmd[1] = (arg),(cmdSyntaxPtr)(ct28))
+
+// -- COMMAND 29 is reserved --
+
+#define CMD_CTSFL_ENAB  (cmdSyntaxPtr)(ct30) // Enable  CTS flow control
+#define CMD_CTSFL_DSAB  (cmdSyntaxPtr)(ct31) // Disable CTS flow control
+#define CMD_RTSFL_ENAB  (cmdSyntaxPtr)(ct32) // Enable  RTS flow control
+#define CMD_RTSFL_DSAB  (cmdSyntaxPtr)(ct33) // Disable RTS flow control
+
+// Specify istrip option
+//
+#define CMD_ISTRIP_OPT(arg) \
+       (((cmdSyntaxPtr)(ct34))->cmd[1] = (arg),(cmdSyntaxPtr)(ct34))
+
+#define CIS_NOSTRIP  0  // Strip characters to character size
+#define CIS_STRIP    1  // Strip any 8-bit characters to 7 bits
+
+// Send a break of arg milliseconds
+//
+#define CMD_SEND_BRK(arg) \
+       (((cmdSyntaxPtr)(ct35))->cmd[1] = (arg),(cmdSyntaxPtr)(ct35))
+
+// Set error reporting mode
+//
+#define CMD_SET_ERROR(arg) \
+       (((cmdSyntaxPtr)(ct36))->cmd[1] = (arg),(cmdSyntaxPtr)(ct36))
+
+#define CSE_ESTAT 0  // Report error in a status packet
+#define CSE_NOREP 1  // Treat character as though it were good
+#define CSE_DROP  2  // Discard the character
+#define CSE_NULL  3  // Replace with a null
+#define CSE_MARK  4  // Replace with a 3-character sequence (as Unix)
+
+#define CSE_REPLACE  0x8       // Replace the errored character with the
+                                                       // replacement character defined here
+
+#define CSE_STAT_REPLACE   0x18        // Replace the errored character with the
+                                                               // replacement character defined here AND
+                                                               // report the error as a status packet (as in
+                                                               // CSE_ESTAT).
+
+
+// COMMAND 37, to send flow control packets, is handled only by low-level
+// library code in response to data movement and shouldn't ever be sent by the
+// user code. See i2pack.h and the body of i2lib.c for details.
+
+// Enable on-board post-processing, using options given in oflag argument.
+// Formerly, this command was automatically preceded by a CMD_OPOST_OFF command
+// because the loadware does not permit sending back-to-back CMD_OPOST_ON
+// commands without an intervening CMD_OPOST_OFF. BUT, WE LEARN 18 MAY 92, that
+// CMD_OPOST_ON and CMD_OPOST_OFF must each be at the end of a packet (or in a
+// solo packet). This means the caller must specify separately CMD_OPOST_OFF,
+// CMD_OPOST_ON(parm) when he calls i2QueueCommands(). That function will ensure
+// each gets a separate packet. Extra CMD_OPOST_OFF's are always ok.
+//
+#define CMD_OPOST_ON(oflag)   \
+       (*(USHORT *)(((cmdSyntaxPtr)(ct39))->cmd[1]) = (oflag), \
+               (cmdSyntaxPtr)(ct39))
+
+#define CMD_OPOST_OFF   (cmdSyntaxPtr)(ct40) // Disable on-board post-proc
+
+#define CMD_RESUME   (cmdSyntaxPtr)(ct41)      // Resume: behave as though an XON
+                                                                                       // were received;
+
+// Set Transmit baud rate (see command 7 for arguments)
+//
+#define CMD_SETBAUD_TX(arg) \
+       (((cmdSyntaxPtr)(ct42))->cmd[1] = (arg),(cmdSyntaxPtr)(ct42))
+
+// Set Receive baud rate (see command 7 for arguments)
+//
+#define CMD_SETBAUD_RX(arg) \
+       (((cmdSyntaxPtr)(ct43))->cmd[1] = (arg),(cmdSyntaxPtr)(ct43))
+
+// Request interrupt from board each arg milliseconds. Interrupt will specify
+// "received data", even though there may be no data present. If arg == 0,
+// disables any such interrupts.
+//
+#define CMD_PING_REQ(arg) \
+       (((cmdSyntaxPtr)(ct44))->cmd[1] = (arg),(cmdSyntaxPtr)(ct44))
+
+#define CMD_HOT_ENAB (cmdSyntaxPtr)(ct45) // Enable Hot-key checking
+#define CMD_HOT_DSAB (cmdSyntaxPtr)(ct46) // Disable Hot-key checking
+
+#if 0
+// COMMAND 47: Send Protocol info via Unix flags:
+// iflag = Unix tty t_iflag
+// cflag = Unix tty t_cflag
+// lflag = Unix tty t_lflag
+// See System V Unix/Xenix documentation for the meanings of the bit fields
+// within these flags
+//
+#define CMD_UNIX_FLAGS(iflag,cflag,lflag) i2cmdUnixFlags(iflag,cflag,lflag)
+#endif  /*  0  */
+
+#define CMD_DSRFL_ENAB  (cmdSyntaxPtr)(ct48) // Enable  DSR receiver ctrl
+#define CMD_DSRFL_DSAB  (cmdSyntaxPtr)(ct49) // Disable DSR receiver ctrl
+#define CMD_DTRFL_ENAB  (cmdSyntaxPtr)(ct50) // Enable  DTR flow control
+#define CMD_DTRFL_DSAB  (cmdSyntaxPtr)(ct51) // Disable DTR flow control
+#define CMD_BAUD_RESET  (cmdSyntaxPtr)(ct52) // Reset baudrate table
+
+// COMMAND 54: Define custom rate #1
+// rate = (short) 1/10 of the desired baud rate
+//
+#define CMD_BAUD_DEF1(rate) i2cmdBaudDef(1,rate)
+
+// COMMAND 55: Define custom rate #2
+// rate = (short) 1/10 of the desired baud rate
+//
+#define CMD_BAUD_DEF2(rate) i2cmdBaudDef(2,rate)
+
+// Pause arg hundredths of seconds. (Note, this is NOT milliseconds.)
+//
+#define CMD_PAUSE(arg) \
+       (((cmdSyntaxPtr)(ct56))->cmd[1] = (arg),(cmdSyntaxPtr)(ct56))
+
+#define CMD_SUSPEND     (cmdSyntaxPtr)(ct57) // Suspend output
+#define CMD_UNSUSPEND   (cmdSyntaxPtr)(ct58) // Un-Suspend output
+
+// Set parity-checking options
+//
+#define CMD_PARCHK(arg) \
+       (((cmdSyntaxPtr)(ct59))->cmd[1] = (arg),(cmdSyntaxPtr)(ct59))
+
+#define CPK_ENAB  0     // Enable parity checking on input
+#define CPK_DSAB  1     // Disable parity checking on input
+
+#define CMD_BMARK_REQ   (cmdSyntaxPtr)(ct60) // Bookmark request
+
+
+// Enable/Disable internal loopback mode
+//
+#define CMD_INLOOP(arg) \
+       (((cmdSyntaxPtr)(ct61))->cmd[1] = (arg),(cmdSyntaxPtr)(ct61))
+
+#define CIN_DISABLE  0  // Normal operation (default)
+#define CIN_ENABLE   1  // Internal (local) loopback
+#define CIN_REMOTE   2  // Remote loopback
+
+// Specify timeout for hotkeys: Delay will be (arg x 10) milliseconds, arg == 0
+// --> no timeout: wait forever.
+//
+#define CMD_HOT_TIME(arg) \
+       (((cmdSyntaxPtr)(ct62))->cmd[1] = (arg),(cmdSyntaxPtr)(ct62))
+
+
+// Define (outgoing) xon for receive flow control
+//
+#define CMD_DEF_OXON(arg) \
+       (((cmdSyntaxPtr)(ct63))->cmd[1] = (arg),(cmdSyntaxPtr)(ct63))
+
+// Define (outgoing) xoff for receiver flow control
+//
+#define CMD_DEF_OXOFF(arg) \
+       (((cmdSyntaxPtr)(ct64))->cmd[1] = (arg),(cmdSyntaxPtr)(ct64))
+
+// Enable/Disable RTS on transmit (1/2 duplex-style)
+//
+#define CMD_RTS_XMIT(arg) \
+       (((cmdSyntaxPtr)(ct65))->cmd[1] = (arg),(cmdSyntaxPtr)(ct65))
+
+#define CHD_DISABLE  0
+#define CHD_ENABLE   1
+
+// Set high-water-mark level (debugging use only)
+//
+#define CMD_SETHIGHWAT(arg) \
+       (((cmdSyntaxPtr)(ct66))->cmd[1] = (arg),(cmdSyntaxPtr)(ct66))
+
+// Start flushing tagged data (tag = 0-14)
+//
+#define CMD_START_SELFL(tag) \
+       (((cmdSyntaxPtr)(ct67))->cmd[1] = (tag),(cmdSyntaxPtr)(ct67))
+
+// End flushing tagged data (tag = 0-14)
+//
+#define CMD_END_SELFL(tag) \
+       (((cmdSyntaxPtr)(ct68))->cmd[1] = (tag),(cmdSyntaxPtr)(ct68))
+
+#define CMD_HWFLOW_OFF  (cmdSyntaxPtr)(ct69) // Disable HW TX flow control
+#define CMD_ODSRFL_ENAB (cmdSyntaxPtr)(ct70) // Enable DSR output f/c
+#define CMD_ODSRFL_DSAB (cmdSyntaxPtr)(ct71) // Disable DSR output f/c
+#define CMD_ODCDFL_ENAB (cmdSyntaxPtr)(ct72) // Enable DCD output f/c
+#define CMD_ODCDFL_DSAB (cmdSyntaxPtr)(ct73) // Disable DCD output f/c
+
+// Set transmit interrupt load level. Count should be an even value 2-12
+//
+#define CMD_LOADLEVEL(count) \
+       (((cmdSyntaxPtr)(ct74))->cmd[1] = (count),(cmdSyntaxPtr)(ct74))
+
+// If reporting DSS changes, map to character sequence FFh, 2, MSR
+//
+#define CMD_STATDATA(arg) \
+       (((cmdSyntaxPtr)(ct75))->cmd[1] = (arg),(cmdSyntaxPtr)(ct75))
+
+#define CSTD_DISABLE// Report DSS changes as status packets only (default)
+#define CSTD_ENABLE    // Report DSS changes as in-band data sequence as well as
+                                       // by status packet.
+
+#define CMD_BREAK_ON    (cmdSyntaxPtr)(ct76)// Set break and stop xmit
+#define CMD_BREAK_OFF   (cmdSyntaxPtr)(ct77)// End break and restart xmit
+#define CMD_GETFC       (cmdSyntaxPtr)(ct78)// Request for flow control packet
+                                                                                       // from board.
+
+// Transmit this character immediately
+//
+#define CMD_XMIT_NOW(ch) \
+       (((cmdSyntaxPtr)(ct79))->cmd[1] = (ch),(cmdSyntaxPtr)(ct79))
+
+// Set baud rate via "divisor latch"
+//
+#define CMD_DIVISOR_LATCH(which,value) \
+                       (((cmdSyntaxPtr)(ct80))->cmd[1] = (which), \
+                       *(USHORT *)(((cmdSyntaxPtr)(ct80))->cmd[2]) = (value), \
+                       (cmdSyntaxPtr)(ct80))
+
+#define CDL_RX 1       // Set receiver rate
+#define CDL_TX 2       // Set transmit rate
+                                       // (CDL_TX | CDL_RX) Set both rates
+
+// Request for special diagnostic status pkt from the board.
+//
+#define CMD_GET_STATUS (cmdSyntaxPtr)(ct81)
+
+// Request time-stamped transmit character count packet.
+//
+#define CMD_GET_TXCNT  (cmdSyntaxPtr)(ct82)
+
+// Request time-stamped receive character count packet.
+//
+#define CMD_GET_RXCNT  (cmdSyntaxPtr)(ct83)
+
+// Request for box/board I.D. packet.
+#define CMD_GET_BOXIDS (cmdSyntaxPtr)(ct84)
+
+// Enable or disable multiple channels according to bit-mapped ushorts box 1-4
+//
+#define CMD_ENAB_MULT(enable, box1, box2, box3, box4)    \
+                       (((cmdSytaxPtr)(ct85))->cmd[1] = (enable),            \
+                       *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[2]) = (box1), \
+                       *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[4]) = (box2), \
+                       *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[6]) = (box3), \
+                       *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[8]) = (box4), \
+                       (cmdSyntaxPtr)(ct85))
+
+#define CEM_DISABLE  0
+#define CEM_ENABLE   1
+
+// Enable or disable receiver or receiver interrupts (default both enabled)
+//
+#define CMD_RCV_ENABLE(ch) \
+       (((cmdSyntaxPtr)(ct86))->cmd[1] = (ch),(cmdSyntaxPtr)(ct86))
+
+#define CRE_OFF      0  // Disable the receiver
+#define CRE_ON       1  // Enable the receiver
+#define CRE_INTOFF   2  // Disable receiver interrupts (to loadware)
+#define CRE_INTON    3  // Enable receiver interrupts (to loadware)
+
+// Starts up a hardware test process, which runs transparently, and sends a
+// STAT_HWFAIL packet in case a hardware failure is detected.
+//
+#define CMD_HW_TEST  (cmdSyntaxPtr)(ct87)
+
+// Change receiver threshold and timeout value:
+// Defaults: timeout = 20mS
+// threshold count = 8 when DTRflow not in use,
+// threshold count = 5 when DTRflow in use.
+//
+#define CMD_RCV_THRESHOLD(count,ms) \
+                       (((cmdSyntaxPtr)(ct88))->cmd[1] = (count), \
+                       ((cmdSyntaxPtr)(ct88))->cmd[2] = (ms), \
+                       (cmdSyntaxPtr)(ct88))
+
+// Makes the loadware report DSS signals for this channel immediately.
+//
+#define CMD_DSS_NOW (cmdSyntaxPtr)(ct89)
+       
+// Set the receive silo parameters 
+//     timeout is ms idle wait until delivery       (~VTIME)
+//     threshold is max characters cause interrupt  (~VMIN)
+//
+#define CMD_SET_SILO(timeout,threshold) \
+                       (((cmdSyntaxPtr)(ct90))->cmd[1] = (timeout), \
+                       ((cmdSyntaxPtr)(ct90))->cmd[2]  = (threshold), \
+                       (cmdSyntaxPtr)(ct90))
+
+// Set timed break in decisecond (1/10s)
+//
+#define CMD_LBREAK(ds) \
+       (((cmdSyntaxPtr)(ct91))->cmd[1] = (ds),(cmdSyntaxPtr)(ct66))
+
+
+
+#endif // I2CMD_H
diff --git a/drivers/staging/tty/ip2/i2ellis.c b/drivers/staging/tty/ip2/i2ellis.c
new file mode 100644 (file)
index 0000000..29db44d
--- /dev/null
@@ -0,0 +1,1403 @@
+/*******************************************************************************
+*
+*   (c) 1998 by Computone Corporation
+*
+********************************************************************************
+*
+*
+*   PACKAGE:     Linux tty Device Driver for IntelliPort family of multiport
+*                serial I/O controllers.
+*
+*   DESCRIPTION: Low-level interface code for the device driver
+*                (This is included source code, not a separate compilation
+*                module.)
+*
+*******************************************************************************/
+//---------------------------------------------
+// Function declarations private to this module
+//---------------------------------------------
+// Functions called only indirectly through i2eBordStr entries.
+
+static int iiWriteBuf16(i2eBordStrPtr, unsigned char *, int);
+static int iiWriteBuf8(i2eBordStrPtr, unsigned char *, int);
+static int iiReadBuf16(i2eBordStrPtr, unsigned char *, int);
+static int iiReadBuf8(i2eBordStrPtr, unsigned char *, int);
+
+static unsigned short iiReadWord16(i2eBordStrPtr);
+static unsigned short iiReadWord8(i2eBordStrPtr);
+static void iiWriteWord16(i2eBordStrPtr, unsigned short);
+static void iiWriteWord8(i2eBordStrPtr, unsigned short);
+
+static int iiWaitForTxEmptyII(i2eBordStrPtr, int);
+static int iiWaitForTxEmptyIIEX(i2eBordStrPtr, int);
+static int iiTxMailEmptyII(i2eBordStrPtr);
+static int iiTxMailEmptyIIEX(i2eBordStrPtr);
+static int iiTrySendMailII(i2eBordStrPtr, unsigned char);
+static int iiTrySendMailIIEX(i2eBordStrPtr, unsigned char);
+
+static unsigned short iiGetMailII(i2eBordStrPtr);
+static unsigned short iiGetMailIIEX(i2eBordStrPtr);
+
+static void iiEnableMailIrqII(i2eBordStrPtr);
+static void iiEnableMailIrqIIEX(i2eBordStrPtr);
+static void iiWriteMaskII(i2eBordStrPtr, unsigned char);
+static void iiWriteMaskIIEX(i2eBordStrPtr, unsigned char);
+
+static void ii2Nop(void);
+
+//***************
+//* Static Data *
+//***************
+
+static int ii2Safe;         // Safe I/O address for delay routine
+
+static int iiDelayed;  // Set when the iiResetDelay function is
+                                                       // called. Cleared when ANY board is reset.
+static DEFINE_RWLOCK(Dl_spinlock);
+
+//********
+//* Code *
+//********
+
+//=======================================================
+// Initialization Routines
+//
+// iiSetAddress
+// iiReset
+// iiResetDelay
+// iiInitialize
+//=======================================================
+
+//******************************************************************************
+// Function:   iiSetAddress(pB, address, delay)
+// Parameters: pB      - pointer to the board structure
+//             address - the purported I/O address of the board
+//             delay   - pointer to the 1-ms delay function to use
+//                       in this and any future operations to this board
+//
+// Returns:    True if everything appears copacetic.
+//             False if there is any error: the pB->i2eError field has the error
+//
+// Description:
+//
+// This routine (roughly) checks for address validity, sets the i2eValid OK and
+// sets the state to II_STATE_COLD which means that we haven't even sent a reset
+// yet.
+//
+//******************************************************************************
+static int
+iiSetAddress( i2eBordStrPtr pB, int address, delayFunc_t delay )
+{
+       // Should any failure occur before init is finished...
+       pB->i2eValid = I2E_INCOMPLETE;
+
+       // Cannot check upper limit except extremely: Might be microchannel
+       // Address must be on an 8-byte boundary
+
+       if ((unsigned int)address <= 0x100
+               || (unsigned int)address >= 0xfff8
+               || (address & 0x7)
+               )
+       {
+               I2_COMPLETE(pB, I2EE_BADADDR);
+       }
+
+       // Initialize accelerators
+       pB->i2eBase    = address;
+       pB->i2eData    = address + FIFO_DATA;
+       pB->i2eStatus  = address + FIFO_STATUS;
+       pB->i2ePointer = address + FIFO_PTR;
+       pB->i2eXMail   = address + FIFO_MAIL;
+       pB->i2eXMask   = address + FIFO_MASK;
+
+       // Initialize i/o address for ii2DelayIO
+       ii2Safe = address + FIFO_NOP;
+
+       // Initialize the delay routine
+       pB->i2eDelay = ((delay != (delayFunc_t)NULL) ? delay : (delayFunc_t)ii2Nop);
+
+       pB->i2eValid = I2E_MAGIC;
+       pB->i2eState = II_STATE_COLD;
+
+       I2_COMPLETE(pB, I2EE_GOOD);
+}
+
+//******************************************************************************
+// Function:   iiReset(pB)
+// Parameters: pB - pointer to the board structure
+//
+// Returns:    True if everything appears copacetic.
+//             False if there is any error: the pB->i2eError field has the error
+//
+// Description:
+//
+// Attempts to reset the board (see also i2hw.h). Normally, we would use this to
+// reset a board immediately after iiSetAddress(), but it is valid to reset a
+// board from any state, say, in order to change or re-load loadware. (Under
+// such circumstances, no reason to re-run iiSetAddress(), which is why it is a
+// separate routine and not included in this routine.
+//
+//******************************************************************************
+static int
+iiReset(i2eBordStrPtr pB)
+{
+       // Magic number should be set, else even the address is suspect
+       if (pB->i2eValid != I2E_MAGIC)
+       {
+               I2_COMPLETE(pB, I2EE_BADMAGIC);
+       }
+
+       outb(0, pB->i2eBase + FIFO_RESET);  /* Any data will do */
+       iiDelay(pB, 50);                    // Pause between resets
+       outb(0, pB->i2eBase + FIFO_RESET);  /* Second reset */
+
+       // We must wait before even attempting to read anything from the FIFO: the
+       // board's P.O.S.T may actually attempt to read and write its end of the
+       // FIFO in order to check flags, loop back (where supported), etc. On
+       // completion of this testing it would reset the FIFO, and on completion
+       // of all // P.O.S.T., write the message. We must not mistake data which
+       // might have been sent for testing as part of the reset message. To
+       // better utilize time, say, when resetting several boards, we allow the
+       // delay to be performed externally; in this way the caller can reset 
+       // several boards, delay a single time, then call the initialization
+       // routine for all.
+
+       pB->i2eState = II_STATE_RESET;
+
+       iiDelayed = 0;  // i.e., the delay routine hasn't been called since the most
+                                       // recent reset.
+
+       // Ensure anything which would have been of use to standard loadware is
+       // blanked out, since board has now forgotten everything!.
+
+       pB->i2eUsingIrq = I2_IRQ_UNDEFINED; /* to not use an interrupt so far */
+       pB->i2eWaitingForEmptyFifo = 0;
+       pB->i2eOutMailWaiting = 0;
+       pB->i2eChannelPtr = NULL;
+       pB->i2eChannelCnt = 0;
+
+       pB->i2eLeadoffWord[0] = 0;
+       pB->i2eFifoInInts = 0;
+       pB->i2eFifoOutInts = 0;
+       pB->i2eFatalTrap = NULL;
+       pB->i2eFatal = 0;
+
+       I2_COMPLETE(pB, I2EE_GOOD);
+}
+
+//******************************************************************************
+// Function:   iiResetDelay(pB)
+// Parameters: pB - pointer to the board structure
+//
+// Returns:    True if everything appears copacetic.
+//             False if there is any error: the pB->i2eError field has the error
+//
+// Description:
+//
+// Using the delay defined in board structure, waits two seconds (for board to
+// reset).
+//
+//******************************************************************************
+static int
+iiResetDelay(i2eBordStrPtr pB)
+{
+       if (pB->i2eValid != I2E_MAGIC) {
+               I2_COMPLETE(pB, I2EE_BADMAGIC);
+       }
+       if (pB->i2eState != II_STATE_RESET) {
+               I2_COMPLETE(pB, I2EE_BADSTATE);
+       }
+       iiDelay(pB,2000);       /* Now we wait for two seconds. */
+       iiDelayed = 1;          /* Delay has been called: ok to initialize */
+       I2_COMPLETE(pB, I2EE_GOOD);
+}
+
+//******************************************************************************
+// Function:   iiInitialize(pB)
+// Parameters: pB - pointer to the board structure
+//
+// Returns:    True if everything appears copacetic.
+//             False if there is any error: the pB->i2eError field has the error
+//
+// Description:
+//
+// Attempts to read the Power-on reset message. Initializes any remaining fields
+// in the pB structure.
+//
+// This should be called as the third step of a process beginning with
+// iiReset(), then iiResetDelay(). This routine checks to see that the structure
+// is "valid" and in the reset state, also confirms that the delay routine has
+// been called since the latest reset (to any board! overly strong!).
+//
+//******************************************************************************
+static int
+iiInitialize(i2eBordStrPtr pB)
+{
+       int itemp;
+       unsigned char c;
+       unsigned short utemp;
+       unsigned int ilimit;
+
+       if (pB->i2eValid != I2E_MAGIC)
+       {
+               I2_COMPLETE(pB, I2EE_BADMAGIC);
+       }
+
+       if (pB->i2eState != II_STATE_RESET || !iiDelayed)
+       {
+               I2_COMPLETE(pB, I2EE_BADSTATE);
+       }
+
+       // In case there is a failure short of our completely reading the power-up
+       // message.
+       pB->i2eValid = I2E_INCOMPLETE;
+
+
+       // Now attempt to read the message.
+
+       for (itemp = 0; itemp < sizeof(porStr); itemp++)
+       {
+               // We expect the entire message is ready.
+               if (!I2_HAS_INPUT(pB)) {
+                       pB->i2ePomSize = itemp;
+                       I2_COMPLETE(pB, I2EE_PORM_SHORT);
+               }
+
+               pB->i2ePom.c[itemp] = c = inb(pB->i2eData);
+
+               // We check the magic numbers as soon as they are supposed to be read
+               // (rather than after) to minimize effect of reading something we
+               // already suspect can't be "us".
+               if (  (itemp == POR_1_INDEX && c != POR_MAGIC_1) ||
+                               (itemp == POR_2_INDEX && c != POR_MAGIC_2))
+               {
+                       pB->i2ePomSize = itemp+1;
+                       I2_COMPLETE(pB, I2EE_BADMAGIC);
+               }
+       }
+
+       pB->i2ePomSize = itemp;
+
+       // Ensure that this was all the data...
+       if (I2_HAS_INPUT(pB))
+               I2_COMPLETE(pB, I2EE_PORM_LONG);
+
+       // For now, we'll fail to initialize if P.O.S.T reports bad chip mapper:
+       // Implying we will not be able to download any code either:  That's ok: the
+       // condition is pretty explicit.
+       if (pB->i2ePom.e.porDiag1 & POR_BAD_MAPPER)
+       {
+               I2_COMPLETE(pB, I2EE_POSTERR);
+       }
+
+       // Determine anything which must be done differently depending on the family
+       // of boards!
+       switch (pB->i2ePom.e.porID & POR_ID_FAMILY)
+       {
+       case POR_ID_FII:  // IntelliPort-II
+
+               pB->i2eFifoStyle   = FIFO_II;
+               pB->i2eFifoSize    = 512;     // 512 bytes, always
+               pB->i2eDataWidth16 = false;
+
+               pB->i2eMaxIrq = 15;     // Because board cannot tell us it is in an 8-bit
+                                                       // slot, we do allow it to be done (documentation!)
+
+               pB->i2eGoodMap[1] =
+               pB->i2eGoodMap[2] =
+               pB->i2eGoodMap[3] =
+               pB->i2eChannelMap[1] =
+               pB->i2eChannelMap[2] =
+               pB->i2eChannelMap[3] = 0;
+
+               switch (pB->i2ePom.e.porID & POR_ID_SIZE)
+               {
+               case POR_ID_II_4:
+                       pB->i2eGoodMap[0] =
+                       pB->i2eChannelMap[0] = 0x0f;  // four-port
+
+                       // Since porPorts1 is based on the Hardware ID register, the numbers
+                       // should always be consistent for IntelliPort-II.  Ditto below...
+                       if (pB->i2ePom.e.porPorts1 != 4)
+                       {
+                               I2_COMPLETE(pB, I2EE_INCONSIST);
+                       }
+                       break;
+
+               case POR_ID_II_8:
+               case POR_ID_II_8R:
+                       pB->i2eGoodMap[0] =
+                       pB->i2eChannelMap[0] = 0xff;  // Eight port
+                       if (pB->i2ePom.e.porPorts1 != 8)
+                       {
+                               I2_COMPLETE(pB, I2EE_INCONSIST);
+                       }
+                       break;
+
+               case POR_ID_II_6:
+                       pB->i2eGoodMap[0] =
+                       pB->i2eChannelMap[0] = 0x3f;  // Six Port
+                       if (pB->i2ePom.e.porPorts1 != 6)
+                       {
+                               I2_COMPLETE(pB, I2EE_INCONSIST);
+                       }
+                       break;
+               }
+
+               // Fix up the "good channel list based on any errors reported.
+               if (pB->i2ePom.e.porDiag1 & POR_BAD_UART1)
+               {
+                       pB->i2eGoodMap[0] &= ~0x0f;
+               }
+
+               if (pB->i2ePom.e.porDiag1 & POR_BAD_UART2)
+               {
+                       pB->i2eGoodMap[0] &= ~0xf0;
+               }
+
+               break;   // POR_ID_FII case
+
+       case POR_ID_FIIEX:   // IntelliPort-IIEX
+
+               pB->i2eFifoStyle = FIFO_IIEX;
+
+               itemp = pB->i2ePom.e.porFifoSize;
+
+               // Implicit assumption that fifo would not grow beyond 32k, 
+               // nor would ever be less than 256.
+
+               if (itemp < 8 || itemp > 15)
+               {
+                       I2_COMPLETE(pB, I2EE_INCONSIST);
+               }
+               pB->i2eFifoSize = (1 << itemp);
+
+               // These are based on what P.O.S.T thinks should be there, based on
+               // box ID registers
+               ilimit = pB->i2ePom.e.porNumBoxes;
+               if (ilimit > ABS_MAX_BOXES)
+               {
+                       ilimit = ABS_MAX_BOXES;
+               }
+
+               // For as many boxes as EXIST, gives the type of box.
+               // Added 8/6/93: check for the ISA-4 (asic) which looks like an
+               // expandable but for whom "8 or 16?" is not the right question.
+
+               utemp = pB->i2ePom.e.porFlags;
+               if (utemp & POR_CEX4)
+               {
+                       pB->i2eChannelMap[0] = 0x000f;
+               } else {
+                       utemp &= POR_BOXES;
+                       for (itemp = 0; itemp < ilimit; itemp++)
+                       {
+                               pB->i2eChannelMap[itemp] = 
+                                       ((utemp & POR_BOX_16) ? 0xffff : 0x00ff);
+                               utemp >>= 1;
+                       }
+               }
+
+               // These are based on what P.O.S.T actually found.
+
+               utemp = (pB->i2ePom.e.porPorts2 << 8) + pB->i2ePom.e.porPorts1;
+
+               for (itemp = 0; itemp < ilimit; itemp++)
+               {
+                       pB->i2eGoodMap[itemp] = 0;
+                       if (utemp & 1) pB->i2eGoodMap[itemp] |= 0x000f;
+                       if (utemp & 2) pB->i2eGoodMap[itemp] |= 0x00f0;
+                       if (utemp & 4) pB->i2eGoodMap[itemp] |= 0x0f00;
+                       if (utemp & 8) pB->i2eGoodMap[itemp] |= 0xf000;
+                       utemp >>= 4;
+               }
+
+               // Now determine whether we should transfer in 8 or 16-bit mode.
+               switch (pB->i2ePom.e.porBus & (POR_BUS_SLOT16 | POR_BUS_DIP16) )
+               {
+               case POR_BUS_SLOT16 | POR_BUS_DIP16:
+                       pB->i2eDataWidth16 = true;
+                       pB->i2eMaxIrq = 15;
+                       break;
+
+               case POR_BUS_SLOT16:
+                       pB->i2eDataWidth16 = false;
+                       pB->i2eMaxIrq = 15;
+                       break;
+
+               case 0:
+               case POR_BUS_DIP16:     // In an 8-bit slot, DIP switch don't care.
+               default:
+                       pB->i2eDataWidth16 = false;
+                       pB->i2eMaxIrq = 7;
+                       break;
+               }
+               break;   // POR_ID_FIIEX case
+
+       default:    // Unknown type of board
+               I2_COMPLETE(pB, I2EE_BAD_FAMILY);
+               break;
+       }  // End the switch based on family
+
+       // Temporarily, claim there is no room in the outbound fifo. 
+       // We will maintain this whenever we check for an empty outbound FIFO.
+       pB->i2eFifoRemains = 0;
+
+       // Now, based on the bus type, should we expect to be able to re-configure
+       // interrupts (say, for testing purposes).
+       switch (pB->i2ePom.e.porBus & POR_BUS_TYPE)
+       {
+       case POR_BUS_T_ISA:
+       case POR_BUS_T_UNK:  // If the type of bus is undeclared, assume ok.
+       case POR_BUS_T_MCA:
+       case POR_BUS_T_EISA:
+               break;
+       default:
+               I2_COMPLETE(pB, I2EE_BADBUS);
+       }
+
+       if (pB->i2eDataWidth16)
+       {
+               pB->i2eWriteBuf  = iiWriteBuf16;
+               pB->i2eReadBuf   = iiReadBuf16;
+               pB->i2eWriteWord = iiWriteWord16;
+               pB->i2eReadWord  = iiReadWord16;
+       } else {
+               pB->i2eWriteBuf  = iiWriteBuf8;
+               pB->i2eReadBuf   = iiReadBuf8;
+               pB->i2eWriteWord = iiWriteWord8;
+               pB->i2eReadWord  = iiReadWord8;
+       }
+
+       switch(pB->i2eFifoStyle)
+       {
+       case FIFO_II:
+               pB->i2eWaitForTxEmpty = iiWaitForTxEmptyII;
+               pB->i2eTxMailEmpty    = iiTxMailEmptyII;
+               pB->i2eTrySendMail    = iiTrySendMailII;
+               pB->i2eGetMail        = iiGetMailII;
+               pB->i2eEnableMailIrq  = iiEnableMailIrqII;
+               pB->i2eWriteMask      = iiWriteMaskII;
+
+               break;
+
+       case FIFO_IIEX:
+               pB->i2eWaitForTxEmpty = iiWaitForTxEmptyIIEX;
+               pB->i2eTxMailEmpty    = iiTxMailEmptyIIEX;
+               pB->i2eTrySendMail    = iiTrySendMailIIEX;
+               pB->i2eGetMail        = iiGetMailIIEX;
+               pB->i2eEnableMailIrq  = iiEnableMailIrqIIEX;
+               pB->i2eWriteMask      = iiWriteMaskIIEX;
+
+               break;
+
+       default:
+               I2_COMPLETE(pB, I2EE_INCONSIST);
+       }
+
+       // Initialize state information.
+       pB->i2eState = II_STATE_READY;   // Ready to load loadware.
+
+       // Some Final cleanup:
+       // For some boards, the bootstrap firmware may perform some sort of test
+       // resulting in a stray character pending in the incoming mailbox. If one is
+       // there, it should be read and discarded, especially since for the standard
+       // firmware, it's the mailbox that interrupts the host.
+
+       pB->i2eStartMail = iiGetMail(pB);
+
+       // Throw it away and clear the mailbox structure element
+       pB->i2eStartMail = NO_MAIL_HERE;
+
+       // Everything is ok now, return with good status/
+
+       pB->i2eValid = I2E_MAGIC;
+       I2_COMPLETE(pB, I2EE_GOOD);
+}
+
+//******************************************************************************
+// Function:   ii2DelayTimer(mseconds)
+// Parameters: mseconds - number of milliseconds to delay
+//
+// Returns:    Nothing
+//
+// Description:
+//
+// This routine delays for approximately mseconds milliseconds and is intended
+// to be called indirectly through i2Delay field in i2eBordStr. It uses the
+// Linux timer_list mechanism.
+//
+// The Linux timers use a unit called "jiffies" which are 10mS in the Intel
+// architecture. This function rounds the delay period up to the next "jiffy".
+// In the Alpha architecture the "jiffy" is 1mS, but this driver is not intended
+// for Alpha platforms at this time.
+//
+//******************************************************************************
+static void
+ii2DelayTimer(unsigned int mseconds)
+{
+       msleep_interruptible(mseconds);
+}
+
+#if 0
+//static void ii2DelayIO(unsigned int);
+//******************************************************************************
+// !!! Not Used, this is DOS crap, some of you young folks may be interested in
+//     in how things were done in the stone age of caculating machines       !!!
+// Function:   ii2DelayIO(mseconds)
+// Parameters: mseconds - number of milliseconds to delay
+//
+// Returns:    Nothing
+//
+// Description:
+//
+// This routine delays for approximately mseconds milliseconds and is intended
+// to be called indirectly through i2Delay field in i2eBordStr. It is intended
+// for use where a clock-based function is impossible: for example, DOS drivers.
+//
+// This function uses the IN instruction to place bounds on the timing and
+// assumes that ii2Safe has been set. This is because I/O instructions are not
+// subject to caching and will therefore take a certain minimum time. To ensure
+// the delay is at least long enough on fast machines, it is based on some
+// fastest-case calculations.  On slower machines this may cause VERY long
+// delays. (3 x fastest case). In the fastest case, everything is cached except
+// the I/O instruction itself.
+//
+// Timing calculations:
+// The fastest bus speed for I/O operations is likely to be 10 MHz. The I/O
+// operation in question is a byte operation to an odd address. For 8-bit
+// operations, the architecture generally enforces two wait states. At 10 MHz, a
+// single cycle time is 100nS. A read operation at two wait states takes 6
+// cycles for a total time of 600nS. Therefore approximately 1666 iterations
+// would be required to generate a single millisecond delay. The worst
+// (reasonable) case would be an 8MHz system with no cacheing. In this case, the
+// I/O instruction would take 125nS x 6 cyles = 750 nS. More importantly, code
+// fetch of other instructions in the loop would take time (zero wait states,
+// however) and would be hard to estimate. This is minimized by using in-line
+// assembler for the in inner loop of IN instructions. This consists of just a
+// few bytes. So we'll guess about four code fetches per loop. Each code fetch
+// should take four cycles, so we have 125nS * 8 = 1000nS. Worst case then is
+// that what should have taken 1 mS takes instead 1666 * (1750) = 2.9 mS.
+//
+// So much for theoretical timings: results using 1666 value on some actual
+// machines:
+// IBM      286      6MHz     3.15 mS
+// Zenith   386      33MHz    2.45 mS
+// (brandX) 386      33MHz    1.90 mS  (has cache)
+// (brandY) 486      33MHz    2.35 mS
+// NCR      486      ??       1.65 mS (microchannel)
+//
+// For most machines, it is probably safe to scale this number back (remember,
+// for robust operation use an actual timed delay if possible), so we are using
+// a value of 1190. This yields 1.17 mS for the fastest machine in our sample,
+// 1.75 mS for typical 386 machines, and 2.25 mS the absolute slowest machine.
+//
+// 1/29/93:
+// The above timings are too slow. Actual cycle times might be faster. ISA cycle
+// times could approach 500 nS, and ...
+// The IBM model 77 being microchannel has no wait states for 8-bit reads and
+// seems to be accessing the I/O at 440 nS per access (from start of one to
+// start of next). This would imply we need 1000/.440 = 2272 iterations to
+// guarantee we are fast enough. In actual testing, we see that 2 * 1190 are in
+// fact enough. For diagnostics, we keep the level at 1190, but developers note
+// this needs tuning.
+//
+// Safe assumption:  2270 i/o reads = 1 millisecond
+//
+//******************************************************************************
+
+
+static int ii2DelValue = 1190;  // See timing calculations below
+                                               // 1666 for fastest theoretical machine
+                                               // 1190 safe for most fast 386 machines
+                                               // 1000 for fastest machine tested here
+                                               //  540 (sic) for AT286/6Mhz
+static void
+ii2DelayIO(unsigned int mseconds)
+{
+       if (!ii2Safe) 
+               return;   /* Do nothing if this variable uninitialized */
+
+       while(mseconds--) {
+               int i = ii2DelValue;
+               while ( i-- ) {
+                       inb(ii2Safe);
+               }
+       }
+}
+#endif 
+
+//******************************************************************************
+// Function:   ii2Nop()
+// Parameters: None
+//
+// Returns:    Nothing
+//
+// Description:
+//
+// iiInitialize will set i2eDelay to this if the delay parameter is NULL. This
+// saves checking for a NULL pointer at every call.
+//******************************************************************************
+static void
+ii2Nop(void)
+{
+       return; // no mystery here
+}
+
+//=======================================================
+// Routines which are available in 8/16-bit versions, or
+// in different fifo styles. These are ALL called
+// indirectly through the board structure.
+//=======================================================
+
+//******************************************************************************
+// Function:   iiWriteBuf16(pB, address, count)
+// Parameters: pB      - pointer to board structure
+//             address - address of data to write
+//             count   - number of data bytes to write
+//
+// Returns:    True if everything appears copacetic.
+//             False if there is any error: the pB->i2eError field has the error
+//
+// Description:
+//
+// Writes 'count' bytes from 'address' to the data fifo specified by the board
+// structure pointer pB. Should count happen to be odd, an extra pad byte is
+// sent (identity unknown...). Uses 16-bit (word) operations. Is called
+// indirectly through pB->i2eWriteBuf.
+//
+//******************************************************************************
+static int
+iiWriteBuf16(i2eBordStrPtr pB, unsigned char *address, int count)
+{
+       // Rudimentary sanity checking here.
+       if (pB->i2eValid != I2E_MAGIC)
+               I2_COMPLETE(pB, I2EE_INVALID);
+
+       I2_OUTSW(pB->i2eData, address, count);
+
+       I2_COMPLETE(pB, I2EE_GOOD);
+}
+
+//******************************************************************************
+// Function:   iiWriteBuf8(pB, address, count)
+// Parameters: pB      - pointer to board structure
+//             address - address of data to write
+//             count   - number of data bytes to write
+//
+// Returns:    True if everything appears copacetic.
+//             False if there is any error: the pB->i2eError field has the error
+//
+// Description:
+//
+// Writes 'count' bytes from 'address' to the data fifo specified by the board
+// structure pointer pB. Should count happen to be odd, an extra pad byte is
+// sent (identity unknown...). This is to be consistent with the 16-bit version.
+// Uses 8-bit (byte) operations. Is called indirectly through pB->i2eWriteBuf.
+//
+//******************************************************************************
+static int
+iiWriteBuf8(i2eBordStrPtr pB, unsigned char *address, int count)
+{
+       /* Rudimentary sanity checking here */
+       if (pB->i2eValid != I2E_MAGIC)
+               I2_COMPLETE(pB, I2EE_INVALID);
+
+       I2_OUTSB(pB->i2eData, address, count);
+
+       I2_COMPLETE(pB, I2EE_GOOD);
+}
+
+//******************************************************************************
+// Function:   iiReadBuf16(pB, address, count)
+// Parameters: pB      - pointer to board structure
+//             address - address to put data read
+//             count   - number of data bytes to read
+//
+// Returns:    True if everything appears copacetic.
+//             False if there is any error: the pB->i2eError field has the error
+//
+// Description:
+//
+// Reads 'count' bytes into 'address' from the data fifo specified by the board
+// structure pointer pB. Should count happen to be odd, an extra pad byte is
+// received (identity unknown...). Uses 16-bit (word) operations. Is called
+// indirectly through pB->i2eReadBuf.
+//
+//******************************************************************************
+static int
+iiReadBuf16(i2eBordStrPtr pB, unsigned char *address, int count)
+{
+       // Rudimentary sanity checking here.
+       if (pB->i2eValid != I2E_MAGIC)
+               I2_COMPLETE(pB, I2EE_INVALID);
+
+       I2_INSW(pB->i2eData, address, count);
+
+       I2_COMPLETE(pB, I2EE_GOOD);
+}
+
+//******************************************************************************
+// Function:   iiReadBuf8(pB, address, count)
+// Parameters: pB      - pointer to board structure
+//             address - address to put data read
+//             count   - number of data bytes to read
+//
+// Returns:    True if everything appears copacetic.
+//             False if there is any error: the pB->i2eError field has the error
+//
+// Description:
+//
+// Reads 'count' bytes into 'address' from the data fifo specified by the board
+// structure pointer pB. Should count happen to be odd, an extra pad byte is
+// received (identity unknown...). This to match the 16-bit behaviour. Uses
+// 8-bit (byte) operations. Is called indirectly through pB->i2eReadBuf.
+//
+//******************************************************************************
+static int
+iiReadBuf8(i2eBordStrPtr pB, unsigned char *address, int count)
+{
+       // Rudimentary sanity checking here.
+       if (pB->i2eValid != I2E_MAGIC)
+               I2_COMPLETE(pB, I2EE_INVALID);
+
+       I2_INSB(pB->i2eData, address, count);
+
+       I2_COMPLETE(pB, I2EE_GOOD);
+}
+
+//******************************************************************************
+// Function:   iiReadWord16(pB)
+// Parameters: pB      - pointer to board structure
+//
+// Returns:    True if everything appears copacetic.
+//             False if there is any error: the pB->i2eError field has the error
+//
+// Description:
+//
+// Returns the word read from the data fifo specified by the board-structure
+// pointer pB. Uses a 16-bit operation. Is called indirectly through
+// pB->i2eReadWord.
+//
+//******************************************************************************
+static unsigned short
+iiReadWord16(i2eBordStrPtr pB)
+{
+       return inw(pB->i2eData);
+}
+
+//******************************************************************************
+// Function:   iiReadWord8(pB)
+// Parameters: pB      - pointer to board structure
+//
+// Returns:    True if everything appears copacetic.
+//             False if there is any error: the pB->i2eError field has the error
+//
+// Description:
+//
+// Returns the word read from the data fifo specified by the board-structure
+// pointer pB. Uses two 8-bit operations. Bytes are assumed to be LSB first. Is
+// called indirectly through pB->i2eReadWord.
+//
+//******************************************************************************
+static unsigned short
+iiReadWord8(i2eBordStrPtr pB)
+{
+       unsigned short urs;
+
+       urs = inb(pB->i2eData);
+
+       return (inb(pB->i2eData) << 8) | urs;
+}
+
+//******************************************************************************
+// Function:   iiWriteWord16(pB, value)
+// Parameters: pB    - pointer to board structure
+//             value - data to write
+//
+// Returns:    True if everything appears copacetic.
+//             False if there is any error: the pB->i2eError field has the error
+//
+// Description:
+//
+// Writes the word 'value' to the data fifo specified by the board-structure
+// pointer pB. Uses 16-bit operation. Is called indirectly through
+// pB->i2eWriteWord.
+//
+//******************************************************************************
+static void
+iiWriteWord16(i2eBordStrPtr pB, unsigned short value)
+{
+       outw((int)value, pB->i2eData);
+}
+
+//******************************************************************************
+// Function:   iiWriteWord8(pB, value)
+// Parameters: pB    - pointer to board structure
+//             value - data to write
+//
+// Returns:    True if everything appears copacetic.
+//             False if there is any error: the pB->i2eError field has the error
+//
+// Description:
+//
+// Writes the word 'value' to the data fifo specified by the board-structure
+// pointer pB. Uses two 8-bit operations (writes LSB first). Is called
+// indirectly through pB->i2eWriteWord.
+//
+//******************************************************************************
+static void
+iiWriteWord8(i2eBordStrPtr pB, unsigned short value)
+{
+       outb((char)value, pB->i2eData);
+       outb((char)(value >> 8), pB->i2eData);
+}
+
+//******************************************************************************
+// Function:   iiWaitForTxEmptyII(pB, mSdelay)
+// Parameters: pB      - pointer to board structure
+//             mSdelay - period to wait before returning
+//
+// Returns:    True if the FIFO is empty.
+//             False if it not empty in the required time: the pB->i2eError
+//             field has the error.
+//
+// Description:
+//
+// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if
+// not empty by the required time, returns false and error in pB->i2eError,
+// otherwise returns true.
+//
+// mSdelay == 0 is taken to mean must be empty on the first test.
+//
+// This version operates on IntelliPort-II - style FIFO's
+//
+// Note this routine is organized so that if status is ok there is no delay at
+// all called either before or after the test.  Is called indirectly through
+// pB->i2eWaitForTxEmpty.
+//
+//******************************************************************************
+static int
+iiWaitForTxEmptyII(i2eBordStrPtr pB, int mSdelay)
+{
+       unsigned long   flags;
+       int itemp;
+
+       for (;;)
+       {
+               // This routine hinges on being able to see the "other" status register
+               // (as seen by the local processor).  His incoming fifo is our outgoing
+               // FIFO.
+               //
+               // By the nature of this routine, you would be using this as part of a
+               // larger atomic context: i.e., you would use this routine to ensure the
+               // fifo empty, then act on this information. Between these two halves, 
+               // you will generally not want to service interrupts or in any way 
+               // disrupt the assumptions implicit in the larger context.
+               //
+               // Even worse, however, this routine "shifts" the status register to 
+               // point to the local status register which is not the usual situation.
+               // Therefore for extra safety, we force the critical section to be
+               // completely atomic, and pick up after ourselves before allowing any
+               // interrupts of any kind.
+
+
+               write_lock_irqsave(&Dl_spinlock, flags);
+               outb(SEL_COMMAND, pB->i2ePointer);
+               outb(SEL_CMD_SH, pB->i2ePointer);
+
+               itemp = inb(pB->i2eStatus);
+
+               outb(SEL_COMMAND, pB->i2ePointer);
+               outb(SEL_CMD_UNSH, pB->i2ePointer);
+
+               if (itemp & ST_IN_EMPTY)
+               {
+                       I2_UPDATE_FIFO_ROOM(pB);
+                       write_unlock_irqrestore(&Dl_spinlock, flags);
+                       I2_COMPLETE(pB, I2EE_GOOD);
+               }
+
+               write_unlock_irqrestore(&Dl_spinlock, flags);
+
+               if (mSdelay-- == 0)
+                       break;
+
+               iiDelay(pB, 1);      /* 1 mS granularity on checking condition */
+       }
+       I2_COMPLETE(pB, I2EE_TXE_TIME);
+}
+
+//******************************************************************************
+// Function:   iiWaitForTxEmptyIIEX(pB, mSdelay)
+// Parameters: pB      - pointer to board structure
+//             mSdelay - period to wait before returning
+//
+// Returns:    True if the FIFO is empty.
+//             False if it not empty in the required time: the pB->i2eError
+//             field has the error.
+//
+// Description:
+//
+// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if
+// not empty by the required time, returns false and error in pB->i2eError,
+// otherwise returns true.
+//
+// mSdelay == 0 is taken to mean must be empty on the first test.
+//
+// This version operates on IntelliPort-IIEX - style FIFO's
+//
+// Note this routine is organized so that if status is ok there is no delay at
+// all called either before or after the test.  Is called indirectly through
+// pB->i2eWaitForTxEmpty.
+//
+//******************************************************************************
+static int
+iiWaitForTxEmptyIIEX(i2eBordStrPtr pB, int mSdelay)
+{
+       unsigned long   flags;
+
+       for (;;)
+       {
+               // By the nature of this routine, you would be using this as part of a
+               // larger atomic context: i.e., you would use this routine to ensure the
+               // fifo empty, then act on this information. Between these two halves,
+               // you will generally not want to service interrupts or in any way
+               // disrupt the assumptions implicit in the larger context.
+
+               write_lock_irqsave(&Dl_spinlock, flags);
+
+               if (inb(pB->i2eStatus) & STE_OUT_MT) {
+                       I2_UPDATE_FIFO_ROOM(pB);
+                       write_unlock_irqrestore(&Dl_spinlock, flags);
+                       I2_COMPLETE(pB, I2EE_GOOD);
+               }
+               write_unlock_irqrestore(&Dl_spinlock, flags);
+
+               if (mSdelay-- == 0)
+                       break;
+
+               iiDelay(pB, 1);      // 1 mS granularity on checking condition
+       }
+       I2_COMPLETE(pB, I2EE_TXE_TIME);
+}
+
+//******************************************************************************
+// Function:   iiTxMailEmptyII(pB)
+// Parameters: pB      - pointer to board structure
+//
+// Returns:    True if the transmit mailbox is empty.
+//             False if it not empty.
+//
+// Description:
+//
+// Returns true or false according to whether the transmit mailbox is empty (and
+// therefore able to accept more mail)
+//
+// This version operates on IntelliPort-II - style FIFO's
+//
+//******************************************************************************
+static int
+iiTxMailEmptyII(i2eBordStrPtr pB)
+{
+       int port = pB->i2ePointer;
+       outb(SEL_OUTMAIL, port);
+       return inb(port) == 0;
+}
+
+//******************************************************************************
+// Function:   iiTxMailEmptyIIEX(pB)
+// Parameters: pB      - pointer to board structure
+//
+// Returns:    True if the transmit mailbox is empty.
+//             False if it not empty.
+//
+// Description:
+//
+// Returns true or false according to whether the transmit mailbox is empty (and
+// therefore able to accept more mail)
+//
+// This version operates on IntelliPort-IIEX - style FIFO's
+//
+//******************************************************************************
+static int
+iiTxMailEmptyIIEX(i2eBordStrPtr pB)
+{
+       return !(inb(pB->i2eStatus) & STE_OUT_MAIL);
+}
+
+//******************************************************************************
+// Function:   iiTrySendMailII(pB,mail)
+// Parameters: pB   - pointer to board structure
+//             mail - value to write to mailbox
+//
+// Returns:    True if the transmit mailbox is empty, and mail is sent.
+//             False if it not empty.
+//
+// Description:
+//
+// If outgoing mailbox is empty, sends mail and returns true. If outgoing
+// mailbox is not empty, returns false.
+//
+// This version operates on IntelliPort-II - style FIFO's
+//
+//******************************************************************************
+static int
+iiTrySendMailII(i2eBordStrPtr pB, unsigned char mail)
+{
+       int port = pB->i2ePointer;
+
+       outb(SEL_OUTMAIL, port);
+       if (inb(port) == 0) {
+               outb(SEL_OUTMAIL, port);
+               outb(mail, port);
+               return 1;
+       }
+       return 0;
+}
+
+//******************************************************************************
+// Function:   iiTrySendMailIIEX(pB,mail)
+// Parameters: pB   - pointer to board structure
+//             mail - value to write to mailbox
+//
+// Returns:    True if the transmit mailbox is empty, and mail is sent.
+//             False if it not empty.
+//
+// Description:
+//
+// If outgoing mailbox is empty, sends mail and returns true. If outgoing
+// mailbox is not empty, returns false.
+//
+// This version operates on IntelliPort-IIEX - style FIFO's
+//
+//******************************************************************************
+static int
+iiTrySendMailIIEX(i2eBordStrPtr pB, unsigned char mail)
+{
+       if (inb(pB->i2eStatus) & STE_OUT_MAIL)
+               return 0;
+       outb(mail, pB->i2eXMail);
+       return 1;
+}
+
+//******************************************************************************
+// Function:   iiGetMailII(pB,mail)
+// Parameters: pB   - pointer to board structure
+//
+// Returns:    Mailbox data or NO_MAIL_HERE.
+//
+// Description:
+//
+// If no mail available, returns NO_MAIL_HERE otherwise returns the data from
+// the mailbox, which is guaranteed != NO_MAIL_HERE.
+//
+// This version operates on IntelliPort-II - style FIFO's
+//
+//******************************************************************************
+static unsigned short
+iiGetMailII(i2eBordStrPtr pB)
+{
+       if (I2_HAS_MAIL(pB)) {
+               outb(SEL_INMAIL, pB->i2ePointer);
+               return inb(pB->i2ePointer);
+       } else {
+               return NO_MAIL_HERE;
+       }
+}
+
+//******************************************************************************
+// Function:   iiGetMailIIEX(pB,mail)
+// Parameters: pB   - pointer to board structure
+//
+// Returns:    Mailbox data or NO_MAIL_HERE.
+//
+// Description:
+//
+// If no mail available, returns NO_MAIL_HERE otherwise returns the data from
+// the mailbox, which is guaranteed != NO_MAIL_HERE.
+//
+// This version operates on IntelliPort-IIEX - style FIFO's
+//
+//******************************************************************************
+static unsigned short
+iiGetMailIIEX(i2eBordStrPtr pB)
+{
+       if (I2_HAS_MAIL(pB))
+               return inb(pB->i2eXMail);
+       else
+               return NO_MAIL_HERE;
+}
+
+//******************************************************************************
+// Function:   iiEnableMailIrqII(pB)
+// Parameters: pB - pointer to board structure
+//
+// Returns:    Nothing
+//
+// Description:
+//
+// Enables board to interrupt host (only) by writing to host's in-bound mailbox.
+//
+// This version operates on IntelliPort-II - style FIFO's
+//
+//******************************************************************************
+static void
+iiEnableMailIrqII(i2eBordStrPtr pB)
+{
+       outb(SEL_MASK, pB->i2ePointer);
+       outb(ST_IN_MAIL, pB->i2ePointer);
+}
+
+//******************************************************************************
+// Function:   iiEnableMailIrqIIEX(pB)
+// Parameters: pB - pointer to board structure
+//
+// Returns:    Nothing
+//
+// Description:
+//
+// Enables board to interrupt host (only) by writing to host's in-bound mailbox.
+//
+// This version operates on IntelliPort-IIEX - style FIFO's
+//
+//******************************************************************************
+static void
+iiEnableMailIrqIIEX(i2eBordStrPtr pB)
+{
+       outb(MX_IN_MAIL, pB->i2eXMask);
+}
+
+//******************************************************************************
+// Function:   iiWriteMaskII(pB)
+// Parameters: pB - pointer to board structure
+//
+// Returns:    Nothing
+//
+// Description:
+//
+// Writes arbitrary value to the mask register.
+//
+// This version operates on IntelliPort-II - style FIFO's
+//
+//******************************************************************************
+static void
+iiWriteMaskII(i2eBordStrPtr pB, unsigned char value)
+{
+       outb(SEL_MASK, pB->i2ePointer);
+       outb(value, pB->i2ePointer);
+}
+
+//******************************************************************************
+// Function:   iiWriteMaskIIEX(pB)
+// Parameters: pB - pointer to board structure
+//
+// Returns:    Nothing
+//
+// Description:
+//
+// Writes arbitrary value to the mask register.
+//
+// This version operates on IntelliPort-IIEX - style FIFO's
+//
+//******************************************************************************
+static void
+iiWriteMaskIIEX(i2eBordStrPtr pB, unsigned char value)
+{
+       outb(value, pB->i2eXMask);
+}
+
+//******************************************************************************
+// Function:   iiDownloadBlock(pB, pSource, isStandard)
+// Parameters: pB         - pointer to board structure
+//             pSource    - loadware block to download
+//             isStandard - True if "standard" loadware, else false.
+//
+// Returns:    Success or Failure
+//
+// Description:
+//
+// Downloads a single block (at pSource)to the board referenced by pB. Caller
+// sets isStandard to true/false according to whether the "standard" loadware is
+// what's being loaded. The normal process, then, is to perform an iiInitialize
+// to the board, then perform some number of iiDownloadBlocks using the returned
+// state to determine when download is complete.
+//
+// Possible return values: (see I2ELLIS.H)
+// II_DOWN_BADVALID
+// II_DOWN_BADFILE
+// II_DOWN_CONTINUING
+// II_DOWN_GOOD
+// II_DOWN_BAD
+// II_DOWN_BADSTATE
+// II_DOWN_TIMEOUT
+//
+// Uses the i2eState and i2eToLoad fields (initialized at iiInitialize) to
+// determine whether this is the first block, whether to check for magic
+// numbers, how many blocks there are to go...
+//
+//******************************************************************************
+static int
+iiDownloadBlock ( i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard)
+{
+       int itemp;
+       int loadedFirst;
+
+       if (pB->i2eValid != I2E_MAGIC) return II_DOWN_BADVALID;
+
+       switch(pB->i2eState)
+       {
+       case II_STATE_READY:
+
+               // Loading the first block after reset. Must check the magic number of the
+               // loadfile, store the number of blocks we expect to load.
+               if (pSource->e.loadMagic != MAGIC_LOADFILE)
+               {
+                       return II_DOWN_BADFILE;
+               }
+
+               // Next we store the total number of blocks to load, including this one.
+               pB->i2eToLoad = 1 + pSource->e.loadBlocksMore;
+
+               // Set the state, store the version numbers. ('Cause this may have come
+               // from a file - we might want to report these versions and revisions in
+               // case of an error!
+               pB->i2eState = II_STATE_LOADING;
+               pB->i2eLVersion = pSource->e.loadVersion;
+               pB->i2eLRevision = pSource->e.loadRevision;
+               pB->i2eLSub = pSource->e.loadSubRevision;
+
+               // The time and date of compilation is also available but don't bother
+               // storing it for normal purposes.
+               loadedFirst = 1;
+               break;
+
+       case II_STATE_LOADING:
+               loadedFirst = 0;
+               break;
+
+       default:
+               return II_DOWN_BADSTATE;
+       }
+
+       // Now we must be in the II_STATE_LOADING state, and we assume i2eToLoad
+       // must be positive still, because otherwise we would have cleaned up last
+       // time and set the state to II_STATE_LOADED.
+       if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) {
+               return II_DOWN_TIMEOUT;
+       }
+
+       if (!iiWriteBuf(pB, pSource->c, LOADWARE_BLOCK_SIZE)) {
+               return II_DOWN_BADVALID;
+       }
+
+       // If we just loaded the first block, wait for the fifo to empty an extra
+       // long time to allow for any special startup code in the firmware, like
+       // sending status messages to the LCD's.
+
+       if (loadedFirst) {
+               if (!iiWaitForTxEmpty(pB, MAX_DLOAD_START_TIME)) {
+                       return II_DOWN_TIMEOUT;
+               }
+       }
+
+       // Determine whether this was our last block!
+       if (--(pB->i2eToLoad)) {
+               return II_DOWN_CONTINUING;    // more to come...
+       }
+
+       // It WAS our last block: Clean up operations...
+       // ...Wait for last buffer to drain from the board...
+       if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) {
+               return II_DOWN_TIMEOUT;
+       }
+       // If there were only a single block written, this would come back
+       // immediately and be harmless, though not strictly necessary.
+       itemp = MAX_DLOAD_ACK_TIME/10;
+       while (--itemp) {
+               if (I2_HAS_INPUT(pB)) {
+                       switch (inb(pB->i2eData)) {
+                       case LOADWARE_OK:
+                               pB->i2eState =
+                                       isStandard ? II_STATE_STDLOADED :II_STATE_LOADED;
+
+                               // Some revisions of the bootstrap firmware (e.g. ISA-8 1.0.2)
+                               // will, // if there is a debug port attached, require some
+                               // time to send information to the debug port now. It will do
+                               // this before // executing any of the code we just downloaded.
+                               // It may take up to 700 milliseconds.
+                               if (pB->i2ePom.e.porDiag2 & POR_DEBUG_PORT) {
+                                       iiDelay(pB, 700);
+                               }
+
+                               return II_DOWN_GOOD;
+
+                       case LOADWARE_BAD:
+                       default:
+                               return II_DOWN_BAD;
+                       }
+               }
+
+               iiDelay(pB, 10);      // 10 mS granularity on checking condition
+       }
+
+       // Drop-through --> timed out waiting for firmware confirmation
+
+       pB->i2eState = II_STATE_BADLOAD;
+       return II_DOWN_TIMEOUT;
+}
+
+//******************************************************************************
+// Function:   iiDownloadAll(pB, pSource, isStandard, size)
+// Parameters: pB         - pointer to board structure
+//             pSource    - loadware block to download
+//             isStandard - True if "standard" loadware, else false.
+//             size       - size of data to download (in bytes)
+//
+// Returns:    Success or Failure
+//
+// Description:
+//
+// Given a pointer to a board structure, a pointer to the beginning of some
+// loadware, whether it is considered the "standard loadware", and the size of
+// the array in bytes loads the entire array to the board as loadware.
+//
+// Assumes the board has been freshly reset and the power-up reset message read.
+// (i.e., in II_STATE_READY). Complains if state is bad, or if there seems to be
+// too much or too little data to load, or if iiDownloadBlock complains.
+//******************************************************************************
+static int
+iiDownloadAll(i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard, int size)
+{
+       int status;
+
+       // We know (from context) board should be ready for the first block of
+       // download.  Complain if not.
+       if (pB->i2eState != II_STATE_READY) return II_DOWN_BADSTATE;
+
+       while (size > 0) {
+               size -= LOADWARE_BLOCK_SIZE;    // How much data should there be left to
+                                                                               // load after the following operation ?
+
+               // Note we just bump pSource by "one", because its size is actually that
+               // of an entire block, same as LOADWARE_BLOCK_SIZE.
+               status = iiDownloadBlock(pB, pSource++, isStandard);
+
+               switch(status)
+               {
+               case II_DOWN_GOOD:
+                       return ( (size > 0) ? II_DOWN_OVER : II_DOWN_GOOD);
+
+               case II_DOWN_CONTINUING:
+                       break;
+
+               default:
+                       return status;
+               }
+       }
+
+       // We shouldn't drop out: it means "while" caught us with nothing left to
+       // download, yet the previous DownloadBlock did not return complete. Ergo,
+       // not enough data to match the size byte in the header.
+       return II_DOWN_UNDER;
+}
diff --git a/drivers/staging/tty/ip2/i2ellis.h b/drivers/staging/tty/ip2/i2ellis.h
new file mode 100644 (file)
index 0000000..fb6df24
--- /dev/null
@@ -0,0 +1,566 @@
+/*******************************************************************************
+*
+*   (c) 1999 by Computone Corporation
+*
+********************************************************************************
+*
+*
+*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
+*                serial I/O controllers.
+*
+*   DESCRIPTION: Mainline code for the device driver
+*
+*******************************************************************************/
+//------------------------------------------------------------------------------
+// i2ellis.h
+//
+// IntelliPort-II and IntelliPort-IIEX
+//
+// Extremely
+// Low
+// Level
+// Interface
+// Services
+//
+// Structure Definitions and declarations for "ELLIS" service routines found in
+// i2ellis.c
+//
+// These routines are based on properties of the IntelliPort-II and -IIEX
+// hardware and bootstrap firmware, and are not sensitive to particular
+// conventions of any particular loadware.
+//
+// Unlike i2hw.h, which provides IRONCLAD hardware definitions, the material
+// here and in i2ellis.c is intended to provice a useful, but not required,
+// layer of insulation from the hardware specifics.
+//------------------------------------------------------------------------------
+#ifndef  I2ELLIS_H   /* To prevent multiple includes */
+#define  I2ELLIS_H   1
+//------------------------------------------------
+// Revision History:
+//
+// 30 September 1991 MAG First Draft Started
+// 12 October   1991 ...continued...
+//
+// 20 December  1996 AKM Linux version
+//-------------------------------------------------
+
+//----------------------
+// Mandatory Includes:
+//----------------------
+#include "ip2types.h"
+#include "i2hw.h"       // The hardware definitions
+
+//------------------------------------------
+// STAT_BOXIDS packets
+//------------------------------------------
+#define MAX_BOX                4
+
+typedef struct _bidStat
+{
+       unsigned char bid_value[MAX_BOX];
+} bidStat, *bidStatPtr;
+
+// This packet is sent in response to a CMD_GET_BOXIDS bypass command. For -IIEX
+// boards, reports the hardware-specific "asynchronous resource register" on
+// each expansion box. Boxes not present report 0xff. For -II boards, the first
+// element contains 0x80 for 8-port, 0x40 for 4-port boards.
+
+// Box IDs aka ARR or Async Resource Register (more than you want to know)
+//   7   6   5   4   3   2   1   0
+//   F   F   N   N   L   S   S   S
+//   =============================
+//   F   F   -  Product Family Designator
+//   =====+++++++++++++++++++++++++++++++
+//   0   0   -  Intelliport II EX / ISA-8
+//   1   0   -  IntelliServer
+//   0   1   -  SAC - Port Device (Intelliport III ??? )
+//           =====+++++++++++++++++++++++++++++++++++++++
+//           N   N   -  Number of Ports
+//           0   0   -  8  (eight)
+//           0   1   -  4  (four)
+//           1   0   -  12 (twelve)
+//           1   1   -  16 (sixteen)
+//                   =++++++++++++++++++++++++++++++++++
+//                   L  -   LCD Display Module Present
+//                   0  -   No
+//                   1  -   LCD module present
+//                   =========+++++++++++++++++++++++++++++++++++++
+//                      S   S   S - Async Signals Supported Designator
+//                      0   0   0 - 8dss, Mod DCE DB25 Female
+//                      0   0   1 - 6dss, RJ-45
+//                      0   1   0 - RS-232/422 dss, DB25 Female
+//                      0   1   1 - RS-232/422 dss, separate 232/422 DB25 Female
+//                      1   0   0 - 6dss, 921.6 I/F with ST654's
+//                      1   0   1 - RS-423/232 8dss, RJ-45 10Pin
+//                      1   1   0 - 6dss, Mod DCE DB25 Female
+//                      1   1   1 - NO BOX PRESENT
+
+#define FF(c)  ((c & 0xC0) >> 6)
+#define NN(c)  ((c & 0x30) >> 4)
+#define L(c)   ((c & 0x08) >> 3)
+#define SSS(c)  (c & 0x07)
+
+#define BID_HAS_654(x) (SSS(x) == 0x04)
+#define BID_NO_BOX     0xff /* no box */
+#define BID_8PORT      0x80 /* IP2-8 port */
+#define BID_4PORT      0x81 /* IP2-4 port */
+#define BID_EXP_MASK           0x30 /* IP2-EX  */
+#define BID_EXP_8PORT  0x00 /*     8, */
+#define BID_EXP_4PORT  0x10 /*     4, */
+#define BID_EXP_UNDEF  0x20 /*     UNDEF, */
+#define BID_EXP_16PORT 0x30 /*    16, */
+#define BID_LCD_CTRL           0x08 /* LCD Controller */
+#define BID_LCD_NONE   0x00 /* - no controller present */
+#define BID_LCD_PRES           0x08 /* - controller present */
+#define BID_CON_MASK   0x07 /* - connector pinouts */
+#define BID_CON_DB25   0x00 /* - DB-25 F */
+#define BID_CON_RJ45   0x01 /* - rj45 */
+
+//------------------------------------------------------------------------------
+// i2eBordStr
+//
+// This structure contains all the information the ELLIS routines require in
+// dealing with a particular board.
+//------------------------------------------------------------------------------
+// There are some queues here which are guaranteed to never contain the entry
+// for a single channel twice. So they must be slightly larger to allow
+// unambiguous full/empty management
+//
+#define CH_QUEUE_SIZE ABS_MOST_PORTS+2
+
+typedef struct _i2eBordStr
+{
+       porStr         i2ePom;  // Structure containing the power-on message.
+
+       unsigned short i2ePomSize;
+                                               // The number of bytes actually read if
+                                               // different from sizeof i2ePom, indicates
+                                               // there is an error!
+
+       unsigned short i2eStartMail;
+                                               // Contains whatever inbound mailbox data
+                                               // present at startup. NO_MAIL_HERE indicates
+                                               // nothing was present. No special
+                                               // significance as of this writing, but may be
+                                               // useful for diagnostic reasons.
+
+       unsigned short i2eValid;
+                                               // Indicates validity of the structure; if
+                                               // i2eValid == I2E_MAGIC, then we can trust
+                                               // the other fields. Some (especially
+                                               // initialization) functions are good about
+                                               // checking for validity.  Many functions do
+                                               // not, it being assumed that the larger
+                                               // context assures we are using a valid
+                                               // i2eBordStrPtr.
+
+       unsigned short i2eError;
+                                               // Used for returning an error condition from
+                                               // several functions which use i2eBordStrPtr
+                                               // as an argument.
+
+       // Accelerators to characterize separate features of a board, derived from a
+       // number of sources.
+
+       unsigned short i2eFifoSize;
+                                               // Always, the size of the FIFO. For
+                                               // IntelliPort-II, always the same, for -IIEX
+                                               // taken from the Power-On reset message.
+
+       volatile 
+       unsigned short i2eFifoRemains;
+                                               // Used during normal operation to indicate a
+                                               // lower bound on the amount of data which
+                                               // might be in the outbound fifo.
+
+       unsigned char  i2eFifoStyle;
+                                               // Accelerator which tells which style (-II or
+                                               // -IIEX) FIFO we are using.
+
+       unsigned char  i2eDataWidth16;
+                                               // Accelerator which tells whether we should
+                                               // do 8 or 16-bit data transfers.
+
+       unsigned char  i2eMaxIrq;
+                                               // The highest allowable IRQ, based on the
+                                               // slot size.
+
+       // Accelerators for various addresses on the board
+       int            i2eBase;        // I/O Address of the Board
+       int            i2eData;        // From here data transfers happen
+       int            i2eStatus;      // From here status reads happen
+       int            i2ePointer;     // (IntelliPort-II: pointer/commands)
+       int            i2eXMail;       // (IntelliPOrt-IIEX: mailboxes
+       int            i2eXMask;       // (IntelliPort-IIEX: mask write
+
+       //-------------------------------------------------------
+       // Information presented in a common format across boards
+       // For each box, bit map of the channels present.  Box closest to 
+       // the host is box 0. LSB is channel 0. IntelliPort-II (non-expandable)
+       // is taken to be box 0. These are derived from product i.d. registers.
+
+       unsigned short i2eChannelMap[ABS_MAX_BOXES];
+
+       // Same as above, except each is derived from firmware attempting to detect
+       // the uart presence (by reading a valid GFRCR register). If bits are set in
+       // i2eChannelMap and not in i2eGoodMap, there is a potential problem.
+
+       unsigned short i2eGoodMap[ABS_MAX_BOXES];
+
+       // ---------------------------
+       // For indirect function calls
+
+       // Routine to cause an N-millisecond delay: Patched by the ii2Initialize
+       // function.
+
+       void  (*i2eDelay)(unsigned int);
+
+       // Routine to write N bytes to the board through the FIFO. Returns true if
+       // all copacetic, otherwise returns false and error is in i2eError field.
+       // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER.
+
+       int   (*i2eWriteBuf)(struct _i2eBordStr *, unsigned char *, int);
+
+       // Routine to read N bytes from the board through the FIFO. Returns true if
+       // copacetic, otherwise returns false and error in i2eError.
+       // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER.
+
+       int   (*i2eReadBuf)(struct _i2eBordStr *, unsigned char *, int);
+
+       // Returns a word from FIFO. Will use 2 byte operations if needed.
+
+       unsigned short (*i2eReadWord)(struct _i2eBordStr *);
+
+       // Writes a word to FIFO. Will use 2 byte operations if needed.
+
+       void  (*i2eWriteWord)(struct _i2eBordStr *, unsigned short);
+
+       // Waits specified time for the Transmit FIFO to go empty. Returns true if
+       //  ok, otherwise returns false and error in i2eError.
+
+       int   (*i2eWaitForTxEmpty)(struct _i2eBordStr *, int);
+
+       // Returns true or false according to whether the outgoing mailbox is empty.
+
+       int   (*i2eTxMailEmpty)(struct _i2eBordStr *);
+
+       // Checks whether outgoing mailbox is empty.  If so, sends mail and returns
+       // true.  Otherwise returns false.
+
+       int   (*i2eTrySendMail)(struct _i2eBordStr *, unsigned char);
+
+       // If no mail available, returns NO_MAIL_HERE, else returns the value in the
+       // mailbox (guaranteed can't be NO_MAIL_HERE).
+
+       unsigned short (*i2eGetMail)(struct _i2eBordStr *);
+
+       // Enables the board to interrupt the host when it writes to the mailbox.
+       // Irqs will not occur, however, until the loadware separately enables
+       // interrupt generation to the host.  The standard loadware does this in
+       // response to a command packet sent by the host. (Also, disables
+       // any other potential interrupt sources from the board -- other than the
+       // inbound mailbox).
+
+       void  (*i2eEnableMailIrq)(struct _i2eBordStr *);
+
+       // Writes an arbitrary value to the mask register.
+
+       void  (*i2eWriteMask)(struct _i2eBordStr *, unsigned char);
+
+
+       // State information
+
+       // During downloading, indicates the number of blocks remaining to download
+       // to the board.
+
+       short i2eToLoad;
+
+       // State of board (see manifests below) (e.g., whether in reset condition,
+       // whether standard loadware is installed, etc.
+
+       unsigned char  i2eState;
+
+       // These three fields are only valid when there is loadware running on the
+       // board. (i2eState == II_STATE_LOADED or i2eState == II_STATE_STDLOADED )
+
+       unsigned char  i2eLVersion;  // Loadware version
+       unsigned char  i2eLRevision; // Loadware revision
+       unsigned char  i2eLSub;      // Loadware subrevision
+
+       // Flags which only have meaning in the context of the standard loadware.
+       // Somewhat violates the layering concept, but there is so little additional
+       // needed at the board level (while much additional at the channel level),
+       // that this beats maintaining two different per-board structures.
+
+       // Indicates which IRQ the board has been initialized (from software) to use
+       // For MicroChannel boards, any value different from IRQ_UNDEFINED means
+       // that the software command has been sent to enable interrupts (or specify
+       // they are disabled). Special value: IRQ_UNDEFINED indicates that the
+       // software command to select the interrupt has not yet been sent, therefore
+       // (since the standard loadware insists that it be sent before any other
+       // packets are sent) no other packets should be sent yet.
+
+       unsigned short i2eUsingIrq;
+
+       // This is set when we hit the MB_OUT_STUFFED mailbox, which prevents us
+       // putting more in the mailbox until an appropriate mailbox message is
+       // received.
+
+       unsigned char  i2eWaitingForEmptyFifo;
+
+       // Any mailbox bits waiting to be sent to the board are OR'ed in here.
+
+       unsigned char  i2eOutMailWaiting;
+
+       // The head of any incoming packet is read into here, is then examined and 
+       // we dispatch accordingly.
+
+       unsigned short i2eLeadoffWord[1];
+
+       // Running counter of interrupts where the mailbox indicated incoming data.
+
+       unsigned short i2eFifoInInts;
+
+       // Running counter of interrupts where the mailbox indicated outgoing data
+       // had been stripped.
+
+       unsigned short i2eFifoOutInts;
+
+       // If not void, gives the address of a routine to call if fatal board error
+       // is found (only applies to standard l/w).
+
+       void  (*i2eFatalTrap)(struct _i2eBordStr *);
+
+       // Will point to an array of some sort of channel structures (whose format
+       // is unknown at this level, being a function of what loadware is
+       // installed and the code configuration (max sizes of buffers, etc.)).
+
+       void  *i2eChannelPtr;
+
+       // Set indicates that the board has gone fatal.
+
+       unsigned short i2eFatal;
+
+       // The number of elements pointed to by i2eChannelPtr.
+
+       unsigned short i2eChannelCnt;
+
+       // Ring-buffers of channel structures whose channels have particular needs.
+
+       rwlock_t        Fbuf_spinlock;
+       volatile
+       unsigned short i2Fbuf_strip;    // Strip index
+       volatile 
+       unsigned short i2Fbuf_stuff;    // Stuff index
+       void  *i2Fbuf[CH_QUEUE_SIZE];   // An array of channel pointers
+                                                                       // of channels who need to send
+                                                                       // flow control packets.
+       rwlock_t        Dbuf_spinlock;
+       volatile
+       unsigned short i2Dbuf_strip;    // Strip index
+       volatile
+       unsigned short i2Dbuf_stuff;    // Stuff index
+       void  *i2Dbuf[CH_QUEUE_SIZE];   // An array of channel pointers
+                                                                       // of channels who need to send
+                                                                       // data or in-line command packets.
+       rwlock_t        Bbuf_spinlock;
+       volatile
+       unsigned short i2Bbuf_strip;    // Strip index
+       volatile
+       unsigned short i2Bbuf_stuff;    // Stuff index
+       void  *i2Bbuf[CH_QUEUE_SIZE];   // An array of channel pointers
+                                                                       // of channels who need to send
+                                                                       // bypass command packets.
+
+       /*
+        * A set of flags to indicate that certain events have occurred on at least
+        * one of the ports on this board. We use this to decide whether to spin
+        * through the channels looking for breaks, etc.
+        */
+       int             got_input;
+       int             status_change;
+       bidStat channelBtypes;
+
+       /*
+        * Debugging counters, etc.
+        */
+       unsigned long debugFlowQueued;
+       unsigned long debugInlineQueued;
+       unsigned long debugDataQueued;
+       unsigned long debugBypassQueued;
+       unsigned long debugFlowCount;
+       unsigned long debugInlineCount;
+       unsigned long debugBypassCount;
+       
+       rwlock_t        read_fifo_spinlock;
+       rwlock_t        write_fifo_spinlock;
+
+//     For queuing interrupt bottom half handlers.     /\/\|=mhw=|\/\/
+       struct work_struct      tqueue_interrupt;
+
+       struct timer_list  SendPendingTimer;   // Used by iiSendPending
+       unsigned int    SendPendingRetry;
+} i2eBordStr, *i2eBordStrPtr;
+
+//-------------------------------------------------------------------
+// Macro Definitions for the indirect calls defined in the i2eBordStr
+//-------------------------------------------------------------------
+//
+#define iiDelay(a,b)          (*(a)->i2eDelay)(b)
+#define iiWriteBuf(a,b,c)     (*(a)->i2eWriteBuf)(a,b,c)
+#define iiReadBuf(a,b,c)      (*(a)->i2eReadBuf)(a,b,c)
+
+#define iiWriteWord(a,b)      (*(a)->i2eWriteWord)(a,b)
+#define iiReadWord(a)         (*(a)->i2eReadWord)(a)
+
+#define iiWaitForTxEmpty(a,b) (*(a)->i2eWaitForTxEmpty)(a,b)
+
+#define iiTxMailEmpty(a)      (*(a)->i2eTxMailEmpty)(a)
+#define iiTrySendMail(a,b)    (*(a)->i2eTrySendMail)(a,b)
+
+#define iiGetMail(a)          (*(a)->i2eGetMail)(a)
+#define iiEnableMailIrq(a)    (*(a)->i2eEnableMailIrq)(a)
+#define iiDisableMailIrq(a)   (*(a)->i2eWriteMask)(a,0)
+#define iiWriteMask(a,b)      (*(a)->i2eWriteMask)(a,b)
+
+//-------------------------------------------
+// Manifests for i2eBordStr:
+//-------------------------------------------
+
+typedef void (*delayFunc_t)(unsigned int);
+
+// i2eValid
+//
+#define I2E_MAGIC       0x4251   // Structure is valid.
+#define I2E_INCOMPLETE  0x1122   // Structure failed during init.
+
+
+// i2eError
+//
+#define I2EE_GOOD       0      // Operation successful
+#define I2EE_BADADDR    1      // Address out of range
+#define I2EE_BADSTATE   2      // Attempt to perform a function when the board
+                                                       // structure was in the incorrect state
+#define I2EE_BADMAGIC   3      // Bad magic number from Power On test (i2ePomSize
+                                                       // reflects what was read
+#define I2EE_PORM_SHORT 4      // Power On message too short
+#define I2EE_PORM_LONG  5      // Power On message too long
+#define I2EE_BAD_FAMILY 6      // Un-supported board family type
+#define I2EE_INCONSIST  7      // Firmware reports something impossible,
+                                                       // e.g. unexpected number of ports... Almost no
+                                                       // excuse other than bad FIFO...
+#define I2EE_POSTERR    8      // Power-On self test reported a bad error
+#define I2EE_BADBUS     9      // Unknown Bus type declared in message
+#define I2EE_TXE_TIME   10     // Timed out waiting for TX Fifo to empty
+#define I2EE_INVALID    11     // i2eValid field does not indicate a valid and
+                                                       // complete board structure (for functions which
+                                                       // require this be so.)
+#define I2EE_BAD_PORT   12     // Discrepancy between channels actually found and
+                                                       // what the product is supposed to have. Check
+                                                       // i2eGoodMap vs i2eChannelMap for details.
+#define I2EE_BAD_IRQ    13     // Someone specified an unsupported IRQ
+#define I2EE_NOCHANNELS 14     // No channel structures have been defined (for
+                                                       // functions requiring this).
+
+// i2eFifoStyle
+//
+#define FIFO_II   0  /* IntelliPort-II style: see also i2hw.h */
+#define FIFO_IIEX 1  /* IntelliPort-IIEX style */
+
+// i2eGetMail
+//
+#define NO_MAIL_HERE    0x1111 // Since mail is unsigned char, cannot possibly
+                                                               // promote to 0x1111.
+// i2eState
+//
+#define II_STATE_COLD      0  // Addresses have been defined, but board not even
+                                                         // reset yet.
+#define II_STATE_RESET     1  // Board,if it exists, has just been reset
+#define II_STATE_READY     2  // Board ready for its first block
+#define II_STATE_LOADING   3  // Board continuing load
+#define II_STATE_LOADED    4  // Board has finished load: status ok
+#define II_STATE_BADLOAD   5  // Board has finished load: failed!
+#define II_STATE_STDLOADED 6  // Board has finished load: standard firmware
+
+// i2eUsingIrq
+//
+#define I2_IRQ_UNDEFINED       0x1352  /* No valid irq (or polling = 0) can
+                                        * ever promote to this! */
+//------------------------------------------
+// Handy Macros for i2ellis.c and others
+// Note these are common to -II and -IIEX
+//------------------------------------------
+
+// Given a pointer to the board structure, does the input FIFO have any data or
+// not?
+//
+#define I2_HAS_INPUT(pB)       !(inb(pB->i2eStatus) & ST_IN_EMPTY)
+
+// Given a pointer to the board structure, is there anything in the incoming
+// mailbox?
+//
+#define I2_HAS_MAIL(pB)                (inb(pB->i2eStatus) & ST_IN_MAIL)
+
+#define I2_UPDATE_FIFO_ROOM(pB)        ((pB)->i2eFifoRemains = (pB)->i2eFifoSize)
+
+//------------------------------------------
+// Function Declarations for i2ellis.c
+//------------------------------------------
+//
+// Functions called directly
+//
+// Initialization of a board & structure is in four (five!) parts:
+//
+// 1) iiSetAddress() - Define the board address & delay function for a board.
+// 2) iiReset()      - Reset the board   (provided it exists)
+//       -- Note you may do this to several boards --
+// 3) iiResetDelay() - Delay for 2 seconds (once for all boards)
+// 4) iiInitialize() - Attempt to read Power-up message; further initialize
+//                     accelerators
+//
+// Then you may use iiDownloadAll() or iiDownloadFile() (in i2file.c) to write
+// loadware.  To change loadware, you must begin again with step 2, resetting
+// the board again (step 1 not needed).
+
+static int iiSetAddress(i2eBordStrPtr, int, delayFunc_t );
+static int iiReset(i2eBordStrPtr);
+static int iiResetDelay(i2eBordStrPtr);
+static int iiInitialize(i2eBordStrPtr);
+
+// Routine to validate that all channels expected are there.
+//
+extern int iiValidateChannels(i2eBordStrPtr);
+
+// Routine used to download a block of loadware.
+//
+static int iiDownloadBlock(i2eBordStrPtr, loadHdrStrPtr, int);
+
+// Return values given by iiDownloadBlock, iiDownloadAll, iiDownloadFile:
+//
+#define II_DOWN_BADVALID   0   // board structure is invalid
+#define II_DOWN_CONTINUING 1   // So far, so good, firmware expects more
+#define II_DOWN_GOOD       2   // Download complete, CRC good
+#define II_DOWN_BAD        3   // Download complete, but CRC bad
+#define II_DOWN_BADFILE    4   // Bad magic number in loadware file
+#define II_DOWN_BADSTATE   5   // Board is in an inappropriate state for
+                                                               // downloading loadware. (see i2eState)
+#define II_DOWN_TIMEOUT    6   // Timeout waiting for firmware
+#define II_DOWN_OVER       7   // Too much data
+#define II_DOWN_UNDER      8   // Not enough data
+#define II_DOWN_NOFILE     9   // Loadware file not found
+
+// Routine to download an entire loadware module: Return values are a subset of
+// iiDownloadBlock's, excluding, of course, II_DOWN_CONTINUING
+//
+static int iiDownloadAll(i2eBordStrPtr, loadHdrStrPtr, int, int);
+
+// Many functions defined here return True if good, False otherwise, with an
+// error code in i2eError field. Here is a handy macro for setting the error
+// code and returning.
+//
+#define I2_COMPLETE(pB,code) do { \
+                pB->i2eError = code; \
+                return (code == I2EE_GOOD);\
+       } while (0)
+
+#endif   // I2ELLIS_H
diff --git a/drivers/staging/tty/ip2/i2hw.h b/drivers/staging/tty/ip2/i2hw.h
new file mode 100644 (file)
index 0000000..c0ba6c0
--- /dev/null
@@ -0,0 +1,652 @@
+/*******************************************************************************
+*
+*   (c) 1999 by Computone Corporation
+*
+********************************************************************************
+*
+*
+*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
+*                serial I/O controllers.
+*
+*   DESCRIPTION: Definitions limited to properties of the hardware or the
+*                bootstrap firmware. As such, they are applicable regardless of
+*                operating system or loadware (standard or diagnostic).
+*
+*******************************************************************************/
+#ifndef I2HW_H
+#define I2HW_H 1
+//------------------------------------------------------------------------------
+// Revision History:
+//
+// 23 September 1991 MAG   First Draft Started...through...
+// 11 October 1991   ...   Continuing development...
+//  6 August 1993          Added support for ISA-4 (asic) which is architected
+//                         as an ISA-CEX with a single 4-port box.
+//
+// 20 December 1996  AKM   Version for Linux
+//
+//------------------------------------------------------------------------------
+/*------------------------------------------------------------------------------
+
+HARDWARE DESCRIPTION:
+
+Introduction:
+
+The IntelliPort-II and IntelliPort-IIEX products occupy a block of eight (8)
+addresses in the host's I/O space.
+
+Some addresses are used to transfer data to/from the board, some to transfer
+so-called "mailbox" messages, and some to read bit-mapped status information.
+While all the products in the line are functionally similar, some use a 16-bit
+data path to transfer data while others use an 8-bit path. Also, the use of
+command /status/mailbox registers differs slightly between the II and IIEX
+branches of the family.
+
+The host determines what type of board it is dealing with by reading a string of
+sixteen characters from the board. These characters are always placed in the
+fifo by the board's local processor whenever the board is reset (either from
+power-on or under software control) and are known as the "Power-on Reset
+Message." In order that this message can be read from either type of board, the
+hardware registers used in reading this message are the same. Once this message
+has been read by the host, then it has the information required to operate.
+
+General Differences between boards:
+
+The greatest structural difference is between the -II and -IIEX families of
+product. The -II boards use the Am4701 dual 512x8 bidirectional fifo to support
+the data path, mailbox registers, and status registers. This chip contains some
+features which are not used in the IntelliPort-II products; a description of
+these is omitted here. Because of these many features, it contains many
+registers, too many to access directly within a small address space. They are
+accessed by first writing a value to a "pointer" register. This value selects
+the register to be accessed.  The next read or write to that address accesses
+the selected register rather than the pointer register.
+
+The -IIEX boards use a proprietary design similar to the Am4701 in function. But
+because of a simpler, more streamlined design it doesn't require so many
+registers. This means they can be accessed directly in single operations rather
+than through a pointer register.
+
+Besides these differences, there are differences in whether 8-bit or 16-bit
+transfers are used to move data to the board.
+
+The -II boards are capable only of 8-bit data transfers, while the -IIEX boards
+may be configured for either 8-bit or 16-bit data transfers. If the on-board DIP
+switch #8 is ON, and the card has been installed in a 16-bit slot, 16-bit
+transfers are supported (and will be expected by the standard loadware). The
+on-board firmware can determine the position of the switch, and whether the
+board is installed in a 16-bit slot; it supplies this information to the host as
+part of the power-up reset message.
+
+The configuration switch (#8) and slot selection do not directly configure the
+hardware. It is up to the on-board loadware and host-based drivers to act
+according to the selected options. That is, loadware and drivers could be
+written to perform 8-bit transfers regardless of the state of the DIP switch or
+slot (and in a diagnostic environment might well do so). Likewise, 16-bit
+transfers could be performed as long as the card is in a 16-bit slot.
+
+Note the slot selection and DIP switch selection are provided separately: a
+board running in 8-bit mode in a 16-bit slot has a greater range of possible
+interrupts to choose from; information of potential use to the host.
+
+All 8-bit data transfers are done in the same way, regardless of whether on a
+-II board or a -IIEX board.
+
+The host must consider two things then: 1) whether a -II or -IIEX product is
+being used, and 2) whether an 8-bit or 16-bit data path is used.
+
+A further difference is that -II boards always have a 512-byte fifo operating in
+each direction. -IIEX boards may use fifos of varying size; this size is
+reported as part of the power-up message.
+
+I/O Map Of IntelliPort-II and IntelliPort-IIEX boards:
+(Relative to the chosen base address)
+
+Addr  R/W      IntelliPort-II    IntelliPort-IIEX
+----  ---      --------------    ----------------
+0     R/W      Data Port (byte)  Data Port (byte or word)
+1     R/W      (Not used)        (MSB of word-wide data written to Data Port)
+2     R        Status Register   Status Register
+2     W        Pointer Register  Interrupt Mask Register
+3     R/W      (Not used)        Mailbox Registers (6 bits: 11111100)
+4,5   --       Reserved for future products
+6     --       Reserved for future products
+7     R        Guaranteed to have no effect
+7     W        Hardware reset of board.
+
+
+Rules:
+All data transfers are performed using the even i/o address. If byte-wide data
+transfers are being used, do INB/OUTB operations on the data port. If word-wide
+transfers are used, do INW/OUTW operations. In some circumstances (such as
+reading the power-up message) you will do INB from the data port, but in this
+case the MSB of each word read is lost. When accessing all other unreserved
+registers, use byte operations only.
+------------------------------------------------------------------------------*/
+
+//------------------------------------------------
+// Mandatory Includes:
+//------------------------------------------------
+//
+#include "ip2types.h"
+
+//-------------------------------------------------------------------------
+// Manifests for the I/O map:
+//-------------------------------------------------------------------------
+// R/W: Data port (byte) for IntelliPort-II,
+// R/W: Data port (byte or word) for IntelliPort-IIEX
+// Incoming or outgoing data passes through a FIFO, the status of which is
+// available in some of the bits in FIFO_STATUS. This (bidirectional) FIFO is
+// the primary means of transferring data, commands, flow-control, and status
+// information between the host and board.
+//
+#define FIFO_DATA 0
+
+// Another way of passing information between the board and the host is
+// through "mailboxes". Unlike a FIFO, a mailbox holds only a single byte of
+// data.  Writing data to the mailbox causes a status bit to be set, and
+// potentially interrupting the intended receiver. The sender has some way to
+// determine whether the data has been read yet; as soon as it has, it may send
+// more. The mailboxes are handled differently on -II and -IIEX products, as
+// suggested below.
+//------------------------------------------------------------------------------
+// Read: Status Register for IntelliPort-II or -IIEX
+// The presence of any bit set here will cause an interrupt to the host,
+// provided the corresponding bit has been unmasked in the interrupt mask
+// register. Furthermore, interrupts to the host are disabled globally until the
+// loadware selects the irq line to use. With the exception of STN_MR, the bits
+// remain set so long as the associated condition is true.
+//
+#define FIFO_STATUS 2
+
+// Bit map of status bits which are identical for -II and -IIEX
+//
+#define ST_OUT_FULL  0x40  // Outbound FIFO full
+#define ST_IN_EMPTY  0x20  // Inbound FIFO empty
+#define ST_IN_MAIL   0x04  // Inbound Mailbox full
+
+// The following exists only on the Intelliport-IIEX, and indicates that the
+// board has not read the last outgoing mailbox data yet. In the IntelliPort-II,
+// the outgoing mailbox may be read back: a zero indicates the board has read
+// the data.
+//
+#define STE_OUT_MAIL 0x80  // Outbound mailbox full (!)
+
+// The following bits are defined differently for -II and -IIEX boards. Code
+// which relies on these bits will need to be functionally different for the two
+// types of boards and should be generally avoided because of the additional
+// complexity this creates:
+
+// Bit map of status bits only on -II
+
+// Fifo has been RESET (cleared when the status register is read). Note that
+// this condition cannot be masked and would always interrupt the host, except
+// that the hardware reset also disables interrupts globally from the board
+// until re-enabled by loadware. This could also arise from the
+// Am4701-supported command to reset the chip, but this command is generally not
+// used here.
+//
+#define STN_MR       0x80
+
+// See the AMD Am4701 data sheet for details on the following four bits. They
+// are not presently used by Computone drivers.
+//
+#define STN_OUT_AF  0x10  // Outbound FIFO almost full (programmable)
+#define STN_IN_AE   0x08  // Inbound FIFO almost empty (programmable)
+#define STN_BD      0x02  // Inbound byte detected
+#define STN_PE      0x01  // Parity/Framing condition detected
+
+// Bit-map of status bits only on -IIEX
+//
+#define STE_OUT_HF  0x10  // Outbound FIFO half full
+#define STE_IN_HF   0x08  // Inbound FIFO half full
+#define STE_IN_FULL 0x02  // Inbound FIFO full
+#define STE_OUT_MT  0x01  // Outbound FIFO empty
+
+//------------------------------------------------------------------------------
+
+// Intelliport-II -- Write Only: the pointer register.
+// Values are written to this register to select the Am4701 internal register to
+// be accessed on the next operation.
+//
+#define FIFO_PTR    0x02
+
+// Values for the pointer register
+//
+#define SEL_COMMAND 0x1    // Selects the Am4701 command register
+
+// Some possible commands:
+//
+#define SEL_CMD_MR  0x80       // Am4701 command to reset the chip
+#define SEL_CMD_SH  0x40       // Am4701 command to map the "other" port into the
+                                                       // status register.
+#define SEL_CMD_UNSH   0       // Am4701 command to "unshift": port maps into its
+                                                       // own status register.
+#define SEL_MASK     0x2       // Selects the Am4701 interrupt mask register. The
+                                                       // interrupt mask register is bit-mapped to match 
+                                                       // the status register (FIFO_STATUS) except for
+                                                       // STN_MR. (See above.)
+#define SEL_BYTE_DET 0x3       // Selects the Am4701 byte-detect register. (Not
+                                                       // normally used except in diagnostics.)
+#define SEL_OUTMAIL  0x4       // Selects the outbound mailbox (R/W). Reading back
+                                                       // a value of zero indicates that the mailbox has
+                                                       // been read by the board and is available for more
+                                                       // data./ Writing to the mailbox optionally
+                                                       // interrupts the board, depending on the loadware's
+                                                       // setting of its interrupt mask register.
+#define SEL_AEAF     0x5       // Selects AE/AF threshold register.
+#define SEL_INMAIL   0x6       // Selects the inbound mailbox (Read)
+
+//------------------------------------------------------------------------------
+// IntelliPort-IIEX --  Write Only: interrupt mask (and misc flags) register:
+// Unlike IntelliPort-II, bit assignments do NOT match those of the status
+// register.
+//
+#define FIFO_MASK    0x2
+
+// Mailbox readback select:
+// If set, reads to FIFO_MAIL will read the OUTBOUND mailbox (host to board). If
+// clear (default on reset) reads to FIFO_MAIL will read the INBOUND mailbox.
+// This is the normal situation. The clearing of a mailbox is determined on
+// -IIEX boards by waiting for the STE_OUT_MAIL bit to clear. Readback
+// capability is provided for diagnostic purposes only.
+//
+#define  MX_OUTMAIL_RSEL   0x80
+
+#define  MX_IN_MAIL  0x40      // Enables interrupts when incoming mailbox goes
+                                                       // full (ST_IN_MAIL set).
+#define  MX_IN_FULL  0x20      // Enables interrupts when incoming FIFO goes full
+                                                       // (STE_IN_FULL).
+#define  MX_IN_MT    0x08      // Enables interrupts when incoming FIFO goes empty
+                                                       // (ST_IN_MT).
+#define  MX_OUT_FULL 0x04      // Enables interrupts when outgoing FIFO goes full
+                                                       // (ST_OUT_FULL).
+#define  MX_OUT_MT   0x01      // Enables interrupts when outgoing FIFO goes empty
+                                                       // (STE_OUT_MT).
+
+// Any remaining bits are reserved, and should be written to ZERO for
+// compatibility with future Computone products.
+
+//------------------------------------------------------------------------------
+// IntelliPort-IIEX: -- These are only 6-bit mailboxes !!! -- 11111100 (low two
+// bits always read back 0).
+// Read:  One of the mailboxes, usually Inbound.
+//        Inbound Mailbox (MX_OUTMAIL_RSEL = 0)
+//        Outbound Mailbox (MX_OUTMAIL_RSEL = 1)
+// Write: Outbound Mailbox
+// For the IntelliPort-II boards, the outbound mailbox is read back to determine
+// whether the board has read the data (0 --> data has been read). For the
+// IntelliPort-IIEX, this is done by reading a status register. To determine
+// whether mailbox is available for more outbound data, use the STE_OUT_MAIL bit
+// in FIFO_STATUS. Moreover, although the Outbound Mailbox can be read back by
+// setting MX_OUTMAIL_RSEL, it is NOT cleared when the board reads it, as is the
+// case with the -II boards. For this reason, FIFO_MAIL is normally used to read
+// the inbound FIFO, and MX_OUTMAIL_RSEL kept clear. (See above for
+// MX_OUTMAIL_RSEL description.)
+//
+#define  FIFO_MAIL   0x3
+
+//------------------------------------------------------------------------------
+// WRITE ONLY:  Resets the board. (Data doesn't matter).
+//
+#define  FIFO_RESET  0x7
+
+//------------------------------------------------------------------------------
+// READ ONLY:  Will have no effect. (Data is undefined.)
+// Actually, there will be an effect, in that the operation is sure to generate
+// a bus cycle: viz., an I/O byte Read. This fact can be used to enforce short
+// delays when no comparable time constant is available.
+//
+#define  FIFO_NOP    0x7
+
+//------------------------------------------------------------------------------
+// RESET & POWER-ON RESET MESSAGE
+/*------------------------------------------------------------------------------
+RESET:
+
+The IntelliPort-II and -IIEX boards are reset in three ways: Power-up, channel
+reset, and via a write to the reset register described above. For products using
+the ISA bus, these three sources of reset are equvalent. For MCA and EISA buses,
+the Power-up and channel reset sources cause additional hardware initialization
+which should only occur at system startup time.
+
+The third type of reset, called a "command reset", is done by writing any data
+to the FIFO_RESET address described above. This resets the on-board processor,
+FIFO, UARTS, and associated hardware.
+
+This passes control of the board to the bootstrap firmware, which performs a
+Power-On Self Test and which detects its current configuration. For example,
+-IIEX products determine the size of FIFO which has been installed, and the
+number and type of expansion boxes attached.
+
+This and other information is then written to the FIFO in a 16-byte data block
+to be read by the host. This block is guaranteed to be present within two (2)
+seconds of having received the command reset. The firmware is now ready to
+receive loadware from the host.
+
+It is good practice to perform a command reset to the board explicitly as part
+of your software initialization.  This allows your code to properly restart from
+a soft boot. (Many systems do not issue channel reset on soft boot).
+
+Because of a hardware reset problem on some of the Cirrus Logic 1400's which are
+used on the product, it is recommended that you reset the board twice, separated
+by an approximately 50 milliseconds delay. (VERY approximately: probably ok to
+be off by a factor of five. The important point is that the first command reset
+in fact generates a reset pulse on the board. This pulse is guaranteed to last
+less than 10 milliseconds. The additional delay ensures the 1400 has had the
+chance to respond sufficiently to the first reset. Why not a longer delay? Much
+more than 50 milliseconds gets to be noticable, but the board would still work.
+
+Once all 16 bytes of the Power-on Reset Message have been read, the bootstrap
+firmware is ready to receive loadware.
+
+Note on Power-on Reset Message format:
+The various fields have been designed with future expansion in view.
+Combinations of bitfields and values have been defined which define products
+which may not currently exist. This has been done to allow drivers to anticipate
+the possible introduction of products in a systematic fashion. This is not
+intended to suggest that each potential product is actually under consideration.
+------------------------------------------------------------------------------*/
+
+//----------------------------------------
+// Format of Power-on Reset Message
+//----------------------------------------
+
+typedef union _porStr          // "por" stands for Power On Reset
+{
+       unsigned char  c[16];   // array used when considering the message as a
+                                                       // string of undifferentiated characters
+
+       struct                                  // Elements used when considering values
+       {
+               // The first two bytes out of the FIFO are two magic numbers. These are
+               // intended to establish that there is indeed a member of the
+               // IntelliPort-II(EX) family present. The remaining bytes may be 
+               // expected // to be valid. When reading the Power-on Reset message, 
+               // if the magic numbers do not match it is probably best to stop
+               // reading immediately. You are certainly not reading our board (unless
+               // hardware is faulty), and may in fact be reading some other piece of
+               // hardware.
+
+               unsigned char porMagic1;   // magic number: first byte == POR_MAGIC_1 
+               unsigned char porMagic2;   // magic number: second byte == POR_MAGIC_2 
+
+               // The Version, Revision, and Subrevision are stored as absolute numbers
+               // and would normally be displayed in the format V.R.S (e.g. 1.0.2)
+
+               unsigned char porVersion;  // Bootstrap firmware version number
+               unsigned char porRevision; // Bootstrap firmware revision number
+               unsigned char porSubRev;   // Bootstrap firmware sub-revision number
+
+               unsigned char porID;    // Product ID:  Bit-mapped according to
+                                                               // conventions described below. Among other
+                                                               // things, this allows us to distinguish
+                                                               // IntelliPort-II boards from IntelliPort-IIEX
+                                                               // boards.
+
+               unsigned char porBus;   // IntelliPort-II: Unused
+                                                               // IntelliPort-IIEX: Bus Information:
+                                                               // Bit-mapped below
+
+               unsigned char porMemory;        // On-board DRAM size: in 32k blocks
+
+               // porPorts1 (and porPorts2) are used to determine the ports which are
+               // available to the board. For non-expandable product, a single number 
+               // is sufficient. For expandable product, the board may be connected
+               // to as many as four boxes. Each box may be (so far) either a 16-port
+               // or an 8-port size. Whenever an 8-port box is used, the remaining 8
+               // ports leave gaps between existing channels. For that reason,
+               // expandable products must report a MAP of available channels. Since 
+               // each UART supports four ports, we represent each UART found by a
+               // single bit. Using two bytes to supply the mapping information we
+               // report the presense or absense of up to 16 UARTS, or 64 ports in
+               // steps of 4 ports. For -IIEX products, the ports are numbered
+               // starting at the box closest to the controller in the "chain".
+
+               // Interpreted Differently for IntelliPort-II and -IIEX.
+               // -II:   Number of ports (Derived actually from product ID). See
+               // Diag1&2 to indicate if uart was actually detected.
+               // -IIEX: Bit-map of UARTS found, LSB (see below for MSB of this). This
+               //        bitmap is based on detecting the uarts themselves; 
+               //        see porFlags for information from the box i.d's.
+               unsigned char  porPorts1;
+
+               unsigned char  porDiag1;        // Results of on-board P.O.S.T, 1st byte
+               unsigned char  porDiag2;        // Results of on-board P.O.S.T, 2nd byte
+               unsigned char  porSpeed;        // Speed of local CPU: given as MHz x10
+                                                                       // e.g., 16.0 MHz CPU is reported as 160
+               unsigned char  porFlags;        // Misc information (see manifests below)
+                                                                       // Bit-mapped: CPU type, UART's present
+
+               unsigned char  porPorts2;       // -II:  Undefined
+                                                                       // -IIEX: Bit-map of UARTS found, MSB (see
+                                                                       //        above for LSB)
+
+               // IntelliPort-II: undefined
+               // IntelliPort-IIEX: 1 << porFifoSize gives the size, in bytes, of the
+               // host interface FIFO, in each direction. When running the -IIEX in
+               // 8-bit mode, fifo capacity is halved. The bootstrap firmware will
+               // have already accounted for this fact in generating this number.
+               unsigned char  porFifoSize;
+
+               // IntelliPort-II: undefined
+               // IntelliPort-IIEX: The number of boxes connected. (Presently 1-4)
+               unsigned char  porNumBoxes;
+       } e;
+} porStr, *porStrPtr;
+
+//--------------------------
+// Values for porStr fields
+//--------------------------
+
+//---------------------
+// porMagic1, porMagic2
+//----------------------
+//
+#define  POR_MAGIC_1    0x96  // The only valid value for porMagic1
+#define  POR_MAGIC_2    0x35  // The only valid value for porMagic2
+#define  POR_1_INDEX    0     // Byte position of POR_MAGIC_1
+#define  POR_2_INDEX    1     // Ditto for POR_MAGIC_2
+
+//----------------------
+// porID
+//----------------------
+//
+#define  POR_ID_FAMILY  0xc0   // These bits indicate the general family of
+                                                               // product.
+#define  POR_ID_FII     0x00   // Family is "IntelliPort-II"
+#define  POR_ID_FIIEX   0x40   // Family is "IntelliPort-IIEX"
+
+// These bits are reserved, presently zero. May be used at a later date to
+// convey other product information.
+//
+#define POR_ID_RESERVED 0x3c
+
+#define POR_ID_SIZE     0x03   // Remaining bits indicate number of ports &
+                                                               // Connector information.
+#define POR_ID_II_8     0x00   // For IntelliPort-II, indicates 8-port using
+                                                               // standard brick.
+#define POR_ID_II_8R    0x01   // For IntelliPort-II, indicates 8-port using
+                                                               // RJ11's (no CTS)
+#define POR_ID_II_6     0x02   // For IntelliPort-II, indicates 6-port using
+                                                               // RJ45's
+#define POR_ID_II_4     0x03   // For IntelliPort-II, indicates 4-port using
+                                                               // 4xRJ45 connectors
+#define POR_ID_EX       0x00   // For IntelliPort-IIEX, indicates standard
+                                                               // expandable controller (other values reserved)
+
+//----------------------
+// porBus
+//----------------------
+
+// IntelliPort-IIEX only: Board is installed in a 16-bit slot
+//
+#define POR_BUS_SLOT16  0x20
+
+// IntelliPort-IIEX only: DIP switch #8 is on, selecting 16-bit host interface
+// operation.
+// 
+#define POR_BUS_DIP16   0x10
+
+// Bits 0-2 indicate type of bus: This information is stored in the bootstrap
+// loadware, different loadware being used on different products for different
+// buses. For most situations, the drivers do not need this information; but it
+// is handy in a diagnostic environment. For example, on microchannel boards,
+// you would not want to try to test several interrupts, only the one for which
+// you were configured.
+//
+#define  POR_BUS_TYPE   0x07
+
+// Unknown:  this product doesn't know what bus it is running in. (e.g. if same
+// bootstrap firmware were wanted for two different buses.)
+//
+#define  POR_BUS_T_UNK  0
+
+// Note: existing firmware for ISA-8 and MC-8 currently report the POR_BUS_T_UNK
+// state, since the same bootstrap firmware is used for each.
+
+#define  POR_BUS_T_MCA  1  // MCA BUS */
+#define  POR_BUS_T_EISA 2  // EISA BUS */
+#define  POR_BUS_T_ISA  3  // ISA BUS */
+
+// Values 4-7 Reserved
+
+// Remaining bits are reserved
+
+//----------------------
+// porDiag1
+//----------------------
+
+#define  POR_BAD_MAPPER 0x80   // HW failure on P.O.S.T: Chip mapper failed
+
+// These two bits valid only for the IntelliPort-II
+//
+#define  POR_BAD_UART1  0x01   // First  1400 bad
+#define  POR_BAD_UART2  0x02   // Second 1400 bad
+
+//----------------------
+// porDiag2
+//----------------------
+
+#define  POR_DEBUG_PORT 0x80   // debug port was detected by the P.O.S.T
+#define  POR_DIAG_OK    0x00   // Indicates passage: Failure codes not yet
+                                                               // available.
+                                                               // Other bits undefined.
+//----------------------
+// porFlags
+//----------------------
+
+#define  POR_CPU     0x03      // These bits indicate supposed CPU type
+#define  POR_CPU_8   0x01      // Board uses an 80188 (no such thing yet)
+#define  POR_CPU_6   0x02      // Board uses an 80186 (all existing products)
+#define  POR_CEX4    0x04      // If set, this is an ISA-CEX/4: An ISA-4 (asic)
+                                                       // which is architected like an ISA-CEX connected
+                                                       // to a (hitherto impossible) 4-port box.
+#define POR_BOXES    0xf0      // Valid for IntelliPort-IIEX only: Map of Box
+                                                       // sizes based on box I.D.
+#define POR_BOX_16   0x10      // Set indicates 16-port, clear 8-port
+
+//-------------------------------------
+// LOADWARE and DOWNLOADING CODE
+//-------------------------------------
+
+/*
+Loadware may be sent to the board in two ways:
+1) It may be read from a (binary image) data file block by block as each block
+       is sent to the board. This is only possible when the initialization is
+       performed by code which can access your file system. This is most suitable
+       for diagnostics and appications which use the interface library directly.
+
+2) It may be hard-coded into your source by including a .h file (typically
+       supplied by Computone), which declares a data array and initializes every
+       element. This achieves the same result as if an entire loadware file had 
+       been read into the array.
+
+       This requires more data space in your program, but access to the file system
+       is not required. This method is more suited to driver code, which typically
+       is running at a level too low to access the file system directly.
+
+At present, loadware can only be generated at Computone.
+
+All Loadware begins with a header area which has a particular format. This
+includes a magic number which identifies the file as being (purportedly)
+loadware, CRC (for the loader), and version information.
+*/
+
+
+//-----------------------------------------------------------------------------
+// Format of loadware block
+//
+// This is defined as a union so we can pass a pointer to one of these items
+// and (if it is the first block) pick out the version information, etc.
+//
+// Otherwise, to deal with this as a simple character array
+//------------------------------------------------------------------------------
+
+#define LOADWARE_BLOCK_SIZE   512   // Number of bytes in each block of loadware
+
+typedef union _loadHdrStr
+{
+       unsigned char c[LOADWARE_BLOCK_SIZE];  // Valid for every block
+
+       struct  // These fields are valid for only the first block of loadware.
+       {
+               unsigned char loadMagic;                // Magic number: see below
+               unsigned char loadBlocksMore;   // How many more blocks?
+               unsigned char loadCRC[2];               // Two CRC bytes: used by loader
+               unsigned char loadVersion;              // Version number
+               unsigned char loadRevision;             // Revision number
+               unsigned char loadSubRevision;  // Sub-revision number
+               unsigned char loadSpares[9];    // Presently unused
+               unsigned char loadDates[32];    // Null-terminated string which can give
+                                                                               // date and time of compilation
+       } e;
+} loadHdrStr, *loadHdrStrPtr;
+
+//------------------------------------
+// Defines for downloading code:
+//------------------------------------
+
+// The loadMagic field in the first block of the loadfile must be this, else the
+// file is not valid.
+//
+#define  MAGIC_LOADFILE 0x3c
+
+// How do we know the load was successful? On completion of the load, the
+// bootstrap firmware returns a code to indicate whether it thought the download
+// was valid and intends to execute it. These are the only possible valid codes:
+//
+#define  LOADWARE_OK    0xc3        // Download was ok
+#define  LOADWARE_BAD   0x5a        // Download was bad (CRC error)
+
+// Constants applicable to writing blocks of loadware:
+// The first block of loadware might take 600 mS to load, in extreme cases.
+// (Expandable board: worst case for sending startup messages to the LCD's).
+// The 600mS figure is not really a calculation, but a conservative
+// guess/guarantee. Usually this will be within 100 mS, like subsequent blocks.
+//
+#define  MAX_DLOAD_START_TIME 1000  // 1000 mS
+#define  MAX_DLOAD_READ_TIME  100   // 100 mS
+
+// Firmware should respond with status (see above) within this long of host
+// having sent the final block.
+//
+#define  MAX_DLOAD_ACK_TIME   100   // 100 mS, again!
+
+//------------------------------------------------------
+// MAXIMUM NUMBER OF PORTS PER BOARD:
+// This is fixed for now (with the expandable), but may
+// be expanding according to even newer products.
+//------------------------------------------------------
+//
+#define ABS_MAX_BOXES   4     // Absolute most boxes per board
+#define ABS_BIGGEST_BOX 16    // Absolute the most ports per box
+#define ABS_MOST_PORTS  (ABS_MAX_BOXES * ABS_BIGGEST_BOX)
+
+#define I2_OUTSW(port, addr, count)    outsw((port), (addr), (((count)+1)/2))
+#define I2_OUTSB(port, addr, count)    outsb((port), (addr), (((count)+1))&-2)
+#define I2_INSW(port, addr, count)     insw((port), (addr), (((count)+1)/2))
+#define I2_INSB(port, addr, count)     insb((port), (addr), (((count)+1))&-2)
+
+#endif   // I2HW_H
+
diff --git a/drivers/staging/tty/ip2/i2lib.c b/drivers/staging/tty/ip2/i2lib.c
new file mode 100644 (file)
index 0000000..0d10b89
--- /dev/null
@@ -0,0 +1,2214 @@
+/*******************************************************************************
+*
+*   (c) 1999 by Computone Corporation
+*
+********************************************************************************
+*
+*
+*   PACKAGE:     Linux tty Device Driver for IntelliPort family of multiport
+*                serial I/O controllers.
+*
+*   DESCRIPTION: High-level interface code for the device driver. Uses the
+*                Extremely Low Level Interface Support (i2ellis.c). Provides an
+*                interface to the standard loadware, to support drivers or
+*                application code. (This is included source code, not a separate
+*                compilation module.)
+*
+*******************************************************************************/
+//------------------------------------------------------------------------------
+// Note on Strategy:
+// Once the board has been initialized, it will interrupt us when:
+// 1) It has something in the fifo for us to read (incoming data, flow control
+// packets, or whatever).
+// 2) It has stripped whatever we have sent last time in the FIFO (and
+// consequently is ready for more).
+//
+// Note also that the buffer sizes declared in i2lib.h are VERY SMALL. This
+// worsens performance considerably, but is done so that a great many channels
+// might use only a little memory.
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// Revision History:
+//
+// 0.00 -  4/16/91 --- First Draft
+// 0.01 -  4/29/91 --- 1st beta release
+// 0.02 -  6/14/91 --- Changes to allow small model compilation
+// 0.03 -  6/17/91 MAG Break reporting protected from interrupts routines with
+//                     in-line asm added for moving data to/from ring buffers,
+//                     replacing a variety of methods used previously.
+// 0.04 -  6/21/91 MAG Initial flow-control packets not queued until
+//                     i2_enable_interrupts time. Former versions would enqueue
+//                     them at i2_init_channel time, before we knew how many
+//                     channels were supposed to exist!
+// 0.05 - 10/12/91 MAG Major changes: works through the ellis.c routines now;
+//                     supports new 16-bit protocol and expandable boards.
+//      - 10/24/91 MAG Most changes in place and stable.
+// 0.06 -  2/20/92 MAG Format of CMD_HOTACK corrected: the command takes no
+//                     argument.
+// 0.07 -- 3/11/92 MAG Support added to store special packet types at interrupt
+//                     level (mostly responses to specific commands.)
+// 0.08 -- 3/30/92 MAG Support added for STAT_MODEM packet
+// 0.09 -- 6/24/93 MAG i2Link... needed to update number of boards BEFORE
+//                     turning on the interrupt.
+// 0.10 -- 6/25/93 MAG To avoid gruesome death from a bad board, we sanity check
+//                     some incoming.
+//
+// 1.1  - 12/25/96 AKM Linux version.
+//      - 10/09/98 DMC Revised Linux version.
+//------------------------------------------------------------------------------
+
+//************
+//* Includes *
+//************
+
+#include <linux/sched.h>
+#include "i2lib.h"
+
+
+//***********************
+//* Function Prototypes *
+//***********************
+static void i2QueueNeeds(i2eBordStrPtr, i2ChanStrPtr, int);
+static i2ChanStrPtr i2DeQueueNeeds(i2eBordStrPtr, int );
+static void i2StripFifo(i2eBordStrPtr);
+static void i2StuffFifoBypass(i2eBordStrPtr);
+static void i2StuffFifoFlow(i2eBordStrPtr);
+static void i2StuffFifoInline(i2eBordStrPtr);
+static int i2RetryFlushOutput(i2ChanStrPtr);
+
+// Not a documented part of the library routines (careful...) but the Diagnostic
+// i2diag.c finds them useful to help the throughput in certain limited
+// single-threaded operations.
+static void iiSendPendingMail(i2eBordStrPtr);
+static void serviceOutgoingFifo(i2eBordStrPtr);
+
+// Functions defined in ip2.c as part of interrupt handling
+static void do_input(struct work_struct *);
+static void do_status(struct work_struct *);
+
+//***************
+//* Debug  Data *
+//***************
+#ifdef DEBUG_FIFO
+
+unsigned char DBGBuf[0x4000];
+unsigned short I = 0;
+
+static void
+WriteDBGBuf(char *s, unsigned char *src, unsigned short n ) 
+{
+       char *p = src;
+
+       // XXX: We need a spin lock here if we ever use this again
+
+       while (*s) {    // copy label
+               DBGBuf[I] = *s++;
+               I = I++ & 0x3fff;
+       }
+       while (n--) {   // copy data
+               DBGBuf[I] = *p++;
+               I = I++ & 0x3fff;
+       }
+}
+
+static void
+fatality(i2eBordStrPtr pB )
+{
+       int i;
+
+       for (i=0;i<sizeof(DBGBuf);i++) {
+               if ((i%16) == 0)
+                       printk("\n%4x:",i);
+               printk("%02x ",DBGBuf[i]);
+       }
+       printk("\n");
+       for (i=0;i<sizeof(DBGBuf);i++) {
+               if ((i%16) == 0)
+                       printk("\n%4x:",i);
+               if (DBGBuf[i] >= ' ' && DBGBuf[i] <= '~') {
+                       printk(" %c ",DBGBuf[i]);
+               } else {
+                       printk(" . ");
+               }
+       }
+       printk("\n");
+       printk("Last index %x\n",I);
+}
+#endif /* DEBUG_FIFO */
+
+//********
+//* Code *
+//********
+
+static inline int
+i2Validate ( i2ChanStrPtr pCh )
+{
+       //ip2trace(pCh->port_index, ITRC_VERIFY,ITRC_ENTER,2,pCh->validity,
+       //      (CHANNEL_MAGIC | CHANNEL_SUPPORT));
+       return ((pCh->validity & (CHANNEL_MAGIC_BITS | CHANNEL_SUPPORT)) 
+                         == (CHANNEL_MAGIC | CHANNEL_SUPPORT));
+}
+
+static void iiSendPendingMail_t(unsigned long data)
+{
+       i2eBordStrPtr pB = (i2eBordStrPtr)data;
+
+       iiSendPendingMail(pB);
+}
+
+//******************************************************************************
+// Function:   iiSendPendingMail(pB)
+// Parameters: Pointer to a board structure
+// Returns:    Nothing
+//
+// Description:
+// If any outgoing mail bits are set and there is outgoing mailbox is empty,
+// send the mail and clear the bits.
+//******************************************************************************
+static void
+iiSendPendingMail(i2eBordStrPtr pB)
+{
+       if (pB->i2eOutMailWaiting && (!pB->i2eWaitingForEmptyFifo) )
+       {
+               if (iiTrySendMail(pB, pB->i2eOutMailWaiting))
+               {
+                       /* If we were already waiting for fifo to empty,
+                        * or just sent MB_OUT_STUFFED, then we are
+                        * still waiting for it to empty, until we should
+                        * receive an MB_IN_STRIPPED from the board.
+                        */
+                       pB->i2eWaitingForEmptyFifo |=
+                               (pB->i2eOutMailWaiting & MB_OUT_STUFFED);
+                       pB->i2eOutMailWaiting = 0;
+                       pB->SendPendingRetry = 0;
+               } else {
+/*             The only time we hit this area is when "iiTrySendMail" has
+               failed.  That only occurs when the outbound mailbox is
+               still busy with the last message.  We take a short breather
+               to let the board catch up with itself and then try again.
+               16 Retries is the limit - then we got a borked board.
+                       /\/\|=mhw=|\/\/                         */
+
+                       if( ++pB->SendPendingRetry < 16 ) {
+                               setup_timer(&pB->SendPendingTimer,
+                                       iiSendPendingMail_t, (unsigned long)pB);
+                               mod_timer(&pB->SendPendingTimer, jiffies + 1);
+                       } else {
+                               printk( KERN_ERR "IP2: iiSendPendingMail unable to queue outbound mail\n" );
+                       }
+               }
+       }
+}
+
+//******************************************************************************
+// Function:   i2InitChannels(pB, nChannels, pCh)
+// Parameters: Pointer to Ellis Board structure
+//             Number of channels to initialize
+//             Pointer to first element in an array of channel structures
+// Returns:    Success or failure
+//
+// Description:
+//
+// This function patches pointers, back-pointers, and initializes all the
+// elements in the channel structure array.
+//
+// This should be run after the board structure is initialized, through having
+// loaded the standard loadware (otherwise it complains).
+//
+// In any case, it must be done before any serious work begins initializing the
+// irq's or sending commands...
+//
+//******************************************************************************
+static int
+i2InitChannels ( i2eBordStrPtr pB, int nChannels, i2ChanStrPtr pCh)
+{
+       int index, stuffIndex;
+       i2ChanStrPtr *ppCh;
+       
+       if (pB->i2eValid != I2E_MAGIC) {
+               I2_COMPLETE(pB, I2EE_BADMAGIC);
+       }
+       if (pB->i2eState != II_STATE_STDLOADED) {
+               I2_COMPLETE(pB, I2EE_BADSTATE);
+       }
+
+       rwlock_init(&pB->read_fifo_spinlock);
+       rwlock_init(&pB->write_fifo_spinlock);
+       rwlock_init(&pB->Dbuf_spinlock);
+       rwlock_init(&pB->Bbuf_spinlock);
+       rwlock_init(&pB->Fbuf_spinlock);
+       
+       // NO LOCK needed yet - this is init
+
+       pB->i2eChannelPtr = pCh;
+       pB->i2eChannelCnt = nChannels;
+
+       pB->i2Fbuf_strip = pB->i2Fbuf_stuff = 0;
+       pB->i2Dbuf_strip = pB->i2Dbuf_stuff = 0;
+       pB->i2Bbuf_strip = pB->i2Bbuf_stuff = 0;
+
+       pB->SendPendingRetry = 0;
+
+       memset ( pCh, 0, sizeof (i2ChanStr) * nChannels );
+
+       for (index = stuffIndex = 0, ppCh = (i2ChanStrPtr *)(pB->i2Fbuf);
+                 nChannels && index < ABS_MOST_PORTS;
+                 index++)
+       {
+               if ( !(pB->i2eChannelMap[index >> 4] & (1 << (index & 0xf)) ) ) {
+                       continue;
+               }
+               rwlock_init(&pCh->Ibuf_spinlock);
+               rwlock_init(&pCh->Obuf_spinlock);
+               rwlock_init(&pCh->Cbuf_spinlock);
+               rwlock_init(&pCh->Pbuf_spinlock);
+               // NO LOCK needed yet - this is init
+               // Set up validity flag according to support level
+               if (pB->i2eGoodMap[index >> 4] & (1 << (index & 0xf)) ) {
+                       pCh->validity = CHANNEL_MAGIC | CHANNEL_SUPPORT;
+               } else {
+                       pCh->validity = CHANNEL_MAGIC;
+               }
+               pCh->pMyBord = pB;      /* Back-pointer */
+
+               // Prepare an outgoing flow-control packet to send as soon as the chance
+               // occurs.
+               if ( pCh->validity & CHANNEL_SUPPORT ) {
+                       pCh->infl.hd.i2sChannel = index;
+                       pCh->infl.hd.i2sCount = 5;
+                       pCh->infl.hd.i2sType = PTYPE_BYPASS;
+                       pCh->infl.fcmd = 37;
+                       pCh->infl.asof = 0;
+                       pCh->infl.room = IBUF_SIZE - 1;
+
+                       pCh->whenSendFlow = (IBUF_SIZE/5)*4; // when 80% full
+
+               // The following is similar to calling i2QueueNeeds, except that this
+               // is done in longhand, since we are setting up initial conditions on
+               // many channels at once.
+                       pCh->channelNeeds = NEED_FLOW;  // Since starting from scratch
+                       pCh->sinceLastFlow = 0;         // No bytes received since last flow
+                                                                                       // control packet was queued
+                       stuffIndex++;
+                       *ppCh++ = pCh;      // List this channel as needing
+                                                               // initial flow control packet sent
+               }
+
+               // Don't allow anything to be sent until the status packets come in from
+               // the board.
+
+               pCh->outfl.asof = 0;
+               pCh->outfl.room = 0;
+
+               // Initialize all the ring buffers
+
+               pCh->Ibuf_stuff = pCh->Ibuf_strip = 0;
+               pCh->Obuf_stuff = pCh->Obuf_strip = 0;
+               pCh->Cbuf_stuff = pCh->Cbuf_strip = 0;
+
+               memset( &pCh->icount, 0, sizeof (struct async_icount) );
+               pCh->hotKeyIn       = HOT_CLEAR;
+               pCh->channelOptions = 0;
+               pCh->bookMarks      = 0;
+               init_waitqueue_head(&pCh->pBookmarkWait);
+
+               init_waitqueue_head(&pCh->open_wait);
+               init_waitqueue_head(&pCh->close_wait);
+               init_waitqueue_head(&pCh->delta_msr_wait);
+
+               // Set base and divisor so default custom rate is 9600
+               pCh->BaudBase    = 921600;      // MAX for ST654, changed after we get
+               pCh->BaudDivisor = 96;          // the boxids (UART types) later
+
+               pCh->dataSetIn   = 0;
+               pCh->dataSetOut  = 0;
+
+               pCh->wopen       = 0;
+               pCh->throttled   = 0;
+
+               pCh->speed       = CBR_9600;
+
+               pCh->flags    = 0;
+
+               pCh->ClosingDelay     = 5*HZ/10;
+               pCh->ClosingWaitTime  = 30*HZ;
+
+               // Initialize task queue objects
+               INIT_WORK(&pCh->tqueue_input, do_input);
+               INIT_WORK(&pCh->tqueue_status, do_status);
+
+#ifdef IP2DEBUG_TRACE
+               pCh->trace = ip2trace;
+#endif
+
+               ++pCh;
+       --nChannels;
+       }
+       // No need to check for wrap here; this is initialization.
+       pB->i2Fbuf_stuff = stuffIndex;
+       I2_COMPLETE(pB, I2EE_GOOD);
+
+}
+
+//******************************************************************************
+// Function:   i2DeQueueNeeds(pB, type)
+// Parameters: Pointer to a board structure
+//             type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW
+// Returns:   
+//             Pointer to a channel structure
+//
+// Description: Returns pointer struct of next channel that needs service of
+//  the type specified. Otherwise returns a NULL reference.
+//
+//******************************************************************************
+static i2ChanStrPtr 
+i2DeQueueNeeds(i2eBordStrPtr pB, int type)
+{
+       unsigned short queueIndex;
+       unsigned long flags;
+
+       i2ChanStrPtr pCh = NULL;
+
+       switch(type) {
+
+       case  NEED_INLINE:
+
+               write_lock_irqsave(&pB->Dbuf_spinlock, flags);
+               if ( pB->i2Dbuf_stuff != pB->i2Dbuf_strip)
+               {
+                       queueIndex = pB->i2Dbuf_strip;
+                       pCh = pB->i2Dbuf[queueIndex];
+                       queueIndex++;
+                       if (queueIndex >= CH_QUEUE_SIZE) {
+                               queueIndex = 0;
+                       }
+                       pB->i2Dbuf_strip = queueIndex;
+                       pCh->channelNeeds &= ~NEED_INLINE;
+               }
+               write_unlock_irqrestore(&pB->Dbuf_spinlock, flags);
+               break;
+
+       case NEED_BYPASS:
+
+               write_lock_irqsave(&pB->Bbuf_spinlock, flags);
+               if (pB->i2Bbuf_stuff != pB->i2Bbuf_strip)
+               {
+                       queueIndex = pB->i2Bbuf_strip;
+                       pCh = pB->i2Bbuf[queueIndex];
+                       queueIndex++;
+                       if (queueIndex >= CH_QUEUE_SIZE) {
+                               queueIndex = 0;
+                       }
+                       pB->i2Bbuf_strip = queueIndex;
+                       pCh->channelNeeds &= ~NEED_BYPASS;
+               }
+               write_unlock_irqrestore(&pB->Bbuf_spinlock, flags);
+               break;
+       
+       case NEED_FLOW:
+
+               write_lock_irqsave(&pB->Fbuf_spinlock, flags);
+               if (pB->i2Fbuf_stuff != pB->i2Fbuf_strip)
+               {
+                       queueIndex = pB->i2Fbuf_strip;
+                       pCh = pB->i2Fbuf[queueIndex];
+                       queueIndex++;
+                       if (queueIndex >= CH_QUEUE_SIZE) {
+                               queueIndex = 0;
+                       }
+                       pB->i2Fbuf_strip = queueIndex;
+                       pCh->channelNeeds &= ~NEED_FLOW;
+               }
+               write_unlock_irqrestore(&pB->Fbuf_spinlock, flags);
+               break;
+       default:
+               printk(KERN_ERR "i2DeQueueNeeds called with bad type:%x\n",type);
+               break;
+       }
+       return pCh;
+}
+
+//******************************************************************************
+// Function:   i2QueueNeeds(pB, pCh, type)
+// Parameters: Pointer to a board structure
+//             Pointer to a channel structure
+//             type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW
+// Returns:    Nothing
+//
+// Description:
+// For each type of need selected, if the given channel is not already in the
+// queue, adds it, and sets the flag indicating it is in the queue.
+//******************************************************************************
+static void
+i2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type)
+{
+       unsigned short queueIndex;
+       unsigned long flags;
+
+       // We turn off all the interrupts during this brief process, since the
+       // interrupt-level code might want to put things on the queue as well.
+
+       switch (type) {
+
+       case NEED_INLINE:
+
+               write_lock_irqsave(&pB->Dbuf_spinlock, flags);
+               if ( !(pCh->channelNeeds & NEED_INLINE) )
+               {
+                       pCh->channelNeeds |= NEED_INLINE;
+                       queueIndex = pB->i2Dbuf_stuff;
+                       pB->i2Dbuf[queueIndex++] = pCh;
+                       if (queueIndex >= CH_QUEUE_SIZE)
+                               queueIndex = 0;
+                       pB->i2Dbuf_stuff = queueIndex;
+               }
+               write_unlock_irqrestore(&pB->Dbuf_spinlock, flags);
+               break;
+
+       case NEED_BYPASS:
+
+               write_lock_irqsave(&pB->Bbuf_spinlock, flags);
+               if ((type & NEED_BYPASS) && !(pCh->channelNeeds & NEED_BYPASS))
+               {
+                       pCh->channelNeeds |= NEED_BYPASS;
+                       queueIndex = pB->i2Bbuf_stuff;
+                       pB->i2Bbuf[queueIndex++] = pCh;
+                       if (queueIndex >= CH_QUEUE_SIZE)
+                               queueIndex = 0;
+                       pB->i2Bbuf_stuff = queueIndex;
+               } 
+               write_unlock_irqrestore(&pB->Bbuf_spinlock, flags);
+               break;
+
+       case NEED_FLOW:
+
+               write_lock_irqsave(&pB->Fbuf_spinlock, flags);
+               if ((type & NEED_FLOW) && !(pCh->channelNeeds & NEED_FLOW))
+               {
+                       pCh->channelNeeds |= NEED_FLOW;
+                       queueIndex = pB->i2Fbuf_stuff;
+                       pB->i2Fbuf[queueIndex++] = pCh;
+                       if (queueIndex >= CH_QUEUE_SIZE)
+                               queueIndex = 0;
+                       pB->i2Fbuf_stuff = queueIndex;
+               }
+               write_unlock_irqrestore(&pB->Fbuf_spinlock, flags);
+               break;
+
+       case NEED_CREDIT:
+               pCh->channelNeeds |= NEED_CREDIT;
+               break;
+       default:
+               printk(KERN_ERR "i2QueueNeeds called with bad type:%x\n",type);
+               break;
+       }
+       return;
+}
+
+//******************************************************************************
+// Function:   i2QueueCommands(type, pCh, timeout, nCommands, pCs,...)
+// Parameters: type - PTYPE_BYPASS or PTYPE_INLINE
+//             pointer to the channel structure
+//             maximum period to wait
+//             number of commands (n)
+//             n commands
+// Returns:    Number of commands sent, or -1 for error
+//
+// get board lock before calling
+//
+// Description:
+// Queues up some commands to be sent to a channel. To send possibly several
+// bypass or inline commands to the given channel. The timeout parameter
+// indicates how many HUNDREDTHS OF SECONDS to wait until there is room:
+// 0 = return immediately if no room, -ive  = wait forever, +ive = number of
+// 1/100 seconds to wait. Return values:
+// -1 Some kind of nasty error: bad channel structure or invalid arguments.
+//  0 No room to send all the commands
+// (+)   Number of commands sent
+//******************************************************************************
+static int
+i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands,
+                                        cmdSyntaxPtr pCs0,...)
+{
+       int totalsize = 0;
+       int blocksize;
+       int lastended;
+       cmdSyntaxPtr *ppCs;
+       cmdSyntaxPtr pCs;
+       int count;
+       int flag;
+       i2eBordStrPtr pB;
+
+       unsigned short maxBlock;
+       unsigned short maxBuff;
+       short bufroom;
+       unsigned short stuffIndex;
+       unsigned char *pBuf;
+       unsigned char *pInsert;
+       unsigned char *pDest, *pSource;
+       unsigned short channel;
+       int cnt;
+       unsigned long flags = 0;
+       rwlock_t *lock_var_p = NULL;
+
+       // Make sure the channel exists, otherwise do nothing
+       if ( !i2Validate ( pCh ) ) {
+               return -1;
+       }
+
+       ip2trace (CHANN, ITRC_QUEUE, ITRC_ENTER, 0 );
+
+       pB = pCh->pMyBord;
+
+       // Board must also exist, and THE INTERRUPT COMMAND ALREADY SENT
+       if (pB->i2eValid != I2E_MAGIC || pB->i2eUsingIrq == I2_IRQ_UNDEFINED)
+               return -2;
+       // If the board has gone fatal, return bad, and also hit the trap routine if
+       // it exists.
+       if (pB->i2eFatal) {
+               if ( pB->i2eFatalTrap ) {
+                       (*(pB)->i2eFatalTrap)(pB);
+               }
+               return -3;
+       }
+       // Set up some variables, Which buffers are we using?  How big are they?
+       switch(type)
+       {
+       case PTYPE_INLINE:
+               flag = INL;
+               maxBlock = MAX_OBUF_BLOCK;
+               maxBuff = OBUF_SIZE;
+               pBuf = pCh->Obuf;
+               break;
+       case PTYPE_BYPASS:
+               flag = BYP;
+               maxBlock = MAX_CBUF_BLOCK;
+               maxBuff = CBUF_SIZE;
+               pBuf = pCh->Cbuf;
+               break;
+       default:
+               return -4;
+       }
+       // Determine the total size required for all the commands
+       totalsize = blocksize = sizeof(i2CmdHeader);
+       lastended = 0;
+       ppCs = &pCs0;
+       for ( count = nCommands; count; count--, ppCs++)
+       {
+               pCs = *ppCs;
+               cnt = pCs->length;
+               // Will a new block be needed for this one? 
+               // Two possible reasons: too
+               // big or previous command has to be at the end of a packet.
+               if ((blocksize + cnt > maxBlock) || lastended) {
+                       blocksize = sizeof(i2CmdHeader);
+                       totalsize += sizeof(i2CmdHeader);
+               }
+               totalsize += cnt;
+               blocksize += cnt;
+
+               // If this command had to end a block, then we will make sure to
+               // account for it should there be any more blocks.
+               lastended = pCs->flags & END;
+       }
+       for (;;) {
+               // Make sure any pending flush commands go out before we add more data.
+               if ( !( pCh->flush_flags && i2RetryFlushOutput( pCh ) ) ) {
+                       // How much room (this time through) ?
+                       switch(type) {
+                       case PTYPE_INLINE:
+                               lock_var_p = &pCh->Obuf_spinlock;
+                               write_lock_irqsave(lock_var_p, flags);
+                               stuffIndex = pCh->Obuf_stuff;
+                               bufroom = pCh->Obuf_strip - stuffIndex;
+                               break;
+                       case PTYPE_BYPASS:
+                               lock_var_p = &pCh->Cbuf_spinlock;
+                               write_lock_irqsave(lock_var_p, flags);
+                               stuffIndex = pCh->Cbuf_stuff;
+                               bufroom = pCh->Cbuf_strip - stuffIndex;
+                               break;
+                       default:
+                               return -5;
+                       }
+                       if (--bufroom < 0) {
+                               bufroom += maxBuff;
+                       }
+
+                       ip2trace (CHANN, ITRC_QUEUE, 2, 1, bufroom );
+
+                       // Check for overflow
+                       if (totalsize <= bufroom) {
+                               // Normal Expected path - We still hold LOCK
+                               break; /* from for()- Enough room: goto proceed */
+                       }
+                       ip2trace(CHANN, ITRC_QUEUE, 3, 1, totalsize);
+                       write_unlock_irqrestore(lock_var_p, flags);
+               } else
+                       ip2trace(CHANN, ITRC_QUEUE, 3, 1, totalsize);
+
+               /* Prepare to wait for buffers to empty */
+               serviceOutgoingFifo(pB);        // Dump what we got
+
+               if (timeout == 0) {
+                       return 0;   // Tired of waiting
+               }
+               if (timeout > 0)
+                       timeout--;   // So negative values == forever
+               
+               if (!in_interrupt()) {
+                       schedule_timeout_interruptible(1);      // short nap
+               } else {
+                       // we cannot sched/sleep in interrupt silly
+                       return 0;   
+               }
+               if (signal_pending(current)) {
+                       return 0;   // Wake up! Time to die!!!
+               }
+
+               ip2trace (CHANN, ITRC_QUEUE, 4, 0 );
+
+       }       // end of for(;;)
+
+       // At this point we have room and the lock - stick them in.
+       channel = pCh->infl.hd.i2sChannel;
+       pInsert = &pBuf[stuffIndex];     // Pointer to start of packet
+       pDest = CMD_OF(pInsert);         // Pointer to start of command
+
+       // When we start counting, the block is the size of the header
+       for (blocksize = sizeof(i2CmdHeader), count = nCommands,
+                       lastended = 0, ppCs = &pCs0;
+               count;
+               count--, ppCs++)
+       {
+               pCs = *ppCs;         // Points to command protocol structure
+
+               // If this is a bookmark request command, post the fact that a bookmark
+               // request is pending. NOTE THIS TRICK ONLY WORKS BECAUSE CMD_BMARK_REQ
+               // has no parameters!  The more general solution would be to reference
+               // pCs->cmd[0].
+               if (pCs == CMD_BMARK_REQ) {
+                       pCh->bookMarks++;
+
+                       ip2trace (CHANN, ITRC_DRAIN, 30, 1, pCh->bookMarks );
+
+               }
+               cnt = pCs->length;
+
+               // If this command would put us over the maximum block size or 
+               // if the last command had to be at the end of a block, we end
+               // the existing block here and start a new one.
+               if ((blocksize + cnt > maxBlock) || lastended) {
+
+                       ip2trace (CHANN, ITRC_QUEUE, 5, 0 );
+
+                       PTYPE_OF(pInsert) = type;
+                       CHANNEL_OF(pInsert) = channel;
+                       // count here does not include the header
+                       CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader);
+                       stuffIndex += blocksize;
+                       if(stuffIndex >= maxBuff) {
+                               stuffIndex = 0;
+                               pInsert = pBuf;
+                       }
+                       pInsert = &pBuf[stuffIndex];  // Pointer to start of next pkt
+                       pDest = CMD_OF(pInsert);
+                       blocksize = sizeof(i2CmdHeader);
+               }
+               // Now we know there is room for this one in the current block
+
+               blocksize += cnt;       // Total bytes in this command
+               pSource = pCs->cmd;     // Copy the command into the buffer
+               while (cnt--) {
+                       *pDest++ = *pSource++;
+               }
+               // If this command had to end a block, then we will make sure to account
+               // for it should there be any more blocks.
+               lastended = pCs->flags & END;
+       }       // end for
+       // Clean up the final block by writing header, etc
+
+       PTYPE_OF(pInsert) = type;
+       CHANNEL_OF(pInsert) = channel;
+       // count here does not include the header
+       CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader);
+       stuffIndex += blocksize;
+       if(stuffIndex >= maxBuff) {
+               stuffIndex = 0;
+               pInsert = pBuf;
+       }
+       // Updates the index, and post the need for service. When adding these to
+       // the queue of channels, we turn off the interrupt while doing so,
+       // because at interrupt level we might want to push a channel back to the
+       // end of the queue.
+       switch(type)
+       {
+       case PTYPE_INLINE:
+               pCh->Obuf_stuff = stuffIndex;  // Store buffer pointer
+               write_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
+
+               pB->debugInlineQueued++;
+               // Add the channel pointer to list of channels needing service (first
+               // come...), if it's not already there.
+               i2QueueNeeds(pB, pCh, NEED_INLINE);
+               break;
+
+       case PTYPE_BYPASS:
+               pCh->Cbuf_stuff = stuffIndex;  // Store buffer pointer
+               write_unlock_irqrestore(&pCh->Cbuf_spinlock, flags);
+
+               pB->debugBypassQueued++;
+               // Add the channel pointer to list of channels needing service (first
+               // come...), if it's not already there.
+               i2QueueNeeds(pB, pCh, NEED_BYPASS);
+               break;
+       }
+
+       ip2trace (CHANN, ITRC_QUEUE, ITRC_RETURN, 1, nCommands );
+
+       return nCommands; // Good status: number of commands sent
+}
+
+//******************************************************************************
+// Function:   i2GetStatus(pCh,resetBits)
+// Parameters: Pointer to a channel structure
+//             Bit map of status bits to clear
+// Returns:    Bit map of current status bits
+//
+// Description:
+// Returns the state of data set signals, and whether a break has been received,
+// (see i2lib.h for bit-mapped result). resetBits is a bit-map of any status
+// bits to be cleared: I2_BRK, I2_PAR, I2_FRA, I2_OVR,... These are cleared
+// AFTER the condition is passed. If pCh does not point to a valid channel,
+// returns -1 (which would be impossible otherwise.
+//******************************************************************************
+static int
+i2GetStatus(i2ChanStrPtr pCh, int resetBits)
+{
+       unsigned short status;
+       i2eBordStrPtr pB;
+
+       ip2trace (CHANN, ITRC_STATUS, ITRC_ENTER, 2, pCh->dataSetIn, resetBits );
+
+       // Make sure the channel exists, otherwise do nothing */
+       if ( !i2Validate ( pCh ) )
+               return -1;
+
+       pB = pCh->pMyBord;
+
+       status = pCh->dataSetIn;
+
+       // Clear any specified error bits: but note that only actual error bits can
+       // be cleared, regardless of the value passed.
+       if (resetBits)
+       {
+               pCh->dataSetIn &= ~(resetBits & (I2_BRK | I2_PAR | I2_FRA | I2_OVR));
+               pCh->dataSetIn &= ~(I2_DDCD | I2_DCTS | I2_DDSR | I2_DRI);
+       }
+
+       ip2trace (CHANN, ITRC_STATUS, ITRC_RETURN, 1, pCh->dataSetIn );
+
+       return status;
+}
+
+//******************************************************************************
+// Function:   i2Input(pChpDest,count)
+// Parameters: Pointer to a channel structure
+//             Pointer to data buffer
+//             Number of bytes to read
+// Returns:    Number of bytes read, or -1 for error
+//
+// Description:
+// Strips data from the input buffer and writes it to pDest. If there is a
+// collosal blunder, (invalid structure pointers or the like), returns -1.
+// Otherwise, returns the number of bytes read.
+//******************************************************************************
+static int
+i2Input(i2ChanStrPtr pCh)
+{
+       int amountToMove;
+       unsigned short stripIndex;
+       int count;
+       unsigned long flags = 0;
+
+       ip2trace (CHANN, ITRC_INPUT, ITRC_ENTER, 0);
+
+       // Ensure channel structure seems real
+       if ( !i2Validate( pCh ) ) {
+               count = -1;
+               goto i2Input_exit;
+       }
+       write_lock_irqsave(&pCh->Ibuf_spinlock, flags);
+
+       // initialize some accelerators and private copies
+       stripIndex = pCh->Ibuf_strip;
+
+       count = pCh->Ibuf_stuff - stripIndex;
+
+       // If buffer is empty or requested data count was 0, (trivial case) return
+       // without any further thought.
+       if ( count == 0 ) {
+               write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
+               goto i2Input_exit;
+       }
+       // Adjust for buffer wrap
+       if ( count < 0 ) {
+               count += IBUF_SIZE;
+       }
+       // Don't give more than can be taken by the line discipline
+       amountToMove = pCh->pTTY->receive_room;
+       if (count > amountToMove) {
+               count = amountToMove;
+       }
+       // How much could we copy without a wrap?
+       amountToMove = IBUF_SIZE - stripIndex;
+
+       if (amountToMove > count) {
+               amountToMove = count;
+       }
+       // Move the first block
+       pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY,
+                &(pCh->Ibuf[stripIndex]), NULL, amountToMove );
+       // If we needed to wrap, do the second data move
+       if (count > amountToMove) {
+               pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY,
+                pCh->Ibuf, NULL, count - amountToMove );
+       }
+       // Bump and wrap the stripIndex all at once by the amount of data read. This
+       // method is good regardless of whether the data was in one or two pieces.
+       stripIndex += count;
+       if (stripIndex >= IBUF_SIZE) {
+               stripIndex -= IBUF_SIZE;
+       }
+       pCh->Ibuf_strip = stripIndex;
+
+       // Update our flow control information and possibly queue ourselves to send
+       // it, depending on how much data has been stripped since the last time a
+       // packet was sent.
+       pCh->infl.asof += count;
+
+       if ((pCh->sinceLastFlow += count) >= pCh->whenSendFlow) {
+               pCh->sinceLastFlow -= pCh->whenSendFlow;
+               write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
+               i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW);
+       } else {
+               write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
+       }
+
+i2Input_exit:
+
+       ip2trace (CHANN, ITRC_INPUT, ITRC_RETURN, 1, count);
+
+       return count;
+}
+
+//******************************************************************************
+// Function:   i2InputFlush(pCh)
+// Parameters: Pointer to a channel structure
+// Returns:    Number of bytes stripped, or -1 for error
+//
+// Description:
+// Strips any data from the input buffer. If there is a collosal blunder,
+// (invalid structure pointers or the like), returns -1. Otherwise, returns the
+// number of bytes stripped.
+//******************************************************************************
+static int
+i2InputFlush(i2ChanStrPtr pCh)
+{
+       int count;
+       unsigned long flags;
+
+       // Ensure channel structure seems real
+       if ( !i2Validate ( pCh ) )
+               return -1;
+
+       ip2trace (CHANN, ITRC_INPUT, 10, 0);
+
+       write_lock_irqsave(&pCh->Ibuf_spinlock, flags);
+       count = pCh->Ibuf_stuff - pCh->Ibuf_strip;
+
+       // Adjust for buffer wrap
+       if (count < 0) {
+               count += IBUF_SIZE;
+       }
+
+       // Expedient way to zero out the buffer
+       pCh->Ibuf_strip = pCh->Ibuf_stuff;
+
+
+       // Update our flow control information and possibly queue ourselves to send
+       // it, depending on how much data has been stripped since the last time a
+       // packet was sent.
+
+       pCh->infl.asof += count;
+
+       if ( (pCh->sinceLastFlow += count) >= pCh->whenSendFlow )
+       {
+               pCh->sinceLastFlow -= pCh->whenSendFlow;
+               write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
+               i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW);
+       } else {
+               write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
+       }
+
+       ip2trace (CHANN, ITRC_INPUT, 19, 1, count);
+
+       return count;
+}
+
+//******************************************************************************
+// Function:   i2InputAvailable(pCh)
+// Parameters: Pointer to a channel structure
+// Returns:    Number of bytes available, or -1 for error
+//
+// Description:
+// If there is a collosal blunder, (invalid structure pointers or the like),
+// returns -1. Otherwise, returns the number of bytes stripped. Otherwise,
+// returns the number of bytes available in the buffer.
+//******************************************************************************
+#if 0
+static int
+i2InputAvailable(i2ChanStrPtr pCh)
+{
+       int count;
+
+       // Ensure channel structure seems real
+       if ( !i2Validate ( pCh ) ) return -1;
+
+
+       // initialize some accelerators and private copies
+       read_lock_irqsave(&pCh->Ibuf_spinlock, flags);
+       count = pCh->Ibuf_stuff - pCh->Ibuf_strip;
+       read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
+
+       // Adjust for buffer wrap
+       if (count < 0)
+       {
+               count += IBUF_SIZE;
+       }
+
+       return count;
+}
+#endif 
+
+//******************************************************************************
+// Function:   i2Output(pCh, pSource, count)
+// Parameters: Pointer to channel structure
+//             Pointer to source data
+//             Number of bytes to send
+// Returns:    Number of bytes sent, or -1 for error
+//
+// Description:
+// Queues the data at pSource to be sent as data packets to the board. If there
+// is a collosal blunder, (invalid structure pointers or the like), returns -1.
+// Otherwise, returns the number of bytes written. What if there is not enough
+// room for all the data? If pCh->channelOptions & CO_NBLOCK_WRITE is set, then
+// we transfer as many characters as we can now, then return. If this bit is
+// clear (default), routine will spin along until all the data is buffered.
+// Should this occur, the 1-ms delay routine is called while waiting to avoid
+// applications that one cannot break out of.
+//******************************************************************************
+static int
+i2Output(i2ChanStrPtr pCh, const char *pSource, int count)
+{
+       i2eBordStrPtr pB;
+       unsigned char *pInsert;
+       int amountToMove;
+       int countOriginal = count;
+       unsigned short channel;
+       unsigned short stuffIndex;
+       unsigned long flags;
+
+       int bailout = 10;
+
+       ip2trace (CHANN, ITRC_OUTPUT, ITRC_ENTER, 2, count, 0 );
+
+       // Ensure channel structure seems real
+       if ( !i2Validate ( pCh ) ) 
+               return -1;
+
+       // initialize some accelerators and private copies
+       pB = pCh->pMyBord;
+       channel = pCh->infl.hd.i2sChannel;
+
+       // If the board has gone fatal, return bad, and also hit the trap routine if
+       // it exists.
+       if (pB->i2eFatal) {
+               if (pB->i2eFatalTrap) {
+                       (*(pB)->i2eFatalTrap)(pB);
+               }
+               return -1;
+       }
+       // Proceed as though we would do everything
+       while ( count > 0 ) {
+
+               // How much room in output buffer is there?
+               read_lock_irqsave(&pCh->Obuf_spinlock, flags);
+               amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1;
+               read_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
+               if (amountToMove < 0) {
+                       amountToMove += OBUF_SIZE;
+               }
+               // Subtract off the headers size and see how much room there is for real
+               // data. If this is negative, we will discover later.
+               amountToMove -= sizeof (i2DataHeader);
+
+               // Don't move more (now) than can go in a single packet
+               if ( amountToMove > (int)(MAX_OBUF_BLOCK - sizeof(i2DataHeader)) ) {
+                       amountToMove = MAX_OBUF_BLOCK - sizeof(i2DataHeader);
+               }
+               // Don't move more than the count we were given
+               if (amountToMove > count) {
+                       amountToMove = count;
+               }
+               // Now we know how much we must move: NB because the ring buffers have
+               // an overflow area at the end, we needn't worry about wrapping in the
+               // middle of a packet.
+
+// Small WINDOW here with no LOCK but I can't call Flush with LOCK
+// We would be flushing (or ending flush) anyway
+
+               ip2trace (CHANN, ITRC_OUTPUT, 10, 1, amountToMove );
+
+               if ( !(pCh->flush_flags && i2RetryFlushOutput(pCh) ) 
+                               && amountToMove > 0 )
+               {
+                       write_lock_irqsave(&pCh->Obuf_spinlock, flags);
+                       stuffIndex = pCh->Obuf_stuff;
+      
+                       // Had room to move some data: don't know whether the block size,
+                       // buffer space, or what was the limiting factor...
+                       pInsert = &(pCh->Obuf[stuffIndex]);
+
+                       // Set up the header
+                       CHANNEL_OF(pInsert)     = channel;
+                       PTYPE_OF(pInsert)       = PTYPE_DATA;
+                       TAG_OF(pInsert)         = 0;
+                       ID_OF(pInsert)          = ID_ORDINARY_DATA;
+                       DATA_COUNT_OF(pInsert)  = amountToMove;
+
+                       // Move the data
+                       memcpy( (char*)(DATA_OF(pInsert)), pSource, amountToMove );
+                       // Adjust pointers and indices
+                       pSource                                 += amountToMove;
+                       pCh->Obuf_char_count    += amountToMove;
+                       stuffIndex                              += amountToMove + sizeof(i2DataHeader);
+                       count                                   -= amountToMove;
+
+                       if (stuffIndex >= OBUF_SIZE) {
+                               stuffIndex = 0;
+                       }
+                       pCh->Obuf_stuff = stuffIndex;
+
+                       write_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
+
+                       ip2trace (CHANN, ITRC_OUTPUT, 13, 1, stuffIndex );
+
+               } else {
+
+                       // Cannot move data
+                       // becuz we need to stuff a flush 
+                       // or amount to move is <= 0
+
+                       ip2trace(CHANN, ITRC_OUTPUT, 14, 3,
+                               amountToMove,  pB->i2eFifoRemains,
+                               pB->i2eWaitingForEmptyFifo );
+
+                       // Put this channel back on queue
+                       // this ultimatly gets more data or wakes write output
+                       i2QueueNeeds(pB, pCh, NEED_INLINE);
+
+                       if ( pB->i2eWaitingForEmptyFifo ) {
+
+                               ip2trace (CHANN, ITRC_OUTPUT, 16, 0 );
+
+                               // or schedule
+                               if (!in_interrupt()) {
+
+                                       ip2trace (CHANN, ITRC_OUTPUT, 61, 0 );
+
+                                       schedule_timeout_interruptible(2);
+                                       if (signal_pending(current)) {
+                                               break;
+                                       }
+                                       continue;
+                               } else {
+
+                                       ip2trace (CHANN, ITRC_OUTPUT, 62, 0 );
+
+                                       // let interrupt in = WAS restore_flags()
+                                       // We hold no lock nor is irq off anymore???
+                                       
+                                       break;
+                               }
+                               break;   // from while(count)
+                       }
+                       else if ( pB->i2eFifoRemains < 32 && !pB->i2eTxMailEmpty ( pB ) )
+                       {
+                               ip2trace (CHANN, ITRC_OUTPUT, 19, 2,
+                                       pB->i2eFifoRemains,
+                                       pB->i2eTxMailEmpty );
+
+                               break;   // from while(count)
+                       } else if ( pCh->channelNeeds & NEED_CREDIT ) {
+
+                               ip2trace (CHANN, ITRC_OUTPUT, 22, 0 );
+
+                               break;   // from while(count)
+                       } else if ( --bailout) {
+
+                               // Try to throw more things (maybe not us) in the fifo if we're
+                               // not already waiting for it.
+       
+                               ip2trace (CHANN, ITRC_OUTPUT, 20, 0 );
+
+                               serviceOutgoingFifo(pB);
+                               //break;  CONTINUE;
+                       } else {
+                               ip2trace (CHANN, ITRC_OUTPUT, 21, 3,
+                                       pB->i2eFifoRemains,
+                                       pB->i2eOutMailWaiting,
+                                       pB->i2eWaitingForEmptyFifo );
+
+                               break;   // from while(count)
+                       }
+               }
+       } // End of while(count)
+
+       i2QueueNeeds(pB, pCh, NEED_INLINE);
+
+       // We drop through either when the count expires, or when there is some
+       // count left, but there was a non-blocking write.
+       if (countOriginal > count) {
+
+               ip2trace (CHANN, ITRC_OUTPUT, 17, 2, countOriginal, count );
+
+               serviceOutgoingFifo( pB );
+       }
+
+       ip2trace (CHANN, ITRC_OUTPUT, ITRC_RETURN, 2, countOriginal, count );
+
+       return countOriginal - count;
+}
+
+//******************************************************************************
+// Function:   i2FlushOutput(pCh)
+// Parameters: Pointer to a channel structure
+// Returns:    Nothing
+//
+// Description:
+// Sends bypass command to start flushing (waiting possibly forever until there
+// is room), then sends inline command to stop flushing output, (again waiting
+// possibly forever).
+//******************************************************************************
+static inline void
+i2FlushOutput(i2ChanStrPtr pCh)
+{
+
+       ip2trace (CHANN, ITRC_FLUSH, 1, 1, pCh->flush_flags );
+
+       if (pCh->flush_flags)
+               return;
+
+       if ( 1 != i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) {
+               pCh->flush_flags = STARTFL_FLAG;                // Failed - flag for later
+
+               ip2trace (CHANN, ITRC_FLUSH, 2, 0 );
+
+       } else if ( 1 != i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL) ) {
+               pCh->flush_flags = STOPFL_FLAG;         // Failed - flag for later
+
+               ip2trace (CHANN, ITRC_FLUSH, 3, 0 );
+       }
+}
+
+static int 
+i2RetryFlushOutput(i2ChanStrPtr pCh)
+{
+       int old_flags = pCh->flush_flags;
+
+       ip2trace (CHANN, ITRC_FLUSH, 14, 1, old_flags );
+
+       pCh->flush_flags = 0;   // Clear flag so we can avoid recursion
+                                                                       // and queue the commands
+
+       if ( old_flags & STARTFL_FLAG ) {
+               if ( 1 == i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) {
+                       old_flags = STOPFL_FLAG;        //Success - send stop flush
+               } else {
+                       old_flags = STARTFL_FLAG;       //Failure - Flag for retry later
+               }
+
+               ip2trace (CHANN, ITRC_FLUSH, 15, 1, old_flags );
+
+       }
+       if ( old_flags & STOPFL_FLAG ) {
+               if (1 == i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL)) {
+                       old_flags = 0;  // Success - clear flags
+               }
+
+               ip2trace (CHANN, ITRC_FLUSH, 16, 1, old_flags );
+       }
+       pCh->flush_flags = old_flags;
+
+       ip2trace (CHANN, ITRC_FLUSH, 17, 1, old_flags );
+
+       return old_flags;
+}
+
+//******************************************************************************
+// Function:   i2DrainOutput(pCh,timeout)
+// Parameters: Pointer to a channel structure
+//             Maximum period to wait
+// Returns:    ?
+//
+// Description:
+// Uses the bookmark request command to ask the board to send a bookmark back as
+// soon as all the data is completely sent.
+//******************************************************************************
+static void
+i2DrainWakeup(unsigned long d)
+{
+       i2ChanStrPtr pCh = (i2ChanStrPtr)d;
+
+       ip2trace (CHANN, ITRC_DRAIN, 10, 1, pCh->BookmarkTimer.expires );
+
+       pCh->BookmarkTimer.expires = 0;
+       wake_up_interruptible( &pCh->pBookmarkWait );
+}
+
+static void
+i2DrainOutput(i2ChanStrPtr pCh, int timeout)
+{
+       wait_queue_t wait;
+       i2eBordStrPtr pB;
+
+       ip2trace (CHANN, ITRC_DRAIN, ITRC_ENTER, 1, pCh->BookmarkTimer.expires);
+
+       pB = pCh->pMyBord;
+       // If the board has gone fatal, return bad, 
+       // and also hit the trap routine if it exists.
+       if (pB->i2eFatal) {
+               if (pB->i2eFatalTrap) {
+                       (*(pB)->i2eFatalTrap)(pB);
+               }
+               return;
+       }
+       if ((timeout > 0) && (pCh->BookmarkTimer.expires == 0 )) {
+               // One per customer (channel)
+               setup_timer(&pCh->BookmarkTimer, i2DrainWakeup,
+                               (unsigned long)pCh);
+
+               ip2trace (CHANN, ITRC_DRAIN, 1, 1, pCh->BookmarkTimer.expires );
+
+               mod_timer(&pCh->BookmarkTimer, jiffies + timeout);
+       }
+       
+       i2QueueCommands( PTYPE_INLINE, pCh, -1, 1, CMD_BMARK_REQ );
+
+       init_waitqueue_entry(&wait, current);
+       add_wait_queue(&(pCh->pBookmarkWait), &wait);
+       set_current_state( TASK_INTERRUPTIBLE );
+
+       serviceOutgoingFifo( pB );
+       
+       schedule();     // Now we take our interruptible sleep on
+
+       // Clean up the queue
+       set_current_state( TASK_RUNNING );
+       remove_wait_queue(&(pCh->pBookmarkWait), &wait);
+
+       // if expires == 0 then timer poped, then do not need to del_timer
+       if ((timeout > 0) && pCh->BookmarkTimer.expires && 
+                            time_before(jiffies, pCh->BookmarkTimer.expires)) {
+               del_timer( &(pCh->BookmarkTimer) );
+               pCh->BookmarkTimer.expires = 0;
+
+               ip2trace (CHANN, ITRC_DRAIN, 3, 1, pCh->BookmarkTimer.expires );
+
+       }
+       ip2trace (CHANN, ITRC_DRAIN, ITRC_RETURN, 1, pCh->BookmarkTimer.expires );
+       return;
+}
+
+//******************************************************************************
+// Function:   i2OutputFree(pCh)
+// Parameters: Pointer to a channel structure
+// Returns:    Space in output buffer
+//
+// Description:
+// Returns -1 if very gross error. Otherwise returns the amount of bytes still
+// free in the output buffer.
+//******************************************************************************
+static int
+i2OutputFree(i2ChanStrPtr pCh)
+{
+       int amountToMove;
+       unsigned long flags;
+
+       // Ensure channel structure seems real
+       if ( !i2Validate ( pCh ) ) {
+               return -1;
+       }
+       read_lock_irqsave(&pCh->Obuf_spinlock, flags);
+       amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1;
+       read_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
+
+       if (amountToMove < 0) {
+               amountToMove += OBUF_SIZE;
+       }
+       // If this is negative, we will discover later
+       amountToMove -= sizeof(i2DataHeader);
+
+       return (amountToMove < 0) ? 0 : amountToMove;
+}
+static void
+
+ip2_owake( PTTY tp)
+{
+       i2ChanStrPtr  pCh;
+
+       if (tp == NULL) return;
+
+       pCh = tp->driver_data;
+
+       ip2trace (CHANN, ITRC_SICMD, 10, 2, tp->flags,
+                       (1 << TTY_DO_WRITE_WAKEUP) );
+
+       tty_wakeup(tp);
+}
+
+static inline void
+set_baud_params(i2eBordStrPtr pB) 
+{
+       int i,j;
+       i2ChanStrPtr  *pCh;
+
+       pCh = (i2ChanStrPtr *) pB->i2eChannelPtr;
+
+       for (i = 0; i < ABS_MAX_BOXES; i++) {
+               if (pB->channelBtypes.bid_value[i]) {
+                       if (BID_HAS_654(pB->channelBtypes.bid_value[i])) {
+                               for (j = 0; j < ABS_BIGGEST_BOX; j++) {
+                                       if (pCh[i*16+j] == NULL)
+                                               break;
+                                       (pCh[i*16+j])->BaudBase    = 921600;    // MAX for ST654
+                                       (pCh[i*16+j])->BaudDivisor = 96;
+                               }
+                       } else {        // has cirrus cd1400
+                               for (j = 0; j < ABS_BIGGEST_BOX; j++) {
+                                       if (pCh[i*16+j] == NULL)
+                                               break;
+                                       (pCh[i*16+j])->BaudBase    = 115200;    // MAX for CD1400
+                                       (pCh[i*16+j])->BaudDivisor = 12;
+                               }
+                       }
+               }
+       }
+}
+
+//******************************************************************************
+// Function:   i2StripFifo(pB)
+// Parameters: Pointer to a board structure
+// Returns:    ?
+//
+// Description:
+// Strips all the available data from the incoming FIFO, identifies the type of
+// packet, and either buffers the data or does what needs to be done.
+//
+// Note there is no overflow checking here: if the board sends more data than it
+// ought to, we will not detect it here, but blindly overflow...
+//******************************************************************************
+
+// A buffer for reading in blocks for unknown channels
+static unsigned char junkBuffer[IBUF_SIZE];
+
+// A buffer to read in a status packet. Because of the size of the count field
+// for these things, the maximum packet size must be less than MAX_CMD_PACK_SIZE
+static unsigned char cmdBuffer[MAX_CMD_PACK_SIZE + 4];
+
+// This table changes the bit order from MSR order given by STAT_MODEM packet to
+// status bits used in our library.
+static char xlatDss[16] = {
+0      | 0     | 0      | 0      ,
+0      | 0     | 0      | I2_CTS ,
+0      | 0     | I2_DSR | 0      ,
+0      | 0     | I2_DSR | I2_CTS ,
+0      | I2_RI | 0      | 0      ,
+0      | I2_RI | 0      | I2_CTS ,
+0      | I2_RI | I2_DSR | 0      ,
+0      | I2_RI | I2_DSR | I2_CTS ,
+I2_DCD | 0     | 0      | 0      ,
+I2_DCD | 0     | 0      | I2_CTS ,
+I2_DCD | 0     | I2_DSR | 0      ,
+I2_DCD | 0     | I2_DSR | I2_CTS ,
+I2_DCD | I2_RI | 0      | 0      ,
+I2_DCD | I2_RI | 0      | I2_CTS ,
+I2_DCD | I2_RI | I2_DSR | 0      ,
+I2_DCD | I2_RI | I2_DSR | I2_CTS };
+
+static inline void
+i2StripFifo(i2eBordStrPtr pB)
+{
+       i2ChanStrPtr pCh;
+       int channel;
+       int count;
+       unsigned short stuffIndex;
+       int amountToRead;
+       unsigned char *pc, *pcLimit;
+       unsigned char uc;
+       unsigned char dss_change;
+       unsigned long bflags,cflags;
+
+//     ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_ENTER, 0 );
+
+       while (I2_HAS_INPUT(pB)) {
+//             ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 2, 0 );
+
+               // Process packet from fifo a one atomic unit
+               write_lock_irqsave(&pB->read_fifo_spinlock, bflags);
+   
+               // The first word (or two bytes) will have channel number and type of
+               // packet, possibly other information
+               pB->i2eLeadoffWord[0] = iiReadWord(pB);
+
+               switch(PTYPE_OF(pB->i2eLeadoffWord))
+               {
+               case PTYPE_DATA:
+                       pB->got_input = 1;
+
+//                     ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 3, 0 );
+
+                       channel = CHANNEL_OF(pB->i2eLeadoffWord); /* Store channel */
+                       count = iiReadWord(pB);          /* Count is in the next word */
+
+// NEW: Check the count for sanity! Should the hardware fail, our death
+// is more pleasant. While an oversize channel is acceptable (just more
+// than the driver supports), an over-length count clearly means we are
+// sick!
+                       if ( ((unsigned int)count) > IBUF_SIZE ) {
+                               pB->i2eFatal = 2;
+                               write_unlock_irqrestore(&pB->read_fifo_spinlock,
+                                               bflags);
+                               return;     /* Bail out ASAP */
+                       }
+                       // Channel is illegally big ?
+                       if ((channel >= pB->i2eChannelCnt) ||
+                               (NULL==(pCh = ((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])))
+                       {
+                               iiReadBuf(pB, junkBuffer, count);
+                               write_unlock_irqrestore(&pB->read_fifo_spinlock,
+                                               bflags);
+                               break;         /* From switch: ready for next packet */
+                       }
+
+                       // Channel should be valid, then
+
+                       // If this is a hot-key, merely post its receipt for now. These are
+                       // always supposed to be 1-byte packets, so we won't even check the
+                       // count. Also we will post an acknowledgement to the board so that
+                       // more data can be forthcoming. Note that we are not trying to use
+                       // these sequences in this driver, merely to robustly ignore them.
+                       if(ID_OF(pB->i2eLeadoffWord) == ID_HOT_KEY)
+                       {
+                               pCh->hotKeyIn = iiReadWord(pB) & 0xff;
+                               write_unlock_irqrestore(&pB->read_fifo_spinlock,
+                                               bflags);
+                               i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_HOTACK);
+                               break;   /* From the switch: ready for next packet */
+                       }
+
+                       // Normal data! We crudely assume there is room for the data in our
+                       // buffer because the board wouldn't have exceeded his credit limit.
+                       write_lock_irqsave(&pCh->Ibuf_spinlock, cflags);
+                                                                                                       // We have 2 locks now
+                       stuffIndex = pCh->Ibuf_stuff;
+                       amountToRead = IBUF_SIZE - stuffIndex;
+                       if (amountToRead > count)
+                               amountToRead = count;
+
+                       // stuffIndex would have been already adjusted so there would 
+                       // always be room for at least one, and count is always at least
+                       // one.
+
+                       iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead);
+                       pCh->icount.rx += amountToRead;
+
+                       // Update the stuffIndex by the amount of data moved. Note we could
+                       // never ask for more data than would just fit. However, we might
+                       // have read in one more byte than we wanted because the read
+                       // rounds up to even bytes. If this byte is on the end of the
+                       // packet, and is padding, we ignore it. If the byte is part of
+                       // the actual data, we need to move it.
+
+                       stuffIndex += amountToRead;
+
+                       if (stuffIndex >= IBUF_SIZE) {
+                               if ((amountToRead & 1) && (count > amountToRead)) {
+                                       pCh->Ibuf[0] = pCh->Ibuf[IBUF_SIZE];
+                                       amountToRead++;
+                                       stuffIndex = 1;
+                               } else {
+                                       stuffIndex = 0;
+                               }
+                       }
+
+                       // If there is anything left over, read it as well
+                       if (count > amountToRead) {
+                               amountToRead = count - amountToRead;
+                               iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead);
+                               pCh->icount.rx += amountToRead;
+                               stuffIndex += amountToRead;
+                       }
+
+                       // Update stuff index
+                       pCh->Ibuf_stuff = stuffIndex;
+                       write_unlock_irqrestore(&pCh->Ibuf_spinlock, cflags);
+                       write_unlock_irqrestore(&pB->read_fifo_spinlock,
+                                       bflags);
+
+#ifdef USE_IQ
+                       schedule_work(&pCh->tqueue_input);
+#else
+                       do_input(&pCh->tqueue_input);
+#endif
+
+                       // Note we do not need to maintain any flow-control credits at this
+                       // time:  if we were to increment .asof and decrement .room, there
+                       // would be no net effect. Instead, when we strip data, we will
+                       // increment .asof and leave .room unchanged.
+
+                       break;   // From switch: ready for next packet
+
+               case PTYPE_STATUS:
+                       ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 4, 0 );
+      
+                       count = CMD_COUNT_OF(pB->i2eLeadoffWord);
+
+                       iiReadBuf(pB, cmdBuffer, count);
+                       // We can release early with buffer grab
+                       write_unlock_irqrestore(&pB->read_fifo_spinlock,
+                                       bflags);
+
+                       pc = cmdBuffer;
+                       pcLimit = &(cmdBuffer[count]);
+
+                       while (pc < pcLimit) {
+                               channel = *pc++;
+
+                               ip2trace (channel, ITRC_SFIFO, 7, 2, channel, *pc );
+
+                               /* check for valid channel */
+                               if (channel < pB->i2eChannelCnt
+                                        && 
+                                        (pCh = (((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])) != NULL
+                                       )
+                               {
+                                       dss_change = 0;
+
+                                       switch (uc = *pc++)
+                                       {
+                                       /* Breaks and modem signals are easy: just update status */
+                                       case STAT_CTS_UP:
+                                               if ( !(pCh->dataSetIn & I2_CTS) )
+                                               {
+                                                       pCh->dataSetIn |= I2_DCTS;
+                                                       pCh->icount.cts++;
+                                                       dss_change = 1;
+                                               }
+                                               pCh->dataSetIn |= I2_CTS;
+                                               break;
+
+                                       case STAT_CTS_DN:
+                                               if ( pCh->dataSetIn & I2_CTS )
+                                               {
+                                                       pCh->dataSetIn |= I2_DCTS;
+                                                       pCh->icount.cts++;
+                                                       dss_change = 1;
+                                               }
+                                               pCh->dataSetIn &= ~I2_CTS;
+                                               break;
+
+                                       case STAT_DCD_UP:
+                                               ip2trace (channel, ITRC_MODEM, 1, 1, pCh->dataSetIn );
+
+                                               if ( !(pCh->dataSetIn & I2_DCD) )
+                                               {
+                                                       ip2trace (CHANN, ITRC_MODEM, 2, 0 );
+                                                       pCh->dataSetIn |= I2_DDCD;
+                                                       pCh->icount.dcd++;
+                                                       dss_change = 1;
+                                               }
+                                               pCh->dataSetIn |= I2_DCD;
+
+                                               ip2trace (channel, ITRC_MODEM, 3, 1, pCh->dataSetIn );
+                                               break;
+
+                                       case STAT_DCD_DN:
+                                               ip2trace (channel, ITRC_MODEM, 4, 1, pCh->dataSetIn );
+                                               if ( pCh->dataSetIn & I2_DCD )
+                                               {
+                                                       ip2trace (channel, ITRC_MODEM, 5, 0 );
+                                                       pCh->dataSetIn |= I2_DDCD;
+                                                       pCh->icount.dcd++;
+                                                       dss_change = 1;
+                                               }
+                                               pCh->dataSetIn &= ~I2_DCD;
+
+                                               ip2trace (channel, ITRC_MODEM, 6, 1, pCh->dataSetIn );
+                                               break;
+
+                                       case STAT_DSR_UP:
+                                               if ( !(pCh->dataSetIn & I2_DSR) )
+                                               {
+                                                       pCh->dataSetIn |= I2_DDSR;
+                                                       pCh->icount.dsr++;
+                                                       dss_change = 1;
+                                               }
+                                               pCh->dataSetIn |= I2_DSR;
+                                               break;
+
+                                       case STAT_DSR_DN:
+                                               if ( pCh->dataSetIn & I2_DSR )
+                                               {
+                                                       pCh->dataSetIn |= I2_DDSR;
+                                                       pCh->icount.dsr++;
+                                                       dss_change = 1;
+                                               }
+                                               pCh->dataSetIn &= ~I2_DSR;
+                                               break;
+
+                                       case STAT_RI_UP:
+                                               if ( !(pCh->dataSetIn & I2_RI) )
+                                               {
+                                                       pCh->dataSetIn |= I2_DRI;
+                                                       pCh->icount.rng++;
+                                                       dss_change = 1;
+                                               }
+                                               pCh->dataSetIn |= I2_RI ;
+                                               break;
+
+                                       case STAT_RI_DN:
+                                               // to be compat with serial.c
+                                               //if ( pCh->dataSetIn & I2_RI )
+                                               //{
+                                               //      pCh->dataSetIn |= I2_DRI;
+                                               //      pCh->icount.rng++; 
+                                               //      dss_change = 1;
+                                               //}
+                                               pCh->dataSetIn &= ~I2_RI ;
+                                               break;
+
+                                       case STAT_BRK_DET:
+                                               pCh->dataSetIn |= I2_BRK;
+                                               pCh->icount.brk++;
+                                               dss_change = 1;
+                                               break;
+
+                                       // Bookmarks? one less request we're waiting for
+                                       case STAT_BMARK:
+                                               pCh->bookMarks--;
+                                               if (pCh->bookMarks <= 0 ) {
+                                                       pCh->bookMarks = 0;
+                                                       wake_up_interruptible( &pCh->pBookmarkWait );
+
+                                               ip2trace (channel, ITRC_DRAIN, 20, 1, pCh->BookmarkTimer.expires );
+                                               }
+                                               break;
+
+                                       // Flow control packets? Update the new credits, and if
+                                       // someone was waiting for output, queue him up again.
+                                       case STAT_FLOW:
+                                               pCh->outfl.room =
+                                                       ((flowStatPtr)pc)->room -
+                                                       (pCh->outfl.asof - ((flowStatPtr)pc)->asof);
+
+                                               ip2trace (channel, ITRC_STFLW, 1, 1, pCh->outfl.room );
+
+                                               if (pCh->channelNeeds & NEED_CREDIT)
+                                               {
+                                                       ip2trace (channel, ITRC_STFLW, 2, 1, pCh->channelNeeds);
+
+                                                       pCh->channelNeeds &= ~NEED_CREDIT;
+                                                       i2QueueNeeds(pB, pCh, NEED_INLINE);
+                                                       if ( pCh->pTTY )
+                                                               ip2_owake(pCh->pTTY);
+                                               }
+
+                                               ip2trace (channel, ITRC_STFLW, 3, 1, pCh->channelNeeds);
+
+                                               pc += sizeof(flowStat);
+                                               break;
+
+                                       /* Special packets: */
+                                       /* Just copy the information into the channel structure */
+
+                                       case STAT_STATUS:
+
+                                               pCh->channelStatus = *((debugStatPtr)pc);
+                                               pc += sizeof(debugStat);
+                                               break;
+
+                                       case STAT_TXCNT:
+
+                                               pCh->channelTcount = *((cntStatPtr)pc);
+                                               pc += sizeof(cntStat);
+                                               break;
+
+                                       case STAT_RXCNT:
+
+                                               pCh->channelRcount = *((cntStatPtr)pc);
+                                               pc += sizeof(cntStat);
+                                               break;
+
+                                       case STAT_BOXIDS:
+                                               pB->channelBtypes = *((bidStatPtr)pc);
+                                               pc += sizeof(bidStat);
+                                               set_baud_params(pB);
+                                               break;
+
+                                       case STAT_HWFAIL:
+                                               i2QueueCommands (PTYPE_INLINE, pCh, 0, 1, CMD_HW_TEST);
+                                               pCh->channelFail = *((failStatPtr)pc);
+                                               pc += sizeof(failStat);
+                                               break;
+
+                                       /* No explicit match? then
+                                        * Might be an error packet...
+                                        */
+                                       default:
+                                               switch (uc & STAT_MOD_ERROR)
+                                               {
+                                               case STAT_ERROR:
+                                                       if (uc & STAT_E_PARITY) {
+                                                               pCh->dataSetIn |= I2_PAR;
+                                                               pCh->icount.parity++;
+                                                       }
+                                                       if (uc & STAT_E_FRAMING){
+                                                               pCh->dataSetIn |= I2_FRA;
+                                                               pCh->icount.frame++;
+                                                       }
+                                                       if (uc & STAT_E_OVERRUN){
+                                                               pCh->dataSetIn |= I2_OVR;
+                                                               pCh->icount.overrun++;
+                                                       }
+                                                       break;
+
+                                               case STAT_MODEM:
+                                                       // the answer to DSS_NOW request (not change)
+                                                       pCh->dataSetIn = (pCh->dataSetIn
+                                                               & ~(I2_RI | I2_CTS | I2_DCD | I2_DSR) )
+                                                               | xlatDss[uc & 0xf];
+                                                       wake_up_interruptible ( &pCh->dss_now_wait );
+                                               default:
+                                                       break;
+                                               }
+                                       }  /* End of switch on status type */
+                                       if (dss_change) {
+#ifdef USE_IQ
+                                               schedule_work(&pCh->tqueue_status);
+#else
+                                               do_status(&pCh->tqueue_status);
+#endif
+                                       }
+                               }
+                               else  /* Or else, channel is invalid */
+                               {
+                                       // Even though the channel is invalid, we must test the
+                                       // status to see how much additional data it has (to be
+                                       // skipped)
+                                       switch (*pc++)
+                                       {
+                                       case STAT_FLOW:
+                                               pc += 4;    /* Skip the data */
+                                               break;
+
+                                       default:
+                                               break;
+                                       }
+                               }
+                       }  // End of while (there is still some status packet left)
+                       break;
+
+               default: // Neither packet? should be impossible
+                       ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 5, 1,
+                               PTYPE_OF(pB->i2eLeadoffWord) );
+                       write_unlock_irqrestore(&pB->read_fifo_spinlock,
+                                       bflags);
+
+                       break;
+               }  // End of switch on type of packets
+       }       /*while(board I2_HAS_INPUT)*/
+
+       ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_RETURN, 0 );
+
+       // Send acknowledgement to the board even if there was no data!
+       pB->i2eOutMailWaiting |= MB_IN_STRIPPED;
+       return;
+}
+
+//******************************************************************************
+// Function:   i2Write2Fifo(pB,address,count)
+// Parameters: Pointer to a board structure, source address, byte count
+// Returns:    bytes written
+//
+// Description:
+//  Writes count bytes to board io address(implied) from source
+//  Adjusts count, leaves reserve for next time around bypass cmds
+//******************************************************************************
+static int
+i2Write2Fifo(i2eBordStrPtr pB, unsigned char *source, int count,int reserve)
+{
+       int rc = 0;
+       unsigned long flags;
+       write_lock_irqsave(&pB->write_fifo_spinlock, flags);
+       if (!pB->i2eWaitingForEmptyFifo) {
+               if (pB->i2eFifoRemains > (count+reserve)) {
+                       pB->i2eFifoRemains -= count;
+                       iiWriteBuf(pB, source, count);
+                       pB->i2eOutMailWaiting |= MB_OUT_STUFFED;
+                       rc =  count;
+               }
+       }
+       write_unlock_irqrestore(&pB->write_fifo_spinlock, flags);
+       return rc;
+}
+//******************************************************************************
+// Function:   i2StuffFifoBypass(pB)
+// Parameters: Pointer to a board structure
+// Returns:    Nothing
+//
+// Description:
+// Stuffs as many bypass commands into the fifo as possible. This is simpler
+// than stuffing data or inline commands to fifo, since we do not have
+// flow-control to deal with.
+//******************************************************************************
+static inline void
+i2StuffFifoBypass(i2eBordStrPtr pB)
+{
+       i2ChanStrPtr pCh;
+       unsigned char *pRemove;
+       unsigned short stripIndex;
+       unsigned short packetSize;
+       unsigned short paddedSize;
+       unsigned short notClogged = 1;
+       unsigned long flags;
+
+       int bailout = 1000;
+
+       // Continue processing so long as there are entries, or there is room in the
+       // fifo. Each entry represents a channel with something to do.
+       while ( --bailout && notClogged && 
+                       (NULL != (pCh = i2DeQueueNeeds(pB,NEED_BYPASS))))
+       {
+               write_lock_irqsave(&pCh->Cbuf_spinlock, flags);
+               stripIndex = pCh->Cbuf_strip;
+
+               // as long as there are packets for this channel...
+
+               while (stripIndex != pCh->Cbuf_stuff) {
+                       pRemove = &(pCh->Cbuf[stripIndex]);
+                       packetSize = CMD_COUNT_OF(pRemove) + sizeof(i2CmdHeader);
+                       paddedSize = roundup(packetSize, 2);
+
+                       if (paddedSize > 0) {
+                               if ( 0 == i2Write2Fifo(pB, pRemove, paddedSize,0)) {
+                                       notClogged = 0; /* fifo full */
+                                       i2QueueNeeds(pB, pCh, NEED_BYPASS);     // Put back on queue
+                                       break;   // Break from the channel
+                               } 
+                       }
+#ifdef DEBUG_FIFO
+WriteDBGBuf("BYPS", pRemove, paddedSize);
+#endif /* DEBUG_FIFO */
+                       pB->debugBypassCount++;
+
+                       pRemove += packetSize;
+                       stripIndex += packetSize;
+                       if (stripIndex >= CBUF_SIZE) {
+                               stripIndex = 0;
+                               pRemove = pCh->Cbuf;
+                       }
+               }
+               // Done with this channel. Move to next, removing this one from 
+               // the queue of channels if we cleaned it out (i.e., didn't get clogged.
+               pCh->Cbuf_strip = stripIndex;
+               write_unlock_irqrestore(&pCh->Cbuf_spinlock, flags);
+       }  // Either clogged or finished all the work
+
+#ifdef IP2DEBUG_TRACE
+       if ( !bailout ) {
+               ip2trace (ITRC_NO_PORT, ITRC_ERROR, 1, 0 );
+       }
+#endif
+}
+
+//******************************************************************************
+// Function:   i2StuffFifoFlow(pB)
+// Parameters: Pointer to a board structure
+// Returns:    Nothing
+//
+// Description:
+// Stuffs as many flow control packets into the fifo as possible. This is easier
+// even than doing normal bypass commands, because there is always at most one
+// packet, already assembled, for each channel.
+//******************************************************************************
+static inline void
+i2StuffFifoFlow(i2eBordStrPtr pB)
+{
+       i2ChanStrPtr pCh;
+       unsigned short paddedSize = roundup(sizeof(flowIn), 2);
+
+       ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_ENTER, 2,
+               pB->i2eFifoRemains, paddedSize );
+
+       // Continue processing so long as there are entries, or there is room in the
+       // fifo. Each entry represents a channel with something to do.
+       while ( (NULL != (pCh = i2DeQueueNeeds(pB,NEED_FLOW)))) {
+               pB->debugFlowCount++;
+
+               // NO Chan LOCK needed ???
+               if ( 0 == i2Write2Fifo(pB,(unsigned char *)&(pCh->infl),paddedSize,0)) {
+                       break;
+               }
+#ifdef DEBUG_FIFO
+               WriteDBGBuf("FLOW",(unsigned char *) &(pCh->infl), paddedSize);
+#endif /* DEBUG_FIFO */
+
+       }  // Either clogged or finished all the work
+
+       ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_RETURN, 0 );
+}
+
+//******************************************************************************
+// Function:   i2StuffFifoInline(pB)
+// Parameters: Pointer to a board structure
+// Returns:    Nothing
+//
+// Description:
+// Stuffs as much data and inline commands into the fifo as possible. This is
+// the most complex fifo-stuffing operation, since there if now the channel
+// flow-control issue to deal with.
+//******************************************************************************
+static inline void
+i2StuffFifoInline(i2eBordStrPtr pB)
+{
+       i2ChanStrPtr pCh;
+       unsigned char *pRemove;
+       unsigned short stripIndex;
+       unsigned short packetSize;
+       unsigned short paddedSize;
+       unsigned short notClogged = 1;
+       unsigned short flowsize;
+       unsigned long flags;
+
+       int bailout  = 1000;
+       int bailout2;
+
+       ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_ENTER, 3, pB->i2eFifoRemains, 
+                       pB->i2Dbuf_strip, pB->i2Dbuf_stuff );
+
+       // Continue processing so long as there are entries, or there is room in the
+       // fifo. Each entry represents a channel with something to do.
+       while ( --bailout && notClogged && 
+                       (NULL != (pCh = i2DeQueueNeeds(pB,NEED_INLINE))) )
+       {
+               write_lock_irqsave(&pCh->Obuf_spinlock, flags);
+               stripIndex = pCh->Obuf_strip;
+
+               ip2trace (CHANN, ITRC_SICMD, 3, 2, stripIndex, pCh->Obuf_stuff );
+
+               // as long as there are packets for this channel...
+               bailout2 = 1000;
+               while ( --bailout2 && stripIndex != pCh->Obuf_stuff) {
+                       pRemove = &(pCh->Obuf[stripIndex]);
+
+                       // Must determine whether this be a data or command packet to
+                       // calculate correctly the header size and the amount of
+                       // flow-control credit this type of packet will use.
+                       if (PTYPE_OF(pRemove) == PTYPE_DATA) {
+                               flowsize = DATA_COUNT_OF(pRemove);
+                               packetSize = flowsize + sizeof(i2DataHeader);
+                       } else {
+                               flowsize = CMD_COUNT_OF(pRemove);
+                               packetSize = flowsize + sizeof(i2CmdHeader);
+                       }
+                       flowsize = CREDIT_USAGE(flowsize);
+                       paddedSize = roundup(packetSize, 2);
+
+                       ip2trace (CHANN, ITRC_SICMD, 4, 2, pB->i2eFifoRemains, paddedSize );
+
+                       // If we don't have enough credits from the board to send the data,
+                       // flag the channel that we are waiting for flow control credit, and
+                       // break out. This will clean up this channel and remove us from the
+                       // queue of hot things to do.
+
+                               ip2trace (CHANN, ITRC_SICMD, 5, 2, pCh->outfl.room, flowsize );
+
+                       if (pCh->outfl.room <= flowsize)        {
+                               // Do Not have the credits to send this packet.
+                               i2QueueNeeds(pB, pCh, NEED_CREDIT);
+                               notClogged = 0;
+                               break;   // So to do next channel
+                       }
+                       if ( (paddedSize > 0) 
+                               && ( 0 == i2Write2Fifo(pB, pRemove, paddedSize, 128))) {
+                               // Do Not have room in fifo to send this packet.
+                               notClogged = 0;
+                               i2QueueNeeds(pB, pCh, NEED_INLINE);     
+                               break;   // Break from the channel
+                       }
+#ifdef DEBUG_FIFO
+WriteDBGBuf("DATA", pRemove, paddedSize);
+#endif /* DEBUG_FIFO */
+                       pB->debugInlineCount++;
+
+                       pCh->icount.tx += flowsize;
+                       // Update current credits
+                       pCh->outfl.room -= flowsize;
+                       pCh->outfl.asof += flowsize;
+                       if (PTYPE_OF(pRemove) == PTYPE_DATA) {
+                               pCh->Obuf_char_count -= DATA_COUNT_OF(pRemove);
+                       }
+                       pRemove += packetSize;
+                       stripIndex += packetSize;
+
+                       ip2trace (CHANN, ITRC_SICMD, 6, 2, stripIndex, pCh->Obuf_strip);
+
+                       if (stripIndex >= OBUF_SIZE) {
+                               stripIndex = 0;
+                               pRemove = pCh->Obuf;
+
+                               ip2trace (CHANN, ITRC_SICMD, 7, 1, stripIndex );
+
+                       }
+               }       /* while */
+               if ( !bailout2 ) {
+                       ip2trace (CHANN, ITRC_ERROR, 3, 0 );
+               }
+               // Done with this channel. Move to next, removing this one from the
+               // queue of channels if we cleaned it out (i.e., didn't get clogged.
+               pCh->Obuf_strip = stripIndex;
+               write_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
+               if ( notClogged )
+               {
+
+                       ip2trace (CHANN, ITRC_SICMD, 8, 0 );
+
+                       if ( pCh->pTTY ) {
+                               ip2_owake(pCh->pTTY);
+                       }
+               }
+       }  // Either clogged or finished all the work
+
+       if ( !bailout ) {
+               ip2trace (ITRC_NO_PORT, ITRC_ERROR, 4, 0 );
+       }
+
+       ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_RETURN, 1,pB->i2Dbuf_strip);
+}
+
+//******************************************************************************
+// Function:   serviceOutgoingFifo(pB)
+// Parameters: Pointer to a board structure
+// Returns:    Nothing
+//
+// Description:
+// Helper routine to put data in the outgoing fifo, if we aren't already waiting
+// for something to be there. If the fifo has only room for a very little data,
+// go head and hit the board with a mailbox hit immediately. Otherwise, it will
+// have to happen later in the interrupt processing. Since this routine may be
+// called both at interrupt and foreground time, we must turn off interrupts
+// during the entire process.
+//******************************************************************************
+static void
+serviceOutgoingFifo(i2eBordStrPtr pB)
+{
+       // If we aren't currently waiting for the board to empty our fifo, service
+       // everything that is pending, in priority order (especially, Bypass before
+       // Inline).
+       if ( ! pB->i2eWaitingForEmptyFifo )
+       {
+               i2StuffFifoFlow(pB);
+               i2StuffFifoBypass(pB);
+               i2StuffFifoInline(pB);
+
+               iiSendPendingMail(pB);
+       } 
+}
+
+//******************************************************************************
+// Function:   i2ServiceBoard(pB)
+// Parameters: Pointer to a board structure
+// Returns:    Nothing
+//
+// Description:
+// Normally this is called from interrupt level, but there is deliberately
+// nothing in here specific to being called from interrupt level. All the
+// hardware-specific, interrupt-specific things happen at the outer levels.
+//
+// For example, a timer interrupt could drive this routine for some sort of
+// polled operation. The only requirement is that the programmer deal with any
+// atomiticity/concurrency issues that result.
+//
+// This routine responds to the board's having sent mailbox information to the
+// host (which would normally cause an interrupt). This routine reads the
+// incoming mailbox. If there is no data in it, this board did not create the
+// interrupt and/or has nothing to be done to it. (Except, if we have been
+// waiting to write mailbox data to it, we may do so.
+//
+// Based on the value in the mailbox, we may take various actions.
+//
+// No checking here of pB validity: after all, it shouldn't have been called by
+// the handler unless pB were on the list.
+//******************************************************************************
+static inline int
+i2ServiceBoard ( i2eBordStrPtr pB )
+{
+       unsigned inmail;
+       unsigned long flags;
+
+
+       /* This should be atomic because of the way we are called... */
+       if (NO_MAIL_HERE == ( inmail = pB->i2eStartMail ) ) {
+               inmail = iiGetMail(pB);
+       }
+       pB->i2eStartMail = NO_MAIL_HERE;
+
+       ip2trace (ITRC_NO_PORT, ITRC_INTR, 2, 1, inmail );
+
+       if (inmail != NO_MAIL_HERE) {
+               // If the board has gone fatal, nothing to do but hit a bit that will
+               // alert foreground tasks to protest!
+               if ( inmail & MB_FATAL_ERROR ) {
+                       pB->i2eFatal = 1;
+                       goto exit_i2ServiceBoard;
+               }
+
+               /* Assuming no fatal condition, we proceed to do work */
+               if ( inmail & MB_IN_STUFFED ) {
+                       pB->i2eFifoInInts++;
+                       i2StripFifo(pB);     /* There might be incoming packets */
+               }
+
+               if (inmail & MB_OUT_STRIPPED) {
+                       pB->i2eFifoOutInts++;
+                       write_lock_irqsave(&pB->write_fifo_spinlock, flags);
+                       pB->i2eFifoRemains = pB->i2eFifoSize;
+                       pB->i2eWaitingForEmptyFifo = 0;
+                       write_unlock_irqrestore(&pB->write_fifo_spinlock,
+                                       flags);
+
+                       ip2trace (ITRC_NO_PORT, ITRC_INTR, 30, 1, pB->i2eFifoRemains );
+
+               }
+               serviceOutgoingFifo(pB);
+       }
+
+       ip2trace (ITRC_NO_PORT, ITRC_INTR, 8, 0 );
+
+exit_i2ServiceBoard:
+
+       return 0;
+}
diff --git a/drivers/staging/tty/ip2/i2lib.h b/drivers/staging/tty/ip2/i2lib.h
new file mode 100644 (file)
index 0000000..e559e9b
--- /dev/null
@@ -0,0 +1,351 @@
+/*******************************************************************************
+*
+*   (c) 1998 by Computone Corporation
+*
+********************************************************************************
+*
+*
+*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
+*                serial I/O controllers.
+*
+*   DESCRIPTION: Header file for high level library functions
+*
+*******************************************************************************/
+#ifndef I2LIB_H
+#define I2LIB_H   1
+//------------------------------------------------------------------------------
+// I2LIB.H
+//
+// IntelliPort-II and IntelliPort-IIEX
+//
+// Defines, structure definitions, and external declarations for i2lib.c
+//------------------------------------------------------------------------------
+//--------------------------------------
+// Mandatory Includes:
+//--------------------------------------
+#include "ip2types.h"
+#include "i2ellis.h"
+#include "i2pack.h"
+#include "i2cmd.h"
+#include <linux/workqueue.h>
+
+//------------------------------------------------------------------------------
+// i2ChanStr -- Channel Structure:
+// Used to track per-channel information for the library routines using standard
+// loadware. Note also, a pointer to an array of these structures is patched
+// into the i2eBordStr (see i2ellis.h)
+//------------------------------------------------------------------------------
+//
+// If we make some limits on the maximum block sizes, we can avoid dealing with
+// buffer wrap. The wrapping of the buffer is based on where the start of the
+// packet is. Then there is always room for the packet contiguously.
+//
+// Maximum total length of an outgoing data or in-line command block. The limit
+// of 36 on data is quite arbitrary and based more on DOS memory limitations
+// than the board interface. However, for commands, the maximum packet length is
+// MAX_CMD_PACK_SIZE, because the field size for the count is only a few bits
+// (see I2PACK.H) in such packets. For data packets, the count field size is not
+// the limiting factor. As of this writing, MAX_OBUF_BLOCK < MAX_CMD_PACK_SIZE,
+// but be careful if wanting to modify either.
+//
+#define MAX_OBUF_BLOCK  36
+
+// Another note on maximum block sizes: we are buffering packets here. Data is
+// put into the buffer (if there is room) regardless of the credits from the
+// board. The board sends new credits whenever it has removed from his buffers a
+// number of characters equal to 80% of total buffer size. (Of course, the total
+// buffer size is what is reported when the very first set of flow control
+// status packets are received from the board. Therefore, to be robust, you must
+// always fill the board to at least 80% of the current credit limit, else you
+// might not give it enough to trigger a new report. These conditions are
+// obtained here so long as the maximum output block size is less than 20% the
+// size of the board's output buffers. This is true at present by "coincidence"
+// or "infernal knowledge": the board's output buffers are at least 700 bytes
+// long (20% = 140 bytes, at least). The 80% figure is "official", so the safest
+// strategy might be to trap the first flow control report and guarantee that
+// the effective maxObufBlock is the minimum of MAX_OBUF_BLOCK and 20% of first
+// reported buffer credit.
+//
+#define MAX_CBUF_BLOCK  6      // Maximum total length of a bypass command block
+
+#define IBUF_SIZE       512    // character capacity of input buffer per channel
+#define OBUF_SIZE       1024// character capacity of output buffer per channel
+#define CBUF_SIZE       10     // character capacity of output bypass buffer
+
+typedef struct _i2ChanStr
+{
+       // First, back-pointers so that given a pointer to this structure, you can
+       // determine the correct board and channel number to reference, (say, when
+       // issuing commands, etc. (Note, channel number is in infl.hd.i2sChannel.)
+
+       int      port_index;    // Index of port in channel structure array attached
+                                                       // to board structure.
+       PTTY     pTTY;          // Pointer to tty structure for port (OS specific)
+       USHORT   validity;      // Indicates whether the given channel has been
+                                                       // initialized, really exists (or is a missing
+                                                       // channel, e.g. channel 9 on an 8-port box.)
+
+       i2eBordStrPtr  pMyBord; // Back-pointer to this channel's board structure 
+
+       int      wopen;                 // waiting fer carrier
+
+       int      throttled;             // Set if upper layer can take no data
+
+       int      flags;         // Defined in tty.h
+
+       PWAITQ   open_wait;     // Pointer for OS sleep function.
+       PWAITQ   close_wait;    // Pointer for OS sleep function.
+       PWAITQ   delta_msr_wait;// Pointer for OS sleep function.
+       PWAITQ   dss_now_wait;  // Pointer for OS sleep function.
+
+       struct timer_list  BookmarkTimer;   // Used by i2DrainOutput
+       wait_queue_head_t pBookmarkWait;   // Used by i2DrainOutput
+
+       int      BaudBase;
+       int      BaudDivisor;
+
+       USHORT   ClosingDelay;
+       USHORT   ClosingWaitTime;
+
+       volatile
+       flowIn   infl;  // This structure is initialized as a completely
+                                       // formed flow-control command packet, and as such
+                                       // has the channel number, also the capacity and
+                                       // "as-of" data needed continuously.
+
+       USHORT   sinceLastFlow; // Counts the number of characters read from input
+                                                       // buffers, since the last time flow control info
+                                                       // was sent.
+
+       USHORT   whenSendFlow;  // Determines when new flow control is to be sent to
+                                                       // the board. Note unlike earlier manifestations of
+                                                       // the driver, these packets can be sent from
+                                                       // in-place.
+
+       USHORT   channelNeeds;  // Bit map of important things which must be done
+                                                       // for this channel. (See bits below )
+
+       volatile
+       flowStat outfl;         // Same type of structure is used to hold current
+                                                       // flow control information used to control our
+                                                       // output. "asof" is kept updated as data is sent,
+                                                       // and "room" never goes to zero.
+
+       // The incoming ring buffer
+       // Unlike the outgoing buffers, this holds raw data, not packets. The two
+       // extra bytes are used to hold the byte-padding when there is room for an
+       // odd number of bytes before we must wrap.
+       //
+       UCHAR    Ibuf[IBUF_SIZE + 2];
+       volatile
+       USHORT   Ibuf_stuff;     // Stuffing index
+       volatile
+       USHORT   Ibuf_strip;     // Stripping index
+
+       // The outgoing ring-buffer: Holds Data and command packets. N.B., even
+       // though these are in the channel structure, the channel is also written
+       // here, the easier to send it to the fifo when ready. HOWEVER, individual
+       // packets here are NOT padded to even length: the routines for writing
+       // blocks to the fifo will pad to even byte counts.
+       //
+       UCHAR   Obuf[OBUF_SIZE+MAX_OBUF_BLOCK+4];
+       volatile
+       USHORT  Obuf_stuff;     // Stuffing index
+       volatile
+       USHORT  Obuf_strip;     // Stripping index
+       int     Obuf_char_count;
+
+       // The outgoing bypass-command buffer. Unlike earlier manifestations, the
+       // flow control packets are sent directly from the structures. As above, the
+       // channel number is included in the packet, but they are NOT padded to even
+       // size.
+       //
+       UCHAR    Cbuf[CBUF_SIZE+MAX_CBUF_BLOCK+2];
+       volatile
+       USHORT   Cbuf_stuff;     // Stuffing index
+       volatile
+       USHORT   Cbuf_strip;     // Stripping index
+
+       // The temporary buffer for the Linux tty driver PutChar entry.
+       //
+       UCHAR    Pbuf[MAX_OBUF_BLOCK - sizeof (i2DataHeader)];
+       volatile
+       USHORT   Pbuf_stuff;     // Stuffing index
+
+       // The state of incoming data-set signals
+       //
+       USHORT   dataSetIn;     // Bit-mapped according to below. Also indicates
+                                                       // whether a break has been detected since last
+                                                       // inquiry.
+
+       // The state of outcoming data-set signals (as far as we can tell!)
+       //
+       USHORT   dataSetOut;     // Bit-mapped according to below. 
+
+       // Most recent hot-key identifier detected
+       //
+       USHORT   hotKeyIn;      // Hot key as sent by the board, HOT_CLEAR indicates
+                               // no hot key detected since last examined.
+
+       // Counter of outstanding requests for bookmarks
+       //
+       short   bookMarks;      // Number of outstanding bookmark requests, (+ive
+                                               // whenever a bookmark request if queued up, -ive
+                                               // whenever a bookmark is received).
+
+       // Misc options
+       //
+       USHORT   channelOptions;   // See below
+
+       // To store various incoming special packets
+       //
+       debugStat   channelStatus;
+       cntStat     channelRcount;
+       cntStat     channelTcount;
+       failStat    channelFail;
+
+       // To store the last values for line characteristics we sent to the board.
+       //
+       int     speed;
+
+       int flush_flags;
+
+       void (*trace)(unsigned short,unsigned char,unsigned char,unsigned long,...);
+
+       /*
+        * Kernel counters for the 4 input interrupts 
+        */
+       struct async_icount icount;
+
+       /*
+        *      Task queues for processing input packets from the board.
+        */
+       struct work_struct      tqueue_input;
+       struct work_struct      tqueue_status;
+       struct work_struct      tqueue_hangup;
+
+       rwlock_t Ibuf_spinlock;
+       rwlock_t Obuf_spinlock;
+       rwlock_t Cbuf_spinlock;
+       rwlock_t Pbuf_spinlock;
+
+} i2ChanStr, *i2ChanStrPtr;
+
+//---------------------------------------------------
+// Manifests and bit-maps for elements in i2ChanStr
+//---------------------------------------------------
+//
+// flush flags
+//
+#define STARTFL_FLAG 1
+#define STOPFL_FLAG  2
+
+// validity
+//
+#define CHANNEL_MAGIC_BITS 0xff00
+#define CHANNEL_MAGIC      0x5300   // (validity & CHANNEL_MAGIC_BITS) ==
+                                                                       // CHANNEL_MAGIC --> structure good
+
+#define CHANNEL_SUPPORT    0x0001   // Indicates channel is supported, exists,
+                                                                       // and passed P.O.S.T.
+
+// channelNeeds
+//
+#define NEED_FLOW    1  // Indicates flow control has been queued
+#define NEED_INLINE  2  // Indicates inline commands or data queued
+#define NEED_BYPASS  4  // Indicates bypass commands queued
+#define NEED_CREDIT  8  // Indicates would be sending except has not sufficient
+                                               // credit. The data is still in the channel structure,
+                                               // but the channel is not enqueued in the board
+                                               // structure again until there is a credit received from
+                                               // the board.
+
+// dataSetIn (Also the bits for i2GetStatus return value)
+//
+#define I2_DCD 1
+#define I2_CTS 2
+#define I2_DSR 4
+#define I2_RI  8
+
+// dataSetOut (Also the bits for i2GetStatus return value)
+//
+#define I2_DTR 1
+#define I2_RTS 2
+
+// i2GetStatus() can optionally clear these bits
+//
+#define I2_BRK    0x10  // A break was detected
+#define I2_PAR    0x20  // A parity error was received 
+#define I2_FRA    0x40  // A framing error was received
+#define I2_OVR    0x80  // An overrun error was received 
+
+// i2GetStatus() automatically clears these bits */
+//
+#define I2_DDCD   0x100 // DCD changed from its  former value
+#define I2_DCTS   0x200 // CTS changed from its former value 
+#define I2_DDSR   0x400 // DSR changed from its former value 
+#define I2_DRI    0x800 // RI changed from its former value 
+
+// hotKeyIn
+//
+#define HOT_CLEAR 0x1322   // Indicates that no hot-key has been detected
+
+// channelOptions
+//
+#define CO_NBLOCK_WRITE 1      // Writes don't block waiting for buffer. (Default
+                                                       // is, they do wait.)
+
+// fcmodes
+//
+#define I2_OUTFLOW_CTS  0x0001
+#define I2_INFLOW_RTS   0x0002
+#define I2_INFLOW_DSR   0x0004
+#define I2_INFLOW_DTR   0x0008
+#define I2_OUTFLOW_DSR  0x0010
+#define I2_OUTFLOW_DTR  0x0020
+#define I2_OUTFLOW_XON  0x0040
+#define I2_OUTFLOW_XANY 0x0080
+#define I2_INFLOW_XON   0x0100
+
+#define I2_CRTSCTS      (I2_OUTFLOW_CTS|I2_INFLOW_RTS)
+#define I2_IXANY_MODE   (I2_OUTFLOW_XON|I2_OUTFLOW_XANY)
+
+//-------------------------------------------
+// Macros used from user level like functions
+//-------------------------------------------
+
+// Macros to set and clear channel options
+//
+#define i2SetOption(pCh, option) pCh->channelOptions |= option
+#define i2ClrOption(pCh, option) pCh->channelOptions &= ~option
+
+// Macro to set fatal-error trap
+//
+#define i2SetFatalTrap(pB, routine) pB->i2eFatalTrap = routine
+
+//--------------------------------------------
+// Declarations and prototypes for i2lib.c
+//--------------------------------------------
+//
+static int  i2InitChannels(i2eBordStrPtr, int, i2ChanStrPtr);
+static int  i2QueueCommands(int, i2ChanStrPtr, int, int, cmdSyntaxPtr,...);
+static int  i2GetStatus(i2ChanStrPtr, int);
+static int  i2Input(i2ChanStrPtr);
+static int  i2InputFlush(i2ChanStrPtr);
+static int  i2Output(i2ChanStrPtr, const char *, int);
+static int  i2OutputFree(i2ChanStrPtr);
+static int  i2ServiceBoard(i2eBordStrPtr);
+static void i2DrainOutput(i2ChanStrPtr, int);
+
+#ifdef IP2DEBUG_TRACE
+void ip2trace(unsigned short,unsigned char,unsigned char,unsigned long,...);
+#else
+#define ip2trace(a,b,c,d...) do {} while (0)
+#endif
+
+// Argument to i2QueueCommands
+//
+#define C_IN_LINE 1
+#define C_BYPASS  0
+
+#endif   // I2LIB_H
diff --git a/drivers/staging/tty/ip2/i2pack.h b/drivers/staging/tty/ip2/i2pack.h
new file mode 100644 (file)
index 0000000..00342a6
--- /dev/null
@@ -0,0 +1,364 @@
+/*******************************************************************************
+*
+*   (c) 1998 by Computone Corporation
+*
+********************************************************************************
+*
+*
+*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
+*                serial I/O controllers.
+*
+*   DESCRIPTION: Definitions of the packets used to transfer data and commands
+*                Host <--> Board. Information provided here is only applicable
+*                when the standard loadware is active.
+*
+*******************************************************************************/
+#ifndef I2PACK_H
+#define I2PACK_H  1
+
+//-----------------------------------------------
+// Revision History:
+//
+// 10 October 1991   MAG First draft
+// 24 February 1992  MAG Additions for 1.4.x loadware
+// 11 March 1992     MAG New status packets
+//
+//-----------------------------------------------
+
+//------------------------------------------------------------------------------
+// Packet Formats:
+//
+// Information passes between the host and board through the FIFO in packets.
+// These have headers which indicate the type of packet. Because the fifo data
+// path may be 16-bits wide, the protocol is constrained such that each packet
+// is always padded to an even byte count. (The lower-level interface routines
+// -- i2ellis.c -- are designed to do this).
+//
+// The sender (be it host or board) must place some number of complete packets
+// in the fifo, then place a message in the mailbox that packets are available.
+// Placing such a message interrupts the "receiver" (be it board or host), who
+// reads the mailbox message and determines that there are incoming packets
+// ready. Since there are no partial packets, and the length of a packet is
+// given in the header, the remainder of the packet can be read without checking
+// for FIFO empty condition. The process is repeated, packet by packet, until
+// the incoming FIFO is empty. Then the receiver uses the outbound mailbox to
+// signal the board that it has read the data. Only then can the sender place
+// additional data in the fifo.
+//------------------------------------------------------------------------------
+//
+//------------------------------------------------
+// Definition of Packet Header Area
+//------------------------------------------------
+//
+// Caution: these only define header areas. In actual use the data runs off
+// beyond the end of these structures.
+//
+// Since these structures are based on sequences of bytes which go to the board,
+// there cannot be ANY padding between the elements.
+#pragma pack(1)
+
+//----------------------------
+// DATA PACKETS
+//----------------------------
+
+typedef struct _i2DataHeader
+{
+       unsigned char i2sChannel;  /* The channel number: 0-255 */
+
+       // -- Bitfields are allocated LSB first --
+
+       // For incoming data, indicates whether this is an ordinary packet or a
+       // special one (e.g., hot key hit).
+       unsigned i2sId : 2 __attribute__ ((__packed__));
+
+       // For tagging data packets. There are flush commands which flush only data
+       // packets bearing a particular tag. (used in implementing IntelliView and
+       // IntelliPrint). THE TAG VALUE 0xf is RESERVED and must not be used (it has
+       // meaning internally to the loadware).
+       unsigned i2sTag : 4;
+
+       // These two bits determine the type of packet sent/received.
+       unsigned i2sType : 2;
+
+       // The count of data to follow: does not include the possible additional
+       // padding byte. MAXIMUM COUNT: 4094. The top four bits must be 0.
+       unsigned short i2sCount;
+
+} i2DataHeader, *i2DataHeaderPtr;
+
+// Structure is immediately followed by the data, proper.
+
+//----------------------------
+// NON-DATA PACKETS
+//----------------------------
+
+typedef struct _i2CmdHeader
+{
+       unsigned char i2sChannel;       // The channel number: 0-255 (Except where noted
+                                                               // - see below
+
+       // Number of bytes of commands, status or whatever to follow
+       unsigned i2sCount : 6;
+
+       // These two bits determine the type of packet sent/received.
+       unsigned i2sType : 2;
+
+} i2CmdHeader, *i2CmdHeaderPtr;
+
+// Structure is immediately followed by the applicable data.
+
+//---------------------------------------
+// Flow Control Packets (Outbound)
+//---------------------------------------
+
+// One type of outbound command packet is so important that the entire structure
+// is explicitly defined here. That is the flow-control packet. This is never
+// sent by user-level code (as would be the commands to raise/lower DTR, for
+// example). These are only sent by the library routines in response to reading
+// incoming data into the buffers.
+//
+// The parameters inside the command block are maintained in place, then the
+// block is sent at the appropriate time.
+
+typedef struct _flowIn
+{
+       i2CmdHeader    hd;      // Channel #, count, type (see above)
+       unsigned char  fcmd;    // The flow control command (37)
+       unsigned short asof;    // As of byte number "asof" (LSB first!) I have room
+                                                       // for "room" bytes
+       unsigned short room;
+} flowIn, *flowInPtr;
+
+//----------------------------------------
+// (Incoming) Status Packets
+//----------------------------------------
+
+// Incoming packets which are non-data packets are status packets. In this case,
+// the channel number in the header is unimportant. What follows are one or more
+// sub-packets, the first word of which consists of the channel (first or low
+// byte) and the status indicator (second or high byte), followed by possibly
+// more data.
+
+#define STAT_CTS_UP     0  /* CTS raised  (no other bytes) */
+#define STAT_CTS_DN     1  /* CTS dropped (no other bytes) */
+#define STAT_DCD_UP     2  /* DCD raised  (no other bytes) */
+#define STAT_DCD_DN     3  /* DCD dropped (no other bytes) */
+#define STAT_DSR_UP     4  /* DSR raised  (no other bytes) */
+#define STAT_DSR_DN     5  /* DSR dropped (no other bytes) */
+#define STAT_RI_UP      6  /* RI  raised  (no other bytes) */
+#define STAT_RI_DN      7  /* RI  dropped (no other bytes) */
+#define STAT_BRK_DET    8  /* BRK detect  (no other bytes) */
+#define STAT_FLOW       9  /* Flow control(-- more: see below */
+#define STAT_BMARK      10 /* Bookmark    (no other bytes)
+                                                       * Bookmark is sent as a response to
+                                                       * a command 60: request for bookmark
+                                                       */
+#define STAT_STATUS     11 /* Special packet: see below */
+#define STAT_TXCNT      12 /* Special packet: see below */
+#define STAT_RXCNT      13 /* Special packet: see below */
+#define STAT_BOXIDS     14 /* Special packet: see below */
+#define STAT_HWFAIL     15 /* Special packet: see below */
+
+#define STAT_MOD_ERROR  0xc0
+#define STAT_MODEM      0xc0/* If status & STAT_MOD_ERROR:
+                                                        * == STAT_MODEM, then this is a modem
+                                                        * status packet, given in response to a
+                                                        * CMD_DSS_NOW command.
+                                                        * The low nibble has each data signal:
+                                                        */
+#define STAT_MOD_DCD    0x8
+#define STAT_MOD_RI     0x4
+#define STAT_MOD_DSR    0x2
+#define STAT_MOD_CTS    0x1
+
+#define STAT_ERROR      0x80/* If status & STAT_MOD_ERROR
+                                                        * == STAT_ERROR, then
+                                                        * sort of error on the channel.
+                                                        * The remaining seven bits indicate
+                                                        * what sort of error it is.
+                                                        */
+/* The low three bits indicate parity, framing, or overrun errors */
+
+#define STAT_E_PARITY   4     /* Parity error */
+#define STAT_E_FRAMING  2     /* Framing error */
+#define STAT_E_OVERRUN  1     /* (uxart) overrun error */
+
+//---------------------------------------
+// STAT_FLOW packets
+//---------------------------------------
+
+typedef struct _flowStat
+{
+       unsigned short asof;
+       unsigned short room;
+}flowStat, *flowStatPtr;
+
+// flowStat packets are received from the board to regulate the flow of outgoing
+// data. A local copy of this structure is also kept to track the amount of
+// credits used and credits remaining. "room" is the amount of space in the
+// board's buffers, "as of" having received a certain byte number. When sending
+// data to the fifo, you must calculate how much buffer space your packet will
+// use.  Add this to the current "asof" and subtract it from the current "room".
+//
+// The calculation for the board's buffer is given by CREDIT_USAGE, where size
+// is the un-rounded count of either data characters or command characters.
+// (Which is to say, the count rounded up, plus two).
+
+#define CREDIT_USAGE(size) (((size) + 3) & ~1)
+
+//---------------------------------------
+// STAT_STATUS packets
+//---------------------------------------
+
+typedef  struct   _debugStat
+{
+       unsigned char d_ccsr;
+       unsigned char d_txinh;
+       unsigned char d_stat1;
+       unsigned char d_stat2;
+} debugStat, *debugStatPtr;
+
+// debugStat packets are sent to the host in response to a CMD_GET_STATUS
+// command.  Each byte is bit-mapped as described below:
+
+#define D_CCSR_XON      2     /* Has received XON, ready to transmit */
+#define D_CCSR_XOFF     4     /* Has received XOFF, not transmitting */
+#define D_CCSR_TXENAB   8     /* Transmitter is enabled */
+#define D_CCSR_RXENAB   0x80  /* Receiver is enabled */
+
+#define D_TXINH_BREAK   1     /* We are sending a break */
+#define D_TXINH_EMPTY   2     /* No data to send */
+#define D_TXINH_SUSP    4     /* Output suspended via command 57 */
+#define D_TXINH_CMD     8     /* We are processing an in-line command */
+#define D_TXINH_LCD     0x10  /* LCD diagnostics are running */
+#define D_TXINH_PAUSE   0x20  /* We are processing a PAUSE command */
+#define D_TXINH_DCD     0x40  /* DCD is low, preventing transmission */
+#define D_TXINH_DSR     0x80  /* DSR is low, preventing transmission */
+
+#define D_STAT1_TXEN    1     /* Transmit INTERRUPTS enabled */
+#define D_STAT1_RXEN    2     /* Receiver INTERRUPTS enabled */
+#define D_STAT1_MDEN    4     /* Modem (data set sigs) interrupts enabled */
+#define D_STAT1_RLM     8     /* Remote loopback mode selected */
+#define D_STAT1_LLM     0x10  /* Local internal loopback mode selected */
+#define D_STAT1_CTS     0x20  /* CTS is low, preventing transmission */
+#define D_STAT1_DTR     0x40  /* DTR is low, to stop remote transmission */
+#define D_STAT1_RTS     0x80  /* RTS is low, to stop remote transmission */
+
+#define D_STAT2_TXMT    1     /* Transmit buffers are all empty */
+#define D_STAT2_RXMT    2     /* Receive buffers are all empty */
+#define D_STAT2_RXINH   4     /* Loadware has tried to inhibit remote
+                                                          * transmission:  dropped DTR, sent XOFF,
+                                                          * whatever...
+                                                          */
+#define D_STAT2_RXFLO   8     /* Loadware can send no more data to host
+                                                          * until it receives a flow-control packet
+                                                          */
+//-----------------------------------------
+// STAT_TXCNT and STAT_RXCNT packets
+//----------------------------------------
+
+typedef  struct   _cntStat
+{
+       unsigned short cs_time;    // (Assumes host is little-endian!)
+       unsigned short cs_count;
+} cntStat, *cntStatPtr;
+
+// These packets are sent in response to a CMD_GET_RXCNT or a CMD_GET_TXCNT
+// bypass command. cs_time is a running 1 Millisecond counter which acts as a
+// time stamp. cs_count is a running counter of data sent or received from the
+// uxarts. (Not including data added by the chip itself, as with CRLF
+// processing).
+//------------------------------------------
+// STAT_HWFAIL packets
+//------------------------------------------
+
+typedef struct _failStat
+{
+       unsigned char fs_written;
+       unsigned char fs_read;
+       unsigned short fs_address;
+} failStat, *failStatPtr;
+
+// This packet is sent whenever the on-board diagnostic process detects an
+// error. At startup, this process is dormant. The host can wake it up by
+// issuing the bypass command CMD_HW_TEST. The process runs at low priority and
+// performs continuous hardware verification; writing data to certain on-board
+// registers, reading it back, and comparing. If it detects an error, this
+// packet is sent to the host, and the process goes dormant again until the host
+// sends another CMD_HW_TEST. It then continues with the next register to be
+// tested.
+
+//------------------------------------------------------------------------------
+// Macros to deal with the headers more easily! Note that these are defined so
+// they may be used as "left" as well as "right" expressions.
+//------------------------------------------------------------------------------
+
+// Given a pointer to the packet, reference the channel number
+//
+#define CHANNEL_OF(pP)  ((i2DataHeaderPtr)(pP))->i2sChannel
+
+// Given a pointer to the packet, reference the Packet type
+//
+#define PTYPE_OF(pP) ((i2DataHeaderPtr)(pP))->i2sType
+
+// The possible types of packets
+//
+#define PTYPE_DATA   0  /* Host <--> Board */
+#define PTYPE_BYPASS 1  /* Host ---> Board */
+#define PTYPE_INLINE 2  /* Host ---> Board */
+#define PTYPE_STATUS 2  /* Host <--- Board */
+
+// Given a pointer to a Data packet, reference the Tag
+//
+#define TAG_OF(pP) ((i2DataHeaderPtr)(pP))->i2sTag
+
+// Given a pointer to a Data packet, reference the data i.d.
+//
+#define ID_OF(pP)  ((i2DataHeaderPtr)(pP))->i2sId
+
+// The possible types of ID's
+//
+#define ID_ORDINARY_DATA   0
+#define ID_HOT_KEY         1
+
+// Given a pointer to a Data packet, reference the count
+//
+#define DATA_COUNT_OF(pP) ((i2DataHeaderPtr)(pP))->i2sCount
+
+// Given a pointer to a Data packet, reference the beginning of data
+//
+#define DATA_OF(pP) &((unsigned char *)(pP))[4] // 4 = size of header
+
+// Given a pointer to a Non-Data packet, reference the count
+//
+#define CMD_COUNT_OF(pP) ((i2CmdHeaderPtr)(pP))->i2sCount
+
+#define MAX_CMD_PACK_SIZE  62 // Maximum size of such a count
+
+// Given a pointer to a Non-Data packet, reference the beginning of data
+//
+#define CMD_OF(pP) &((unsigned char *)(pP))[2]  // 2 = size of header
+
+//--------------------------------
+// MailBox Bits:
+//--------------------------------
+
+//--------------------------
+// Outgoing (host to board)
+//--------------------------
+//
+#define MB_OUT_STUFFED     0x80  // Host has placed output in fifo 
+#define MB_IN_STRIPPED     0x40  // Host has read in all input from fifo 
+
+//--------------------------
+// Incoming (board to host)
+//--------------------------
+//
+#define MB_IN_STUFFED      0x80  // Board has placed input in fifo 
+#define MB_OUT_STRIPPED    0x40  // Board has read all output from fifo 
+#define MB_FATAL_ERROR     0x20  // Board has encountered a fatal error
+
+#pragma pack()                  // Reset padding to command-line default
+
+#endif      // I2PACK_H
+
diff --git a/drivers/staging/tty/ip2/ip2.h b/drivers/staging/tty/ip2/ip2.h
new file mode 100644 (file)
index 0000000..936ccc5
--- /dev/null
@@ -0,0 +1,107 @@
+/*******************************************************************************
+*
+*   (c) 1998 by Computone Corporation
+*
+********************************************************************************
+*
+*
+*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
+*                serial I/O controllers.
+*
+*   DESCRIPTION: Driver constants for configuration and tuning
+*
+*   NOTES:
+*
+*******************************************************************************/
+#ifndef IP2_H
+#define IP2_H
+
+#include "ip2types.h"
+#include "i2cmd.h"
+
+/*************/
+/* Constants */
+/*************/
+
+/* Device major numbers - since version 2.0.26. */
+#define IP2_TTY_MAJOR      71
+#define IP2_CALLOUT_MAJOR  72
+#define IP2_IPL_MAJOR      73
+
+/* Board configuration array.
+ * This array defines the hardware irq and address for up to IP2_MAX_BOARDS
+ * (4 supported per ip2_types.h) ISA board addresses and irqs MUST be specified,
+ * PCI and EISA boards are probed for and automagicly configed
+ * iff the addresses are set to 1 and 2 respectivily.
+ *    0x0100 - 0x03f0 == ISA
+ *              1        == PCI
+ *              2        == EISA
+ *              0        == (skip this board)
+ * This array defines the hardware addresses for them. Special 
+ * addresses are EISA and PCI which go sniffing for boards. 
+
+ * In a multiboard system the position in the array determines which port
+ * devices are assigned to each board: 
+ *             board 0 is assigned ttyF0.. to ttyF63, 
+ *             board 1 is assigned ttyF64  to ttyF127,
+ *             board 2 is assigned ttyF128 to ttyF191,
+ *             board 3 is assigned ttyF192 to ttyF255. 
+ *
+ * In PCI and EISA bus systems each range is mapped to card in 
+ * monotonically increasing slot number order, ISA position is as specified
+ * here.
+
+ * If the irqs are ALL set to 0,0,0,0 all boards operate in 
+ * polled mode. For interrupt operation ISA boards require that the IRQ be 
+ * specified, while PCI and EISA boards any nonzero entry 
+ * will enable interrupts using the BIOS configured irq for the board. 
+ * An invalid irq entry will default to polled mode for that card and print
+ * console warning.
+ * When the driver is loaded as a module these setting can be overridden on the 
+ * modprobe command line or on an option line in /etc/modprobe.conf.
+ * If the driver is built-in the configuration must be 
+ * set here for ISA cards and address set to 1 and 2 for PCI and EISA.
+ *
+ * Here is an example that shows most if not all possibe combinations:
+
+ *static ip2config_t ip2config =
+ *{
+ *     {11,1,0,0},             // irqs
+ *     {                               // Addresses
+ *             0x0308,         // Board 0, ttyF0   - ttyF63// ISA card at io=0x308, irq=11
+ *             0x0001,         // Board 1, ttyF64  - ttyF127//PCI card configured by BIOS
+ *             0x0000,         // Board 2, ttyF128 - ttyF191// Slot skipped
+ *             0x0002          // Board 3, ttyF192 - ttyF255//EISA card configured by BIOS
+ *                                                                                              // but polled not irq driven
+ *     }
+ *};
+ */
+
+ /* this structure is zeroed out because the suggested method is to configure
+  * the driver as a module, set up the parameters with an options line in
+  * /etc/modprobe.conf and load with modprobe or kmod, the kernel
+  * module loader
+  */
+
+ /* This structure is NOW always initialized when the driver is initialized.
+  * Compiled in defaults MUST be added to the io and irq arrays in
+  * ip2.c.  Those values are configurable from insmod parameters in the
+  * case of modules or from command line parameters (ip2=io,irq) when
+  * compiled in.
+  */
+
+static ip2config_t ip2config =
+{
+       {0,0,0,0},              // irqs
+       {                               // Addresses
+       /* Do NOT set compile time defaults HERE!  Use the arrays in
+               ip2.c!  These WILL be overwritten!  =mhw= */
+               0x0000,         // Board 0, ttyF0   - ttyF63
+               0x0000,         // Board 1, ttyF64  - ttyF127
+               0x0000,         // Board 2, ttyF128 - ttyF191
+               0x0000          // Board 3, ttyF192 - ttyF255
+       }
+};
+
+#endif
diff --git a/drivers/staging/tty/ip2/ip2ioctl.h b/drivers/staging/tty/ip2/ip2ioctl.h
new file mode 100644 (file)
index 0000000..aa0a9da
--- /dev/null
@@ -0,0 +1,35 @@
+/*******************************************************************************
+*
+*   (c) 1998 by Computone Corporation
+*
+********************************************************************************
+*
+*
+*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
+*                serial I/O controllers.
+*
+*   DESCRIPTION: Driver constants for configuration and tuning
+*
+*   NOTES:
+*
+*******************************************************************************/
+
+#ifndef IP2IOCTL_H
+#define IP2IOCTL_H
+
+//*************
+//* Constants *
+//*************
+
+// High baud rates (if not defined elsewhere.
+#ifndef B153600   
+#      define B153600   0010005
+#endif
+#ifndef B307200   
+#      define B307200   0010006
+#endif
+#ifndef B921600   
+#      define B921600   0010007
+#endif
+
+#endif
diff --git a/drivers/staging/tty/ip2/ip2main.c b/drivers/staging/tty/ip2/ip2main.c
new file mode 100644 (file)
index 0000000..ea7a8fb
--- /dev/null
@@ -0,0 +1,3234 @@
+/*
+*
+*   (c) 1999 by Computone Corporation
+*
+********************************************************************************
+*
+*   PACKAGE:     Linux tty Device Driver for IntelliPort family of multiport
+*                serial I/O controllers.
+*
+*   DESCRIPTION: Mainline code for the device driver
+*
+*******************************************************************************/
+// ToDo:
+//
+// Fix the immediate DSS_NOW problem.
+// Work over the channel stats return logic in ip2_ipl_ioctl so they
+//     make sense for all 256 possible channels and so the user space
+//     utilities will compile and work properly.
+//
+// Done:
+//
+// 1.2.14      /\/\|=mhw=|\/\/
+// Added bounds checking to ip2_ipl_ioctl to avoid potential terroristic acts.
+// Changed the definition of ip2trace to be more consistent with kernel style
+//     Thanks to Andreas Dilger <adilger@turbolabs.com> for these updates
+//
+// 1.2.13      /\/\|=mhw=|\/\/
+// DEVFS: Renamed ttf/{n} to tts/F{n} and cuf/{n} to cua/F{n} to conform
+//     to agreed devfs serial device naming convention.
+//
+// 1.2.12      /\/\|=mhw=|\/\/
+// Cleaned up some remove queue cut and paste errors
+//
+// 1.2.11      /\/\|=mhw=|\/\/
+// Clean up potential NULL pointer dereferences
+// Clean up devfs registration
+// Add kernel command line parsing for io and irq
+//     Compile defaults for io and irq are now set in ip2.c not ip2.h!
+// Reworked poll_only hack for explicit parameter setting
+//     You must now EXPLICITLY set poll_only = 1 or set all irqs to 0
+// Merged ip2_loadmain and old_ip2_init
+// Converted all instances of interruptible_sleep_on into queue calls
+//     Most of these had no race conditions but better to clean up now
+//
+// 1.2.10      /\/\|=mhw=|\/\/
+// Fixed the bottom half interrupt handler and enabled USE_IQI
+//     to split the interrupt handler into a formal top-half / bottom-half
+// Fixed timing window on high speed processors that queued messages to
+//     the outbound mail fifo faster than the board could handle.
+//
+// 1.2.9
+// Four box EX was barfing on >128k kmalloc, made structure smaller by
+// reducing output buffer size
+//
+// 1.2.8
+// Device file system support (MHW)
+//
+// 1.2.7 
+// Fixed
+// Reload of ip2 without unloading ip2main hangs system on cat of /proc/modules
+//
+// 1.2.6
+//Fixes DCD problems
+//     DCD was not reported when CLOCAL was set on call to TIOCMGET
+//
+//Enhancements:
+//     TIOCMGET requests and waits for status return
+//     No DSS interrupts enabled except for DCD when needed
+//
+// For internal use only
+//
+//#define IP2DEBUG_INIT
+//#define IP2DEBUG_OPEN
+//#define IP2DEBUG_WRITE
+//#define IP2DEBUG_READ
+//#define IP2DEBUG_IOCTL
+//#define IP2DEBUG_IPL
+
+//#define IP2DEBUG_TRACE
+//#define DEBUG_FIFO
+
+/************/
+/* Includes */
+/************/
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/major.h>
+#include <linux/wait.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/termios.h>
+#include <linux/tty_driver.h>
+#include <linux/serial.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+
+#include <linux/cdk.h>
+#include <linux/comstats.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+
+#include "ip2types.h"
+#include "ip2trace.h"
+#include "ip2ioctl.h"
+#include "ip2.h"
+#include "i2ellis.h"
+#include "i2lib.h"
+
+/*****************
+ * /proc/ip2mem  *
+ *****************/
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+static DEFINE_MUTEX(ip2_mutex);
+static const struct file_operations ip2mem_proc_fops;
+static const struct file_operations ip2_proc_fops;
+
+/********************/
+/* Type Definitions */
+/********************/
+
+/*************/
+/* Constants */
+/*************/
+
+/* String constants to identify ourselves */
+static const char pcName[] = "Computone IntelliPort Plus multiport driver";
+static const char pcVersion[] = "1.2.14";
+
+/* String constants for port names */
+static const char pcDriver_name[] = "ip2";
+static const char pcIpl[] = "ip2ipl";
+
+/***********************/
+/* Function Prototypes */
+/***********************/
+
+/* Global module entry functions */
+
+/* Private (static) functions */
+static int  ip2_open(PTTY, struct file *);
+static void ip2_close(PTTY, struct file *);
+static int  ip2_write(PTTY, const unsigned char *, int);
+static int  ip2_putchar(PTTY, unsigned char);
+static void ip2_flush_chars(PTTY);
+static int  ip2_write_room(PTTY);
+static int  ip2_chars_in_buf(PTTY);
+static void ip2_flush_buffer(PTTY);
+static int  ip2_ioctl(PTTY, UINT, ULONG);
+static void ip2_set_termios(PTTY, struct ktermios *);
+static void ip2_set_line_discipline(PTTY);
+static void ip2_throttle(PTTY);
+static void ip2_unthrottle(PTTY);
+static void ip2_stop(PTTY);
+static void ip2_start(PTTY);
+static void ip2_hangup(PTTY);
+static int  ip2_tiocmget(struct tty_struct *tty);
+static int  ip2_tiocmset(struct tty_struct *tty,
+                        unsigned int set, unsigned int clear);
+static int ip2_get_icount(struct tty_struct *tty,
+               struct serial_icounter_struct *icount);
+
+static void set_irq(int, int);
+static void ip2_interrupt_bh(struct work_struct *work);
+static irqreturn_t ip2_interrupt(int irq, void *dev_id);
+static void ip2_poll(unsigned long arg);
+static inline void service_all_boards(void);
+static void do_input(struct work_struct *);
+static void do_status(struct work_struct *);
+
+static void ip2_wait_until_sent(PTTY,int);
+
+static void set_params (i2ChanStrPtr, struct ktermios *);
+static int get_serial_info(i2ChanStrPtr, struct serial_struct __user *);
+static int set_serial_info(i2ChanStrPtr, struct serial_struct __user *);
+
+static ssize_t ip2_ipl_read(struct file *, char __user *, size_t, loff_t *);
+static ssize_t ip2_ipl_write(struct file *, const char __user *, size_t, loff_t *);
+static long ip2_ipl_ioctl(struct file *, UINT, ULONG);
+static int ip2_ipl_open(struct inode *, struct file *);
+
+static int DumpTraceBuffer(char __user *, int);
+static int DumpFifoBuffer( char __user *, int);
+
+static void ip2_init_board(int, const struct firmware *);
+static unsigned short find_eisa_board(int);
+static int ip2_setup(char *str);
+
+/***************/
+/* Static Data */
+/***************/
+
+static struct tty_driver *ip2_tty_driver;
+
+/* Here, then is a table of board pointers which the interrupt routine should
+ * scan through to determine who it must service.
+ */
+static unsigned short i2nBoards; // Number of boards here
+
+static i2eBordStrPtr i2BoardPtrTable[IP2_MAX_BOARDS];
+
+static i2ChanStrPtr  DevTable[IP2_MAX_PORTS];
+//DevTableMem just used to save addresses for kfree
+static void  *DevTableMem[IP2_MAX_BOARDS];
+
+/* This is the driver descriptor for the ip2ipl device, which is used to
+ * download the loadware to the boards.
+ */
+static const struct file_operations ip2_ipl = {
+       .owner          = THIS_MODULE,
+       .read           = ip2_ipl_read,
+       .write          = ip2_ipl_write,
+       .unlocked_ioctl = ip2_ipl_ioctl,
+       .open           = ip2_ipl_open,
+       .llseek         = noop_llseek,
+}; 
+
+static unsigned long irq_counter;
+static unsigned long bh_counter;
+
+// Use immediate queue to service interrupts
+#define USE_IQI
+//#define USE_IQ       // PCI&2.2 needs work
+
+/* The timer_list entry for our poll routine. If interrupt operation is not
+ * selected, the board is serviced periodically to see if anything needs doing.
+ */
+#define  POLL_TIMEOUT   (jiffies + 1)
+static DEFINE_TIMER(PollTimer, ip2_poll, 0, 0);
+
+#ifdef IP2DEBUG_TRACE
+/* Trace (debug) buffer data */
+#define TRACEMAX  1000
+static unsigned long tracebuf[TRACEMAX];
+static int tracestuff;
+static int tracestrip;
+static int tracewrap;
+#endif
+
+/**********/
+/* Macros */
+/**********/
+
+#ifdef IP2DEBUG_OPEN
+#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] ttyc=%d, modc=%x -> %s\n", \
+                   tty->name,(pCh->flags), \
+                   tty->count,/*GET_USE_COUNT(module)*/0,s)
+#else
+#define DBG_CNT(s)
+#endif
+
+/********/
+/* Code */
+/********/
+
+#include "i2ellis.c"    /* Extremely low-level interface services */
+#include "i2cmd.c"      /* Standard loadware command definitions */
+#include "i2lib.c"      /* High level interface services */
+
+/* Configuration area for modprobe */
+
+MODULE_AUTHOR("Doug McNash");
+MODULE_DESCRIPTION("Computone IntelliPort Plus Driver");
+MODULE_LICENSE("GPL");
+
+#define        MAX_CMD_STR     50
+
+static int poll_only;
+static char cmd[MAX_CMD_STR];
+
+static int Eisa_irq;
+static int Eisa_slot;
+
+static int iindx;
+static char rirqs[IP2_MAX_BOARDS];
+static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0};
+
+/* Note: Add compiled in defaults to these arrays, not to the structure
+       in ip2.h any longer.  That structure WILL get overridden
+       by these values, or command line values, or insmod values!!!  =mhw=
+*/
+static int io[IP2_MAX_BOARDS];
+static int irq[IP2_MAX_BOARDS] = { -1, -1, -1, -1 };
+
+MODULE_AUTHOR("Doug McNash");
+MODULE_DESCRIPTION("Computone IntelliPort Plus Driver");
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(irq, "Interrupts for IntelliPort Cards");
+module_param_array(io, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O ports for IntelliPort Cards");
+module_param(poll_only, bool, 0);
+MODULE_PARM_DESC(poll_only, "Do not use card interrupts");
+module_param_string(ip2, cmd, MAX_CMD_STR, 0);
+MODULE_PARM_DESC(ip2, "Contains module parameter passed with 'ip2='");
+
+/* for sysfs class support */
+static struct class *ip2_class;
+
+/* Some functions to keep track of what irqs we have */
+
+static int __init is_valid_irq(int irq)
+{
+       int *i = Valid_Irqs;
+       
+       while (*i != 0 && *i != irq)
+               i++;
+
+       return *i;
+}
+
+static void __init mark_requested_irq(char irq)
+{
+       rirqs[iindx++] = irq;
+}
+
+static int __exit clear_requested_irq(char irq)
+{
+       int i;
+       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+               if (rirqs[i] == irq) {
+                       rirqs[i] = 0;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static int have_requested_irq(char irq)
+{
+       /* array init to zeros so 0 irq will not be requested as a side
+        * effect */
+       int i;
+       for (i = 0; i < IP2_MAX_BOARDS; ++i)
+               if (rirqs[i] == irq)
+                       return 1;
+       return 0;
+}
+
+/******************************************************************************/
+/* Function:   cleanup_module()                                               */
+/* Parameters: None                                                           */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/* This is a required entry point for an installable module. It has to return */
+/* the device and the driver to a passive state. It should not be necessary   */
+/* to reset the board fully, especially as the loadware is downloaded         */
+/* externally rather than in the driver. We just want to disable the board    */
+/* and clear the loadware to a reset state. To allow this there has to be a   */
+/* way to detect whether the board has the loadware running at init time to   */
+/* handle subsequent installations of the driver. All memory allocated by the */
+/* driver should be returned since it may be unloaded from memory.            */
+/******************************************************************************/
+static void __exit ip2_cleanup_module(void)
+{
+       int err;
+       int i;
+
+       del_timer_sync(&PollTimer);
+
+       /* Reset the boards we have. */
+       for (i = 0; i < IP2_MAX_BOARDS; i++)
+               if (i2BoardPtrTable[i])
+                       iiReset(i2BoardPtrTable[i]);
+
+       /* The following is done at most once, if any boards were installed. */
+       for (i = 0; i < IP2_MAX_BOARDS; i++) {
+               if (i2BoardPtrTable[i]) {
+                       iiResetDelay(i2BoardPtrTable[i]);
+                       /* free io addresses and Tibet */
+                       release_region(ip2config.addr[i], 8);
+                       device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, 4 * i));
+                       device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR,
+                                               4 * i + 1));
+               }
+               /* Disable and remove interrupt handler. */
+               if (ip2config.irq[i] > 0 &&
+                               have_requested_irq(ip2config.irq[i])) {
+                       free_irq(ip2config.irq[i], (void *)&pcName);
+                       clear_requested_irq(ip2config.irq[i]);
+               }
+       }
+       class_destroy(ip2_class);
+       err = tty_unregister_driver(ip2_tty_driver);
+       if (err)
+               printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n",
+                               err);
+       put_tty_driver(ip2_tty_driver);
+       unregister_chrdev(IP2_IPL_MAJOR, pcIpl);
+       remove_proc_entry("ip2mem", NULL);
+
+       /* free memory */
+       for (i = 0; i < IP2_MAX_BOARDS; i++) {
+               void *pB;
+#ifdef CONFIG_PCI
+               if (ip2config.type[i] == PCI && ip2config.pci_dev[i]) {
+                       pci_disable_device(ip2config.pci_dev[i]);
+                       pci_dev_put(ip2config.pci_dev[i]);
+                       ip2config.pci_dev[i] = NULL;
+               }
+#endif
+               pB = i2BoardPtrTable[i];
+               if (pB != NULL) {
+                       kfree(pB);
+                       i2BoardPtrTable[i] = NULL;
+               }
+               if (DevTableMem[i] != NULL) {
+                       kfree(DevTableMem[i]);
+                       DevTableMem[i] = NULL;
+               }
+       }
+}
+module_exit(ip2_cleanup_module);
+
+static const struct tty_operations ip2_ops = {
+       .open            = ip2_open,
+       .close           = ip2_close,
+       .write           = ip2_write,
+       .put_char        = ip2_putchar,
+       .flush_chars     = ip2_flush_chars,
+       .write_room      = ip2_write_room,
+       .chars_in_buffer = ip2_chars_in_buf,
+       .flush_buffer    = ip2_flush_buffer,
+       .ioctl           = ip2_ioctl,
+       .throttle        = ip2_throttle,
+       .unthrottle      = ip2_unthrottle,
+       .set_termios     = ip2_set_termios,
+       .set_ldisc       = ip2_set_line_discipline,
+       .stop            = ip2_stop,
+       .start           = ip2_start,
+       .hangup          = ip2_hangup,
+       .tiocmget        = ip2_tiocmget,
+       .tiocmset        = ip2_tiocmset,
+       .get_icount      = ip2_get_icount,
+       .proc_fops       = &ip2_proc_fops,
+};
+
+/******************************************************************************/
+/* Function:   ip2_loadmain()                                                 */
+/* Parameters: irq, io from command line of insmod et. al.                    */
+/*             pointer to fip firmware and firmware size for boards          */
+/* Returns:    Success (0)                                                    */
+/*                                                                            */
+/* Description:                                                               */
+/* This was the required entry point for all drivers (now in ip2.c)           */
+/* It performs all                                                            */
+/* initialisation of the devices and driver structures, and registers itself  */
+/* with the relevant kernel modules.                                          */
+/******************************************************************************/
+/* IRQF_DISABLED - if set blocks all interrupts else only this line */
+/* IRQF_SHARED    - for shared irq PCI or maybe EISA only */
+/* SA_RANDOM   - can be source for cert. random number generators */
+#define IP2_SA_FLAGS   0
+
+
+static const struct firmware *ip2_request_firmware(void)
+{
+       struct platform_device *pdev;
+       const struct firmware *fw;
+
+       pdev = platform_device_register_simple("ip2", 0, NULL, 0);
+       if (IS_ERR(pdev)) {
+               printk(KERN_ERR "Failed to register platform device for ip2\n");
+               return NULL;
+       }
+       if (request_firmware(&fw, "intelliport2.bin", &pdev->dev)) {
+               printk(KERN_ERR "Failed to load firmware 'intelliport2.bin'\n");
+               fw = NULL;
+       }
+       platform_device_unregister(pdev);
+       return fw;
+}
+
+/******************************************************************************
+ *     ip2_setup:
+ *             str: kernel command line string
+ *
+ *     Can't autoprobe the boards so user must specify configuration on
+ *     kernel command line.  Sane people build it modular but the others
+ *     come here.
+ *
+ *     Alternating pairs of io,irq for up to 4 boards.
+ *             ip2=io0,irq0,io1,irq1,io2,irq2,io3,irq3
+ *
+ *             io=0 => No board
+ *             io=1 => PCI
+ *             io=2 => EISA
+ *             else => ISA I/O address
+ *
+ *             irq=0 or invalid for ISA will revert to polling mode
+ *
+ *             Any value = -1, do not overwrite compiled in value.
+ *
+ ******************************************************************************/
+static int __init ip2_setup(char *str)
+{
+       int j, ints[10];        /* 4 boards, 2 parameters + 2 */
+       unsigned int i;
+
+       str = get_options(str, ARRAY_SIZE(ints), ints);
+
+       for (i = 0, j = 1; i < 4; i++) {
+               if (j > ints[0])
+                       break;
+               if (ints[j] >= 0)
+                       io[i] = ints[j];
+               j++;
+               if (j > ints[0])
+                       break;
+               if (ints[j] >= 0)
+                       irq[i] = ints[j];
+               j++;
+       }
+       return 1;
+}
+__setup("ip2=", ip2_setup);
+
+static int __init ip2_loadmain(void)
+{
+       int i, j, box;
+       int err = 0;
+       i2eBordStrPtr pB = NULL;
+       int rc = -1;
+       const struct firmware *fw = NULL;
+       char *str;
+
+       str = cmd;
+
+       if (poll_only) {
+               /* Hard lock the interrupts to zero */
+               irq[0] = irq[1] = irq[2] = irq[3] = poll_only = 0;
+       }
+
+       /* Check module parameter with 'ip2=' has been passed or not */
+       if (!poll_only && (!strncmp(str, "ip2=", 4)))
+               ip2_setup(str);
+
+       ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0);
+
+       /* process command line arguments to modprobe or
+               insmod i.e. iop & irqp */
+       /* irqp and iop should ALWAYS be specified now...  But we check
+               them individually just to be sure, anyways... */
+       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+               ip2config.addr[i] = io[i];
+               if (irq[i] >= 0)
+                       ip2config.irq[i] = irq[i];
+               else
+                       ip2config.irq[i] = 0;
+       /* This is a little bit of a hack.  If poll_only=1 on command
+          line back in ip2.c OR all IRQs on all specified boards are
+          explicitly set to 0, then drop to poll only mode and override
+          PCI or EISA interrupts.  This superceeds the old hack of
+          triggering if all interrupts were zero (like da default).
+          Still a hack but less prone to random acts of terrorism.
+
+          What we really should do, now that the IRQ default is set
+          to -1, is to use 0 as a hard coded, do not probe.
+
+               /\/\|=mhw=|\/\/
+       */
+               poll_only |= irq[i];
+       }
+       poll_only = !poll_only;
+
+       /* Announce our presence */
+       printk(KERN_INFO "%s version %s\n", pcName, pcVersion);
+
+       ip2_tty_driver = alloc_tty_driver(IP2_MAX_PORTS);
+       if (!ip2_tty_driver)
+               return -ENOMEM;
+
+       /* Initialise all the boards we can find (up to the maximum). */
+       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+               switch (ip2config.addr[i]) {
+               case 0: /* skip this slot even if card is present */
+                       break;
+               default: /* ISA */
+                  /* ISA address must be specified */
+                       if (ip2config.addr[i] < 0x100 ||
+                                       ip2config.addr[i] > 0x3f8) {
+                               printk(KERN_ERR "IP2: Bad ISA board %d "
+                                               "address %x\n", i,
+                                               ip2config.addr[i]);
+                               ip2config.addr[i] = 0;
+                               break;
+                       }
+                       ip2config.type[i] = ISA;
+
+                       /* Check for valid irq argument, set for polling if
+                        * invalid */
+                       if (ip2config.irq[i] &&
+                                       !is_valid_irq(ip2config.irq[i])) {
+                               printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n",
+                                               ip2config.irq[i]);
+                               /* 0 is polling and is valid in that sense */
+                               ip2config.irq[i] = 0;
+                       }
+                       break;
+               case PCI:
+#ifdef CONFIG_PCI
+               {
+                       struct pci_dev *pdev = NULL;
+                       u32 addr;
+                       int status;
+
+                       pdev = pci_get_device(PCI_VENDOR_ID_COMPUTONE,
+                                       PCI_DEVICE_ID_COMPUTONE_IP2EX, pdev);
+                       if (pdev == NULL) {
+                               ip2config.addr[i] = 0;
+                               printk(KERN_ERR "IP2: PCI board %d not "
+                                               "found\n", i);
+                               break;
+                       }
+
+                       if (pci_enable_device(pdev)) {
+                               dev_err(&pdev->dev, "can't enable device\n");
+                               goto out;
+                       }
+                       ip2config.type[i] = PCI;
+                       ip2config.pci_dev[i] = pci_dev_get(pdev);
+                       status = pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1,
+                                       &addr);
+                       if (addr & 1)
+                               ip2config.addr[i] = (USHORT)(addr & 0xfffe);
+                       else
+                               dev_err(&pdev->dev, "I/O address error\n");
+
+                       ip2config.irq[i] = pdev->irq;
+out:
+                       pci_dev_put(pdev);
+               }
+#else
+                       printk(KERN_ERR "IP2: PCI card specified but PCI "
+                                       "support not enabled.\n");
+                       printk(KERN_ERR "IP2: Recompile kernel with CONFIG_PCI "
+                                       "defined!\n");
+#endif /* CONFIG_PCI */
+                       break;
+               case EISA:
+                       ip2config.addr[i] = find_eisa_board(Eisa_slot + 1);
+                       if (ip2config.addr[i] != 0) {
+                               /* Eisa_irq set as side effect, boo */
+                               ip2config.type[i] = EISA;
+                       } 
+                       ip2config.irq[i] = Eisa_irq;
+                       break;
+               }       /* switch */
+       }       /* for */
+
+       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+               if (ip2config.addr[i]) {
+                       pB = kzalloc(sizeof(i2eBordStr), GFP_KERNEL);
+                       if (pB) {
+                               i2BoardPtrTable[i] = pB;
+                               iiSetAddress(pB, ip2config.addr[i],
+                                               ii2DelayTimer);
+                               iiReset(pB);
+                       } else
+                               printk(KERN_ERR "IP2: board memory allocation "
+                                               "error\n");
+               }
+       }
+       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+               pB = i2BoardPtrTable[i];
+               if (pB != NULL) {
+                       iiResetDelay(pB);
+                       break;
+               }
+       }
+       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+               /* We don't want to request the firmware unless we have at
+                  least one board */
+               if (i2BoardPtrTable[i] != NULL) {
+                       if (!fw)
+                               fw = ip2_request_firmware();
+                       if (!fw)
+                               break;
+                       ip2_init_board(i, fw);
+               }
+       }
+       if (fw)
+               release_firmware(fw);
+
+       ip2trace(ITRC_NO_PORT, ITRC_INIT, 2, 0);
+
+       ip2_tty_driver->owner               = THIS_MODULE;
+       ip2_tty_driver->name                 = "ttyF";
+       ip2_tty_driver->driver_name          = pcDriver_name;
+       ip2_tty_driver->major                = IP2_TTY_MAJOR;
+       ip2_tty_driver->minor_start          = 0;
+       ip2_tty_driver->type                 = TTY_DRIVER_TYPE_SERIAL;
+       ip2_tty_driver->subtype              = SERIAL_TYPE_NORMAL;
+       ip2_tty_driver->init_termios         = tty_std_termios;
+       ip2_tty_driver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL;
+       ip2_tty_driver->flags                = TTY_DRIVER_REAL_RAW |
+               TTY_DRIVER_DYNAMIC_DEV;
+       tty_set_operations(ip2_tty_driver, &ip2_ops);
+
+       ip2trace(ITRC_NO_PORT, ITRC_INIT, 3, 0);
+
+       err = tty_register_driver(ip2_tty_driver);
+       if (err) {
+               printk(KERN_ERR "IP2: failed to register tty driver\n");
+               put_tty_driver(ip2_tty_driver);
+               return err; /* leaking resources */
+       }
+
+       err = register_chrdev(IP2_IPL_MAJOR, pcIpl, &ip2_ipl);
+       if (err) {
+               printk(KERN_ERR "IP2: failed to register IPL device (%d)\n",
+                               err);
+       } else {
+               /* create the sysfs class */
+               ip2_class = class_create(THIS_MODULE, "ip2");
+               if (IS_ERR(ip2_class)) {
+                       err = PTR_ERR(ip2_class);
+                       goto out_chrdev;        
+               }
+       }
+       /* Register the read_procmem thing */
+       if (!proc_create("ip2mem",0,NULL,&ip2mem_proc_fops)) {
+               printk(KERN_ERR "IP2: failed to register read_procmem\n");
+               return -EIO; /* leaking resources */
+       }
+
+       ip2trace(ITRC_NO_PORT, ITRC_INIT, 4, 0);
+       /* Register the interrupt handler or poll handler, depending upon the
+        * specified interrupt.
+        */
+
+       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+               if (ip2config.addr[i] == 0)
+                       continue;
+
+               pB = i2BoardPtrTable[i];
+               if (pB != NULL) {
+                       device_create(ip2_class, NULL,
+                                     MKDEV(IP2_IPL_MAJOR, 4 * i),
+                                     NULL, "ipl%d", i);
+                       device_create(ip2_class, NULL,
+                                     MKDEV(IP2_IPL_MAJOR, 4 * i + 1),
+                                     NULL, "stat%d", i);
+
+                       for (box = 0; box < ABS_MAX_BOXES; box++)
+                               for (j = 0; j < ABS_BIGGEST_BOX; j++)
+                                       if (pB->i2eChannelMap[box] & (1 << j))
+                                               tty_register_device(
+                                                       ip2_tty_driver,
+                                                       j + ABS_BIGGEST_BOX *
+                                                       (box+i*ABS_MAX_BOXES),
+                                                       NULL);
+               }
+
+               if (poll_only) {
+                       /* Poll only forces driver to only use polling and
+                          to ignore the probed PCI or EISA interrupts. */
+                       ip2config.irq[i] = CIR_POLL;
+               }
+               if (ip2config.irq[i] == CIR_POLL) {
+retry:
+                       if (!timer_pending(&PollTimer)) {
+                               mod_timer(&PollTimer, POLL_TIMEOUT);
+                               printk(KERN_INFO "IP2: polling\n");
+                       }
+               } else {
+                       if (have_requested_irq(ip2config.irq[i]))
+                               continue;
+                       rc = request_irq(ip2config.irq[i], ip2_interrupt,
+                               IP2_SA_FLAGS |
+                               (ip2config.type[i] == PCI ? IRQF_SHARED : 0),
+                               pcName, i2BoardPtrTable[i]);
+                       if (rc) {
+                               printk(KERN_ERR "IP2: request_irq failed: "
+                                               "error %d\n", rc);
+                               ip2config.irq[i] = CIR_POLL;
+                               printk(KERN_INFO "IP2: Polling %ld/sec.\n",
+                                               (POLL_TIMEOUT - jiffies));
+                               goto retry;
+                       }
+                       mark_requested_irq(ip2config.irq[i]);
+                       /* Initialise the interrupt handler bottom half
+                        * (aka slih). */
+               }
+       }
+
+       for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+               if (i2BoardPtrTable[i]) {
+                       /* set and enable board interrupt */
+                       set_irq(i, ip2config.irq[i]);
+               }
+       }
+
+       ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0);
+
+       return 0;
+
+out_chrdev:
+       unregister_chrdev(IP2_IPL_MAJOR, "ip2");
+       /* unregister and put tty here */
+       return err;
+}
+module_init(ip2_loadmain);
+
+/******************************************************************************/
+/* Function:   ip2_init_board()                                               */
+/* Parameters: Index of board in configuration structure                      */
+/* Returns:    Success (0)                                                    */
+/*                                                                            */
+/* Description:                                                               */
+/* This function initializes the specified board. The loadware is copied to   */
+/* the board, the channel structures are initialized, and the board details   */
+/* are reported on the console.                                               */
+/******************************************************************************/
+static void
+ip2_init_board(int boardnum, const struct firmware *fw)
+{
+       int i;
+       int nports = 0, nboxes = 0;
+       i2ChanStrPtr pCh;
+       i2eBordStrPtr pB = i2BoardPtrTable[boardnum];
+
+       if ( !iiInitialize ( pB ) ) {
+               printk ( KERN_ERR "IP2: Failed to initialize board at 0x%x, error %d\n",
+                        pB->i2eBase, pB->i2eError );
+               goto err_initialize;
+       }
+       printk(KERN_INFO "IP2: Board %d: addr=0x%x irq=%d\n", boardnum + 1,
+              ip2config.addr[boardnum], ip2config.irq[boardnum] );
+
+       if (!request_region( ip2config.addr[boardnum], 8, pcName )) {
+               printk(KERN_ERR "IP2: bad addr=0x%x\n", ip2config.addr[boardnum]);
+               goto err_initialize;
+       }
+
+       if ( iiDownloadAll ( pB, (loadHdrStrPtr)fw->data, 1, fw->size )
+           != II_DOWN_GOOD ) {
+               printk ( KERN_ERR "IP2: failed to download loadware\n" );
+               goto err_release_region;
+       } else {
+               printk ( KERN_INFO "IP2: fv=%d.%d.%d lv=%d.%d.%d\n",
+                        pB->i2ePom.e.porVersion,
+                        pB->i2ePom.e.porRevision,
+                        pB->i2ePom.e.porSubRev, pB->i2eLVersion,
+                        pB->i2eLRevision, pB->i2eLSub );
+       }
+
+       switch ( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) {
+
+       default:
+               printk( KERN_ERR "IP2: Unknown board type, ID = %x\n",
+                               pB->i2ePom.e.porID );
+               nports = 0;
+               goto err_release_region;
+               break;
+
+       case POR_ID_II_4: /* IntelliPort-II, ISA-4 (4xRJ45) */
+               printk ( KERN_INFO "IP2: ISA-4\n" );
+               nports = 4;
+               break;
+
+       case POR_ID_II_8: /* IntelliPort-II, 8-port using standard brick. */
+               printk ( KERN_INFO "IP2: ISA-8 std\n" );
+               nports = 8;
+               break;
+
+       case POR_ID_II_8R: /* IntelliPort-II, 8-port using RJ11's (no CTS) */
+               printk ( KERN_INFO "IP2: ISA-8 RJ11\n" );
+               nports = 8;
+               break;
+
+       case POR_ID_FIIEX: /* IntelliPort IIEX */
+       {
+               int portnum = IP2_PORTS_PER_BOARD * boardnum;
+               int            box;
+
+               for( box = 0; box < ABS_MAX_BOXES; ++box ) {
+                       if ( pB->i2eChannelMap[box] != 0 ) {
+                               ++nboxes;
+                       }
+                       for( i = 0; i < ABS_BIGGEST_BOX; ++i ) {
+                               if ( pB->i2eChannelMap[box] & 1<< i ) {
+                                       ++nports;
+                               }
+                       }
+               }
+               DevTableMem[boardnum] = pCh =
+                       kmalloc( sizeof(i2ChanStr) * nports, GFP_KERNEL );
+               if ( !pCh ) {
+                       printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n");
+                       goto err_release_region;
+               }
+               if ( !i2InitChannels( pB, nports, pCh ) ) {
+                       printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError);
+                       kfree ( pCh );
+                       goto err_release_region;
+               }
+               pB->i2eChannelPtr = &DevTable[portnum];
+               pB->i2eChannelCnt = ABS_MOST_PORTS;
+
+               for( box = 0; box < ABS_MAX_BOXES; ++box, portnum += ABS_BIGGEST_BOX ) {
+                       for( i = 0; i < ABS_BIGGEST_BOX; ++i ) {
+                               if ( pB->i2eChannelMap[box] & (1 << i) ) {
+                                       DevTable[portnum + i] = pCh;
+                                       pCh->port_index = portnum + i;
+                                       pCh++;
+                               }
+                       }
+               }
+               printk(KERN_INFO "IP2: EX box=%d ports=%d %d bit\n",
+                       nboxes, nports, pB->i2eDataWidth16 ? 16 : 8 );
+               }
+               goto ex_exit;
+       }
+       DevTableMem[boardnum] = pCh =
+               kmalloc ( sizeof (i2ChanStr) * nports, GFP_KERNEL );
+       if ( !pCh ) {
+               printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n");
+               goto err_release_region;
+       }
+       pB->i2eChannelPtr = pCh;
+       pB->i2eChannelCnt = nports;
+       if ( !i2InitChannels( pB, nports, pCh ) ) {
+               printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError);
+               kfree ( pCh );
+               goto err_release_region;
+       }
+       pB->i2eChannelPtr = &DevTable[IP2_PORTS_PER_BOARD * boardnum];
+
+       for( i = 0; i < pB->i2eChannelCnt; ++i ) {
+               DevTable[IP2_PORTS_PER_BOARD * boardnum + i] = pCh;
+               pCh->port_index = (IP2_PORTS_PER_BOARD * boardnum) + i;
+               pCh++;
+       }
+ex_exit:
+       INIT_WORK(&pB->tqueue_interrupt, ip2_interrupt_bh);
+       return;
+
+err_release_region:
+       release_region(ip2config.addr[boardnum], 8);
+err_initialize:
+       kfree ( pB );
+       i2BoardPtrTable[boardnum] = NULL;
+       return;
+}
+
+/******************************************************************************/
+/* Function:   find_eisa_board ( int start_slot )                             */
+/* Parameters: First slot to check                                            */
+/* Returns:    Address of EISA IntelliPort II controller                      */
+/*                                                                            */
+/* Description:                                                               */
+/* This function searches for an EISA IntelliPort controller, starting        */
+/* from the specified slot number. If the motherboard is not identified as an */
+/* EISA motherboard, or no valid board ID is selected it returns 0. Otherwise */
+/* it returns the base address of the controller.                             */
+/******************************************************************************/
+static unsigned short
+find_eisa_board( int start_slot )
+{
+       int i, j;
+       unsigned int idm = 0;
+       unsigned int idp = 0;
+       unsigned int base = 0;
+       unsigned int value;
+       int setup_address;
+       int setup_irq;
+       int ismine = 0;
+
+       /*
+        * First a check for an EISA motherboard, which we do by comparing the
+        * EISA ID registers for the system board and the first couple of slots.
+        * No slot ID should match the system board ID, but on an ISA or PCI
+        * machine the odds are that an empty bus will return similar values for
+        * each slot.
+        */
+       i = 0x0c80;
+       value = (inb(i) << 24) + (inb(i+1) << 16) + (inb(i+2) << 8) + inb(i+3);
+       for( i = 0x1c80; i <= 0x4c80; i += 0x1000 ) {
+               j = (inb(i)<<24)+(inb(i+1)<<16)+(inb(i+2)<<8)+inb(i+3);
+               if ( value == j )
+                       return 0;
+       }
+
+       /*
+        * OK, so we are inclined to believe that this is an EISA machine. Find
+        * an IntelliPort controller.
+        */
+       for( i = start_slot; i < 16; i++ ) {
+               base = i << 12;
+               idm = (inb(base + 0xc80) << 8) | (inb(base + 0xc81) & 0xff);
+               idp = (inb(base + 0xc82) << 8) | (inb(base + 0xc83) & 0xff);
+               ismine = 0;
+               if ( idm == 0x0e8e ) {
+                       if ( idp == 0x0281 || idp == 0x0218 ) {
+                               ismine = 1;
+                       } else if ( idp == 0x0282 || idp == 0x0283 ) {
+                               ismine = 3;     /* Can do edge-trigger */
+                       }
+                       if ( ismine ) {
+                               Eisa_slot = i;
+                               break;
+                       }
+               }
+       }
+       if ( !ismine )
+               return 0;
+
+       /* It's some sort of EISA card, but at what address is it configured? */
+
+       setup_address = base + 0xc88;
+       value = inb(base + 0xc86);
+       setup_irq = (value & 8) ? Valid_Irqs[value & 7] : 0;
+
+       if ( (ismine & 2) && !(value & 0x10) ) {
+               ismine = 1;     /* Could be edging, but not */
+       }
+
+       if ( Eisa_irq == 0 ) {
+               Eisa_irq = setup_irq;
+       } else if ( Eisa_irq != setup_irq ) {
+               printk ( KERN_ERR "IP2: EISA irq mismatch between EISA controllers\n" );
+       }
+
+#ifdef IP2DEBUG_INIT
+printk(KERN_DEBUG "Computone EISA board in slot %d, I.D. 0x%x%x, Address 0x%x",
+              base >> 12, idm, idp, setup_address);
+       if ( Eisa_irq ) {
+               printk(KERN_DEBUG ", Interrupt %d %s\n",
+                      setup_irq, (ismine & 2) ? "(edge)" : "(level)");
+       } else {
+               printk(KERN_DEBUG ", (polled)\n");
+       }
+#endif
+       return setup_address;
+}
+
+/******************************************************************************/
+/* Function:   set_irq()                                                      */
+/* Parameters: index to board in board table                                  */
+/*             IRQ to use                                                     */
+/* Returns:    Success (0)                                                    */
+/*                                                                            */
+/* Description:                                                               */
+/******************************************************************************/
+static void
+set_irq( int boardnum, int boardIrq )
+{
+       unsigned char tempCommand[16];
+       i2eBordStrPtr pB = i2BoardPtrTable[boardnum];
+       unsigned long flags;
+
+       /*
+        * Notify the boards they may generate interrupts. This is done by
+        * sending an in-line command to channel 0 on each board. This is why
+        * the channels have to be defined already. For each board, if the
+        * interrupt has never been defined, we must do so NOW, directly, since
+        * board will not send flow control or even give an interrupt until this
+        * is done.  If polling we must send 0 as the interrupt parameter.
+        */
+
+       // We will get an interrupt here at the end of this function
+
+       iiDisableMailIrq(pB);
+
+       /* We build up the entire packet header. */
+       CHANNEL_OF(tempCommand) = 0;
+       PTYPE_OF(tempCommand) = PTYPE_INLINE;
+       CMD_COUNT_OF(tempCommand) = 2;
+       (CMD_OF(tempCommand))[0] = CMDVALUE_IRQ;
+       (CMD_OF(tempCommand))[1] = boardIrq;
+       /*
+        * Write to FIFO; don't bother to adjust fifo capacity for this, since
+        * board will respond almost immediately after SendMail hit.
+        */
+       write_lock_irqsave(&pB->write_fifo_spinlock, flags);
+       iiWriteBuf(pB, tempCommand, 4);
+       write_unlock_irqrestore(&pB->write_fifo_spinlock, flags);
+       pB->i2eUsingIrq = boardIrq;
+       pB->i2eOutMailWaiting |= MB_OUT_STUFFED;
+
+       /* Need to update number of boards before you enable mailbox int */
+       ++i2nBoards;
+
+       CHANNEL_OF(tempCommand) = 0;
+       PTYPE_OF(tempCommand) = PTYPE_BYPASS;
+       CMD_COUNT_OF(tempCommand) = 6;
+       (CMD_OF(tempCommand))[0] = 88;  // SILO
+       (CMD_OF(tempCommand))[1] = 64;  // chars
+       (CMD_OF(tempCommand))[2] = 32;  // ms
+
+       (CMD_OF(tempCommand))[3] = 28;  // MAX_BLOCK
+       (CMD_OF(tempCommand))[4] = 64;  // chars
+
+       (CMD_OF(tempCommand))[5] = 87;  // HW_TEST
+       write_lock_irqsave(&pB->write_fifo_spinlock, flags);
+       iiWriteBuf(pB, tempCommand, 8);
+       write_unlock_irqrestore(&pB->write_fifo_spinlock, flags);
+
+       CHANNEL_OF(tempCommand) = 0;
+       PTYPE_OF(tempCommand) = PTYPE_BYPASS;
+       CMD_COUNT_OF(tempCommand) = 1;
+       (CMD_OF(tempCommand))[0] = 84;  /* get BOX_IDS */
+       iiWriteBuf(pB, tempCommand, 3);
+
+#ifdef XXX
+       // enable heartbeat for test porpoises
+       CHANNEL_OF(tempCommand) = 0;
+       PTYPE_OF(tempCommand) = PTYPE_BYPASS;
+       CMD_COUNT_OF(tempCommand) = 2;
+       (CMD_OF(tempCommand))[0] = 44;  /* get ping */
+       (CMD_OF(tempCommand))[1] = 200; /* 200 ms */
+       write_lock_irqsave(&pB->write_fifo_spinlock, flags);
+       iiWriteBuf(pB, tempCommand, 4);
+       write_unlock_irqrestore(&pB->write_fifo_spinlock, flags);
+#endif
+
+       iiEnableMailIrq(pB);
+       iiSendPendingMail(pB);
+}
+
+/******************************************************************************/
+/* Interrupt Handler Section                                                  */
+/******************************************************************************/
+
+static inline void
+service_all_boards(void)
+{
+       int i;
+       i2eBordStrPtr  pB;
+
+       /* Service every board on the list */
+       for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
+               pB = i2BoardPtrTable[i];
+               if ( pB ) {
+                       i2ServiceBoard( pB );
+               }
+       }
+}
+
+
+/******************************************************************************/
+/* Function:   ip2_interrupt_bh(work)                                         */
+/* Parameters: work - pointer to the board structure                          */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/*     Service the board in a bottom half interrupt handler and then         */
+/*     reenable the board's interrupts if it has an IRQ number               */
+/*                                                                            */
+/******************************************************************************/
+static void
+ip2_interrupt_bh(struct work_struct *work)
+{
+       i2eBordStrPtr pB = container_of(work, i2eBordStr, tqueue_interrupt);
+//     pB better well be set or we have a problem!  We can only get
+//     here from the IMMEDIATE queue.  Here, we process the boards.
+//     Checking pB doesn't cost much and it saves us from the sanity checkers.
+
+       bh_counter++; 
+
+       if ( pB ) {
+               i2ServiceBoard( pB );
+               if( pB->i2eUsingIrq ) {
+//                     Re-enable his interrupts
+                       iiEnableMailIrq(pB);
+               }
+       }
+}
+
+
+/******************************************************************************/
+/* Function:   ip2_interrupt(int irq, void *dev_id)    */
+/* Parameters: irq - interrupt number                                         */
+/*             pointer to optional device ID structure                        */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*     Our task here is simply to identify each board which needs servicing. */
+/*     If we are queuing then, queue it to be serviced, and disable its irq  */
+/*     mask otherwise process the board directly.                            */
+/*                                                                            */
+/*     We could queue by IRQ but that just complicates things on both ends   */
+/*     with very little gain in performance (how many instructions does      */
+/*     it take to iterate on the immediate queue).                           */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static void
+ip2_irq_work(i2eBordStrPtr pB)
+{
+#ifdef USE_IQI
+       if (NO_MAIL_HERE != ( pB->i2eStartMail = iiGetMail(pB))) {
+//             Disable his interrupt (will be enabled when serviced)
+//             This is mostly to protect from reentrancy.
+               iiDisableMailIrq(pB);
+
+//             Park the board on the immediate queue for processing.
+               schedule_work(&pB->tqueue_interrupt);
+
+//             Make sure the immediate queue is flagged to fire.
+       }
+#else
+
+//     We are using immediate servicing here.  This sucks and can
+//     cause all sorts of havoc with ppp and others.  The failsafe
+//     check on iiSendPendingMail could also throw a hairball.
+
+       i2ServiceBoard( pB );
+
+#endif /* USE_IQI */
+}
+
+static void
+ip2_polled_interrupt(void)
+{
+       int i;
+       i2eBordStrPtr  pB;
+
+       ip2trace(ITRC_NO_PORT, ITRC_INTR, 99, 1, 0);
+
+       /* Service just the boards on the list using this irq */
+       for( i = 0; i < i2nBoards; ++i ) {
+               pB = i2BoardPtrTable[i];
+
+//             Only process those boards which match our IRQ.
+//                     IRQ = 0 for polled boards, we won't poll "IRQ" boards
+
+               if (pB && pB->i2eUsingIrq == 0)
+                       ip2_irq_work(pB);
+       }
+
+       ++irq_counter;
+
+       ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 );
+}
+
+static irqreturn_t
+ip2_interrupt(int irq, void *dev_id)
+{
+       i2eBordStrPtr pB = dev_id;
+
+       ip2trace (ITRC_NO_PORT, ITRC_INTR, 99, 1, pB->i2eUsingIrq );
+
+       ip2_irq_work(pB);
+
+       ++irq_counter;
+
+       ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 );
+       return IRQ_HANDLED;
+}
+
+/******************************************************************************/
+/* Function:   ip2_poll(unsigned long arg)                                    */
+/* Parameters: ?                                                              */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/* This function calls the library routine i2ServiceBoard for each board in   */
+/* the board table. This is used instead of the interrupt routine when polled */
+/* mode is specified.                                                         */
+/******************************************************************************/
+static void
+ip2_poll(unsigned long arg)
+{
+       ip2trace (ITRC_NO_PORT, ITRC_INTR, 100, 0 );
+
+       // Just polled boards, IRQ = 0 will hit all non-interrupt boards.
+       // It will NOT poll boards handled by hard interrupts.
+       // The issue of queued BH interrupts is handled in ip2_interrupt().
+       ip2_polled_interrupt();
+
+       mod_timer(&PollTimer, POLL_TIMEOUT);
+
+       ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 );
+}
+
+static void do_input(struct work_struct *work)
+{
+       i2ChanStrPtr pCh = container_of(work, i2ChanStr, tqueue_input);
+       unsigned long flags;
+
+       ip2trace(CHANN, ITRC_INPUT, 21, 0 );
+
+       // Data input
+       if ( pCh->pTTY != NULL ) {
+               read_lock_irqsave(&pCh->Ibuf_spinlock, flags);
+               if (!pCh->throttled && (pCh->Ibuf_stuff != pCh->Ibuf_strip)) {
+                       read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
+                       i2Input( pCh );
+               } else
+                       read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
+       } else {
+               ip2trace(CHANN, ITRC_INPUT, 22, 0 );
+
+               i2InputFlush( pCh );
+       }
+}
+
+// code duplicated from n_tty (ldisc)
+static inline void  isig(int sig, struct tty_struct *tty, int flush)
+{
+       /* FIXME: This is completely bogus */
+       if (tty->pgrp)
+               kill_pgrp(tty->pgrp, sig, 1);
+       if (flush || !L_NOFLSH(tty)) {
+               if ( tty->ldisc->ops->flush_buffer )  
+                       tty->ldisc->ops->flush_buffer(tty);
+               i2InputFlush( tty->driver_data );
+       }
+}
+
+static void do_status(struct work_struct *work)
+{
+       i2ChanStrPtr pCh = container_of(work, i2ChanStr, tqueue_status);
+       int status;
+
+       status =  i2GetStatus( pCh, (I2_BRK|I2_PAR|I2_FRA|I2_OVR) );
+
+       ip2trace (CHANN, ITRC_STATUS, 21, 1, status );
+
+       if (pCh->pTTY && (status & (I2_BRK|I2_PAR|I2_FRA|I2_OVR)) ) {
+               if ( (status & I2_BRK) ) {
+                       // code duplicated from n_tty (ldisc)
+                       if (I_IGNBRK(pCh->pTTY))
+                               goto skip_this;
+                       if (I_BRKINT(pCh->pTTY)) {
+                               isig(SIGINT, pCh->pTTY, 1);
+                               goto skip_this;
+                       }
+                       wake_up_interruptible(&pCh->pTTY->read_wait);
+               }
+#ifdef NEVER_HAPPENS_AS_SETUP_XXX
+       // and can't work because we don't know the_char
+       // as the_char is reported on a separate path
+       // The intelligent board does this stuff as setup
+       {
+       char brkf = TTY_NORMAL;
+       unsigned char brkc = '\0';
+       unsigned char tmp;
+               if ( (status & I2_BRK) ) {
+                       brkf = TTY_BREAK;
+                       brkc = '\0';
+               } 
+               else if (status & I2_PAR) {
+                       brkf = TTY_PARITY;
+                       brkc = the_char;
+               } else if (status & I2_FRA) {
+                       brkf = TTY_FRAME;
+                       brkc = the_char;
+               } else if (status & I2_OVR) {
+                       brkf = TTY_OVERRUN;
+                       brkc = the_char;
+               }
+               tmp = pCh->pTTY->real_raw;
+               pCh->pTTY->real_raw = 0;
+               pCh->pTTY->ldisc->ops.receive_buf( pCh->pTTY, &brkc, &brkf, 1 );
+               pCh->pTTY->real_raw = tmp;
+       }
+#endif /* NEVER_HAPPENS_AS_SETUP_XXX */
+       }
+skip_this:
+
+       if ( status & (I2_DDCD | I2_DDSR | I2_DCTS | I2_DRI) ) {
+               wake_up_interruptible(&pCh->delta_msr_wait);
+
+               if ( (pCh->flags & ASYNC_CHECK_CD) && (status & I2_DDCD) ) {
+                       if ( status & I2_DCD ) {
+                               if ( pCh->wopen ) {
+                                       wake_up_interruptible ( &pCh->open_wait );
+                               }
+                       } else {
+                               if (pCh->pTTY &&  (!(pCh->pTTY->termios->c_cflag & CLOCAL)) ) {
+                                       tty_hangup( pCh->pTTY );
+                               }
+                       }
+               }
+       }
+
+       ip2trace (CHANN, ITRC_STATUS, 26, 0 );
+}
+
+/******************************************************************************/
+/* Device Open/Close/Ioctl Entry Point Section                                */
+/******************************************************************************/
+
+/******************************************************************************/
+/* Function:   open_sanity_check()                                            */
+/* Parameters: Pointer to tty structure                                       */
+/*             Pointer to file structure                                      */
+/* Returns:    Success or failure                                             */
+/*                                                                            */
+/* Description:                                                               */
+/* Verifies the structure magic numbers and cross links.                      */
+/******************************************************************************/
+#ifdef IP2DEBUG_OPEN
+static void 
+open_sanity_check( i2ChanStrPtr pCh, i2eBordStrPtr pBrd )
+{
+       if ( pBrd->i2eValid != I2E_MAGIC ) {
+               printk(KERN_ERR "IP2: invalid board structure\n" );
+       } else if ( pBrd != pCh->pMyBord ) {
+               printk(KERN_ERR "IP2: board structure pointer mismatch (%p)\n",
+                        pCh->pMyBord );
+       } else if ( pBrd->i2eChannelCnt < pCh->port_index ) {
+               printk(KERN_ERR "IP2: bad device index (%d)\n", pCh->port_index );
+       } else if (&((i2ChanStrPtr)pBrd->i2eChannelPtr)[pCh->port_index] != pCh) {
+       } else {
+               printk(KERN_INFO "IP2: all pointers check out!\n" );
+       }
+}
+#endif
+
+
+/******************************************************************************/
+/* Function:   ip2_open()                                                     */
+/* Parameters: Pointer to tty structure                                       */
+/*             Pointer to file structure                                      */
+/* Returns:    Success or failure                                             */
+/*                                                                            */
+/* Description: (MANDATORY)                                                   */
+/* A successful device open has to run a gauntlet of checks before it         */
+/* completes. After some sanity checking and pointer setup, the function      */
+/* blocks until all conditions are satisfied. It then initialises the port to */
+/* the default characteristics and returns.                                   */
+/******************************************************************************/
+static int
+ip2_open( PTTY tty, struct file *pFile )
+{
+       wait_queue_t wait;
+       int rc = 0;
+       int do_clocal = 0;
+       i2ChanStrPtr  pCh = DevTable[tty->index];
+
+       ip2trace (tty->index, ITRC_OPEN, ITRC_ENTER, 0 );
+
+       if ( pCh == NULL ) {
+               return -ENODEV;
+       }
+       /* Setup pointer links in device and tty structures */
+       pCh->pTTY = tty;
+       tty->driver_data = pCh;
+
+#ifdef IP2DEBUG_OPEN
+       printk(KERN_DEBUG \
+                       "IP2:open(tty=%p,pFile=%p):dev=%s,ch=%d,idx=%d\n",
+              tty, pFile, tty->name, pCh->infl.hd.i2sChannel, pCh->port_index);
+       open_sanity_check ( pCh, pCh->pMyBord );
+#endif
+
+       i2QueueCommands(PTYPE_INLINE, pCh, 100, 3, CMD_DTRUP,CMD_RTSUP,CMD_DCD_REP);
+       pCh->dataSetOut |= (I2_DTR | I2_RTS);
+       serviceOutgoingFifo( pCh->pMyBord );
+
+       /* Block here until the port is ready (per serial and istallion) */
+       /*
+        * 1. If the port is in the middle of closing wait for the completion
+        *    and then return the appropriate error.
+        */
+       init_waitqueue_entry(&wait, current);
+       add_wait_queue(&pCh->close_wait, &wait);
+       set_current_state( TASK_INTERRUPTIBLE );
+
+       if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) {
+               if ( pCh->flags & ASYNC_CLOSING ) {
+                       tty_unlock();
+                       schedule();
+                       tty_lock();
+               }
+               if ( tty_hung_up_p(pFile) ) {
+                       set_current_state( TASK_RUNNING );
+                       remove_wait_queue(&pCh->close_wait, &wait);
+                       return( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS;
+               }
+       }
+       set_current_state( TASK_RUNNING );
+       remove_wait_queue(&pCh->close_wait, &wait);
+
+       /*
+        * 3. Handle a non-blocking open of a normal port.
+        */
+       if ( (pFile->f_flags & O_NONBLOCK) || (tty->flags & (1<<TTY_IO_ERROR) )) {
+               pCh->flags |= ASYNC_NORMAL_ACTIVE;
+               goto noblock;
+       }
+       /*
+        * 4. Now loop waiting for the port to be free and carrier present
+        *    (if required).
+        */
+       if ( tty->termios->c_cflag & CLOCAL )
+               do_clocal = 1;
+
+#ifdef IP2DEBUG_OPEN
+       printk(KERN_DEBUG "OpenBlock: do_clocal = %d\n", do_clocal);
+#endif
+
+       ++pCh->wopen;
+
+       init_waitqueue_entry(&wait, current);
+       add_wait_queue(&pCh->open_wait, &wait);
+
+       for(;;) {
+               i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP);
+               pCh->dataSetOut |= (I2_DTR | I2_RTS);
+               set_current_state( TASK_INTERRUPTIBLE );
+               serviceOutgoingFifo( pCh->pMyBord );
+               if ( tty_hung_up_p(pFile) ) {
+                       set_current_state( TASK_RUNNING );
+                       remove_wait_queue(&pCh->open_wait, &wait);
+                       return ( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EBUSY : -ERESTARTSYS;
+               }
+               if (!(pCh->flags & ASYNC_CLOSING) && 
+                               (do_clocal || (pCh->dataSetIn & I2_DCD) )) {
+                       rc = 0;
+                       break;
+               }
+
+#ifdef IP2DEBUG_OPEN
+               printk(KERN_DEBUG "ASYNC_CLOSING = %s\n",
+                       (pCh->flags & ASYNC_CLOSING)?"True":"False");
+               printk(KERN_DEBUG "OpenBlock: waiting for CD or signal\n");
+#endif
+               ip2trace (CHANN, ITRC_OPEN, 3, 2, 0,
+                               (pCh->flags & ASYNC_CLOSING) );
+               /* check for signal */
+               if (signal_pending(current)) {
+                       rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS);
+                       break;
+               }
+               tty_unlock();
+               schedule();
+               tty_lock();
+       }
+       set_current_state( TASK_RUNNING );
+       remove_wait_queue(&pCh->open_wait, &wait);
+
+       --pCh->wopen; //why count?
+
+       ip2trace (CHANN, ITRC_OPEN, 4, 0 );
+
+       if (rc != 0 ) {
+               return rc;
+       }
+       pCh->flags |= ASYNC_NORMAL_ACTIVE;
+
+noblock:
+
+       /* first open - Assign termios structure to port */
+       if ( tty->count == 1 ) {
+               i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB);
+               /* Now we must send the termios settings to the loadware */
+               set_params( pCh, NULL );
+       }
+
+       /*
+        * Now set any i2lib options. These may go away if the i2lib code ends
+        * up rolled into the mainline.
+        */
+       pCh->channelOptions |= CO_NBLOCK_WRITE;
+
+#ifdef IP2DEBUG_OPEN
+       printk (KERN_DEBUG "IP2: open completed\n" );
+#endif
+       serviceOutgoingFifo( pCh->pMyBord );
+
+       ip2trace (CHANN, ITRC_OPEN, ITRC_RETURN, 0 );
+
+       return 0;
+}
+
+/******************************************************************************/
+/* Function:   ip2_close()                                                    */
+/* Parameters: Pointer to tty structure                                       */
+/*             Pointer to file structure                                      */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static void
+ip2_close( PTTY tty, struct file *pFile )
+{
+       i2ChanStrPtr  pCh = tty->driver_data;
+
+       if ( !pCh ) {
+               return;
+       }
+
+       ip2trace (CHANN, ITRC_CLOSE, ITRC_ENTER, 0 );
+
+#ifdef IP2DEBUG_OPEN
+       printk(KERN_DEBUG "IP2:close %s:\n",tty->name);
+#endif
+
+       if ( tty_hung_up_p ( pFile ) ) {
+
+               ip2trace (CHANN, ITRC_CLOSE, 2, 1, 2 );
+
+               return;
+       }
+       if ( tty->count > 1 ) { /* not the last close */
+
+               ip2trace (CHANN, ITRC_CLOSE, 2, 1, 3 );
+
+               return;
+       }
+       pCh->flags |= ASYNC_CLOSING;    // last close actually
+
+       tty->closing = 1;
+
+       if (pCh->ClosingWaitTime != ASYNC_CLOSING_WAIT_NONE) {
+               /*
+                * Before we drop DTR, make sure the transmitter has completely drained.
+                * This uses an timeout, after which the close
+                * completes.
+                */
+               ip2_wait_until_sent(tty, pCh->ClosingWaitTime );
+       }
+       /*
+        * At this point we stop accepting input. Here we flush the channel
+        * input buffer which will allow the board to send up more data. Any
+        * additional input is tossed at interrupt/poll time.
+        */
+       i2InputFlush( pCh );
+
+       /* disable DSS reporting */
+       i2QueueCommands(PTYPE_INLINE, pCh, 100, 4,
+                               CMD_DCD_NREP, CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP);
+       if (tty->termios->c_cflag & HUPCL) {
+               i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN);
+               pCh->dataSetOut &= ~(I2_DTR | I2_RTS);
+               i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25));
+       }
+
+       serviceOutgoingFifo ( pCh->pMyBord );
+
+       tty_ldisc_flush(tty);
+       tty_driver_flush_buffer(tty);
+       tty->closing = 0;
+       
+       pCh->pTTY = NULL;
+
+       if (pCh->wopen) {
+               if (pCh->ClosingDelay) {
+                       msleep_interruptible(jiffies_to_msecs(pCh->ClosingDelay));
+               }
+               wake_up_interruptible(&pCh->open_wait);
+       }
+
+       pCh->flags &=~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
+       wake_up_interruptible(&pCh->close_wait);
+
+#ifdef IP2DEBUG_OPEN
+       DBG_CNT("ip2_close: after wakeups--");
+#endif
+
+
+       ip2trace (CHANN, ITRC_CLOSE, ITRC_RETURN, 1, 1 );
+
+       return;
+}
+
+/******************************************************************************/
+/* Function:   ip2_hangup()                                                   */
+/* Parameters: Pointer to tty structure                                       */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static void
+ip2_hangup ( PTTY tty )
+{
+       i2ChanStrPtr  pCh = tty->driver_data;
+
+       if( !pCh ) {
+               return;
+       }
+
+       ip2trace (CHANN, ITRC_HANGUP, ITRC_ENTER, 0 );
+
+       ip2_flush_buffer(tty);
+
+       /* disable DSS reporting */
+
+       i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_DCD_NREP);
+       i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB);
+       if ( (tty->termios->c_cflag & HUPCL) ) {
+               i2QueueCommands(PTYPE_BYPASS, pCh, 0, 2, CMD_RTSDN, CMD_DTRDN);
+               pCh->dataSetOut &= ~(I2_DTR | I2_RTS);
+               i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25));
+       }
+       i2QueueCommands(PTYPE_INLINE, pCh, 1, 3, 
+                               CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP);
+       serviceOutgoingFifo ( pCh->pMyBord );
+
+       wake_up_interruptible ( &pCh->delta_msr_wait );
+
+       pCh->flags &= ~ASYNC_NORMAL_ACTIVE;
+       pCh->pTTY = NULL;
+       wake_up_interruptible ( &pCh->open_wait );
+
+       ip2trace (CHANN, ITRC_HANGUP, ITRC_RETURN, 0 );
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/* Device Output Section                                                      */
+/******************************************************************************/
+/******************************************************************************/
+
+/******************************************************************************/
+/* Function:   ip2_write()                                                    */
+/* Parameters: Pointer to tty structure                                       */
+/*             Flag denoting data is in user (1) or kernel (0) space          */
+/*             Pointer to data                                                */
+/*             Number of bytes to write                                       */
+/* Returns:    Number of bytes actually written                               */
+/*                                                                            */
+/* Description: (MANDATORY)                                                   */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static int
+ip2_write( PTTY tty, const unsigned char *pData, int count)
+{
+       i2ChanStrPtr  pCh = tty->driver_data;
+       int bytesSent = 0;
+       unsigned long flags;
+
+       ip2trace (CHANN, ITRC_WRITE, ITRC_ENTER, 2, count, -1 );
+
+       /* Flush out any buffered data left over from ip2_putchar() calls. */
+       ip2_flush_chars( tty );
+
+       /* This is the actual move bit. Make sure it does what we need!!!!! */
+       write_lock_irqsave(&pCh->Pbuf_spinlock, flags);
+       bytesSent = i2Output( pCh, pData, count);
+       write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
+
+       ip2trace (CHANN, ITRC_WRITE, ITRC_RETURN, 1, bytesSent );
+
+       return bytesSent > 0 ? bytesSent : 0;
+}
+
+/******************************************************************************/
+/* Function:   ip2_putchar()                                                  */
+/* Parameters: Pointer to tty structure                                       */
+/*             Character to write                                             */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static int
+ip2_putchar( PTTY tty, unsigned char ch )
+{
+       i2ChanStrPtr  pCh = tty->driver_data;
+       unsigned long flags;
+
+//     ip2trace (CHANN, ITRC_PUTC, ITRC_ENTER, 1, ch );
+
+       write_lock_irqsave(&pCh->Pbuf_spinlock, flags);
+       pCh->Pbuf[pCh->Pbuf_stuff++] = ch;
+       if ( pCh->Pbuf_stuff == sizeof pCh->Pbuf ) {
+               write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
+               ip2_flush_chars( tty );
+       } else
+               write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
+       return 1;
+
+//     ip2trace (CHANN, ITRC_PUTC, ITRC_RETURN, 1, ch );
+}
+
+/******************************************************************************/
+/* Function:   ip2_flush_chars()                                              */
+/* Parameters: Pointer to tty structure                                       */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/******************************************************************************/
+static void
+ip2_flush_chars( PTTY tty )
+{
+       int   strip;
+       i2ChanStrPtr  pCh = tty->driver_data;
+       unsigned long flags;
+
+       write_lock_irqsave(&pCh->Pbuf_spinlock, flags);
+       if ( pCh->Pbuf_stuff ) {
+
+//             ip2trace (CHANN, ITRC_PUTC, 10, 1, strip );
+
+               //
+               // We may need to restart i2Output if it does not fullfill this request
+               //
+               strip = i2Output( pCh, pCh->Pbuf, pCh->Pbuf_stuff);
+               if ( strip != pCh->Pbuf_stuff ) {
+                       memmove( pCh->Pbuf, &pCh->Pbuf[strip], pCh->Pbuf_stuff - strip );
+               }
+               pCh->Pbuf_stuff -= strip;
+       }
+       write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
+}
+
+/******************************************************************************/
+/* Function:   ip2_write_room()                                               */
+/* Parameters: Pointer to tty structure                                       */
+/* Returns:    Number of bytes that the driver can accept                     */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/******************************************************************************/
+static int
+ip2_write_room ( PTTY tty )
+{
+       int bytesFree;
+       i2ChanStrPtr  pCh = tty->driver_data;
+       unsigned long flags;
+
+       read_lock_irqsave(&pCh->Pbuf_spinlock, flags);
+       bytesFree = i2OutputFree( pCh ) - pCh->Pbuf_stuff;
+       read_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
+
+       ip2trace (CHANN, ITRC_WRITE, 11, 1, bytesFree );
+
+       return ((bytesFree > 0) ? bytesFree : 0);
+}
+
+/******************************************************************************/
+/* Function:   ip2_chars_in_buf()                                             */
+/* Parameters: Pointer to tty structure                                       */
+/* Returns:    Number of bytes queued for transmission                        */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static int
+ip2_chars_in_buf ( PTTY tty )
+{
+       i2ChanStrPtr  pCh = tty->driver_data;
+       int rc;
+       unsigned long flags;
+
+       ip2trace (CHANN, ITRC_WRITE, 12, 1, pCh->Obuf_char_count + pCh->Pbuf_stuff );
+
+#ifdef IP2DEBUG_WRITE
+       printk (KERN_DEBUG "IP2: chars in buffer = %d (%d,%d)\n",
+                                pCh->Obuf_char_count + pCh->Pbuf_stuff,
+                                pCh->Obuf_char_count, pCh->Pbuf_stuff );
+#endif
+       read_lock_irqsave(&pCh->Obuf_spinlock, flags);
+       rc =  pCh->Obuf_char_count;
+       read_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
+       read_lock_irqsave(&pCh->Pbuf_spinlock, flags);
+       rc +=  pCh->Pbuf_stuff;
+       read_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
+       return rc;
+}
+
+/******************************************************************************/
+/* Function:   ip2_flush_buffer()                                             */
+/* Parameters: Pointer to tty structure                                       */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static void
+ip2_flush_buffer( PTTY tty )
+{
+       i2ChanStrPtr  pCh = tty->driver_data;
+       unsigned long flags;
+
+       ip2trace (CHANN, ITRC_FLUSH, ITRC_ENTER, 0 );
+
+#ifdef IP2DEBUG_WRITE
+       printk (KERN_DEBUG "IP2: flush buffer\n" );
+#endif
+       write_lock_irqsave(&pCh->Pbuf_spinlock, flags);
+       pCh->Pbuf_stuff = 0;
+       write_unlock_irqrestore(&pCh->Pbuf_spinlock, flags);
+       i2FlushOutput( pCh );
+       ip2_owake(tty);
+
+       ip2trace (CHANN, ITRC_FLUSH, ITRC_RETURN, 0 );
+
+}
+
+/******************************************************************************/
+/* Function:   ip2_wait_until_sent()                                          */
+/* Parameters: Pointer to tty structure                                       */
+/*             Timeout for wait.                                              */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/* This function is used in place of the normal tty_wait_until_sent, which    */
+/* only waits for the driver buffers to be empty (or rather, those buffers    */
+/* reported by chars_in_buffer) which doesn't work for IP2 due to the         */
+/* indeterminate number of bytes buffered on the board.                       */
+/******************************************************************************/
+static void
+ip2_wait_until_sent ( PTTY tty, int timeout )
+{
+       int i = jiffies;
+       i2ChanStrPtr  pCh = tty->driver_data;
+
+       tty_wait_until_sent(tty, timeout );
+       if ( (i = timeout - (jiffies -i)) > 0)
+               i2DrainOutput( pCh, i );
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/* Device Input Section                                                       */
+/******************************************************************************/
+/******************************************************************************/
+
+/******************************************************************************/
+/* Function:   ip2_throttle()                                                 */
+/* Parameters: Pointer to tty structure                                       */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static void
+ip2_throttle ( PTTY tty )
+{
+       i2ChanStrPtr  pCh = tty->driver_data;
+
+#ifdef IP2DEBUG_READ
+       printk (KERN_DEBUG "IP2: throttle\n" );
+#endif
+       /*
+        * Signal the poll/interrupt handlers not to forward incoming data to
+        * the line discipline. This will cause the buffers to fill up in the
+        * library and thus cause the library routines to send the flow control
+        * stuff.
+        */
+       pCh->throttled = 1;
+}
+
+/******************************************************************************/
+/* Function:   ip2_unthrottle()                                               */
+/* Parameters: Pointer to tty structure                                       */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static void
+ip2_unthrottle ( PTTY tty )
+{
+       i2ChanStrPtr  pCh = tty->driver_data;
+       unsigned long flags;
+
+#ifdef IP2DEBUG_READ
+       printk (KERN_DEBUG "IP2: unthrottle\n" );
+#endif
+
+       /* Pass incoming data up to the line discipline again. */
+       pCh->throttled = 0;
+       i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME);
+       serviceOutgoingFifo( pCh->pMyBord );
+       read_lock_irqsave(&pCh->Ibuf_spinlock, flags);
+       if ( pCh->Ibuf_stuff != pCh->Ibuf_strip ) {
+               read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
+#ifdef IP2DEBUG_READ
+               printk (KERN_DEBUG "i2Input called from unthrottle\n" );
+#endif
+               i2Input( pCh );
+       } else
+               read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
+}
+
+static void
+ip2_start ( PTTY tty )
+{
+       i2ChanStrPtr  pCh = DevTable[tty->index];
+
+       i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME);
+       i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_UNSUSPEND);
+       i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_RESUME);
+#ifdef IP2DEBUG_WRITE
+       printk (KERN_DEBUG "IP2: start tx\n" );
+#endif
+}
+
+static void
+ip2_stop ( PTTY tty )
+{
+       i2ChanStrPtr  pCh = DevTable[tty->index];
+
+       i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_SUSPEND);
+#ifdef IP2DEBUG_WRITE
+       printk (KERN_DEBUG "IP2: stop tx\n" );
+#endif
+}
+
+/******************************************************************************/
+/* Device Ioctl Section                                                       */
+/******************************************************************************/
+
+static int ip2_tiocmget(struct tty_struct *tty)
+{
+       i2ChanStrPtr pCh = DevTable[tty->index];
+#ifdef ENABLE_DSSNOW
+       wait_queue_t wait;
+#endif
+
+       if (pCh == NULL)
+               return -ENODEV;
+
+/*
+       FIXME - the following code is causing a NULL pointer dereference in
+       2.3.51 in an interrupt handler.  It's suppose to prompt the board
+       to return the DSS signal status immediately.  Why doesn't it do
+       the same thing in 2.2.14?
+*/
+
+/*     This thing is still busted in the 1.2.12 driver on 2.4.x
+       and even hoses the serial console so the oops can be trapped.
+               /\/\|=mhw=|\/\/                 */
+
+#ifdef ENABLE_DSSNOW
+       i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DSS_NOW);
+
+       init_waitqueue_entry(&wait, current);
+       add_wait_queue(&pCh->dss_now_wait, &wait);
+       set_current_state( TASK_INTERRUPTIBLE );
+
+       serviceOutgoingFifo( pCh->pMyBord );
+
+       schedule();
+
+       set_current_state( TASK_RUNNING );
+       remove_wait_queue(&pCh->dss_now_wait, &wait);
+
+       if (signal_pending(current)) {
+               return -EINTR;
+       }
+#endif
+       return  ((pCh->dataSetOut & I2_RTS) ? TIOCM_RTS : 0)
+             | ((pCh->dataSetOut & I2_DTR) ? TIOCM_DTR : 0)
+             | ((pCh->dataSetIn  & I2_DCD) ? TIOCM_CAR : 0)
+             | ((pCh->dataSetIn  & I2_RI)  ? TIOCM_RNG : 0)
+             | ((pCh->dataSetIn  & I2_DSR) ? TIOCM_DSR : 0)
+             | ((pCh->dataSetIn  & I2_CTS) ? TIOCM_CTS : 0);
+}
+
+static int ip2_tiocmset(struct tty_struct *tty,
+                       unsigned int set, unsigned int clear)
+{
+       i2ChanStrPtr pCh = DevTable[tty->index];
+
+       if (pCh == NULL)
+               return -ENODEV;
+
+       if (set & TIOCM_RTS) {
+               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSUP);
+               pCh->dataSetOut |= I2_RTS;
+       }
+       if (set & TIOCM_DTR) {
+               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRUP);
+               pCh->dataSetOut |= I2_DTR;
+       }
+
+       if (clear & TIOCM_RTS) {
+               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSDN);
+               pCh->dataSetOut &= ~I2_RTS;
+       }
+       if (clear & TIOCM_DTR) {
+               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRDN);
+               pCh->dataSetOut &= ~I2_DTR;
+       }
+       serviceOutgoingFifo( pCh->pMyBord );
+       return 0;
+}
+
+/******************************************************************************/
+/* Function:   ip2_ioctl()                                                    */
+/* Parameters: Pointer to tty structure                                       */
+/*             Pointer to file structure                                      */
+/*             Command                                                        */
+/*             Argument                                                       */
+/* Returns:    Success or failure                                             */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static int
+ip2_ioctl ( PTTY tty, UINT cmd, ULONG arg )
+{
+       wait_queue_t wait;
+       i2ChanStrPtr pCh = DevTable[tty->index];
+       i2eBordStrPtr pB;
+       struct async_icount cprev, cnow;        /* kernel counter temps */
+       int rc = 0;
+       unsigned long flags;
+       void __user *argp = (void __user *)arg;
+
+       if ( pCh == NULL )
+               return -ENODEV;
+
+       pB = pCh->pMyBord;
+
+       ip2trace (CHANN, ITRC_IOCTL, ITRC_ENTER, 2, cmd, arg );
+
+#ifdef IP2DEBUG_IOCTL
+       printk(KERN_DEBUG "IP2: ioctl cmd (%x), arg (%lx)\n", cmd, arg );
+#endif
+
+       switch(cmd) {
+       case TIOCGSERIAL:
+
+               ip2trace (CHANN, ITRC_IOCTL, 2, 1, rc );
+
+               rc = get_serial_info(pCh, argp);
+               if (rc)
+                       return rc;
+               break;
+
+       case TIOCSSERIAL:
+
+               ip2trace (CHANN, ITRC_IOCTL, 3, 1, rc );
+
+               rc = set_serial_info(pCh, argp);
+               if (rc)
+                       return rc;
+               break;
+
+       case TCXONC:
+               rc = tty_check_change(tty);
+               if (rc)
+                       return rc;
+               switch (arg) {
+               case TCOOFF:
+                       //return  -ENOIOCTLCMD;
+                       break;
+               case TCOON:
+                       //return  -ENOIOCTLCMD;
+                       break;
+               case TCIOFF:
+                       if (STOP_CHAR(tty) != __DISABLED_CHAR) {
+                               i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1,
+                                               CMD_XMIT_NOW(STOP_CHAR(tty)));
+                       }
+                       break;
+               case TCION:
+                       if (START_CHAR(tty) != __DISABLED_CHAR) {
+                               i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1,
+                                               CMD_XMIT_NOW(START_CHAR(tty)));
+                       }
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               return 0;
+
+       case TCSBRK:   /* SVID version: non-zero arg --> no break */
+               rc = tty_check_change(tty);
+
+               ip2trace (CHANN, ITRC_IOCTL, 4, 1, rc );
+
+               if (!rc) {
+                       ip2_wait_until_sent(tty,0);
+                       if (!arg) {
+                               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SEND_BRK(250));
+                               serviceOutgoingFifo( pCh->pMyBord );
+                       }
+               }
+               break;
+
+       case TCSBRKP:  /* support for POSIX tcsendbreak() */
+               rc = tty_check_change(tty);
+
+               ip2trace (CHANN, ITRC_IOCTL, 5, 1, rc );
+
+               if (!rc) {
+                       ip2_wait_until_sent(tty,0);
+                       i2QueueCommands(PTYPE_INLINE, pCh, 100, 1,
+                               CMD_SEND_BRK(arg ? arg*100 : 250));
+                       serviceOutgoingFifo ( pCh->pMyBord );   
+               }
+               break;
+
+       case TIOCGSOFTCAR:
+
+               ip2trace (CHANN, ITRC_IOCTL, 6, 1, rc );
+
+                       rc = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *)argp);
+               if (rc) 
+                       return rc;
+       break;
+
+       case TIOCSSOFTCAR:
+
+               ip2trace (CHANN, ITRC_IOCTL, 7, 1, rc );
+
+               rc = get_user(arg,(unsigned long __user *) argp);
+               if (rc) 
+                       return rc;
+               tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL)
+                                        | (arg ? CLOCAL : 0));
+               
+               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:
+               write_lock_irqsave(&pB->read_fifo_spinlock, flags);
+               cprev = pCh->icount;     /* note the counters on entry */
+               write_unlock_irqrestore(&pB->read_fifo_spinlock, flags);
+               i2QueueCommands(PTYPE_BYPASS, pCh, 100, 4, 
+                                               CMD_DCD_REP, CMD_CTS_REP, CMD_DSR_REP, CMD_RI_REP);
+               init_waitqueue_entry(&wait, current);
+               add_wait_queue(&pCh->delta_msr_wait, &wait);
+               set_current_state( TASK_INTERRUPTIBLE );
+
+               serviceOutgoingFifo( pCh->pMyBord );
+               for(;;) {
+                       ip2trace (CHANN, ITRC_IOCTL, 10, 0 );
+
+                       schedule();
+
+                       ip2trace (CHANN, ITRC_IOCTL, 11, 0 );
+
+                       /* see if a signal did it */
+                       if (signal_pending(current)) {
+                               rc = -ERESTARTSYS;
+                               break;
+                       }
+                       write_lock_irqsave(&pB->read_fifo_spinlock, flags);
+                       cnow = pCh->icount; /* atomic copy */
+                       write_unlock_irqrestore(&pB->read_fifo_spinlock, flags);
+                       if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+                               cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
+                               rc =  -EIO; /* no change => rc */
+                               break;
+                       }
+                       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;
+               }
+               set_current_state( TASK_RUNNING );
+               remove_wait_queue(&pCh->delta_msr_wait, &wait);
+
+               i2QueueCommands(PTYPE_BYPASS, pCh, 100, 3, 
+                                                CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP);
+               if ( ! (pCh->flags      & ASYNC_CHECK_CD)) {
+                       i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DCD_NREP);
+               }
+               serviceOutgoingFifo( pCh->pMyBord );
+               return rc;
+               break;
+
+       /*
+        * The rest are not supported by this driver. By returning -ENOIOCTLCMD they
+        * will be passed to the line discipline for it to handle.
+        */
+       case TIOCSERCONFIG:
+       case TIOCSERGWILD:
+       case TIOCSERGETLSR:
+       case TIOCSERSWILD:
+       case TIOCSERGSTRUCT:
+       case TIOCSERGETMULTI:
+       case TIOCSERSETMULTI:
+
+       default:
+               ip2trace (CHANN, ITRC_IOCTL, 12, 0 );
+
+               rc =  -ENOIOCTLCMD;
+               break;
+       }
+
+       ip2trace (CHANN, ITRC_IOCTL, ITRC_RETURN, 0 );
+
+       return rc;
+}
+
+static int ip2_get_icount(struct tty_struct *tty,
+               struct serial_icounter_struct *icount)
+{
+       i2ChanStrPtr pCh = DevTable[tty->index];
+       i2eBordStrPtr pB;
+       struct async_icount cnow;       /* kernel counter temp */
+       unsigned long flags;
+
+       if ( pCh == NULL )
+               return -ENODEV;
+
+       pB = pCh->pMyBord;
+
+       /*
+        * 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. The controller is quite capable of counting
+        * both, but this done to preserve compatibility with the standard
+        * serial driver.
+        */
+
+       write_lock_irqsave(&pB->read_fifo_spinlock, flags);
+       cnow = pCh->icount;
+       write_unlock_irqrestore(&pB->read_fifo_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;
+}
+
+/******************************************************************************/
+/* Function:   GetSerialInfo()                                                */
+/* Parameters: Pointer to channel structure                                   */
+/*             Pointer to old termios structure                               */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/* This is to support the setserial command, and requires processing of the   */
+/* standard Linux serial structure.                                           */
+/******************************************************************************/
+static int
+get_serial_info ( i2ChanStrPtr pCh, struct serial_struct __user *retinfo )
+{
+       struct serial_struct tmp;
+
+       memset ( &tmp, 0, sizeof(tmp) );
+       tmp.type = pCh->pMyBord->channelBtypes.bid_value[(pCh->port_index & (IP2_PORTS_PER_BOARD-1))/16];
+       if (BID_HAS_654(tmp.type)) {
+               tmp.type = PORT_16650;
+       } else {
+               tmp.type = PORT_CIRRUS;
+       }
+       tmp.line = pCh->port_index;
+       tmp.port = pCh->pMyBord->i2eBase;
+       tmp.irq  = ip2config.irq[pCh->port_index/64];
+       tmp.flags = pCh->flags;
+       tmp.baud_base = pCh->BaudBase;
+       tmp.close_delay = pCh->ClosingDelay;
+       tmp.closing_wait = pCh->ClosingWaitTime;
+       tmp.custom_divisor = pCh->BaudDivisor;
+       return copy_to_user(retinfo,&tmp,sizeof(*retinfo));
+}
+
+/******************************************************************************/
+/* Function:   SetSerialInfo()                                                */
+/* Parameters: Pointer to channel structure                                   */
+/*             Pointer to old termios structure                               */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/* This function provides support for setserial, which uses the TIOCSSERIAL   */
+/* ioctl. Not all setserial parameters are relevant. If the user attempts to  */
+/* change the IRQ, address or type of the port the ioctl fails.               */
+/******************************************************************************/
+static int
+set_serial_info( i2ChanStrPtr pCh, struct serial_struct __user *new_info )
+{
+       struct serial_struct ns;
+       int   old_flags, old_baud_divisor;
+
+       if (copy_from_user(&ns, new_info, sizeof (ns)))
+               return -EFAULT;
+
+       /*
+        * We don't allow setserial to change IRQ, board address, type or baud
+        * base. Also line nunber as such is meaningless but we use it for our
+        * array index so it is fixed also.
+        */
+       if ( (ns.irq        != ip2config.irq[pCh->port_index])
+           || ((int) ns.port      != ((int) (pCh->pMyBord->i2eBase)))
+           || (ns.baud_base != pCh->BaudBase)
+           || (ns.line      != pCh->port_index) ) {
+               return -EINVAL;
+       }
+
+       old_flags = pCh->flags;
+       old_baud_divisor = pCh->BaudDivisor;
+
+       if ( !capable(CAP_SYS_ADMIN) ) {
+               if ( ( ns.close_delay != pCh->ClosingDelay ) ||
+                   ( (ns.flags & ~ASYNC_USR_MASK) !=
+                     (pCh->flags & ~ASYNC_USR_MASK) ) ) {
+                       return -EPERM;
+               }
+
+               pCh->flags = (pCh->flags & ~ASYNC_USR_MASK) |
+                              (ns.flags & ASYNC_USR_MASK);
+               pCh->BaudDivisor = ns.custom_divisor;
+       } else {
+               pCh->flags = (pCh->flags & ~ASYNC_FLAGS) |
+                              (ns.flags & ASYNC_FLAGS);
+               pCh->BaudDivisor = ns.custom_divisor;
+               pCh->ClosingDelay = ns.close_delay * HZ/100;
+               pCh->ClosingWaitTime = ns.closing_wait * HZ/100;
+       }
+
+       if ( ( (old_flags & ASYNC_SPD_MASK) != (pCh->flags & ASYNC_SPD_MASK) )
+           || (old_baud_divisor != pCh->BaudDivisor) ) {
+               // Invalidate speed and reset parameters
+               set_params( pCh, NULL );
+       }
+
+       return 0;
+}
+
+/******************************************************************************/
+/* Function:   ip2_set_termios()                                              */
+/* Parameters: Pointer to tty structure                                       */
+/*             Pointer to old termios structure                               */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static void
+ip2_set_termios( PTTY tty, struct ktermios *old_termios )
+{
+       i2ChanStrPtr pCh = (i2ChanStrPtr)tty->driver_data;
+
+#ifdef IP2DEBUG_IOCTL
+       printk (KERN_DEBUG "IP2: set termios %p\n", old_termios );
+#endif
+
+       set_params( pCh, old_termios );
+}
+
+/******************************************************************************/
+/* Function:   ip2_set_line_discipline()                                      */
+/* Parameters: Pointer to tty structure                                       */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:  Does nothing                                                 */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static void
+ip2_set_line_discipline ( PTTY tty )
+{
+#ifdef IP2DEBUG_IOCTL
+       printk (KERN_DEBUG "IP2: set line discipline\n" );
+#endif
+
+       ip2trace (((i2ChanStrPtr)tty->driver_data)->port_index, ITRC_IOCTL, 16, 0 );
+
+}
+
+/******************************************************************************/
+/* Function:   SetLine Characteristics()                                      */
+/* Parameters: Pointer to channel structure                                   */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/* This routine is called to update the channel structure with the new line   */
+/* characteristics, and send the appropriate commands to the board when they  */
+/* change.                                                                    */
+/******************************************************************************/
+static void
+set_params( i2ChanStrPtr pCh, struct ktermios *o_tios )
+{
+       tcflag_t cflag, iflag, lflag;
+       char stop_char, start_char;
+       struct ktermios dummy;
+
+       lflag = pCh->pTTY->termios->c_lflag;
+       cflag = pCh->pTTY->termios->c_cflag;
+       iflag = pCh->pTTY->termios->c_iflag;
+
+       if (o_tios == NULL) {
+               dummy.c_lflag = ~lflag;
+               dummy.c_cflag = ~cflag;
+               dummy.c_iflag = ~iflag;
+               o_tios = &dummy;
+       }
+
+       {
+               switch ( cflag & CBAUD ) {
+               case B0:
+                       i2QueueCommands( PTYPE_BYPASS, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN);
+                       pCh->dataSetOut &= ~(I2_DTR | I2_RTS);
+                       i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25));
+                       pCh->pTTY->termios->c_cflag |= (CBAUD & o_tios->c_cflag);
+                       goto service_it;
+                       break;
+               case B38400:
+                       /*
+                        * This is the speed that is overloaded with all the other high
+                        * speeds, depending upon the flag settings.
+                        */
+                       if ( ( pCh->flags & ASYNC_SPD_MASK ) == ASYNC_SPD_HI ) {
+                               pCh->speed = CBR_57600;
+                       } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI ) {
+                               pCh->speed = CBR_115200;
+                       } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST ) {
+                               pCh->speed = CBR_C1;
+                       } else {
+                               pCh->speed = CBR_38400;
+                       }
+                       break;
+               case B50:      pCh->speed = CBR_50;      break;
+               case B75:      pCh->speed = CBR_75;      break;
+               case B110:     pCh->speed = CBR_110;     break;
+               case B134:     pCh->speed = CBR_134;     break;
+               case B150:     pCh->speed = CBR_150;     break;
+               case B200:     pCh->speed = CBR_200;     break;
+               case B300:     pCh->speed = CBR_300;     break;
+               case B600:     pCh->speed = CBR_600;     break;
+               case B1200:    pCh->speed = CBR_1200;    break;
+               case B1800:    pCh->speed = CBR_1800;    break;
+               case B2400:    pCh->speed = CBR_2400;    break;
+               case B4800:    pCh->speed = CBR_4800;    break;
+               case B9600:    pCh->speed = CBR_9600;    break;
+               case B19200:   pCh->speed = CBR_19200;   break;
+               case B57600:   pCh->speed = CBR_57600;   break;
+               case B115200:  pCh->speed = CBR_115200;  break;
+               case B153600:  pCh->speed = CBR_153600;  break;
+               case B230400:  pCh->speed = CBR_230400;  break;
+               case B307200:  pCh->speed = CBR_307200;  break;
+               case B460800:  pCh->speed = CBR_460800;  break;
+               case B921600:  pCh->speed = CBR_921600;  break;
+               default:       pCh->speed = CBR_9600;    break;
+               }
+               if ( pCh->speed == CBR_C1 ) {
+                       // Process the custom speed parameters.
+                       int bps = pCh->BaudBase / pCh->BaudDivisor;
+                       if ( bps == 921600 ) {
+                               pCh->speed = CBR_921600;
+                       } else {
+                               bps = bps/10;
+                               i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_BAUD_DEF1(bps) );
+                       }
+               }
+               i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_SETBAUD(pCh->speed));
+               
+               i2QueueCommands ( PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP);
+               pCh->dataSetOut |= (I2_DTR | I2_RTS);
+       }
+       if ( (CSTOPB & cflag) ^ (CSTOPB & o_tios->c_cflag)) 
+       {
+               i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, 
+                       CMD_SETSTOP( ( cflag & CSTOPB ) ? CST_2 : CST_1));
+       }
+       if (((PARENB|PARODD) & cflag) ^ ((PARENB|PARODD) & o_tios->c_cflag)) 
+       {
+               i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1,
+                       CMD_SETPAR( 
+                               (cflag & PARENB ?  (cflag & PARODD ? CSP_OD : CSP_EV) : CSP_NP)
+                       )
+               );
+       }
+       /* byte size and parity */
+       if ( (CSIZE & cflag)^(CSIZE & o_tios->c_cflag)) 
+       {
+               int datasize;
+               switch ( cflag & CSIZE ) {
+               case CS5: datasize = CSZ_5; break;
+               case CS6: datasize = CSZ_6; break;
+               case CS7: datasize = CSZ_7; break;
+               case CS8: datasize = CSZ_8; break;
+               default:  datasize = CSZ_5; break;      /* as per serial.c */
+               }
+               i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, CMD_SETBITS(datasize) );
+       }
+       /* Process CTS flow control flag setting */
+       if ( (cflag & CRTSCTS) ) {
+               i2QueueCommands(PTYPE_INLINE, pCh, 100,
+                                               2, CMD_CTSFL_ENAB, CMD_RTSFL_ENAB);
+       } else {
+               i2QueueCommands(PTYPE_INLINE, pCh, 100,
+                                               2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB);
+       }
+       //
+       // Process XON/XOFF flow control flags settings
+       //
+       stop_char = STOP_CHAR(pCh->pTTY);
+       start_char = START_CHAR(pCh->pTTY);
+
+       //////////// can't be \000
+       if (stop_char == __DISABLED_CHAR ) 
+       {
+               stop_char = ~__DISABLED_CHAR; 
+       }
+       if (start_char == __DISABLED_CHAR ) 
+       {
+               start_char = ~__DISABLED_CHAR;
+       }
+       /////////////////////////////////
+
+       if ( o_tios->c_cc[VSTART] != start_char ) 
+       {
+               i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXON(start_char));
+               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXON(start_char));
+       }
+       if ( o_tios->c_cc[VSTOP] != stop_char ) 
+       {
+                i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXOFF(stop_char));
+                i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXOFF(stop_char));
+       }
+       if (stop_char == __DISABLED_CHAR ) 
+       {
+               stop_char = ~__DISABLED_CHAR;  //TEST123
+               goto no_xoff;
+       }
+       if ((iflag & (IXOFF))^(o_tios->c_iflag & (IXOFF))) 
+       {
+               if ( iflag & IXOFF ) {  // Enable XOFF output flow control
+                       i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_XON));
+               } else {        // Disable XOFF output flow control
+no_xoff:
+                       i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_NONE));
+               }
+       }
+       if (start_char == __DISABLED_CHAR ) 
+       {
+               goto no_xon;
+       }
+       if ((iflag & (IXON|IXANY)) ^ (o_tios->c_iflag & (IXON|IXANY))) 
+       {
+               if ( iflag & IXON ) {
+                       if ( iflag & IXANY ) { // Enable XON/XANY output flow control
+                               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XANY));
+                       } else { // Enable XON output flow control
+                               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XON));
+                       }
+               } else { // Disable XON output flow control
+no_xon:
+                       i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_NONE));
+               }
+       }
+       if ( (iflag & ISTRIP) ^ ( o_tios->c_iflag & (ISTRIP)) ) 
+       {
+               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, 
+                               CMD_ISTRIP_OPT((iflag & ISTRIP ? 1 : 0)));
+       }
+       if ( (iflag & INPCK) ^ ( o_tios->c_iflag & (INPCK)) ) 
+       {
+               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, 
+                               CMD_PARCHK((iflag & INPCK) ? CPK_ENAB : CPK_DSAB));
+       }
+
+       if ( (iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) 
+                       ^       ( o_tios->c_iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) ) 
+       {
+               char brkrpt = 0;
+               char parrpt = 0;
+
+               if ( iflag & IGNBRK ) { /* Ignore breaks altogether */
+                       /* Ignore breaks altogether */
+                       i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_NREP);
+               } else {
+                       if ( iflag & BRKINT ) {
+                               if ( iflag & PARMRK ) {
+                                       brkrpt = 0x0a;  // exception an inline triple
+                               } else {
+                                       brkrpt = 0x1a;  // exception and NULL
+                               }
+                               brkrpt |= 0x04; // flush input
+                       } else {
+                               if ( iflag & PARMRK ) {
+                                       brkrpt = 0x0b;  //POSIX triple \0377 \0 \0
+                               } else {
+                                       brkrpt = 0x01;  // Null only
+                               }
+                       }
+                       i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_REP(brkrpt));
+               } 
+
+               if (iflag & IGNPAR) {
+                       parrpt = 0x20;
+                                                                                                       /* would be 2 for not cirrus bug */
+                                                                                                       /* would be 0x20 cept for cirrus bug */
+               } else {
+                       if ( iflag & PARMRK ) {
+                               /*
+                                * Replace error characters with 3-byte sequence (\0377,\0,char)
+                                */
+                               parrpt = 0x04 ;
+                               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_ISTRIP_OPT((char)0));
+                       } else {
+                               parrpt = 0x03;
+                       } 
+               }
+               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SET_ERROR(parrpt));
+       }
+       if (cflag & CLOCAL) {
+               // Status reporting fails for DCD if this is off
+               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_NREP);
+               pCh->flags &= ~ASYNC_CHECK_CD;
+       } else {
+               i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_REP);
+               pCh->flags      |= ASYNC_CHECK_CD;
+       }
+
+service_it:
+       i2DrainOutput( pCh, 100 );              
+}
+
+/******************************************************************************/
+/* IPL Device Section                                                         */
+/******************************************************************************/
+
+/******************************************************************************/
+/* Function:   ip2_ipl_read()                                                  */
+/* Parameters: Pointer to device inode                                        */
+/*             Pointer to file structure                                      */
+/*             Pointer to data                                                */
+/*             Number of bytes to read                                        */
+/* Returns:    Success or failure                                             */
+/*                                                                            */
+/* Description:   Ugly                                                        */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+
+static 
+ssize_t
+ip2_ipl_read(struct file *pFile, char __user *pData, size_t count, loff_t *off )
+{
+       unsigned int minor = iminor(pFile->f_path.dentry->d_inode);
+       int rc = 0;
+
+#ifdef IP2DEBUG_IPL
+       printk (KERN_DEBUG "IP2IPL: read %p, %d bytes\n", pData, count );
+#endif
+
+       switch( minor ) {
+       case 0:     // IPL device
+               rc = -EINVAL;
+               break;
+       case 1:     // Status dump
+               rc = -EINVAL;
+               break;
+       case 2:     // Ping device
+               rc = -EINVAL;
+               break;
+       case 3:     // Trace device
+               rc = DumpTraceBuffer ( pData, count );
+               break;
+       case 4:     // Trace device
+               rc = DumpFifoBuffer ( pData, count );
+               break;
+       default:
+               rc = -ENODEV;
+               break;
+       }
+       return rc;
+}
+
+static int
+DumpFifoBuffer ( char __user *pData, int count )
+{
+#ifdef DEBUG_FIFO
+       int rc;
+       rc = copy_to_user(pData, DBGBuf, count);
+
+       printk(KERN_DEBUG "Last index %d\n", I );
+
+       return count;
+#endif /* DEBUG_FIFO */
+       return 0;
+}
+
+static int
+DumpTraceBuffer ( char __user *pData, int count )
+{
+#ifdef IP2DEBUG_TRACE
+       int rc;
+       int dumpcount;
+       int chunk;
+       int *pIndex = (int __user *)pData;
+
+       if ( count < (sizeof(int) * 6) ) {
+               return -EIO;
+       }
+       rc = put_user(tracewrap, pIndex );
+       rc = put_user(TRACEMAX, ++pIndex );
+       rc = put_user(tracestrip, ++pIndex );
+       rc = put_user(tracestuff, ++pIndex );
+       pData += sizeof(int) * 6;
+       count -= sizeof(int) * 6;
+
+       dumpcount = tracestuff - tracestrip;
+       if ( dumpcount < 0 ) {
+               dumpcount += TRACEMAX;
+       }
+       if ( dumpcount > count ) {
+               dumpcount = count;
+       }
+       chunk = TRACEMAX - tracestrip;
+       if ( dumpcount > chunk ) {
+               rc = copy_to_user(pData, &tracebuf[tracestrip],
+                             chunk * sizeof(tracebuf[0]) );
+               pData += chunk * sizeof(tracebuf[0]);
+               tracestrip = 0;
+               chunk = dumpcount - chunk;
+       } else {
+               chunk = dumpcount;
+       }
+       rc = copy_to_user(pData, &tracebuf[tracestrip],
+                     chunk * sizeof(tracebuf[0]) );
+       tracestrip += chunk;
+       tracewrap = 0;
+
+       rc = put_user(tracestrip, ++pIndex );
+       rc = put_user(tracestuff, ++pIndex );
+
+       return dumpcount;
+#else
+       return 0;
+#endif
+}
+
+/******************************************************************************/
+/* Function:   ip2_ipl_write()                                                 */
+/* Parameters:                                                                */
+/*             Pointer to file structure                                      */
+/*             Pointer to data                                                */
+/*             Number of bytes to write                                       */
+/* Returns:    Success or failure                                             */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static ssize_t
+ip2_ipl_write(struct file *pFile, const char __user *pData, size_t count, loff_t *off)
+{
+#ifdef IP2DEBUG_IPL
+       printk (KERN_DEBUG "IP2IPL: write %p, %d bytes\n", pData, count );
+#endif
+       return 0;
+}
+
+/******************************************************************************/
+/* Function:   ip2_ipl_ioctl()                                                */
+/* Parameters: Pointer to device inode                                        */
+/*             Pointer to file structure                                      */
+/*             Command                                                        */
+/*             Argument                                                       */
+/* Returns:    Success or failure                                             */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static long
+ip2_ipl_ioctl (struct file *pFile, UINT cmd, ULONG arg )
+{
+       unsigned int iplminor = iminor(pFile->f_path.dentry->d_inode);
+       int rc = 0;
+       void __user *argp = (void __user *)arg;
+       ULONG __user *pIndex = argp;
+       i2eBordStrPtr pB = i2BoardPtrTable[iplminor / 4];
+       i2ChanStrPtr pCh;
+
+#ifdef IP2DEBUG_IPL
+       printk (KERN_DEBUG "IP2IPL: ioctl cmd %d, arg %ld\n", cmd, arg );
+#endif
+
+       mutex_lock(&ip2_mutex);
+
+       switch ( iplminor ) {
+       case 0:     // IPL device
+               rc = -EINVAL;
+               break;
+       case 1:     // Status dump
+       case 5:
+       case 9:
+       case 13:
+               switch ( cmd ) {
+               case 64:        /* Driver - ip2stat */
+                       rc = put_user(-1, pIndex++ );
+                       rc = put_user(irq_counter, pIndex++  );
+                       rc = put_user(bh_counter, pIndex++  );
+                       break;
+
+               case 65:        /* Board  - ip2stat */
+                       if ( pB ) {
+                               rc = copy_to_user(argp, pB, sizeof(i2eBordStr));
+                               rc = put_user(inb(pB->i2eStatus),
+                                       (ULONG __user *)(arg + (ULONG)(&pB->i2eStatus) - (ULONG)pB ) );
+                       } else {
+                               rc = -ENODEV;
+                       }
+                       break;
+
+               default:
+                       if (cmd < IP2_MAX_PORTS) {
+                               pCh = DevTable[cmd];
+                               if ( pCh )
+                               {
+                                       rc = copy_to_user(argp, pCh, sizeof(i2ChanStr));
+                                       if (rc)
+                                               rc = -EFAULT;
+                               } else {
+                                       rc = -ENODEV;
+                               }
+                       } else {
+                               rc = -EINVAL;
+                       }
+               }
+               break;
+
+       case 2:     // Ping device
+               rc = -EINVAL;
+               break;
+       case 3:     // Trace device
+               /*
+                * akpm: This used to write a whole bunch of function addresses
+                * to userspace, which generated lots of put_user() warnings.
+                * I killed it all.  Just return "success" and don't do
+                * anything.
+                */
+               if (cmd == 1)
+                       rc = 0;
+               else
+                       rc = -EINVAL;
+               break;
+
+       default:
+               rc = -ENODEV;
+               break;
+       }
+       mutex_unlock(&ip2_mutex);
+       return rc;
+}
+
+/******************************************************************************/
+/* Function:   ip2_ipl_open()                                                 */
+/* Parameters: Pointer to device inode                                        */
+/*             Pointer to file structure                                      */
+/* Returns:    Success or failure                                             */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+static int
+ip2_ipl_open( struct inode *pInode, struct file *pFile )
+{
+
+#ifdef IP2DEBUG_IPL
+       printk (KERN_DEBUG "IP2IPL: open\n" );
+#endif
+       return 0;
+}
+
+static int
+proc_ip2mem_show(struct seq_file *m, void *v)
+{
+       i2eBordStrPtr  pB;
+       i2ChanStrPtr  pCh;
+       PTTY tty;
+       int i;
+
+#define FMTLINE        "%3d: 0x%08x 0x%08x 0%011o 0%011o\n"
+#define FMTLIN2        "     0x%04x 0x%04x tx flow 0x%x\n"
+#define FMTLIN3        "     0x%04x 0x%04x rc flow\n"
+
+       seq_printf(m,"\n");
+
+       for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
+               pB = i2BoardPtrTable[i];
+               if ( pB ) {
+                       seq_printf(m,"board %d:\n",i);
+                       seq_printf(m,"\tFifo rem: %d mty: %x outM %x\n",
+                               pB->i2eFifoRemains,pB->i2eWaitingForEmptyFifo,pB->i2eOutMailWaiting);
+               }
+       }
+
+       seq_printf(m,"#: tty flags, port flags,     cflags,     iflags\n");
+       for (i=0; i < IP2_MAX_PORTS; i++) {
+               pCh = DevTable[i];
+               if (pCh) {
+                       tty = pCh->pTTY;
+                       if (tty && tty->count) {
+                               seq_printf(m,FMTLINE,i,(int)tty->flags,pCh->flags,
+                                                                       tty->termios->c_cflag,tty->termios->c_iflag);
+
+                               seq_printf(m,FMTLIN2,
+                                               pCh->outfl.asof,pCh->outfl.room,pCh->channelNeeds);
+                               seq_printf(m,FMTLIN3,pCh->infl.asof,pCh->infl.room);
+                       }
+               }
+       }
+       return 0;
+}
+
+static int proc_ip2mem_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_ip2mem_show, NULL);
+}
+
+static const struct file_operations ip2mem_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = proc_ip2mem_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/*
+ * This is the handler for /proc/tty/driver/ip2
+ *
+ * This stretch of code has been largely plagerized from at least three
+ * different sources including ip2mkdev.c and a couple of other drivers.
+ * The bugs are all mine.  :-) =mhw=
+ */
+static int ip2_proc_show(struct seq_file *m, void *v)
+{
+       int     i, j, box;
+       int     boxes = 0;
+       int     ports = 0;
+       int     tports = 0;
+       i2eBordStrPtr  pB;
+       char *sep;
+
+       seq_printf(m, "ip2info: 1.0 driver: %s\n", pcVersion);
+       seq_printf(m, "Driver: SMajor=%d CMajor=%d IMajor=%d MaxBoards=%d MaxBoxes=%d MaxPorts=%d\n",
+                       IP2_TTY_MAJOR, IP2_CALLOUT_MAJOR, IP2_IPL_MAJOR,
+                       IP2_MAX_BOARDS, ABS_MAX_BOXES, ABS_BIGGEST_BOX);
+
+       for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
+               /* This need to be reset for a board by board count... */
+               boxes = 0;
+               pB = i2BoardPtrTable[i];
+               if( pB ) {
+                       switch( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) 
+                       {
+                       case POR_ID_FIIEX:
+                               seq_printf(m, "Board %d: EX ports=", i);
+                               sep = "";
+                               for( box = 0; box < ABS_MAX_BOXES; ++box )
+                               {
+                                       ports = 0;
+
+                                       if( pB->i2eChannelMap[box] != 0 ) ++boxes;
+                                       for( j = 0; j < ABS_BIGGEST_BOX; ++j ) 
+                                       {
+                                               if( pB->i2eChannelMap[box] & 1<< j ) {
+                                                       ++ports;
+                                               }
+                                       }
+                                       seq_printf(m, "%s%d", sep, ports);
+                                       sep = ",";
+                                       tports += ports;
+                               }
+                               seq_printf(m, " boxes=%d width=%d", boxes, pB->i2eDataWidth16 ? 16 : 8);
+                               break;
+
+                       case POR_ID_II_4:
+                               seq_printf(m, "Board %d: ISA-4 ports=4 boxes=1", i);
+                               tports = ports = 4;
+                               break;
+
+                       case POR_ID_II_8:
+                               seq_printf(m, "Board %d: ISA-8-std ports=8 boxes=1", i);
+                               tports = ports = 8;
+                               break;
+
+                       case POR_ID_II_8R:
+                               seq_printf(m, "Board %d: ISA-8-RJ11 ports=8 boxes=1", i);
+                               tports = ports = 8;
+                               break;
+
+                       default:
+                               seq_printf(m, "Board %d: unknown", i);
+                               /* Don't try and probe for minor numbers */
+                               tports = ports = 0;
+                       }
+
+               } else {
+                       /* Don't try and probe for minor numbers */
+                       seq_printf(m, "Board %d: vacant", i);
+                       tports = ports = 0;
+               }
+
+               if( tports ) {
+                       seq_puts(m, " minors=");
+                       sep = "";
+                       for ( box = 0; box < ABS_MAX_BOXES; ++box )
+                       {
+                               for ( j = 0; j < ABS_BIGGEST_BOX; ++j )
+                               {
+                                       if ( pB->i2eChannelMap[box] & (1 << j) )
+                                       {
+                                               seq_printf(m, "%s%d", sep,
+                                                       j + ABS_BIGGEST_BOX *
+                                                       (box+i*ABS_MAX_BOXES));
+                                               sep = ",";
+                                       }
+                               }
+                       }
+               }
+               seq_putc(m, '\n');
+       }
+       return 0;
+ }
+
+static int ip2_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ip2_proc_show, NULL);
+}
+
+static const struct file_operations ip2_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = ip2_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+/******************************************************************************/
+/* Function:   ip2trace()                                                     */
+/* Parameters: Value to add to trace buffer                                   */
+/* Returns:    Nothing                                                        */
+/*                                                                            */
+/* Description:                                                               */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+#ifdef IP2DEBUG_TRACE
+void
+ip2trace (unsigned short pn, unsigned char cat, unsigned char label, unsigned long codes, ...)
+{
+       long flags;
+       unsigned long *pCode = &codes;
+       union ip2breadcrumb bc;
+       i2ChanStrPtr  pCh;
+
+
+       tracebuf[tracestuff++] = jiffies;
+       if ( tracestuff == TRACEMAX ) {
+               tracestuff = 0;
+       }
+       if ( tracestuff == tracestrip ) {
+               if ( ++tracestrip == TRACEMAX ) {
+                       tracestrip = 0;
+               }
+               ++tracewrap;
+       }
+
+       bc.hdr.port  = 0xff & pn;
+       bc.hdr.cat   = cat;
+       bc.hdr.codes = (unsigned char)( codes & 0xff );
+       bc.hdr.label = label;
+       tracebuf[tracestuff++] = bc.value;
+
+       for (;;) {
+               if ( tracestuff == TRACEMAX ) {
+                       tracestuff = 0;
+               }
+               if ( tracestuff == tracestrip ) {
+                       if ( ++tracestrip == TRACEMAX ) {
+                               tracestrip = 0;
+                       }
+                       ++tracewrap;
+               }
+
+               if ( !codes-- )
+                       break;
+
+               tracebuf[tracestuff++] = *++pCode;
+       }
+}
+#endif
+
+
+MODULE_LICENSE("GPL");
+
+static struct pci_device_id ip2main_pci_tbl[] __devinitdata __used = {
+       { PCI_DEVICE(PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_IP2EX) },
+       { }
+};
+
+MODULE_DEVICE_TABLE(pci, ip2main_pci_tbl);
+
+MODULE_FIRMWARE("intelliport2.bin");
diff --git a/drivers/staging/tty/ip2/ip2trace.h b/drivers/staging/tty/ip2/ip2trace.h
new file mode 100644 (file)
index 0000000..da20435
--- /dev/null
@@ -0,0 +1,42 @@
+
+//
+union ip2breadcrumb 
+{
+       struct { 
+               unsigned char port, cat, codes, label;
+       } __attribute__ ((packed)) hdr;
+       unsigned long value;
+};
+
+#define ITRC_NO_PORT   0xFF
+#define CHANN  (pCh->port_index)
+
+#define        ITRC_ERROR      '!'
+#define        ITRC_INIT       'A'
+#define        ITRC_OPEN       'B'
+#define        ITRC_CLOSE      'C'
+#define        ITRC_DRAIN      'D'
+#define        ITRC_IOCTL      'E'
+#define        ITRC_FLUSH      'F'
+#define        ITRC_STATUS     'G'
+#define        ITRC_HANGUP     'H'
+#define        ITRC_INTR       'I'
+#define        ITRC_SFLOW      'J'
+#define        ITRC_SBCMD      'K'
+#define        ITRC_SICMD      'L'
+#define        ITRC_MODEM      'M'
+#define        ITRC_INPUT      'N'
+#define        ITRC_OUTPUT     'O'
+#define        ITRC_PUTC       'P'
+#define        ITRC_QUEUE      'Q'
+#define        ITRC_STFLW      'R'
+#define        ITRC_SFIFO      'S'
+#define        ITRC_VERIFY     'V'
+#define        ITRC_WRITE      'W'
+
+#define        ITRC_ENTER      0x00
+#define        ITRC_RETURN     0xFF
+
+#define        ITRC_QUEUE_ROOM 2
+#define        ITRC_QUEUE_CMD  6
+
diff --git a/drivers/staging/tty/ip2/ip2types.h b/drivers/staging/tty/ip2/ip2types.h
new file mode 100644 (file)
index 0000000..9d67b26
--- /dev/null
@@ -0,0 +1,57 @@
+/*******************************************************************************
+*
+*   (c) 1998 by Computone Corporation
+*
+********************************************************************************
+*
+*
+*   PACKAGE:     Linux tty Device Driver for IntelliPort II family of multiport
+*                serial I/O controllers.
+*
+*   DESCRIPTION: Driver constants and type definitions.
+*
+*   NOTES:
+*
+*******************************************************************************/
+#ifndef IP2TYPES_H
+#define IP2TYPES_H
+
+//*************
+//* Constants *
+//*************
+
+// Define some limits for this driver. Ports per board is a hardware limitation
+// that will not change. Current hardware limits this to 64 ports per board.
+// Boards per driver is a self-imposed limit.
+//
+#define IP2_MAX_BOARDS        4
+#define IP2_PORTS_PER_BOARD   ABS_MOST_PORTS
+#define IP2_MAX_PORTS         (IP2_MAX_BOARDS*IP2_PORTS_PER_BOARD)
+
+#define ISA    0
+#define PCI    1
+#define EISA   2
+
+//********************
+//* Type Definitions *
+//********************
+
+typedef struct tty_struct *   PTTY;
+typedef wait_queue_head_t   PWAITQ;
+
+typedef unsigned char         UCHAR;
+typedef unsigned int          UINT;
+typedef unsigned short        USHORT;
+typedef unsigned long         ULONG;
+
+typedef struct 
+{
+       short irq[IP2_MAX_BOARDS]; 
+       unsigned short addr[IP2_MAX_BOARDS];
+       int type[IP2_MAX_BOARDS];
+#ifdef CONFIG_PCI
+       struct pci_dev *pci_dev[IP2_MAX_BOARDS];
+#endif
+} ip2config_t;
+
+#endif
diff --git a/drivers/staging/tty/istallion.c b/drivers/staging/tty/istallion.c
new file mode 100644 (file)
index 0000000..0b26627
--- /dev/null
@@ -0,0 +1,4507 @@
+/*****************************************************************************/
+
+/*
+ *     istallion.c  -- stallion intelligent multiport serial driver.
+ *
+ *     Copyright (C) 1996-1999  Stallion Technologies
+ *     Copyright (C) 1994-1996  Greg Ungerer.
+ *
+ *     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.
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/seq_file.h>
+#include <linux/cdk.h>
+#include <linux/comstats.h>
+#include <linux/istallion.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/eisa.h>
+#include <linux/ctype.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include <linux/pci.h>
+
+/*****************************************************************************/
+
+/*
+ *     Define different board types. Not all of the following board types
+ *     are supported by this driver. But I will use the standard "assigned"
+ *     board numbers. Currently supported boards are abbreviated as:
+ *     ECP = EasyConnection 8/64, ONB = ONboard, BBY = Brumby and
+ *     STAL = Stallion.
+ */
+#define        BRD_UNKNOWN     0
+#define        BRD_STALLION    1
+#define        BRD_BRUMBY4     2
+#define        BRD_ONBOARD2    3
+#define        BRD_ONBOARD     4
+#define        BRD_ONBOARDE    7
+#define        BRD_ECP         23
+#define BRD_ECPE       24
+#define        BRD_ECPMC       25
+#define        BRD_ECPPCI      29
+
+#define        BRD_BRUMBY      BRD_BRUMBY4
+
+/*
+ *     Define a configuration structure to hold the board configuration.
+ *     Need to set this up in the code (for now) with the boards that are
+ *     to be configured into the system. This is what needs to be modified
+ *     when adding/removing/modifying boards. Each line entry in the
+ *     stli_brdconf[] array is a board. Each line contains io/irq/memory
+ *     ranges for that board (as well as what type of board it is).
+ *     Some examples:
+ *             { BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },
+ *     This line will configure an EasyConnection 8/64 at io address 2a0,
+ *     and shared memory address of cc000. Multiple EasyConnection 8/64
+ *     boards can share the same shared memory address space. No interrupt
+ *     is required for this board type.
+ *     Another example:
+ *             { BRD_ECPE, 0x5000, 0, 0x80000000, 0, 0 },
+ *     This line will configure an EasyConnection 8/64 EISA in slot 5 and
+ *     shared memory address of 0x80000000 (2 GByte). Multiple
+ *     EasyConnection 8/64 EISA boards can share the same shared memory
+ *     address space. No interrupt is required for this board type.
+ *     Another example:
+ *             { BRD_ONBOARD, 0x240, 0, 0xd0000, 0, 0 },
+ *     This line will configure an ONboard (ISA type) at io address 240,
+ *     and shared memory address of d0000. Multiple ONboards can share
+ *     the same shared memory address space. No interrupt required.
+ *     Another example:
+ *             { BRD_BRUMBY4, 0x360, 0, 0xc8000, 0, 0 },
+ *     This line will configure a Brumby board (any number of ports!) at
+ *     io address 360 and shared memory address of c8000. All Brumby boards
+ *     configured into a system must have their own separate io and memory
+ *     addresses. No interrupt is required.
+ *     Another example:
+ *             { BRD_STALLION, 0x330, 0, 0xd0000, 0, 0 },
+ *     This line will configure an original Stallion board at io address 330
+ *     and shared memory address d0000 (this would only be valid for a "V4.0"
+ *     or Rev.O Stallion board). All Stallion boards configured into the
+ *     system must have their own separate io and memory addresses. No
+ *     interrupt is required.
+ */
+
+struct stlconf {
+       int             brdtype;
+       int             ioaddr1;
+       int             ioaddr2;
+       unsigned long   memaddr;
+       int             irq;
+       int             irqtype;
+};
+
+static unsigned int stli_nrbrds;
+
+/* stli_lock must NOT be taken holding brd_lock */
+static spinlock_t stli_lock;   /* TTY logic lock */
+static spinlock_t brd_lock;    /* Board logic lock */
+
+/*
+ *     There is some experimental EISA board detection code in this driver.
+ *     By default it is disabled, but for those that want to try it out,
+ *     then set the define below to be 1.
+ */
+#define        STLI_EISAPROBE  0
+
+/*****************************************************************************/
+
+/*
+ *     Define some important driver characteristics. Device major numbers
+ *     allocated as per Linux Device Registry.
+ */
+#ifndef        STL_SIOMEMMAJOR
+#define        STL_SIOMEMMAJOR         28
+#endif
+#ifndef        STL_SERIALMAJOR
+#define        STL_SERIALMAJOR         24
+#endif
+#ifndef        STL_CALLOUTMAJOR
+#define        STL_CALLOUTMAJOR        25
+#endif
+
+/*****************************************************************************/
+
+/*
+ *     Define our local driver identity first. Set up stuff to deal with
+ *     all the local structures required by a serial tty driver.
+ */
+static char    *stli_drvtitle = "Stallion Intelligent Multiport Serial Driver";
+static char    *stli_drvname = "istallion";
+static char    *stli_drvversion = "5.6.0";
+static char    *stli_serialname = "ttyE";
+
+static struct tty_driver       *stli_serial;
+static const struct tty_port_operations stli_port_ops;
+
+#define        STLI_TXBUFSIZE          4096
+
+/*
+ *     Use a fast local buffer for cooked characters. Typically a whole
+ *     bunch of cooked characters come in for a port, 1 at a time. So we
+ *     save those up into a local buffer, then write out the whole lot
+ *     with a large memcpy. Just use 1 buffer for all ports, since its
+ *     use it is only need for short periods of time by each port.
+ */
+static char                    *stli_txcookbuf;
+static int                     stli_txcooksize;
+static int                     stli_txcookrealsize;
+static struct tty_struct       *stli_txcooktty;
+
+/*
+ *     Define a local default termios struct. All ports will be created
+ *     with this termios initially. Basically all it defines is a raw port
+ *     at 9600 baud, 8 data bits, no parity, 1 stop bit.
+ */
+static struct ktermios         stli_deftermios = {
+       .c_cflag        = (B9600 | CS8 | CREAD | HUPCL | CLOCAL),
+       .c_cc           = INIT_C_CC,
+       .c_ispeed       = 9600,
+       .c_ospeed       = 9600,
+};
+
+/*
+ *     Define global stats structures. Not used often, and can be
+ *     re-used for each stats call.
+ */
+static comstats_t      stli_comstats;
+static combrd_t                stli_brdstats;
+static struct asystats stli_cdkstats;
+
+/*****************************************************************************/
+
+static DEFINE_MUTEX(stli_brdslock);
+static struct stlibrd  *stli_brds[STL_MAXBRDS];
+
+static int             stli_shared;
+
+/*
+ *     Per board state flags. Used with the state field of the board struct.
+ *     Not really much here... All we need to do is keep track of whether
+ *     the board has been detected, and whether it is actually running a slave
+ *     or not.
+ */
+#define        BST_FOUND       0
+#define        BST_STARTED     1
+#define        BST_PROBED      2
+
+/*
+ *     Define the set of port state flags. These are marked for internal
+ *     state purposes only, usually to do with the state of communications
+ *     with the slave. Most of them need to be updated atomically, so always
+ *     use the bit setting operations (unless protected by cli/sti).
+ */
+#define        ST_OPENING      2
+#define        ST_CLOSING      3
+#define        ST_CMDING       4
+#define        ST_TXBUSY       5
+#define        ST_RXING        6
+#define        ST_DOFLUSHRX    7
+#define        ST_DOFLUSHTX    8
+#define        ST_DOSIGS       9
+#define        ST_RXSTOP       10
+#define        ST_GETSIGS      11
+
+/*
+ *     Define an array of board names as printable strings. Handy for
+ *     referencing boards when printing trace and stuff.
+ */
+static char    *stli_brdnames[] = {
+       "Unknown",
+       "Stallion",
+       "Brumby",
+       "ONboard-MC",
+       "ONboard",
+       "Brumby",
+       "Brumby",
+       "ONboard-EI",
+       NULL,
+       "ONboard",
+       "ONboard-MC",
+       "ONboard-MC",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "EasyIO",
+       "EC8/32-AT",
+       "EC8/32-MC",
+       "EC8/64-AT",
+       "EC8/64-EI",
+       "EC8/64-MC",
+       "EC8/32-PCI",
+       "EC8/64-PCI",
+       "EasyIO-PCI",
+       "EC/RA-PCI",
+};
+
+/*****************************************************************************/
+
+/*
+ *     Define some string labels for arguments passed from the module
+ *     load line. These allow for easy board definitions, and easy
+ *     modification of the io, memory and irq resoucres.
+ */
+
+static char    *board0[8];
+static char    *board1[8];
+static char    *board2[8];
+static char    *board3[8];
+
+static char    **stli_brdsp[] = {
+       (char **) &board0,
+       (char **) &board1,
+       (char **) &board2,
+       (char **) &board3
+};
+
+/*
+ *     Define a set of common board names, and types. This is used to
+ *     parse any module arguments.
+ */
+
+static struct stlibrdtype {
+       char    *name;
+       int     type;
+} stli_brdstr[] = {
+       { "stallion", BRD_STALLION },
+       { "1", BRD_STALLION },
+       { "brumby", BRD_BRUMBY },
+       { "brumby4", BRD_BRUMBY },
+       { "brumby/4", BRD_BRUMBY },
+       { "brumby-4", BRD_BRUMBY },
+       { "brumby8", BRD_BRUMBY },
+       { "brumby/8", BRD_BRUMBY },
+       { "brumby-8", BRD_BRUMBY },
+       { "brumby16", BRD_BRUMBY },
+       { "brumby/16", BRD_BRUMBY },
+       { "brumby-16", BRD_BRUMBY },
+       { "2", BRD_BRUMBY },
+       { "onboard2", BRD_ONBOARD2 },
+       { "onboard-2", BRD_ONBOARD2 },
+       { "onboard/2", BRD_ONBOARD2 },
+       { "onboard-mc", BRD_ONBOARD2 },
+       { "onboard/mc", BRD_ONBOARD2 },
+       { "onboard-mca", BRD_ONBOARD2 },
+       { "onboard/mca", BRD_ONBOARD2 },
+       { "3", BRD_ONBOARD2 },
+       { "onboard", BRD_ONBOARD },
+       { "onboardat", BRD_ONBOARD },
+       { "4", BRD_ONBOARD },
+       { "onboarde", BRD_ONBOARDE },
+       { "onboard-e", BRD_ONBOARDE },
+       { "onboard/e", BRD_ONBOARDE },
+       { "onboard-ei", BRD_ONBOARDE },
+       { "onboard/ei", BRD_ONBOARDE },
+       { "7", BRD_ONBOARDE },
+       { "ecp", BRD_ECP },
+       { "ecpat", BRD_ECP },
+       { "ec8/64", BRD_ECP },
+       { "ec8/64-at", BRD_ECP },
+       { "ec8/64-isa", BRD_ECP },
+       { "23", BRD_ECP },
+       { "ecpe", BRD_ECPE },
+       { "ecpei", BRD_ECPE },
+       { "ec8/64-e", BRD_ECPE },
+       { "ec8/64-ei", BRD_ECPE },
+       { "24", BRD_ECPE },
+       { "ecpmc", BRD_ECPMC },
+       { "ec8/64-mc", BRD_ECPMC },
+       { "ec8/64-mca", BRD_ECPMC },
+       { "25", BRD_ECPMC },
+       { "ecppci", BRD_ECPPCI },
+       { "ec/ra", BRD_ECPPCI },
+       { "ec/ra-pc", BRD_ECPPCI },
+       { "ec/ra-pci", BRD_ECPPCI },
+       { "29", BRD_ECPPCI },
+};
+
+/*
+ *     Define the module agruments.
+ */
+MODULE_AUTHOR("Greg Ungerer");
+MODULE_DESCRIPTION("Stallion Intelligent Multiport Serial Driver");
+MODULE_LICENSE("GPL");
+
+
+module_param_array(board0, charp, NULL, 0);
+MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,memaddr]");
+module_param_array(board1, charp, NULL, 0);
+MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,memaddr]");
+module_param_array(board2, charp, NULL, 0);
+MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,memaddr]");
+module_param_array(board3, charp, NULL, 0);
+MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,memaddr]");
+
+#if STLI_EISAPROBE != 0
+/*
+ *     Set up a default memory address table for EISA board probing.
+ *     The default addresses are all bellow 1Mbyte, which has to be the
+ *     case anyway. They should be safe, since we only read values from
+ *     them, and interrupts are disabled while we do it. If the higher
+ *     memory support is compiled in then we also try probing around
+ *     the 1Gb, 2Gb and 3Gb areas as well...
+ */
+static unsigned long   stli_eisamemprobeaddrs[] = {
+       0xc0000,    0xd0000,    0xe0000,    0xf0000,
+       0x80000000, 0x80010000, 0x80020000, 0x80030000,
+       0x40000000, 0x40010000, 0x40020000, 0x40030000,
+       0xc0000000, 0xc0010000, 0xc0020000, 0xc0030000,
+       0xff000000, 0xff010000, 0xff020000, 0xff030000,
+};
+
+static int     stli_eisamempsize = ARRAY_SIZE(stli_eisamemprobeaddrs);
+#endif
+
+/*
+ *     Define the Stallion PCI vendor and device IDs.
+ */
+#ifndef PCI_DEVICE_ID_ECRA
+#define        PCI_DEVICE_ID_ECRA              0x0004
+#endif
+
+static struct pci_device_id istallion_pci_tbl[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECRA), },
+       { 0 }
+};
+MODULE_DEVICE_TABLE(pci, istallion_pci_tbl);
+
+static struct pci_driver stli_pcidriver;
+
+/*****************************************************************************/
+
+/*
+ *     Hardware configuration info for ECP boards. These defines apply
+ *     to the directly accessible io ports of the ECP. There is a set of
+ *     defines for each ECP board type, ISA, EISA, MCA and PCI.
+ */
+#define        ECP_IOSIZE      4
+
+#define        ECP_MEMSIZE     (128 * 1024)
+#define        ECP_PCIMEMSIZE  (256 * 1024)
+
+#define        ECP_ATPAGESIZE  (4 * 1024)
+#define        ECP_MCPAGESIZE  (4 * 1024)
+#define        ECP_EIPAGESIZE  (64 * 1024)
+#define        ECP_PCIPAGESIZE (64 * 1024)
+
+#define        STL_EISAID      0x8c4e
+
+/*
+ *     Important defines for the ISA class of ECP board.
+ */
+#define        ECP_ATIREG      0
+#define        ECP_ATCONFR     1
+#define        ECP_ATMEMAR     2
+#define        ECP_ATMEMPR     3
+#define        ECP_ATSTOP      0x1
+#define        ECP_ATINTENAB   0x10
+#define        ECP_ATENABLE    0x20
+#define        ECP_ATDISABLE   0x00
+#define        ECP_ATADDRMASK  0x3f000
+#define        ECP_ATADDRSHFT  12
+
+/*
+ *     Important defines for the EISA class of ECP board.
+ */
+#define        ECP_EIIREG      0
+#define        ECP_EIMEMARL    1
+#define        ECP_EICONFR     2
+#define        ECP_EIMEMARH    3
+#define        ECP_EIENABLE    0x1
+#define        ECP_EIDISABLE   0x0
+#define        ECP_EISTOP      0x4
+#define        ECP_EIEDGE      0x00
+#define        ECP_EILEVEL     0x80
+#define        ECP_EIADDRMASKL 0x00ff0000
+#define        ECP_EIADDRSHFTL 16
+#define        ECP_EIADDRMASKH 0xff000000
+#define        ECP_EIADDRSHFTH 24
+#define        ECP_EIBRDENAB   0xc84
+
+#define        ECP_EISAID      0x4
+
+/*
+ *     Important defines for the Micro-channel class of ECP board.
+ *     (It has a lot in common with the ISA boards.)
+ */
+#define        ECP_MCIREG      0
+#define        ECP_MCCONFR     1
+#define        ECP_MCSTOP      0x20
+#define        ECP_MCENABLE    0x80
+#define        ECP_MCDISABLE   0x00
+
+/*
+ *     Important defines for the PCI class of ECP board.
+ *     (It has a lot in common with the other ECP boards.)
+ */
+#define        ECP_PCIIREG     0
+#define        ECP_PCICONFR    1
+#define        ECP_PCISTOP     0x01
+
+/*
+ *     Hardware configuration info for ONboard and Brumby boards. These
+ *     defines apply to the directly accessible io ports of these boards.
+ */
+#define        ONB_IOSIZE      16
+#define        ONB_MEMSIZE     (64 * 1024)
+#define        ONB_ATPAGESIZE  (64 * 1024)
+#define        ONB_MCPAGESIZE  (64 * 1024)
+#define        ONB_EIMEMSIZE   (128 * 1024)
+#define        ONB_EIPAGESIZE  (64 * 1024)
+
+/*
+ *     Important defines for the ISA class of ONboard board.
+ */
+#define        ONB_ATIREG      0
+#define        ONB_ATMEMAR     1
+#define        ONB_ATCONFR     2
+#define        ONB_ATSTOP      0x4
+#define        ONB_ATENABLE    0x01
+#define        ONB_ATDISABLE   0x00
+#define        ONB_ATADDRMASK  0xff0000
+#define        ONB_ATADDRSHFT  16
+
+#define        ONB_MEMENABLO   0
+#define        ONB_MEMENABHI   0x02
+
+/*
+ *     Important defines for the EISA class of ONboard board.
+ */
+#define        ONB_EIIREG      0
+#define        ONB_EIMEMARL    1
+#define        ONB_EICONFR     2
+#define        ONB_EIMEMARH    3
+#define        ONB_EIENABLE    0x1
+#define        ONB_EIDISABLE   0x0
+#define        ONB_EISTOP      0x4
+#define        ONB_EIEDGE      0x00
+#define        ONB_EILEVEL     0x80
+#define        ONB_EIADDRMASKL 0x00ff0000
+#define        ONB_EIADDRSHFTL 16
+#define        ONB_EIADDRMASKH 0xff000000
+#define        ONB_EIADDRSHFTH 24
+#define        ONB_EIBRDENAB   0xc84
+
+#define        ONB_EISAID      0x1
+
+/*
+ *     Important defines for the Brumby boards. They are pretty simple,
+ *     there is not much that is programmably configurable.
+ */
+#define        BBY_IOSIZE      16
+#define        BBY_MEMSIZE     (64 * 1024)
+#define        BBY_PAGESIZE    (16 * 1024)
+
+#define        BBY_ATIREG      0
+#define        BBY_ATCONFR     1
+#define        BBY_ATSTOP      0x4
+
+/*
+ *     Important defines for the Stallion boards. They are pretty simple,
+ *     there is not much that is programmably configurable.
+ */
+#define        STAL_IOSIZE     16
+#define        STAL_MEMSIZE    (64 * 1024)
+#define        STAL_PAGESIZE   (64 * 1024)
+
+/*
+ *     Define the set of status register values for EasyConnection panels.
+ *     The signature will return with the status value for each panel. From
+ *     this we can determine what is attached to the board - before we have
+ *     actually down loaded any code to it.
+ */
+#define        ECH_PNLSTATUS   2
+#define        ECH_PNL16PORT   0x20
+#define        ECH_PNLIDMASK   0x07
+#define        ECH_PNLXPID     0x40
+#define        ECH_PNLINTRPEND 0x80
+
+/*
+ *     Define some macros to do things to the board. Even those these boards
+ *     are somewhat related there is often significantly different ways of
+ *     doing some operation on it (like enable, paging, reset, etc). So each
+ *     board class has a set of functions which do the commonly required
+ *     operations. The macros below basically just call these functions,
+ *     generally checking for a NULL function - which means that the board
+ *     needs nothing done to it to achieve this operation!
+ */
+#define        EBRDINIT(brdp)                                          \
+       if (brdp->init != NULL)                                 \
+               (* brdp->init)(brdp)
+
+#define        EBRDENABLE(brdp)                                        \
+       if (brdp->enable != NULL)                               \
+               (* brdp->enable)(brdp);
+
+#define        EBRDDISABLE(brdp)                                       \
+       if (brdp->disable != NULL)                              \
+               (* brdp->disable)(brdp);
+
+#define        EBRDINTR(brdp)                                          \
+       if (brdp->intr != NULL)                                 \
+               (* brdp->intr)(brdp);
+
+#define        EBRDRESET(brdp)                                         \
+       if (brdp->reset != NULL)                                \
+               (* brdp->reset)(brdp);
+
+#define        EBRDGETMEMPTR(brdp,offset)                              \
+       (* brdp->getmemptr)(brdp, offset, __LINE__)
+
+/*
+ *     Define the maximal baud rate, and the default baud base for ports.
+ */
+#define        STL_MAXBAUD     460800
+#define        STL_BAUDBASE    115200
+#define        STL_CLOSEDELAY  (5 * HZ / 10)
+
+/*****************************************************************************/
+
+/*
+ *     Define macros to extract a brd or port number from a minor number.
+ */
+#define        MINOR2BRD(min)          (((min) & 0xc0) >> 6)
+#define        MINOR2PORT(min)         ((min) & 0x3f)
+
+/*****************************************************************************/
+
+/*
+ *     Prototype all functions in this driver!
+ */
+
+static int     stli_parsebrd(struct stlconf *confp, char **argp);
+static int     stli_open(struct tty_struct *tty, struct file *filp);
+static void    stli_close(struct tty_struct *tty, struct file *filp);
+static int     stli_write(struct tty_struct *tty, const unsigned char *buf, int count);
+static int     stli_putchar(struct tty_struct *tty, unsigned char ch);
+static void    stli_flushchars(struct tty_struct *tty);
+static int     stli_writeroom(struct tty_struct *tty);
+static int     stli_charsinbuffer(struct tty_struct *tty);
+static int     stli_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
+static void    stli_settermios(struct tty_struct *tty, struct ktermios *old);
+static void    stli_throttle(struct tty_struct *tty);
+static void    stli_unthrottle(struct tty_struct *tty);
+static void    stli_stop(struct tty_struct *tty);
+static void    stli_start(struct tty_struct *tty);
+static void    stli_flushbuffer(struct tty_struct *tty);
+static int     stli_breakctl(struct tty_struct *tty, int state);
+static void    stli_waituntilsent(struct tty_struct *tty, int timeout);
+static void    stli_sendxchar(struct tty_struct *tty, char ch);
+static void    stli_hangup(struct tty_struct *tty);
+
+static int     stli_brdinit(struct stlibrd *brdp);
+static int     stli_startbrd(struct stlibrd *brdp);
+static ssize_t stli_memread(struct file *fp, char __user *buf, size_t count, loff_t *offp);
+static ssize_t stli_memwrite(struct file *fp, const char __user *buf, size_t count, loff_t *offp);
+static long    stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg);
+static void    stli_brdpoll(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp);
+static void    stli_poll(unsigned long arg);
+static int     stli_hostcmd(struct stlibrd *brdp, struct stliport *portp);
+static int     stli_initopen(struct tty_struct *tty, struct stlibrd *brdp, struct stliport *portp);
+static int     stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait);
+static int     stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait);
+static int     stli_setport(struct tty_struct *tty);
+static int     stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback);
+static void    stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback);
+static void    __stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback);
+static void    stli_dodelaycmd(struct stliport *portp, cdkctrl_t __iomem *cp);
+static void    stli_mkasyport(struct tty_struct *tty, struct stliport *portp, asyport_t *pp, struct ktermios *tiosp);
+static void    stli_mkasysigs(asysigs_t *sp, int dtr, int rts);
+static long    stli_mktiocm(unsigned long sigvalue);
+static void    stli_read(struct stlibrd *brdp, struct stliport *portp);
+static int     stli_getserial(struct stliport *portp, struct serial_struct __user *sp);
+static int     stli_setserial(struct tty_struct *tty, struct serial_struct __user *sp);
+static int     stli_getbrdstats(combrd_t __user *bp);
+static int     stli_getportstats(struct tty_struct *tty, struct stliport *portp, comstats_t __user *cp);
+static int     stli_portcmdstats(struct tty_struct *tty, struct stliport *portp);
+static int     stli_clrportstats(struct stliport *portp, comstats_t __user *cp);
+static int     stli_getportstruct(struct stliport __user *arg);
+static int     stli_getbrdstruct(struct stlibrd __user *arg);
+static struct stlibrd *stli_allocbrd(void);
+
+static void    stli_ecpinit(struct stlibrd *brdp);
+static void    stli_ecpenable(struct stlibrd *brdp);
+static void    stli_ecpdisable(struct stlibrd *brdp);
+static void __iomem *stli_ecpgetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
+static void    stli_ecpreset(struct stlibrd *brdp);
+static void    stli_ecpintr(struct stlibrd *brdp);
+static void    stli_ecpeiinit(struct stlibrd *brdp);
+static void    stli_ecpeienable(struct stlibrd *brdp);
+static void    stli_ecpeidisable(struct stlibrd *brdp);
+static void __iomem *stli_ecpeigetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
+static void    stli_ecpeireset(struct stlibrd *brdp);
+static void    stli_ecpmcenable(struct stlibrd *brdp);
+static void    stli_ecpmcdisable(struct stlibrd *brdp);
+static void __iomem *stli_ecpmcgetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
+static void    stli_ecpmcreset(struct stlibrd *brdp);
+static void    stli_ecppciinit(struct stlibrd *brdp);
+static void __iomem *stli_ecppcigetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
+static void    stli_ecppcireset(struct stlibrd *brdp);
+
+static void    stli_onbinit(struct stlibrd *brdp);
+static void    stli_onbenable(struct stlibrd *brdp);
+static void    stli_onbdisable(struct stlibrd *brdp);
+static void __iomem *stli_onbgetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
+static void    stli_onbreset(struct stlibrd *brdp);
+static void    stli_onbeinit(struct stlibrd *brdp);
+static void    stli_onbeenable(struct stlibrd *brdp);
+static void    stli_onbedisable(struct stlibrd *brdp);
+static void __iomem *stli_onbegetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
+static void    stli_onbereset(struct stlibrd *brdp);
+static void    stli_bbyinit(struct stlibrd *brdp);
+static void __iomem *stli_bbygetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
+static void    stli_bbyreset(struct stlibrd *brdp);
+static void    stli_stalinit(struct stlibrd *brdp);
+static void __iomem *stli_stalgetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
+static void    stli_stalreset(struct stlibrd *brdp);
+
+static struct stliport *stli_getport(unsigned int brdnr, unsigned int panelnr, unsigned int portnr);
+
+static int     stli_initecp(struct stlibrd *brdp);
+static int     stli_initonb(struct stlibrd *brdp);
+#if STLI_EISAPROBE != 0
+static int     stli_eisamemprobe(struct stlibrd *brdp);
+#endif
+static int     stli_initports(struct stlibrd *brdp);
+
+/*****************************************************************************/
+
+/*
+ *     Define the driver info for a user level shared memory device. This
+ *     device will work sort of like the /dev/kmem device - except that it
+ *     will give access to the shared memory on the Stallion intelligent
+ *     board. This is also a very useful debugging tool.
+ */
+static const struct file_operations    stli_fsiomem = {
+       .owner          = THIS_MODULE,
+       .read           = stli_memread,
+       .write          = stli_memwrite,
+       .unlocked_ioctl = stli_memioctl,
+       .llseek         = default_llseek,
+};
+
+/*****************************************************************************/
+
+/*
+ *     Define a timer_list entry for our poll routine. The slave board
+ *     is polled every so often to see if anything needs doing. This is
+ *     much cheaper on host cpu than using interrupts. It turns out to
+ *     not increase character latency by much either...
+ */
+static DEFINE_TIMER(stli_timerlist, stli_poll, 0, 0);
+
+static int     stli_timeron;
+
+/*
+ *     Define the calculation for the timeout routine.
+ */
+#define        STLI_TIMEOUT    (jiffies + 1)
+
+/*****************************************************************************/
+
+static struct class *istallion_class;
+
+static void stli_cleanup_ports(struct stlibrd *brdp)
+{
+       struct stliport *portp;
+       unsigned int j;
+       struct tty_struct *tty;
+
+       for (j = 0; j < STL_MAXPORTS; j++) {
+               portp = brdp->ports[j];
+               if (portp != NULL) {
+                       tty = tty_port_tty_get(&portp->port);
+                       if (tty != NULL) {
+                               tty_hangup(tty);
+                               tty_kref_put(tty);
+                       }
+                       kfree(portp);
+               }
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Parse the supplied argument string, into the board conf struct.
+ */
+
+static int stli_parsebrd(struct stlconf *confp, char **argp)
+{
+       unsigned int i;
+       char *sp;
+
+       if (argp[0] == NULL || *argp[0] == 0)
+               return 0;
+
+       for (sp = argp[0], i = 0; ((*sp != 0) && (i < 25)); sp++, i++)
+               *sp = tolower(*sp);
+
+       for (i = 0; i < ARRAY_SIZE(stli_brdstr); i++) {
+               if (strcmp(stli_brdstr[i].name, argp[0]) == 0)
+                       break;
+       }
+       if (i == ARRAY_SIZE(stli_brdstr)) {
+               printk(KERN_WARNING "istallion: unknown board name, %s?\n", argp[0]);
+               return 0;
+       }
+
+       confp->brdtype = stli_brdstr[i].type;
+       if (argp[1] != NULL && *argp[1] != 0)
+               confp->ioaddr1 = simple_strtoul(argp[1], NULL, 0);
+       if (argp[2] !=  NULL && *argp[2] != 0)
+               confp->memaddr = simple_strtoul(argp[2], NULL, 0);
+       return(1);
+}
+
+/*****************************************************************************/
+
+/*
+ *     On the first open of the device setup the port hardware, and
+ *     initialize the per port data structure. Since initializing the port
+ *     requires several commands to the board we will need to wait for any
+ *     other open that is already initializing the port.
+ *
+ *     Locking: protected by the port mutex.
+ */
+
+static int stli_activate(struct tty_port *port, struct tty_struct *tty)
+{
+       struct stliport *portp = container_of(port, struct stliport, port);
+       struct stlibrd *brdp = stli_brds[portp->brdnr];
+       int rc;
+
+       if ((rc = stli_initopen(tty, brdp, portp)) >= 0)
+               clear_bit(TTY_IO_ERROR, &tty->flags);
+       wake_up_interruptible(&portp->raw_wait);
+       return rc;
+}
+
+static int stli_open(struct tty_struct *tty, struct file *filp)
+{
+       struct stlibrd *brdp;
+       struct stliport *portp;
+       unsigned int minordev, brdnr, portnr;
+
+       minordev = tty->index;
+       brdnr = MINOR2BRD(minordev);
+       if (brdnr >= stli_nrbrds)
+               return -ENODEV;
+       brdp = stli_brds[brdnr];
+       if (brdp == NULL)
+               return -ENODEV;
+       if (!test_bit(BST_STARTED, &brdp->state))
+               return -ENODEV;
+       portnr = MINOR2PORT(minordev);
+       if (portnr > brdp->nrports)
+               return -ENODEV;
+
+       portp = brdp->ports[portnr];
+       if (portp == NULL)
+               return -ENODEV;
+       if (portp->devnr < 1)
+               return -ENODEV;
+
+       tty->driver_data = portp;
+       return tty_port_open(&portp->port, tty, filp);
+}
+
+
+/*****************************************************************************/
+
+static void stli_shutdown(struct tty_port *port)
+{
+       struct stlibrd *brdp;
+       unsigned long ftype;
+       unsigned long flags;
+       struct stliport *portp = container_of(port, struct stliport, port);
+
+       if (portp->brdnr >= stli_nrbrds)
+               return;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return;
+
+       /*
+        *      May want to wait for data to drain before closing. The BUSY
+        *      flag keeps track of whether we are still transmitting or not.
+        *      It is updated by messages from the slave - indicating when all
+        *      chars really have drained.
+        */
+
+       if (!test_bit(ST_CLOSING, &portp->state))
+               stli_rawclose(brdp, portp, 0, 0);
+
+       spin_lock_irqsave(&stli_lock, flags);
+       clear_bit(ST_TXBUSY, &portp->state);
+       clear_bit(ST_RXSTOP, &portp->state);
+       spin_unlock_irqrestore(&stli_lock, flags);
+
+       ftype = FLUSHTX | FLUSHRX;
+       stli_cmdwait(brdp, portp, A_FLUSH, &ftype, sizeof(u32), 0);
+}
+
+static void stli_close(struct tty_struct *tty, struct file *filp)
+{
+       struct stliport *portp = tty->driver_data;
+       unsigned long flags;
+       if (portp == NULL)
+               return;
+       spin_lock_irqsave(&stli_lock, flags);
+       /*      Flush any internal buffering out first */
+       if (tty == stli_txcooktty)
+               stli_flushchars(tty);
+       spin_unlock_irqrestore(&stli_lock, flags);
+       tty_port_close(&portp->port, tty, filp);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Carry out first open operations on a port. This involves a number of
+ *     commands to be sent to the slave. We need to open the port, set the
+ *     notification events, set the initial port settings, get and set the
+ *     initial signal values. We sleep and wait in between each one. But
+ *     this still all happens pretty quickly.
+ */
+
+static int stli_initopen(struct tty_struct *tty,
+                               struct stlibrd *brdp, struct stliport *portp)
+{
+       asynotify_t nt;
+       asyport_t aport;
+       int rc;
+
+       if ((rc = stli_rawopen(brdp, portp, 0, 1)) < 0)
+               return rc;
+
+       memset(&nt, 0, sizeof(asynotify_t));
+       nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK);
+       nt.signal = SG_DCD;
+       if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt,
+           sizeof(asynotify_t), 0)) < 0)
+               return rc;
+
+       stli_mkasyport(tty, portp, &aport, tty->termios);
+       if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport,
+           sizeof(asyport_t), 0)) < 0)
+               return rc;
+
+       set_bit(ST_GETSIGS, &portp->state);
+       if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig,
+           sizeof(asysigs_t), 1)) < 0)
+               return rc;
+       if (test_and_clear_bit(ST_GETSIGS, &portp->state))
+               portp->sigs = stli_mktiocm(portp->asig.sigvalue);
+       stli_mkasysigs(&portp->asig, 1, 1);
+       if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
+           sizeof(asysigs_t), 0)) < 0)
+               return rc;
+
+       return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Send an open message to the slave. This will sleep waiting for the
+ *     acknowledgement, so must have user context. We need to co-ordinate
+ *     with close events here, since we don't want open and close events
+ *     to overlap.
+ */
+
+static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait)
+{
+       cdkhdr_t __iomem *hdrp;
+       cdkctrl_t __iomem *cp;
+       unsigned char __iomem *bits;
+       unsigned long flags;
+       int rc;
+
+/*
+ *     Send a message to the slave to open this port.
+ */
+
+/*
+ *     Slave is already closing this port. This can happen if a hangup
+ *     occurs on this port. So we must wait until it is complete. The
+ *     order of opens and closes may not be preserved across shared
+ *     memory, so we must wait until it is complete.
+ */
+       wait_event_interruptible_tty(portp->raw_wait,
+                       !test_bit(ST_CLOSING, &portp->state));
+       if (signal_pending(current)) {
+               return -ERESTARTSYS;
+       }
+
+/*
+ *     Everything is ready now, so write the open message into shared
+ *     memory. Once the message is in set the service bits to say that
+ *     this port wants service.
+ */
+       spin_lock_irqsave(&brd_lock, flags);
+       EBRDENABLE(brdp);
+       cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
+       writel(arg, &cp->openarg);
+       writeb(1, &cp->open);
+       hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+       bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
+               portp->portidx;
+       writeb(readb(bits) | portp->portbit, bits);
+       EBRDDISABLE(brdp);
+
+       if (wait == 0) {
+               spin_unlock_irqrestore(&brd_lock, flags);
+               return 0;
+       }
+
+/*
+ *     Slave is in action, so now we must wait for the open acknowledgment
+ *     to come back.
+ */
+       rc = 0;
+       set_bit(ST_OPENING, &portp->state);
+       spin_unlock_irqrestore(&brd_lock, flags);
+
+       wait_event_interruptible_tty(portp->raw_wait,
+                       !test_bit(ST_OPENING, &portp->state));
+       if (signal_pending(current))
+               rc = -ERESTARTSYS;
+
+       if ((rc == 0) && (portp->rc != 0))
+               rc = -EIO;
+       return rc;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Send a close message to the slave. Normally this will sleep waiting
+ *     for the acknowledgement, but if wait parameter is 0 it will not. If
+ *     wait is true then must have user context (to sleep).
+ */
+
+static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait)
+{
+       cdkhdr_t __iomem *hdrp;
+       cdkctrl_t __iomem *cp;
+       unsigned char __iomem *bits;
+       unsigned long flags;
+       int rc;
+
+/*
+ *     Slave is already closing this port. This can happen if a hangup
+ *     occurs on this port.
+ */
+       if (wait) {
+               wait_event_interruptible_tty(portp->raw_wait,
+                               !test_bit(ST_CLOSING, &portp->state));
+               if (signal_pending(current)) {
+                       return -ERESTARTSYS;
+               }
+       }
+
+/*
+ *     Write the close command into shared memory.
+ */
+       spin_lock_irqsave(&brd_lock, flags);
+       EBRDENABLE(brdp);
+       cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
+       writel(arg, &cp->closearg);
+       writeb(1, &cp->close);
+       hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+       bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
+               portp->portidx;
+       writeb(readb(bits) |portp->portbit, bits);
+       EBRDDISABLE(brdp);
+
+       set_bit(ST_CLOSING, &portp->state);
+       spin_unlock_irqrestore(&brd_lock, flags);
+
+       if (wait == 0)
+               return 0;
+
+/*
+ *     Slave is in action, so now we must wait for the open acknowledgment
+ *     to come back.
+ */
+       rc = 0;
+       wait_event_interruptible_tty(portp->raw_wait,
+                       !test_bit(ST_CLOSING, &portp->state));
+       if (signal_pending(current))
+               rc = -ERESTARTSYS;
+
+       if ((rc == 0) && (portp->rc != 0))
+               rc = -EIO;
+       return rc;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Send a command to the slave and wait for the response. This must
+ *     have user context (it sleeps). This routine is generic in that it
+ *     can send any type of command. Its purpose is to wait for that command
+ *     to complete (as opposed to initiating the command then returning).
+ */
+
+static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback)
+{
+       /*
+        * no need for wait_event_tty because clearing ST_CMDING cannot block
+        * on BTM
+        */
+       wait_event_interruptible(portp->raw_wait,
+                       !test_bit(ST_CMDING, &portp->state));
+       if (signal_pending(current))
+               return -ERESTARTSYS;
+
+       stli_sendcmd(brdp, portp, cmd, arg, size, copyback);
+
+       wait_event_interruptible(portp->raw_wait,
+                       !test_bit(ST_CMDING, &portp->state));
+       if (signal_pending(current))
+               return -ERESTARTSYS;
+
+       if (portp->rc != 0)
+               return -EIO;
+       return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Send the termios settings for this port to the slave. This sleeps
+ *     waiting for the command to complete - so must have user context.
+ */
+
+static int stli_setport(struct tty_struct *tty)
+{
+       struct stliport *portp = tty->driver_data;
+       struct stlibrd *brdp;
+       asyport_t aport;
+
+       if (portp == NULL)
+               return -ENODEV;
+       if (portp->brdnr >= stli_nrbrds)
+               return -ENODEV;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return -ENODEV;
+
+       stli_mkasyport(tty, portp, &aport, tty->termios);
+       return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0));
+}
+
+/*****************************************************************************/
+
+static int stli_carrier_raised(struct tty_port *port)
+{
+       struct stliport *portp = container_of(port, struct stliport, port);
+       return (portp->sigs & TIOCM_CD) ? 1 : 0;
+}
+
+static void stli_dtr_rts(struct tty_port *port, int on)
+{
+       struct stliport *portp = container_of(port, struct stliport, port);
+       struct stlibrd *brdp = stli_brds[portp->brdnr];
+       stli_mkasysigs(&portp->asig, on, on);
+       if (stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
+               sizeof(asysigs_t), 0) < 0)
+                       printk(KERN_WARNING "istallion: dtr set failed.\n");
+}
+
+
+/*****************************************************************************/
+
+/*
+ *     Write routine. Take the data and put it in the shared memory ring
+ *     queue. If port is not already sending chars then need to mark the
+ *     service bits for this port.
+ */
+
+static int stli_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+       cdkasy_t __iomem *ap;
+       cdkhdr_t __iomem *hdrp;
+       unsigned char __iomem *bits;
+       unsigned char __iomem *shbuf;
+       unsigned char *chbuf;
+       struct stliport *portp;
+       struct stlibrd *brdp;
+       unsigned int len, stlen, head, tail, size;
+       unsigned long flags;
+
+       if (tty == stli_txcooktty)
+               stli_flushchars(tty);
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return 0;
+       if (portp->brdnr >= stli_nrbrds)
+               return 0;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return 0;
+       chbuf = (unsigned char *) buf;
+
+/*
+ *     All data is now local, shove as much as possible into shared memory.
+ */
+       spin_lock_irqsave(&brd_lock, flags);
+       EBRDENABLE(brdp);
+       ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
+       head = (unsigned int) readw(&ap->txq.head);
+       tail = (unsigned int) readw(&ap->txq.tail);
+       if (tail != ((unsigned int) readw(&ap->txq.tail)))
+               tail = (unsigned int) readw(&ap->txq.tail);
+       size = portp->txsize;
+       if (head >= tail) {
+               len = size - (head - tail) - 1;
+               stlen = size - head;
+       } else {
+               len = tail - head - 1;
+               stlen = len;
+       }
+
+       len = min(len, (unsigned int)count);
+       count = 0;
+       shbuf = (char __iomem *) EBRDGETMEMPTR(brdp, portp->txoffset);
+
+       while (len > 0) {
+               stlen = min(len, stlen);
+               memcpy_toio(shbuf + head, chbuf, stlen);
+               chbuf += stlen;
+               len -= stlen;
+               count += stlen;
+               head += stlen;
+               if (head >= size) {
+                       head = 0;
+                       stlen = tail;
+               }
+       }
+
+       ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
+       writew(head, &ap->txq.head);
+       if (test_bit(ST_TXBUSY, &portp->state)) {
+               if (readl(&ap->changed.data) & DT_TXEMPTY)
+                       writel(readl(&ap->changed.data) & ~DT_TXEMPTY, &ap->changed.data);
+       }
+       hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+       bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
+               portp->portidx;
+       writeb(readb(bits) | portp->portbit, bits);
+       set_bit(ST_TXBUSY, &portp->state);
+       EBRDDISABLE(brdp);
+       spin_unlock_irqrestore(&brd_lock, flags);
+
+       return(count);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Output a single character. We put it into a temporary local buffer
+ *     (for speed) then write out that buffer when the flushchars routine
+ *     is called. There is a safety catch here so that if some other port
+ *     writes chars before the current buffer has been, then we write them
+ *     first them do the new ports.
+ */
+
+static int stli_putchar(struct tty_struct *tty, unsigned char ch)
+{
+       if (tty != stli_txcooktty) {
+               if (stli_txcooktty != NULL)
+                       stli_flushchars(stli_txcooktty);
+               stli_txcooktty = tty;
+       }
+
+       stli_txcookbuf[stli_txcooksize++] = ch;
+       return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Transfer characters from the local TX cooking buffer to the board.
+ *     We sort of ignore the tty that gets passed in here. We rely on the
+ *     info stored with the TX cook buffer to tell us which port to flush
+ *     the data on. In any case we clean out the TX cook buffer, for re-use
+ *     by someone else.
+ */
+
+static void stli_flushchars(struct tty_struct *tty)
+{
+       cdkhdr_t __iomem *hdrp;
+       unsigned char __iomem *bits;
+       cdkasy_t __iomem *ap;
+       struct tty_struct *cooktty;
+       struct stliport *portp;
+       struct stlibrd *brdp;
+       unsigned int len, stlen, head, tail, size, count, cooksize;
+       unsigned char *buf;
+       unsigned char __iomem *shbuf;
+       unsigned long flags;
+
+       cooksize = stli_txcooksize;
+       cooktty = stli_txcooktty;
+       stli_txcooksize = 0;
+       stli_txcookrealsize = 0;
+       stli_txcooktty = NULL;
+
+       if (cooktty == NULL)
+               return;
+       if (tty != cooktty)
+               tty = cooktty;
+       if (cooksize == 0)
+               return;
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+       if (portp->brdnr >= stli_nrbrds)
+               return;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       EBRDENABLE(brdp);
+
+       ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
+       head = (unsigned int) readw(&ap->txq.head);
+       tail = (unsigned int) readw(&ap->txq.tail);
+       if (tail != ((unsigned int) readw(&ap->txq.tail)))
+               tail = (unsigned int) readw(&ap->txq.tail);
+       size = portp->txsize;
+       if (head >= tail) {
+               len = size - (head - tail) - 1;
+               stlen = size - head;
+       } else {
+               len = tail - head - 1;
+               stlen = len;
+       }
+
+       len = min(len, cooksize);
+       count = 0;
+       shbuf = EBRDGETMEMPTR(brdp, portp->txoffset);
+       buf = stli_txcookbuf;
+
+       while (len > 0) {
+               stlen = min(len, stlen);
+               memcpy_toio(shbuf + head, buf, stlen);
+               buf += stlen;
+               len -= stlen;
+               count += stlen;
+               head += stlen;
+               if (head >= size) {
+                       head = 0;
+                       stlen = tail;
+               }
+       }
+
+       ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
+       writew(head, &ap->txq.head);
+
+       if (test_bit(ST_TXBUSY, &portp->state)) {
+               if (readl(&ap->changed.data) & DT_TXEMPTY)
+                       writel(readl(&ap->changed.data) & ~DT_TXEMPTY, &ap->changed.data);
+       }
+       hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+       bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
+               portp->portidx;
+       writeb(readb(bits) | portp->portbit, bits);
+       set_bit(ST_TXBUSY, &portp->state);
+
+       EBRDDISABLE(brdp);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+static int stli_writeroom(struct tty_struct *tty)
+{
+       cdkasyrq_t __iomem *rp;
+       struct stliport *portp;
+       struct stlibrd *brdp;
+       unsigned int head, tail, len;
+       unsigned long flags;
+
+       if (tty == stli_txcooktty) {
+               if (stli_txcookrealsize != 0) {
+                       len = stli_txcookrealsize - stli_txcooksize;
+                       return len;
+               }
+       }
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return 0;
+       if (portp->brdnr >= stli_nrbrds)
+               return 0;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return 0;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       EBRDENABLE(brdp);
+       rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->txq;
+       head = (unsigned int) readw(&rp->head);
+       tail = (unsigned int) readw(&rp->tail);
+       if (tail != ((unsigned int) readw(&rp->tail)))
+               tail = (unsigned int) readw(&rp->tail);
+       len = (head >= tail) ? (portp->txsize - (head - tail)) : (tail - head);
+       len--;
+       EBRDDISABLE(brdp);
+       spin_unlock_irqrestore(&brd_lock, flags);
+
+       if (tty == stli_txcooktty) {
+               stli_txcookrealsize = len;
+               len -= stli_txcooksize;
+       }
+       return len;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the number of characters in the transmit buffer. Normally we
+ *     will return the number of chars in the shared memory ring queue.
+ *     We need to kludge around the case where the shared memory buffer is
+ *     empty but not all characters have drained yet, for this case just
+ *     return that there is 1 character in the buffer!
+ */
+
+static int stli_charsinbuffer(struct tty_struct *tty)
+{
+       cdkasyrq_t __iomem *rp;
+       struct stliport *portp;
+       struct stlibrd *brdp;
+       unsigned int head, tail, len;
+       unsigned long flags;
+
+       if (tty == stli_txcooktty)
+               stli_flushchars(tty);
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return 0;
+       if (portp->brdnr >= stli_nrbrds)
+               return 0;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return 0;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       EBRDENABLE(brdp);
+       rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->txq;
+       head = (unsigned int) readw(&rp->head);
+       tail = (unsigned int) readw(&rp->tail);
+       if (tail != ((unsigned int) readw(&rp->tail)))
+               tail = (unsigned int) readw(&rp->tail);
+       len = (head >= tail) ? (head - tail) : (portp->txsize - (tail - head));
+       if ((len == 0) && test_bit(ST_TXBUSY, &portp->state))
+               len = 1;
+       EBRDDISABLE(brdp);
+       spin_unlock_irqrestore(&brd_lock, flags);
+
+       return len;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Generate the serial struct info.
+ */
+
+static int stli_getserial(struct stliport *portp, struct serial_struct __user *sp)
+{
+       struct serial_struct sio;
+       struct stlibrd *brdp;
+
+       memset(&sio, 0, sizeof(struct serial_struct));
+       sio.type = PORT_UNKNOWN;
+       sio.line = portp->portnr;
+       sio.irq = 0;
+       sio.flags = portp->port.flags;
+       sio.baud_base = portp->baud_base;
+       sio.close_delay = portp->port.close_delay;
+       sio.closing_wait = portp->closing_wait;
+       sio.custom_divisor = portp->custom_divisor;
+       sio.xmit_fifo_size = 0;
+       sio.hub6 = 0;
+
+       brdp = stli_brds[portp->brdnr];
+       if (brdp != NULL)
+               sio.port = brdp->iobase;
+               
+       return copy_to_user(sp, &sio, sizeof(struct serial_struct)) ?
+                       -EFAULT : 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Set port according to the serial struct info.
+ *     At this point we do not do any auto-configure stuff, so we will
+ *     just quietly ignore any requests to change irq, etc.
+ */
+
+static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *sp)
+{
+       struct serial_struct sio;
+       int rc;
+       struct stliport *portp = tty->driver_data;
+
+       if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
+               return -EFAULT;
+       if (!capable(CAP_SYS_ADMIN)) {
+               if ((sio.baud_base != portp->baud_base) ||
+                   (sio.close_delay != portp->port.close_delay) ||
+                   ((sio.flags & ~ASYNC_USR_MASK) !=
+                   (portp->port.flags & ~ASYNC_USR_MASK)))
+                       return -EPERM;
+       } 
+
+       portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) |
+               (sio.flags & ASYNC_USR_MASK);
+       portp->baud_base = sio.baud_base;
+       portp->port.close_delay = sio.close_delay;
+       portp->closing_wait = sio.closing_wait;
+       portp->custom_divisor = sio.custom_divisor;
+
+       if ((rc = stli_setport(tty)) < 0)
+               return rc;
+       return 0;
+}
+
+/*****************************************************************************/
+
+static int stli_tiocmget(struct tty_struct *tty)
+{
+       struct stliport *portp = tty->driver_data;
+       struct stlibrd *brdp;
+       int rc;
+
+       if (portp == NULL)
+               return -ENODEV;
+       if (portp->brdnr >= stli_nrbrds)
+               return 0;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return 0;
+       if (tty->flags & (1 << TTY_IO_ERROR))
+               return -EIO;
+
+       if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS,
+                              &portp->asig, sizeof(asysigs_t), 1)) < 0)
+               return rc;
+
+       return stli_mktiocm(portp->asig.sigvalue);
+}
+
+static int stli_tiocmset(struct tty_struct *tty,
+                        unsigned int set, unsigned int clear)
+{
+       struct stliport *portp = tty->driver_data;
+       struct stlibrd *brdp;
+       int rts = -1, dtr = -1;
+
+       if (portp == NULL)
+               return -ENODEV;
+       if (portp->brdnr >= stli_nrbrds)
+               return 0;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return 0;
+       if (tty->flags & (1 << TTY_IO_ERROR))
+               return -EIO;
+
+       if (set & TIOCM_RTS)
+               rts = 1;
+       if (set & TIOCM_DTR)
+               dtr = 1;
+       if (clear & TIOCM_RTS)
+               rts = 0;
+       if (clear & TIOCM_DTR)
+               dtr = 0;
+
+       stli_mkasysigs(&portp->asig, dtr, rts);
+
+       return stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
+                           sizeof(asysigs_t), 0);
+}
+
+static int stli_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
+{
+       struct stliport *portp;
+       struct stlibrd *brdp;
+       int rc;
+       void __user *argp = (void __user *)arg;
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return -ENODEV;
+       if (portp->brdnr >= stli_nrbrds)
+               return 0;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return 0;
+
+       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+           (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) {
+               if (tty->flags & (1 << TTY_IO_ERROR))
+                       return -EIO;
+       }
+
+       rc = 0;
+
+       switch (cmd) {
+       case TIOCGSERIAL:
+               rc = stli_getserial(portp, argp);
+               break;
+       case TIOCSSERIAL:
+               rc = stli_setserial(tty, argp);
+               break;
+       case STL_GETPFLAG:
+               rc = put_user(portp->pflag, (unsigned __user *)argp);
+               break;
+       case STL_SETPFLAG:
+               if ((rc = get_user(portp->pflag, (unsigned __user *)argp)) == 0)
+                       stli_setport(tty);
+               break;
+       case COM_GETPORTSTATS:
+               rc = stli_getportstats(tty, portp, argp);
+               break;
+       case COM_CLRPORTSTATS:
+               rc = stli_clrportstats(portp, argp);
+               break;
+       case TIOCSERCONFIG:
+       case TIOCSERGWILD:
+       case TIOCSERSWILD:
+       case TIOCSERGETLSR:
+       case TIOCSERGSTRUCT:
+       case TIOCSERGETMULTI:
+       case TIOCSERSETMULTI:
+       default:
+               rc = -ENOIOCTLCMD;
+               break;
+       }
+
+       return rc;
+}
+
+/*****************************************************************************/
+
+/*
+ *     This routine assumes that we have user context and can sleep.
+ *     Looks like it is true for the current ttys implementation..!!
+ */
+
+static void stli_settermios(struct tty_struct *tty, struct ktermios *old)
+{
+       struct stliport *portp;
+       struct stlibrd *brdp;
+       struct ktermios *tiosp;
+       asyport_t aport;
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+       if (portp->brdnr >= stli_nrbrds)
+               return;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return;
+
+       tiosp = tty->termios;
+
+       stli_mkasyport(tty, portp, &aport, tiosp);
+       stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0);
+       stli_mkasysigs(&portp->asig, ((tiosp->c_cflag & CBAUD) ? 1 : 0), -1);
+       stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
+               sizeof(asysigs_t), 0);
+       if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0))
+               tty->hw_stopped = 0;
+       if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL))
+               wake_up_interruptible(&portp->port.open_wait);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Attempt to flow control who ever is sending us data. We won't really
+ *     do any flow control action here. We can't directly, and even if we
+ *     wanted to we would have to send a command to the slave. The slave
+ *     knows how to flow control, and will do so when its buffers reach its
+ *     internal high water marks. So what we will do is set a local state
+ *     bit that will stop us sending any RX data up from the poll routine
+ *     (which is the place where RX data from the slave is handled).
+ */
+
+static void stli_throttle(struct tty_struct *tty)
+{
+       struct stliport *portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+       set_bit(ST_RXSTOP, &portp->state);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Unflow control the device sending us data... That means that all
+ *     we have to do is clear the RXSTOP state bit. The next poll call
+ *     will then be able to pass the RX data back up.
+ */
+
+static void stli_unthrottle(struct tty_struct *tty)
+{
+       struct stliport *portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+       clear_bit(ST_RXSTOP, &portp->state);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Stop the transmitter.
+ */
+
+static void stli_stop(struct tty_struct *tty)
+{
+}
+
+/*****************************************************************************/
+
+/*
+ *     Start the transmitter again.
+ */
+
+static void stli_start(struct tty_struct *tty)
+{
+}
+
+/*****************************************************************************/
+
+
+/*
+ *     Hangup this port. This is pretty much like closing the port, only
+ *     a little more brutal. No waiting for data to drain. Shutdown the
+ *     port and maybe drop signals. This is rather tricky really. We want
+ *     to close the port as well.
+ */
+
+static void stli_hangup(struct tty_struct *tty)
+{
+       struct stliport *portp = tty->driver_data;
+       tty_port_hangup(&portp->port);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Flush characters from the lower buffer. We may not have user context
+ *     so we cannot sleep waiting for it to complete. Also we need to check
+ *     if there is chars for this port in the TX cook buffer, and flush them
+ *     as well.
+ */
+
+static void stli_flushbuffer(struct tty_struct *tty)
+{
+       struct stliport *portp;
+       struct stlibrd *brdp;
+       unsigned long ftype, flags;
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+       if (portp->brdnr >= stli_nrbrds)
+               return;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       if (tty == stli_txcooktty) {
+               stli_txcooktty = NULL;
+               stli_txcooksize = 0;
+               stli_txcookrealsize = 0;
+       }
+       if (test_bit(ST_CMDING, &portp->state)) {
+               set_bit(ST_DOFLUSHTX, &portp->state);
+       } else {
+               ftype = FLUSHTX;
+               if (test_bit(ST_DOFLUSHRX, &portp->state)) {
+                       ftype |= FLUSHRX;
+                       clear_bit(ST_DOFLUSHRX, &portp->state);
+               }
+               __stli_sendcmd(brdp, portp, A_FLUSH, &ftype, sizeof(u32), 0);
+       }
+       spin_unlock_irqrestore(&brd_lock, flags);
+       tty_wakeup(tty);
+}
+
+/*****************************************************************************/
+
+static int stli_breakctl(struct tty_struct *tty, int state)
+{
+       struct stlibrd  *brdp;
+       struct stliport *portp;
+       long            arg;
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return -EINVAL;
+       if (portp->brdnr >= stli_nrbrds)
+               return -EINVAL;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return -EINVAL;
+
+       arg = (state == -1) ? BREAKON : BREAKOFF;
+       stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(long), 0);
+       return 0;
+}
+
+/*****************************************************************************/
+
+static void stli_waituntilsent(struct tty_struct *tty, int timeout)
+{
+       struct stliport *portp;
+       unsigned long tend;
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+
+       if (timeout == 0)
+               timeout = HZ;
+       tend = jiffies + timeout;
+
+       while (test_bit(ST_TXBUSY, &portp->state)) {
+               if (signal_pending(current))
+                       break;
+               msleep_interruptible(20);
+               if (time_after_eq(jiffies, tend))
+                       break;
+       }
+}
+
+/*****************************************************************************/
+
+static void stli_sendxchar(struct tty_struct *tty, char ch)
+{
+       struct stlibrd  *brdp;
+       struct stliport *portp;
+       asyctrl_t       actrl;
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+       if (portp->brdnr >= stli_nrbrds)
+               return;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return;
+
+       memset(&actrl, 0, sizeof(asyctrl_t));
+       if (ch == STOP_CHAR(tty)) {
+               actrl.rxctrl = CT_STOPFLOW;
+       } else if (ch == START_CHAR(tty)) {
+               actrl.rxctrl = CT_STARTFLOW;
+       } else {
+               actrl.txctrl = CT_SENDCHR;
+               actrl.tximdch = ch;
+       }
+       stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0);
+}
+
+static void stli_portinfo(struct seq_file *m, struct stlibrd *brdp, struct stliport *portp, int portnr)
+{
+       char *uart;
+       int rc;
+
+       rc = stli_portcmdstats(NULL, portp);
+
+       uart = "UNKNOWN";
+       if (test_bit(BST_STARTED, &brdp->state)) {
+               switch (stli_comstats.hwid) {
+               case 0: uart = "2681"; break;
+               case 1: uart = "SC26198"; break;
+               default:uart = "CD1400"; break;
+               }
+       }
+       seq_printf(m, "%d: uart:%s ", portnr, uart);
+
+       if (test_bit(BST_STARTED, &brdp->state) && rc >= 0) {
+               char sep;
+
+               seq_printf(m, "tx:%d rx:%d", (int) stli_comstats.txtotal,
+                       (int) stli_comstats.rxtotal);
+
+               if (stli_comstats.rxframing)
+                       seq_printf(m, " fe:%d",
+                               (int) stli_comstats.rxframing);
+               if (stli_comstats.rxparity)
+                       seq_printf(m, " pe:%d",
+                               (int) stli_comstats.rxparity);
+               if (stli_comstats.rxbreaks)
+                       seq_printf(m, " brk:%d",
+                               (int) stli_comstats.rxbreaks);
+               if (stli_comstats.rxoverrun)
+                       seq_printf(m, " oe:%d",
+                               (int) stli_comstats.rxoverrun);
+
+               sep = ' ';
+               if (stli_comstats.signals & TIOCM_RTS) {
+                       seq_printf(m, "%c%s", sep, "RTS");
+                       sep = '|';
+               }
+               if (stli_comstats.signals & TIOCM_CTS) {
+                       seq_printf(m, "%c%s", sep, "CTS");
+                       sep = '|';
+               }
+               if (stli_comstats.signals & TIOCM_DTR) {
+                       seq_printf(m, "%c%s", sep, "DTR");
+                       sep = '|';
+               }
+               if (stli_comstats.signals & TIOCM_CD) {
+                       seq_printf(m, "%c%s", sep, "DCD");
+                       sep = '|';
+               }
+               if (stli_comstats.signals & TIOCM_DSR) {
+                       seq_printf(m, "%c%s", sep, "DSR");
+                       sep = '|';
+               }
+       }
+       seq_putc(m, '\n');
+}
+
+/*****************************************************************************/
+
+/*
+ *     Port info, read from the /proc file system.
+ */
+
+static int stli_proc_show(struct seq_file *m, void *v)
+{
+       struct stlibrd *brdp;
+       struct stliport *portp;
+       unsigned int brdnr, portnr, totalport;
+
+       totalport = 0;
+
+       seq_printf(m, "%s: version %s\n", stli_drvtitle, stli_drvversion);
+
+/*
+ *     We scan through for each board, panel and port. The offset is
+ *     calculated on the fly, and irrelevant ports are skipped.
+ */
+       for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) {
+               brdp = stli_brds[brdnr];
+               if (brdp == NULL)
+                       continue;
+               if (brdp->state == 0)
+                       continue;
+
+               totalport = brdnr * STL_MAXPORTS;
+               for (portnr = 0; (portnr < brdp->nrports); portnr++,
+                   totalport++) {
+                       portp = brdp->ports[portnr];
+                       if (portp == NULL)
+                               continue;
+                       stli_portinfo(m, brdp, portp, totalport);
+               }
+       }
+       return 0;
+}
+
+static int stli_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, stli_proc_show, NULL);
+}
+
+static const struct file_operations stli_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = stli_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/*****************************************************************************/
+
+/*
+ *     Generic send command routine. This will send a message to the slave,
+ *     of the specified type with the specified argument. Must be very
+ *     careful of data that will be copied out from shared memory -
+ *     containing command results. The command completion is all done from
+ *     a poll routine that does not have user context. Therefore you cannot
+ *     copy back directly into user space, or to the kernel stack of a
+ *     process. This routine does not sleep, so can be called from anywhere.
+ *
+ *     The caller must hold the brd_lock (see also stli_sendcmd the usual
+ *     entry point)
+ */
+
+static void __stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback)
+{
+       cdkhdr_t __iomem *hdrp;
+       cdkctrl_t __iomem *cp;
+       unsigned char __iomem *bits;
+
+       if (test_bit(ST_CMDING, &portp->state)) {
+               printk(KERN_ERR "istallion: command already busy, cmd=%x!\n",
+                               (int) cmd);
+               return;
+       }
+
+       EBRDENABLE(brdp);
+       cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
+       if (size > 0) {
+               memcpy_toio((void __iomem *) &(cp->args[0]), arg, size);
+               if (copyback) {
+                       portp->argp = arg;
+                       portp->argsize = size;
+               }
+       }
+       writel(0, &cp->status);
+       writel(cmd, &cp->cmd);
+       hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+       bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
+               portp->portidx;
+       writeb(readb(bits) | portp->portbit, bits);
+       set_bit(ST_CMDING, &portp->state);
+       EBRDDISABLE(brdp);
+}
+
+static void stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback)
+{
+       unsigned long           flags;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       __stli_sendcmd(brdp, portp, cmd, arg, size, copyback);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Read data from shared memory. This assumes that the shared memory
+ *     is enabled and that interrupts are off. Basically we just empty out
+ *     the shared memory buffer into the tty buffer. Must be careful to
+ *     handle the case where we fill up the tty buffer, but still have
+ *     more chars to unload.
+ */
+
+static void stli_read(struct stlibrd *brdp, struct stliport *portp)
+{
+       cdkasyrq_t __iomem *rp;
+       char __iomem *shbuf;
+       struct tty_struct       *tty;
+       unsigned int head, tail, size;
+       unsigned int len, stlen;
+
+       if (test_bit(ST_RXSTOP, &portp->state))
+               return;
+       tty = tty_port_tty_get(&portp->port);
+       if (tty == NULL)
+               return;
+
+       rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->rxq;
+       head = (unsigned int) readw(&rp->head);
+       if (head != ((unsigned int) readw(&rp->head)))
+               head = (unsigned int) readw(&rp->head);
+       tail = (unsigned int) readw(&rp->tail);
+       size = portp->rxsize;
+       if (head >= tail) {
+               len = head - tail;
+               stlen = len;
+       } else {
+               len = size - (tail - head);
+               stlen = size - tail;
+       }
+
+       len = tty_buffer_request_room(tty, len);
+
+       shbuf = (char __iomem *) EBRDGETMEMPTR(brdp, portp->rxoffset);
+
+       while (len > 0) {
+               unsigned char *cptr;
+
+               stlen = min(len, stlen);
+               tty_prepare_flip_string(tty, &cptr, stlen);
+               memcpy_fromio(cptr, shbuf + tail, stlen);
+               len -= stlen;
+               tail += stlen;
+               if (tail >= size) {
+                       tail = 0;
+                       stlen = head;
+               }
+       }
+       rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->rxq;
+       writew(tail, &rp->tail);
+
+       if (head != tail)
+               set_bit(ST_RXING, &portp->state);
+
+       tty_schedule_flip(tty);
+       tty_kref_put(tty);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Set up and carry out any delayed commands. There is only a small set
+ *     of slave commands that can be done "off-level". So it is not too
+ *     difficult to deal with them here.
+ */
+
+static void stli_dodelaycmd(struct stliport *portp, cdkctrl_t __iomem *cp)
+{
+       int cmd;
+
+       if (test_bit(ST_DOSIGS, &portp->state)) {
+               if (test_bit(ST_DOFLUSHTX, &portp->state) &&
+                   test_bit(ST_DOFLUSHRX, &portp->state))
+                       cmd = A_SETSIGNALSF;
+               else if (test_bit(ST_DOFLUSHTX, &portp->state))
+                       cmd = A_SETSIGNALSFTX;
+               else if (test_bit(ST_DOFLUSHRX, &portp->state))
+                       cmd = A_SETSIGNALSFRX;
+               else
+                       cmd = A_SETSIGNALS;
+               clear_bit(ST_DOFLUSHTX, &portp->state);
+               clear_bit(ST_DOFLUSHRX, &portp->state);
+               clear_bit(ST_DOSIGS, &portp->state);
+               memcpy_toio((void __iomem *) &(cp->args[0]), (void *) &portp->asig,
+                       sizeof(asysigs_t));
+               writel(0, &cp->status);
+               writel(cmd, &cp->cmd);
+               set_bit(ST_CMDING, &portp->state);
+       } else if (test_bit(ST_DOFLUSHTX, &portp->state) ||
+           test_bit(ST_DOFLUSHRX, &portp->state)) {
+               cmd = ((test_bit(ST_DOFLUSHTX, &portp->state)) ? FLUSHTX : 0);
+               cmd |= ((test_bit(ST_DOFLUSHRX, &portp->state)) ? FLUSHRX : 0);
+               clear_bit(ST_DOFLUSHTX, &portp->state);
+               clear_bit(ST_DOFLUSHRX, &portp->state);
+               memcpy_toio((void __iomem *) &(cp->args[0]), (void *) &cmd, sizeof(int));
+               writel(0, &cp->status);
+               writel(A_FLUSH, &cp->cmd);
+               set_bit(ST_CMDING, &portp->state);
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Host command service checking. This handles commands or messages
+ *     coming from the slave to the host. Must have board shared memory
+ *     enabled and interrupts off when called. Notice that by servicing the
+ *     read data last we don't need to change the shared memory pointer
+ *     during processing (which is a slow IO operation).
+ *     Return value indicates if this port is still awaiting actions from
+ *     the slave (like open, command, or even TX data being sent). If 0
+ *     then port is still busy, otherwise no longer busy.
+ */
+
+static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp)
+{
+       cdkasy_t __iomem *ap;
+       cdkctrl_t __iomem *cp;
+       struct tty_struct *tty;
+       asynotify_t nt;
+       unsigned long oldsigs;
+       int rc, donerx;
+
+       ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
+       cp = &ap->ctrl;
+
+/*
+ *     Check if we are waiting for an open completion message.
+ */
+       if (test_bit(ST_OPENING, &portp->state)) {
+               rc = readl(&cp->openarg);
+               if (readb(&cp->open) == 0 && rc != 0) {
+                       if (rc > 0)
+                               rc--;
+                       writel(0, &cp->openarg);
+                       portp->rc = rc;
+                       clear_bit(ST_OPENING, &portp->state);
+                       wake_up_interruptible(&portp->raw_wait);
+               }
+       }
+
+/*
+ *     Check if we are waiting for a close completion message.
+ */
+       if (test_bit(ST_CLOSING, &portp->state)) {
+               rc = (int) readl(&cp->closearg);
+               if (readb(&cp->close) == 0 && rc != 0) {
+                       if (rc > 0)
+                               rc--;
+                       writel(0, &cp->closearg);
+                       portp->rc = rc;
+                       clear_bit(ST_CLOSING, &portp->state);
+                       wake_up_interruptible(&portp->raw_wait);
+               }
+       }
+
+/*
+ *     Check if we are waiting for a command completion message. We may
+ *     need to copy out the command results associated with this command.
+ */
+       if (test_bit(ST_CMDING, &portp->state)) {
+               rc = readl(&cp->status);
+               if (readl(&cp->cmd) == 0 && rc != 0) {
+                       if (rc > 0)
+                               rc--;
+                       if (portp->argp != NULL) {
+                               memcpy_fromio(portp->argp, (void __iomem *) &(cp->args[0]),
+                                       portp->argsize);
+                               portp->argp = NULL;
+                       }
+                       writel(0, &cp->status);
+                       portp->rc = rc;
+                       clear_bit(ST_CMDING, &portp->state);
+                       stli_dodelaycmd(portp, cp);
+                       wake_up_interruptible(&portp->raw_wait);
+               }
+       }
+
+/*
+ *     Check for any notification messages ready. This includes lots of
+ *     different types of events - RX chars ready, RX break received,
+ *     TX data low or empty in the slave, modem signals changed state.
+ */
+       donerx = 0;
+
+       if (ap->notify) {
+               nt = ap->changed;
+               ap->notify = 0;
+               tty = tty_port_tty_get(&portp->port);
+
+               if (nt.signal & SG_DCD) {
+                       oldsigs = portp->sigs;
+                       portp->sigs = stli_mktiocm(nt.sigvalue);
+                       clear_bit(ST_GETSIGS, &portp->state);
+                       if ((portp->sigs & TIOCM_CD) &&
+                           ((oldsigs & TIOCM_CD) == 0))
+                               wake_up_interruptible(&portp->port.open_wait);
+                       if ((oldsigs & TIOCM_CD) &&
+                           ((portp->sigs & TIOCM_CD) == 0)) {
+                               if (portp->port.flags & ASYNC_CHECK_CD) {
+                                       if (tty)
+                                               tty_hangup(tty);
+                               }
+                       }
+               }
+
+               if (nt.data & DT_TXEMPTY)
+                       clear_bit(ST_TXBUSY, &portp->state);
+               if (nt.data & (DT_TXEMPTY | DT_TXLOW)) {
+                       if (tty != NULL) {
+                               tty_wakeup(tty);
+                               EBRDENABLE(brdp);
+                       }
+               }
+
+               if ((nt.data & DT_RXBREAK) && (portp->rxmarkmsk & BRKINT)) {
+                       if (tty != NULL) {
+                               tty_insert_flip_char(tty, 0, TTY_BREAK);
+                               if (portp->port.flags & ASYNC_SAK) {
+                                       do_SAK(tty);
+                                       EBRDENABLE(brdp);
+                               }
+                               tty_schedule_flip(tty);
+                       }
+               }
+               tty_kref_put(tty);
+
+               if (nt.data & DT_RXBUSY) {
+                       donerx++;
+                       stli_read(brdp, portp);
+               }
+       }
+
+/*
+ *     It might seem odd that we are checking for more RX chars here.
+ *     But, we need to handle the case where the tty buffer was previously
+ *     filled, but we had more characters to pass up. The slave will not
+ *     send any more RX notify messages until the RX buffer has been emptied.
+ *     But it will leave the service bits on (since the buffer is not empty).
+ *     So from here we can try to process more RX chars.
+ */
+       if ((!donerx) && test_bit(ST_RXING, &portp->state)) {
+               clear_bit(ST_RXING, &portp->state);
+               stli_read(brdp, portp);
+       }
+
+       return((test_bit(ST_OPENING, &portp->state) ||
+               test_bit(ST_CLOSING, &portp->state) ||
+               test_bit(ST_CMDING, &portp->state) ||
+               test_bit(ST_TXBUSY, &portp->state) ||
+               test_bit(ST_RXING, &portp->state)) ? 0 : 1);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Service all ports on a particular board. Assumes that the boards
+ *     shared memory is enabled, and that the page pointer is pointed
+ *     at the cdk header structure.
+ */
+
+static void stli_brdpoll(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp)
+{
+       struct stliport *portp;
+       unsigned char hostbits[(STL_MAXCHANS / 8) + 1];
+       unsigned char slavebits[(STL_MAXCHANS / 8) + 1];
+       unsigned char __iomem *slavep;
+       int bitpos, bitat, bitsize;
+       int channr, nrdevs, slavebitchange;
+
+       bitsize = brdp->bitsize;
+       nrdevs = brdp->nrdevs;
+
+/*
+ *     Check if slave wants any service. Basically we try to do as
+ *     little work as possible here. There are 2 levels of service
+ *     bits. So if there is nothing to do we bail early. We check
+ *     8 service bits at a time in the inner loop, so we can bypass
+ *     the lot if none of them want service.
+ */
+       memcpy_fromio(&hostbits[0], (((unsigned char __iomem *) hdrp) + brdp->hostoffset),
+               bitsize);
+
+       memset(&slavebits[0], 0, bitsize);
+       slavebitchange = 0;
+
+       for (bitpos = 0; (bitpos < bitsize); bitpos++) {
+               if (hostbits[bitpos] == 0)
+                       continue;
+               channr = bitpos * 8;
+               for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) {
+                       if (hostbits[bitpos] & bitat) {
+                               portp = brdp->ports[(channr - 1)];
+                               if (stli_hostcmd(brdp, portp)) {
+                                       slavebitchange++;
+                                       slavebits[bitpos] |= bitat;
+                               }
+                       }
+               }
+       }
+
+/*
+ *     If any of the ports are no longer busy then update them in the
+ *     slave request bits. We need to do this after, since a host port
+ *     service may initiate more slave requests.
+ */
+       if (slavebitchange) {
+               hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+               slavep = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset;
+               for (bitpos = 0; (bitpos < bitsize); bitpos++) {
+                       if (readb(slavebits + bitpos))
+                               writeb(readb(slavep + bitpos) & ~slavebits[bitpos], slavebits + bitpos);
+               }
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Driver poll routine. This routine polls the boards in use and passes
+ *     messages back up to host when necessary. This is actually very
+ *     CPU efficient, since we will always have the kernel poll clock, it
+ *     adds only a few cycles when idle (since board service can be
+ *     determined very easily), but when loaded generates no interrupts
+ *     (with their expensive associated context change).
+ */
+
+static void stli_poll(unsigned long arg)
+{
+       cdkhdr_t __iomem *hdrp;
+       struct stlibrd *brdp;
+       unsigned int brdnr;
+
+       mod_timer(&stli_timerlist, STLI_TIMEOUT);
+
+/*
+ *     Check each board and do any servicing required.
+ */
+       for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) {
+               brdp = stli_brds[brdnr];
+               if (brdp == NULL)
+                       continue;
+               if (!test_bit(BST_STARTED, &brdp->state))
+                       continue;
+
+               spin_lock(&brd_lock);
+               EBRDENABLE(brdp);
+               hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+               if (readb(&hdrp->hostreq))
+                       stli_brdpoll(brdp, hdrp);
+               EBRDDISABLE(brdp);
+               spin_unlock(&brd_lock);
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Translate the termios settings into the port setting structure of
+ *     the slave.
+ */
+
+static void stli_mkasyport(struct tty_struct *tty, struct stliport *portp,
+                               asyport_t *pp, struct ktermios *tiosp)
+{
+       memset(pp, 0, sizeof(asyport_t));
+
+/*
+ *     Start of by setting the baud, char size, parity and stop bit info.
+ */
+       pp->baudout = tty_get_baud_rate(tty);
+       if ((tiosp->c_cflag & CBAUD) == B38400) {
+               if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                       pp->baudout = 57600;
+               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                       pp->baudout = 115200;
+               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                       pp->baudout = 230400;
+               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                       pp->baudout = 460800;
+               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+                       pp->baudout = (portp->baud_base / portp->custom_divisor);
+       }
+       if (pp->baudout > STL_MAXBAUD)
+               pp->baudout = STL_MAXBAUD;
+       pp->baudin = pp->baudout;
+
+       switch (tiosp->c_cflag & CSIZE) {
+       case CS5:
+               pp->csize = 5;
+               break;
+       case CS6:
+               pp->csize = 6;
+               break;
+       case CS7:
+               pp->csize = 7;
+               break;
+       default:
+               pp->csize = 8;
+               break;
+       }
+
+       if (tiosp->c_cflag & CSTOPB)
+               pp->stopbs = PT_STOP2;
+       else
+               pp->stopbs = PT_STOP1;
+
+       if (tiosp->c_cflag & PARENB) {
+               if (tiosp->c_cflag & PARODD)
+                       pp->parity = PT_ODDPARITY;
+               else
+                       pp->parity = PT_EVENPARITY;
+       } else {
+               pp->parity = PT_NOPARITY;
+       }
+
+/*
+ *     Set up any flow control options enabled.
+ */
+       if (tiosp->c_iflag & IXON) {
+               pp->flow |= F_IXON;
+               if (tiosp->c_iflag & IXANY)
+                       pp->flow |= F_IXANY;
+       }
+       if (tiosp->c_cflag & CRTSCTS)
+               pp->flow |= (F_RTSFLOW | F_CTSFLOW);
+
+       pp->startin = tiosp->c_cc[VSTART];
+       pp->stopin = tiosp->c_cc[VSTOP];
+       pp->startout = tiosp->c_cc[VSTART];
+       pp->stopout = tiosp->c_cc[VSTOP];
+
+/*
+ *     Set up the RX char marking mask with those RX error types we must
+ *     catch. We can get the slave to help us out a little here, it will
+ *     ignore parity errors and breaks for us, and mark parity errors in
+ *     the data stream.
+ */
+       if (tiosp->c_iflag & IGNPAR)
+               pp->iflag |= FI_IGNRXERRS;
+       if (tiosp->c_iflag & IGNBRK)
+               pp->iflag |= FI_IGNBREAK;
+
+       portp->rxmarkmsk = 0;
+       if (tiosp->c_iflag & (INPCK | PARMRK))
+               pp->iflag |= FI_1MARKRXERRS;
+       if (tiosp->c_iflag & BRKINT)
+               portp->rxmarkmsk |= BRKINT;
+
+/*
+ *     Set up clocal processing as required.
+ */
+       if (tiosp->c_cflag & CLOCAL)
+               portp->port.flags &= ~ASYNC_CHECK_CD;
+       else
+               portp->port.flags |= ASYNC_CHECK_CD;
+
+/*
+ *     Transfer any persistent flags into the asyport structure.
+ */
+       pp->pflag = (portp->pflag & 0xffff);
+       pp->vmin = (portp->pflag & P_RXIMIN) ? 1 : 0;
+       pp->vtime = (portp->pflag & P_RXITIME) ? 1 : 0;
+       pp->cc[1] = (portp->pflag & P_RXTHOLD) ? 1 : 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Construct a slave signals structure for setting the DTR and RTS
+ *     signals as specified.
+ */
+
+static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts)
+{
+       memset(sp, 0, sizeof(asysigs_t));
+       if (dtr >= 0) {
+               sp->signal |= SG_DTR;
+               sp->sigvalue |= ((dtr > 0) ? SG_DTR : 0);
+       }
+       if (rts >= 0) {
+               sp->signal |= SG_RTS;
+               sp->sigvalue |= ((rts > 0) ? SG_RTS : 0);
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Convert the signals returned from the slave into a local TIOCM type
+ *     signals value. We keep them locally in TIOCM format.
+ */
+
+static long stli_mktiocm(unsigned long sigvalue)
+{
+       long    tiocm = 0;
+       tiocm |= ((sigvalue & SG_DCD) ? TIOCM_CD : 0);
+       tiocm |= ((sigvalue & SG_CTS) ? TIOCM_CTS : 0);
+       tiocm |= ((sigvalue & SG_RI) ? TIOCM_RI : 0);
+       tiocm |= ((sigvalue & SG_DSR) ? TIOCM_DSR : 0);
+       tiocm |= ((sigvalue & SG_DTR) ? TIOCM_DTR : 0);
+       tiocm |= ((sigvalue & SG_RTS) ? TIOCM_RTS : 0);
+       return(tiocm);
+}
+
+/*****************************************************************************/
+
+/*
+ *     All panels and ports actually attached have been worked out. All
+ *     we need to do here is set up the appropriate per port data structures.
+ */
+
+static int stli_initports(struct stlibrd *brdp)
+{
+       struct stliport *portp;
+       unsigned int i, panelnr, panelport;
+
+       for (i = 0, panelnr = 0, panelport = 0; (i < brdp->nrports); i++) {
+               portp = kzalloc(sizeof(struct stliport), GFP_KERNEL);
+               if (!portp) {
+                       printk(KERN_WARNING "istallion: failed to allocate port structure\n");
+                       continue;
+               }
+               tty_port_init(&portp->port);
+               portp->port.ops = &stli_port_ops;
+               portp->magic = STLI_PORTMAGIC;
+               portp->portnr = i;
+               portp->brdnr = brdp->brdnr;
+               portp->panelnr = panelnr;
+               portp->baud_base = STL_BAUDBASE;
+               portp->port.close_delay = STL_CLOSEDELAY;
+               portp->closing_wait = 30 * HZ;
+               init_waitqueue_head(&portp->port.open_wait);
+               init_waitqueue_head(&portp->port.close_wait);
+               init_waitqueue_head(&portp->raw_wait);
+               panelport++;
+               if (panelport >= brdp->panels[panelnr]) {
+                       panelport = 0;
+                       panelnr++;
+               }
+               brdp->ports[i] = portp;
+       }
+
+       return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     All the following routines are board specific hardware operations.
+ */
+
+static void stli_ecpinit(struct stlibrd *brdp)
+{
+       unsigned long   memconf;
+
+       outb(ECP_ATSTOP, (brdp->iobase + ECP_ATCONFR));
+       udelay(10);
+       outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
+       udelay(100);
+
+       memconf = (brdp->memaddr & ECP_ATADDRMASK) >> ECP_ATADDRSHFT;
+       outb(memconf, (brdp->iobase + ECP_ATMEMAR));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpenable(struct stlibrd *brdp)
+{      
+       outb(ECP_ATENABLE, (brdp->iobase + ECP_ATCONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpdisable(struct stlibrd *brdp)
+{      
+       outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
+}
+
+/*****************************************************************************/
+
+static void __iomem *stli_ecpgetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
+{      
+       void __iomem *ptr;
+       unsigned char val;
+
+       if (offset > brdp->memsize) {
+               printk(KERN_ERR "istallion: shared memory pointer=%x out of "
+                               "range at line=%d(%d), brd=%d\n",
+                       (int) offset, line, __LINE__, brdp->brdnr);
+               ptr = NULL;
+               val = 0;
+       } else {
+               ptr = brdp->membase + (offset % ECP_ATPAGESIZE);
+               val = (unsigned char) (offset / ECP_ATPAGESIZE);
+       }
+       outb(val, (brdp->iobase + ECP_ATMEMPR));
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_ecpreset(struct stlibrd *brdp)
+{      
+       outb(ECP_ATSTOP, (brdp->iobase + ECP_ATCONFR));
+       udelay(10);
+       outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
+       udelay(500);
+}
+
+/*****************************************************************************/
+
+static void stli_ecpintr(struct stlibrd *brdp)
+{      
+       outb(0x1, brdp->iobase);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The following set of functions act on ECP EISA boards.
+ */
+
+static void stli_ecpeiinit(struct stlibrd *brdp)
+{
+       unsigned long   memconf;
+
+       outb(0x1, (brdp->iobase + ECP_EIBRDENAB));
+       outb(ECP_EISTOP, (brdp->iobase + ECP_EICONFR));
+       udelay(10);
+       outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
+       udelay(500);
+
+       memconf = (brdp->memaddr & ECP_EIADDRMASKL) >> ECP_EIADDRSHFTL;
+       outb(memconf, (brdp->iobase + ECP_EIMEMARL));
+       memconf = (brdp->memaddr & ECP_EIADDRMASKH) >> ECP_EIADDRSHFTH;
+       outb(memconf, (brdp->iobase + ECP_EIMEMARH));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpeienable(struct stlibrd *brdp)
+{      
+       outb(ECP_EIENABLE, (brdp->iobase + ECP_EICONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpeidisable(struct stlibrd *brdp)
+{      
+       outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
+}
+
+/*****************************************************************************/
+
+static void __iomem *stli_ecpeigetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
+{      
+       void __iomem *ptr;
+       unsigned char   val;
+
+       if (offset > brdp->memsize) {
+               printk(KERN_ERR "istallion: shared memory pointer=%x out of "
+                               "range at line=%d(%d), brd=%d\n",
+                       (int) offset, line, __LINE__, brdp->brdnr);
+               ptr = NULL;
+               val = 0;
+       } else {
+               ptr = brdp->membase + (offset % ECP_EIPAGESIZE);
+               if (offset < ECP_EIPAGESIZE)
+                       val = ECP_EIENABLE;
+               else
+                       val = ECP_EIENABLE | 0x40;
+       }
+       outb(val, (brdp->iobase + ECP_EICONFR));
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_ecpeireset(struct stlibrd *brdp)
+{      
+       outb(ECP_EISTOP, (brdp->iobase + ECP_EICONFR));
+       udelay(10);
+       outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
+       udelay(500);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The following set of functions act on ECP MCA boards.
+ */
+
+static void stli_ecpmcenable(struct stlibrd *brdp)
+{      
+       outb(ECP_MCENABLE, (brdp->iobase + ECP_MCCONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_ecpmcdisable(struct stlibrd *brdp)
+{      
+       outb(ECP_MCDISABLE, (brdp->iobase + ECP_MCCONFR));
+}
+
+/*****************************************************************************/
+
+static void __iomem *stli_ecpmcgetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
+{      
+       void __iomem *ptr;
+       unsigned char val;
+
+       if (offset > brdp->memsize) {
+               printk(KERN_ERR "istallion: shared memory pointer=%x out of "
+                               "range at line=%d(%d), brd=%d\n",
+                       (int) offset, line, __LINE__, brdp->brdnr);
+               ptr = NULL;
+               val = 0;
+       } else {
+               ptr = brdp->membase + (offset % ECP_MCPAGESIZE);
+               val = ((unsigned char) (offset / ECP_MCPAGESIZE)) | ECP_MCENABLE;
+       }
+       outb(val, (brdp->iobase + ECP_MCCONFR));
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_ecpmcreset(struct stlibrd *brdp)
+{      
+       outb(ECP_MCSTOP, (brdp->iobase + ECP_MCCONFR));
+       udelay(10);
+       outb(ECP_MCDISABLE, (brdp->iobase + ECP_MCCONFR));
+       udelay(500);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The following set of functions act on ECP PCI boards.
+ */
+
+static void stli_ecppciinit(struct stlibrd *brdp)
+{
+       outb(ECP_PCISTOP, (brdp->iobase + ECP_PCICONFR));
+       udelay(10);
+       outb(0, (brdp->iobase + ECP_PCICONFR));
+       udelay(500);
+}
+
+/*****************************************************************************/
+
+static void __iomem *stli_ecppcigetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
+{      
+       void __iomem *ptr;
+       unsigned char   val;
+
+       if (offset > brdp->memsize) {
+               printk(KERN_ERR "istallion: shared memory pointer=%x out of "
+                               "range at line=%d(%d), board=%d\n",
+                               (int) offset, line, __LINE__, brdp->brdnr);
+               ptr = NULL;
+               val = 0;
+       } else {
+               ptr = brdp->membase + (offset % ECP_PCIPAGESIZE);
+               val = (offset / ECP_PCIPAGESIZE) << 1;
+       }
+       outb(val, (brdp->iobase + ECP_PCICONFR));
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_ecppcireset(struct stlibrd *brdp)
+{      
+       outb(ECP_PCISTOP, (brdp->iobase + ECP_PCICONFR));
+       udelay(10);
+       outb(0, (brdp->iobase + ECP_PCICONFR));
+       udelay(500);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The following routines act on ONboards.
+ */
+
+static void stli_onbinit(struct stlibrd *brdp)
+{
+       unsigned long   memconf;
+
+       outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR));
+       udelay(10);
+       outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
+       mdelay(1000);
+
+       memconf = (brdp->memaddr & ONB_ATADDRMASK) >> ONB_ATADDRSHFT;
+       outb(memconf, (brdp->iobase + ONB_ATMEMAR));
+       outb(0x1, brdp->iobase);
+       mdelay(1);
+}
+
+/*****************************************************************************/
+
+static void stli_onbenable(struct stlibrd *brdp)
+{      
+       outb((brdp->enabval | ONB_ATENABLE), (brdp->iobase + ONB_ATCONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_onbdisable(struct stlibrd *brdp)
+{      
+       outb((brdp->enabval | ONB_ATDISABLE), (brdp->iobase + ONB_ATCONFR));
+}
+
+/*****************************************************************************/
+
+static void __iomem *stli_onbgetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
+{      
+       void __iomem *ptr;
+
+       if (offset > brdp->memsize) {
+               printk(KERN_ERR "istallion: shared memory pointer=%x out of "
+                               "range at line=%d(%d), brd=%d\n",
+                               (int) offset, line, __LINE__, brdp->brdnr);
+               ptr = NULL;
+       } else {
+               ptr = brdp->membase + (offset % ONB_ATPAGESIZE);
+       }
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_onbreset(struct stlibrd *brdp)
+{      
+       outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR));
+       udelay(10);
+       outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
+       mdelay(1000);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The following routines act on ONboard EISA.
+ */
+
+static void stli_onbeinit(struct stlibrd *brdp)
+{
+       unsigned long   memconf;
+
+       outb(0x1, (brdp->iobase + ONB_EIBRDENAB));
+       outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
+       udelay(10);
+       outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
+       mdelay(1000);
+
+       memconf = (brdp->memaddr & ONB_EIADDRMASKL) >> ONB_EIADDRSHFTL;
+       outb(memconf, (brdp->iobase + ONB_EIMEMARL));
+       memconf = (brdp->memaddr & ONB_EIADDRMASKH) >> ONB_EIADDRSHFTH;
+       outb(memconf, (brdp->iobase + ONB_EIMEMARH));
+       outb(0x1, brdp->iobase);
+       mdelay(1);
+}
+
+/*****************************************************************************/
+
+static void stli_onbeenable(struct stlibrd *brdp)
+{      
+       outb(ONB_EIENABLE, (brdp->iobase + ONB_EICONFR));
+}
+
+/*****************************************************************************/
+
+static void stli_onbedisable(struct stlibrd *brdp)
+{      
+       outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
+}
+
+/*****************************************************************************/
+
+static void __iomem *stli_onbegetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
+{      
+       void __iomem *ptr;
+       unsigned char val;
+
+       if (offset > brdp->memsize) {
+               printk(KERN_ERR "istallion: shared memory pointer=%x out of "
+                               "range at line=%d(%d), brd=%d\n",
+                       (int) offset, line, __LINE__, brdp->brdnr);
+               ptr = NULL;
+               val = 0;
+       } else {
+               ptr = brdp->membase + (offset % ONB_EIPAGESIZE);
+               if (offset < ONB_EIPAGESIZE)
+                       val = ONB_EIENABLE;
+               else
+                       val = ONB_EIENABLE | 0x40;
+       }
+       outb(val, (brdp->iobase + ONB_EICONFR));
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_onbereset(struct stlibrd *brdp)
+{      
+       outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
+       udelay(10);
+       outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
+       mdelay(1000);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The following routines act on Brumby boards.
+ */
+
+static void stli_bbyinit(struct stlibrd *brdp)
+{
+       outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR));
+       udelay(10);
+       outb(0, (brdp->iobase + BBY_ATCONFR));
+       mdelay(1000);
+       outb(0x1, brdp->iobase);
+       mdelay(1);
+}
+
+/*****************************************************************************/
+
+static void __iomem *stli_bbygetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
+{      
+       void __iomem *ptr;
+       unsigned char val;
+
+       BUG_ON(offset > brdp->memsize);
+
+       ptr = brdp->membase + (offset % BBY_PAGESIZE);
+       val = (unsigned char) (offset / BBY_PAGESIZE);
+       outb(val, (brdp->iobase + BBY_ATCONFR));
+       return(ptr);
+}
+
+/*****************************************************************************/
+
+static void stli_bbyreset(struct stlibrd *brdp)
+{      
+       outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR));
+       udelay(10);
+       outb(0, (brdp->iobase + BBY_ATCONFR));
+       mdelay(1000);
+}
+
+/*****************************************************************************/
+
+/*
+ *     The following routines act on original old Stallion boards.
+ */
+
+static void stli_stalinit(struct stlibrd *brdp)
+{
+       outb(0x1, brdp->iobase);
+       mdelay(1000);
+}
+
+/*****************************************************************************/
+
+static void __iomem *stli_stalgetmemptr(struct stlibrd *brdp, unsigned long offset, int line)
+{      
+       BUG_ON(offset > brdp->memsize);
+       return brdp->membase + (offset % STAL_PAGESIZE);
+}
+
+/*****************************************************************************/
+
+static void stli_stalreset(struct stlibrd *brdp)
+{      
+       u32 __iomem *vecp;
+
+       vecp = (u32 __iomem *) (brdp->membase + 0x30);
+       writel(0xffff0000, vecp);
+       outb(0, brdp->iobase);
+       mdelay(1000);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Try to find an ECP board and initialize it. This handles only ECP
+ *     board types.
+ */
+
+static int stli_initecp(struct stlibrd *brdp)
+{
+       cdkecpsig_t sig;
+       cdkecpsig_t __iomem *sigsp;
+       unsigned int status, nxtid;
+       char *name;
+       int retval, panelnr, nrports;
+
+       if ((brdp->iobase == 0) || (brdp->memaddr == 0)) {
+               retval = -ENODEV;
+               goto err;
+       }
+
+       brdp->iosize = ECP_IOSIZE;
+
+       if (!request_region(brdp->iobase, brdp->iosize, "istallion")) {
+               retval = -EIO;
+               goto err;
+       }
+
+/*
+ *     Based on the specific board type setup the common vars to access
+ *     and enable shared memory. Set all board specific information now
+ *     as well.
+ */
+       switch (brdp->brdtype) {
+       case BRD_ECP:
+               brdp->memsize = ECP_MEMSIZE;
+               brdp->pagesize = ECP_ATPAGESIZE;
+               brdp->init = stli_ecpinit;
+               brdp->enable = stli_ecpenable;
+               brdp->reenable = stli_ecpenable;
+               brdp->disable = stli_ecpdisable;
+               brdp->getmemptr = stli_ecpgetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_ecpreset;
+               name = "serial(EC8/64)";
+               break;
+
+       case BRD_ECPE:
+               brdp->memsize = ECP_MEMSIZE;
+               brdp->pagesize = ECP_EIPAGESIZE;
+               brdp->init = stli_ecpeiinit;
+               brdp->enable = stli_ecpeienable;
+               brdp->reenable = stli_ecpeienable;
+               brdp->disable = stli_ecpeidisable;
+               brdp->getmemptr = stli_ecpeigetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_ecpeireset;
+               name = "serial(EC8/64-EI)";
+               break;
+
+       case BRD_ECPMC:
+               brdp->memsize = ECP_MEMSIZE;
+               brdp->pagesize = ECP_MCPAGESIZE;
+               brdp->init = NULL;
+               brdp->enable = stli_ecpmcenable;
+               brdp->reenable = stli_ecpmcenable;
+               brdp->disable = stli_ecpmcdisable;
+               brdp->getmemptr = stli_ecpmcgetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_ecpmcreset;
+               name = "serial(EC8/64-MCA)";
+               break;
+
+       case BRD_ECPPCI:
+               brdp->memsize = ECP_PCIMEMSIZE;
+               brdp->pagesize = ECP_PCIPAGESIZE;
+               brdp->init = stli_ecppciinit;
+               brdp->enable = NULL;
+               brdp->reenable = NULL;
+               brdp->disable = NULL;
+               brdp->getmemptr = stli_ecppcigetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_ecppcireset;
+               name = "serial(EC/RA-PCI)";
+               break;
+
+       default:
+               retval = -EINVAL;
+               goto err_reg;
+       }
+
+/*
+ *     The per-board operations structure is all set up, so now let's go
+ *     and get the board operational. Firstly initialize board configuration
+ *     registers. Set the memory mapping info so we can get at the boards
+ *     shared memory.
+ */
+       EBRDINIT(brdp);
+
+       brdp->membase = ioremap_nocache(brdp->memaddr, brdp->memsize);
+       if (brdp->membase == NULL) {
+               retval = -ENOMEM;
+               goto err_reg;
+       }
+
+/*
+ *     Now that all specific code is set up, enable the shared memory and
+ *     look for the a signature area that will tell us exactly what board
+ *     this is, and what it is connected to it.
+ */
+       EBRDENABLE(brdp);
+       sigsp = (cdkecpsig_t __iomem *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
+       memcpy_fromio(&sig, sigsp, sizeof(cdkecpsig_t));
+       EBRDDISABLE(brdp);
+
+       if (sig.magic != cpu_to_le32(ECP_MAGIC)) {
+               retval = -ENODEV;
+               goto err_unmap;
+       }
+
+/*
+ *     Scan through the signature looking at the panels connected to the
+ *     board. Calculate the total number of ports as we go.
+ */
+       for (panelnr = 0, nxtid = 0; (panelnr < STL_MAXPANELS); panelnr++) {
+               status = sig.panelid[nxtid];
+               if ((status & ECH_PNLIDMASK) != nxtid)
+                       break;
+
+               brdp->panelids[panelnr] = status;
+               nrports = (status & ECH_PNL16PORT) ? 16 : 8;
+               if ((nrports == 16) && ((status & ECH_PNLXPID) == 0))
+                       nxtid++;
+               brdp->panels[panelnr] = nrports;
+               brdp->nrports += nrports;
+               nxtid++;
+               brdp->nrpanels++;
+       }
+
+
+       set_bit(BST_FOUND, &brdp->state);
+       return 0;
+err_unmap:
+       iounmap(brdp->membase);
+       brdp->membase = NULL;
+err_reg:
+       release_region(brdp->iobase, brdp->iosize);
+err:
+       return retval;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Try to find an ONboard, Brumby or Stallion board and initialize it.
+ *     This handles only these board types.
+ */
+
+static int stli_initonb(struct stlibrd *brdp)
+{
+       cdkonbsig_t sig;
+       cdkonbsig_t __iomem *sigsp;
+       char *name;
+       int i, retval;
+
+/*
+ *     Do a basic sanity check on the IO and memory addresses.
+ */
+       if (brdp->iobase == 0 || brdp->memaddr == 0) {
+               retval = -ENODEV;
+               goto err;
+       }
+
+       brdp->iosize = ONB_IOSIZE;
+       
+       if (!request_region(brdp->iobase, brdp->iosize, "istallion")) {
+               retval = -EIO;
+               goto err;
+       }
+
+/*
+ *     Based on the specific board type setup the common vars to access
+ *     and enable shared memory. Set all board specific information now
+ *     as well.
+ */
+       switch (brdp->brdtype) {
+       case BRD_ONBOARD:
+       case BRD_ONBOARD2:
+               brdp->memsize = ONB_MEMSIZE;
+               brdp->pagesize = ONB_ATPAGESIZE;
+               brdp->init = stli_onbinit;
+               brdp->enable = stli_onbenable;
+               brdp->reenable = stli_onbenable;
+               brdp->disable = stli_onbdisable;
+               brdp->getmemptr = stli_onbgetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_onbreset;
+               if (brdp->memaddr > 0x100000)
+                       brdp->enabval = ONB_MEMENABHI;
+               else
+                       brdp->enabval = ONB_MEMENABLO;
+               name = "serial(ONBoard)";
+               break;
+
+       case BRD_ONBOARDE:
+               brdp->memsize = ONB_EIMEMSIZE;
+               brdp->pagesize = ONB_EIPAGESIZE;
+               brdp->init = stli_onbeinit;
+               brdp->enable = stli_onbeenable;
+               brdp->reenable = stli_onbeenable;
+               brdp->disable = stli_onbedisable;
+               brdp->getmemptr = stli_onbegetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_onbereset;
+               name = "serial(ONBoard/E)";
+               break;
+
+       case BRD_BRUMBY4:
+               brdp->memsize = BBY_MEMSIZE;
+               brdp->pagesize = BBY_PAGESIZE;
+               brdp->init = stli_bbyinit;
+               brdp->enable = NULL;
+               brdp->reenable = NULL;
+               brdp->disable = NULL;
+               brdp->getmemptr = stli_bbygetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_bbyreset;
+               name = "serial(Brumby)";
+               break;
+
+       case BRD_STALLION:
+               brdp->memsize = STAL_MEMSIZE;
+               brdp->pagesize = STAL_PAGESIZE;
+               brdp->init = stli_stalinit;
+               brdp->enable = NULL;
+               brdp->reenable = NULL;
+               brdp->disable = NULL;
+               brdp->getmemptr = stli_stalgetmemptr;
+               brdp->intr = stli_ecpintr;
+               brdp->reset = stli_stalreset;
+               name = "serial(Stallion)";
+               break;
+
+       default:
+               retval = -EINVAL;
+               goto err_reg;
+       }
+
+/*
+ *     The per-board operations structure is all set up, so now let's go
+ *     and get the board operational. Firstly initialize board configuration
+ *     registers. Set the memory mapping info so we can get at the boards
+ *     shared memory.
+ */
+       EBRDINIT(brdp);
+
+       brdp->membase = ioremap_nocache(brdp->memaddr, brdp->memsize);
+       if (brdp->membase == NULL) {
+               retval = -ENOMEM;
+               goto err_reg;
+       }
+
+/*
+ *     Now that all specific code is set up, enable the shared memory and
+ *     look for the a signature area that will tell us exactly what board
+ *     this is, and how many ports.
+ */
+       EBRDENABLE(brdp);
+       sigsp = (cdkonbsig_t __iomem *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
+       memcpy_fromio(&sig, sigsp, sizeof(cdkonbsig_t));
+       EBRDDISABLE(brdp);
+
+       if (sig.magic0 != cpu_to_le16(ONB_MAGIC0) ||
+           sig.magic1 != cpu_to_le16(ONB_MAGIC1) ||
+           sig.magic2 != cpu_to_le16(ONB_MAGIC2) ||
+           sig.magic3 != cpu_to_le16(ONB_MAGIC3)) {
+               retval = -ENODEV;
+               goto err_unmap;
+       }
+
+/*
+ *     Scan through the signature alive mask and calculate how many ports
+ *     there are on this board.
+ */
+       brdp->nrpanels = 1;
+       if (sig.amask1) {
+               brdp->nrports = 32;
+       } else {
+               for (i = 0; (i < 16); i++) {
+                       if (((sig.amask0 << i) & 0x8000) == 0)
+                               break;
+               }
+               brdp->nrports = i;
+       }
+       brdp->panels[0] = brdp->nrports;
+
+
+       set_bit(BST_FOUND, &brdp->state);
+       return 0;
+err_unmap:
+       iounmap(brdp->membase);
+       brdp->membase = NULL;
+err_reg:
+       release_region(brdp->iobase, brdp->iosize);
+err:
+       return retval;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Start up a running board. This routine is only called after the
+ *     code has been down loaded to the board and is operational. It will
+ *     read in the memory map, and get the show on the road...
+ */
+
+static int stli_startbrd(struct stlibrd *brdp)
+{
+       cdkhdr_t __iomem *hdrp;
+       cdkmem_t __iomem *memp;
+       cdkasy_t __iomem *ap;
+       unsigned long flags;
+       unsigned int portnr, nrdevs, i;
+       struct stliport *portp;
+       int rc = 0;
+       u32 memoff;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       EBRDENABLE(brdp);
+       hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+       nrdevs = hdrp->nrdevs;
+
+#if 0
+       printk("%s(%d): CDK version %d.%d.%d --> "
+               "nrdevs=%d memp=%x hostp=%x slavep=%x\n",
+                __FILE__, __LINE__, readb(&hdrp->ver_release), readb(&hdrp->ver_modification),
+                readb(&hdrp->ver_fix), nrdevs, (int) readl(&hdrp->memp), readl(&hdrp->hostp),
+                readl(&hdrp->slavep));
+#endif
+
+       if (nrdevs < (brdp->nrports + 1)) {
+               printk(KERN_ERR "istallion: slave failed to allocate memory for "
+                               "all devices, devices=%d\n", nrdevs);
+               brdp->nrports = nrdevs - 1;
+       }
+       brdp->nrdevs = nrdevs;
+       brdp->hostoffset = hdrp->hostp - CDK_CDKADDR;
+       brdp->slaveoffset = hdrp->slavep - CDK_CDKADDR;
+       brdp->bitsize = (nrdevs + 7) / 8;
+       memoff = readl(&hdrp->memp);
+       if (memoff > brdp->memsize) {
+               printk(KERN_ERR "istallion: corrupted shared memory region?\n");
+               rc = -EIO;
+               goto stli_donestartup;
+       }
+       memp = (cdkmem_t __iomem *) EBRDGETMEMPTR(brdp, memoff);
+       if (readw(&memp->dtype) != TYP_ASYNCTRL) {
+               printk(KERN_ERR "istallion: no slave control device found\n");
+               goto stli_donestartup;
+       }
+       memp++;
+
+/*
+ *     Cycle through memory allocation of each port. We are guaranteed to
+ *     have all ports inside the first page of slave window, so no need to
+ *     change pages while reading memory map.
+ */
+       for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++, memp++) {
+               if (readw(&memp->dtype) != TYP_ASYNC)
+                       break;
+               portp = brdp->ports[portnr];
+               if (portp == NULL)
+                       break;
+               portp->devnr = i;
+               portp->addr = readl(&memp->offset);
+               portp->reqbit = (unsigned char) (0x1 << (i * 8 / nrdevs));
+               portp->portidx = (unsigned char) (i / 8);
+               portp->portbit = (unsigned char) (0x1 << (i % 8));
+       }
+
+       writeb(0xff, &hdrp->slavereq);
+
+/*
+ *     For each port setup a local copy of the RX and TX buffer offsets
+ *     and sizes. We do this separate from the above, because we need to
+ *     move the shared memory page...
+ */
+       for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++) {
+               portp = brdp->ports[portnr];
+               if (portp == NULL)
+                       break;
+               if (portp->addr == 0)
+                       break;
+               ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
+               if (ap != NULL) {
+                       portp->rxsize = readw(&ap->rxq.size);
+                       portp->txsize = readw(&ap->txq.size);
+                       portp->rxoffset = readl(&ap->rxq.offset);
+                       portp->txoffset = readl(&ap->txq.offset);
+               }
+       }
+
+stli_donestartup:
+       EBRDDISABLE(brdp);
+       spin_unlock_irqrestore(&brd_lock, flags);
+
+       if (rc == 0)
+               set_bit(BST_STARTED, &brdp->state);
+
+       if (! stli_timeron) {
+               stli_timeron++;
+               mod_timer(&stli_timerlist, STLI_TIMEOUT);
+       }
+
+       return rc;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Probe and initialize the specified board.
+ */
+
+static int __devinit stli_brdinit(struct stlibrd *brdp)
+{
+       int retval;
+
+       switch (brdp->brdtype) {
+       case BRD_ECP:
+       case BRD_ECPE:
+       case BRD_ECPMC:
+       case BRD_ECPPCI:
+               retval = stli_initecp(brdp);
+               break;
+       case BRD_ONBOARD:
+       case BRD_ONBOARDE:
+       case BRD_ONBOARD2:
+       case BRD_BRUMBY4:
+       case BRD_STALLION:
+               retval = stli_initonb(brdp);
+               break;
+       default:
+               printk(KERN_ERR "istallion: board=%d is unknown board "
+                               "type=%d\n", brdp->brdnr, brdp->brdtype);
+               retval = -ENODEV;
+       }
+
+       if (retval)
+               return retval;
+
+       stli_initports(brdp);
+       printk(KERN_INFO "istallion: %s found, board=%d io=%x mem=%x "
+               "nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype],
+               brdp->brdnr, brdp->iobase, (int) brdp->memaddr,
+               brdp->nrpanels, brdp->nrports);
+       return 0;
+}
+
+#if STLI_EISAPROBE != 0
+/*****************************************************************************/
+
+/*
+ *     Probe around trying to find where the EISA boards shared memory
+ *     might be. This is a bit if hack, but it is the best we can do.
+ */
+
+static int stli_eisamemprobe(struct stlibrd *brdp)
+{
+       cdkecpsig_t     ecpsig, __iomem *ecpsigp;
+       cdkonbsig_t     onbsig, __iomem *onbsigp;
+       int             i, foundit;
+
+/*
+ *     First up we reset the board, to get it into a known state. There
+ *     is only 2 board types here we need to worry about. Don;t use the
+ *     standard board init routine here, it programs up the shared
+ *     memory address, and we don't know it yet...
+ */
+       if (brdp->brdtype == BRD_ECPE) {
+               outb(0x1, (brdp->iobase + ECP_EIBRDENAB));
+               outb(ECP_EISTOP, (brdp->iobase + ECP_EICONFR));
+               udelay(10);
+               outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
+               udelay(500);
+               stli_ecpeienable(brdp);
+       } else if (brdp->brdtype == BRD_ONBOARDE) {
+               outb(0x1, (brdp->iobase + ONB_EIBRDENAB));
+               outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
+               udelay(10);
+               outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
+               mdelay(100);
+               outb(0x1, brdp->iobase);
+               mdelay(1);
+               stli_onbeenable(brdp);
+       } else {
+               return -ENODEV;
+       }
+
+       foundit = 0;
+       brdp->memsize = ECP_MEMSIZE;
+
+/*
+ *     Board shared memory is enabled, so now we have a poke around and
+ *     see if we can find it.
+ */
+       for (i = 0; (i < stli_eisamempsize); i++) {
+               brdp->memaddr = stli_eisamemprobeaddrs[i];
+               brdp->membase = ioremap_nocache(brdp->memaddr, brdp->memsize);
+               if (brdp->membase == NULL)
+                       continue;
+
+               if (brdp->brdtype == BRD_ECPE) {
+                       ecpsigp = stli_ecpeigetmemptr(brdp,
+                               CDK_SIGADDR, __LINE__);
+                       memcpy_fromio(&ecpsig, ecpsigp, sizeof(cdkecpsig_t));
+                       if (ecpsig.magic == cpu_to_le32(ECP_MAGIC))
+                               foundit = 1;
+               } else {
+                       onbsigp = (cdkonbsig_t __iomem *) stli_onbegetmemptr(brdp,
+                               CDK_SIGADDR, __LINE__);
+                       memcpy_fromio(&onbsig, onbsigp, sizeof(cdkonbsig_t));
+                       if ((onbsig.magic0 == cpu_to_le16(ONB_MAGIC0)) &&
+                           (onbsig.magic1 == cpu_to_le16(ONB_MAGIC1)) &&
+                           (onbsig.magic2 == cpu_to_le16(ONB_MAGIC2)) &&
+                           (onbsig.magic3 == cpu_to_le16(ONB_MAGIC3)))
+                               foundit = 1;
+               }
+
+               iounmap(brdp->membase);
+               if (foundit)
+                       break;
+       }
+
+/*
+ *     Regardless of whether we found the shared memory or not we must
+ *     disable the region. After that return success or failure.
+ */
+       if (brdp->brdtype == BRD_ECPE)
+               stli_ecpeidisable(brdp);
+       else
+               stli_onbedisable(brdp);
+
+       if (! foundit) {
+               brdp->memaddr = 0;
+               brdp->membase = NULL;
+               printk(KERN_ERR "istallion: failed to probe shared memory "
+                               "region for %s in EISA slot=%d\n",
+                       stli_brdnames[brdp->brdtype], (brdp->iobase >> 12));
+               return -ENODEV;
+       }
+       return 0;
+}
+#endif
+
+static int stli_getbrdnr(void)
+{
+       unsigned int i;
+
+       for (i = 0; i < STL_MAXBRDS; i++) {
+               if (!stli_brds[i]) {
+                       if (i >= stli_nrbrds)
+                               stli_nrbrds = i + 1;
+                       return i;
+               }
+       }
+       return -1;
+}
+
+#if STLI_EISAPROBE != 0
+/*****************************************************************************/
+
+/*
+ *     Probe around and try to find any EISA boards in system. The biggest
+ *     problem here is finding out what memory address is associated with
+ *     an EISA board after it is found. The registers of the ECPE and
+ *     ONboardE are not readable - so we can't read them from there. We
+ *     don't have access to the EISA CMOS (or EISA BIOS) so we don't
+ *     actually have any way to find out the real value. The best we can
+ *     do is go probing around in the usual places hoping we can find it.
+ */
+
+static int __init stli_findeisabrds(void)
+{
+       struct stlibrd *brdp;
+       unsigned int iobase, eid, i;
+       int brdnr, found = 0;
+
+/*
+ *     Firstly check if this is an EISA system.  If this is not an EISA system then
+ *     don't bother going any further!
+ */
+       if (EISA_bus)
+               return 0;
+
+/*
+ *     Looks like an EISA system, so go searching for EISA boards.
+ */
+       for (iobase = 0x1000; (iobase <= 0xc000); iobase += 0x1000) {
+               outb(0xff, (iobase + 0xc80));
+               eid = inb(iobase + 0xc80);
+               eid |= inb(iobase + 0xc81) << 8;
+               if (eid != STL_EISAID)
+                       continue;
+
+/*
+ *             We have found a board. Need to check if this board was
+ *             statically configured already (just in case!).
+ */
+               for (i = 0; (i < STL_MAXBRDS); i++) {
+                       brdp = stli_brds[i];
+                       if (brdp == NULL)
+                               continue;
+                       if (brdp->iobase == iobase)
+                               break;
+               }
+               if (i < STL_MAXBRDS)
+                       continue;
+
+/*
+ *             We have found a Stallion board and it is not configured already.
+ *             Allocate a board structure and initialize it.
+ */
+               if ((brdp = stli_allocbrd()) == NULL)
+                       return found ? : -ENOMEM;
+               brdnr = stli_getbrdnr();
+               if (brdnr < 0)
+                       return found ? : -ENOMEM;
+               brdp->brdnr = (unsigned int)brdnr;
+               eid = inb(iobase + 0xc82);
+               if (eid == ECP_EISAID)
+                       brdp->brdtype = BRD_ECPE;
+               else if (eid == ONB_EISAID)
+                       brdp->brdtype = BRD_ONBOARDE;
+               else
+                       brdp->brdtype = BRD_UNKNOWN;
+               brdp->iobase = iobase;
+               outb(0x1, (iobase + 0xc84));
+               if (stli_eisamemprobe(brdp))
+                       outb(0, (iobase + 0xc84));
+               if (stli_brdinit(brdp) < 0) {
+                       kfree(brdp);
+                       continue;
+               }
+
+               stli_brds[brdp->brdnr] = brdp;
+               found++;
+
+               for (i = 0; i < brdp->nrports; i++)
+                       tty_register_device(stli_serial,
+                                       brdp->brdnr * STL_MAXPORTS + i, NULL);
+       }
+
+       return found;
+}
+#else
+static inline int stli_findeisabrds(void) { return 0; }
+#endif
+
+/*****************************************************************************/
+
+/*
+ *     Find the next available board number that is free.
+ */
+
+/*****************************************************************************/
+
+/*
+ *     We have a Stallion board. Allocate a board structure and
+ *     initialize it. Read its IO and MEMORY resources from PCI
+ *     configuration space.
+ */
+
+static int __devinit stli_pciprobe(struct pci_dev *pdev,
+               const struct pci_device_id *ent)
+{
+       struct stlibrd *brdp;
+       unsigned int i;
+       int brdnr, retval = -EIO;
+
+       retval = pci_enable_device(pdev);
+       if (retval)
+               goto err;
+       brdp = stli_allocbrd();
+       if (brdp == NULL) {
+               retval = -ENOMEM;
+               goto err;
+       }
+       mutex_lock(&stli_brdslock);
+       brdnr = stli_getbrdnr();
+       if (brdnr < 0) {
+               printk(KERN_INFO "istallion: too many boards found, "
+                       "maximum supported %d\n", STL_MAXBRDS);
+               mutex_unlock(&stli_brdslock);
+               retval = -EIO;
+               goto err_fr;
+       }
+       brdp->brdnr = (unsigned int)brdnr;
+       stli_brds[brdp->brdnr] = brdp;
+       mutex_unlock(&stli_brdslock);
+       brdp->brdtype = BRD_ECPPCI;
+/*
+ *     We have all resources from the board, so lets setup the actual
+ *     board structure now.
+ */
+       brdp->iobase = pci_resource_start(pdev, 3);
+       brdp->memaddr = pci_resource_start(pdev, 2);
+       retval = stli_brdinit(brdp);
+       if (retval)
+               goto err_null;
+
+       set_bit(BST_PROBED, &brdp->state);
+       pci_set_drvdata(pdev, brdp);
+
+       EBRDENABLE(brdp);
+       brdp->enable = NULL;
+       brdp->disable = NULL;
+
+       for (i = 0; i < brdp->nrports; i++)
+               tty_register_device(stli_serial, brdp->brdnr * STL_MAXPORTS + i,
+                               &pdev->dev);
+
+       return 0;
+err_null:
+       stli_brds[brdp->brdnr] = NULL;
+err_fr:
+       kfree(brdp);
+err:
+       return retval;
+}
+
+static void __devexit stli_pciremove(struct pci_dev *pdev)
+{
+       struct stlibrd *brdp = pci_get_drvdata(pdev);
+
+       stli_cleanup_ports(brdp);
+
+       iounmap(brdp->membase);
+       if (brdp->iosize > 0)
+               release_region(brdp->iobase, brdp->iosize);
+
+       stli_brds[brdp->brdnr] = NULL;
+       kfree(brdp);
+}
+
+static struct pci_driver stli_pcidriver = {
+       .name = "istallion",
+       .id_table = istallion_pci_tbl,
+       .probe = stli_pciprobe,
+       .remove = __devexit_p(stli_pciremove)
+};
+/*****************************************************************************/
+
+/*
+ *     Allocate a new board structure. Fill out the basic info in it.
+ */
+
+static struct stlibrd *stli_allocbrd(void)
+{
+       struct stlibrd *brdp;
+
+       brdp = kzalloc(sizeof(struct stlibrd), GFP_KERNEL);
+       if (!brdp) {
+               printk(KERN_ERR "istallion: failed to allocate memory "
+                               "(size=%Zd)\n", sizeof(struct stlibrd));
+               return NULL;
+       }
+       brdp->magic = STLI_BOARDMAGIC;
+       return brdp;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Scan through all the boards in the configuration and see what we
+ *     can find.
+ */
+
+static int __init stli_initbrds(void)
+{
+       struct stlibrd *brdp, *nxtbrdp;
+       struct stlconf conf;
+       unsigned int i, j, found = 0;
+       int retval;
+
+       for (stli_nrbrds = 0; stli_nrbrds < ARRAY_SIZE(stli_brdsp);
+                       stli_nrbrds++) {
+               memset(&conf, 0, sizeof(conf));
+               if (stli_parsebrd(&conf, stli_brdsp[stli_nrbrds]) == 0)
+                       continue;
+               if ((brdp = stli_allocbrd()) == NULL)
+                       continue;
+               brdp->brdnr = stli_nrbrds;
+               brdp->brdtype = conf.brdtype;
+               brdp->iobase = conf.ioaddr1;
+               brdp->memaddr = conf.memaddr;
+               if (stli_brdinit(brdp) < 0) {
+                       kfree(brdp);
+                       continue;
+               }
+               stli_brds[brdp->brdnr] = brdp;
+               found++;
+
+               for (i = 0; i < brdp->nrports; i++)
+                       tty_register_device(stli_serial,
+                                       brdp->brdnr * STL_MAXPORTS + i, NULL);
+       }
+
+       retval = stli_findeisabrds();
+       if (retval > 0)
+               found += retval;
+
+/*
+ *     All found boards are initialized. Now for a little optimization, if
+ *     no boards are sharing the "shared memory" regions then we can just
+ *     leave them all enabled. This is in fact the usual case.
+ */
+       stli_shared = 0;
+       if (stli_nrbrds > 1) {
+               for (i = 0; (i < stli_nrbrds); i++) {
+                       brdp = stli_brds[i];
+                       if (brdp == NULL)
+                               continue;
+                       for (j = i + 1; (j < stli_nrbrds); j++) {
+                               nxtbrdp = stli_brds[j];
+                               if (nxtbrdp == NULL)
+                                       continue;
+                               if ((brdp->membase >= nxtbrdp->membase) &&
+                                   (brdp->membase <= (nxtbrdp->membase +
+                                   nxtbrdp->memsize - 1))) {
+                                       stli_shared++;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if (stli_shared == 0) {
+               for (i = 0; (i < stli_nrbrds); i++) {
+                       brdp = stli_brds[i];
+                       if (brdp == NULL)
+                               continue;
+                       if (test_bit(BST_FOUND, &brdp->state)) {
+                               EBRDENABLE(brdp);
+                               brdp->enable = NULL;
+                               brdp->disable = NULL;
+                       }
+               }
+       }
+
+       retval = pci_register_driver(&stli_pcidriver);
+       if (retval && found == 0) {
+               printk(KERN_ERR "Neither isa nor eisa cards found nor pci "
+                               "driver can be registered!\n");
+               goto err;
+       }
+
+       return 0;
+err:
+       return retval;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Code to handle an "staliomem" read operation. This device is the 
+ *     contents of the board shared memory. It is used for down loading
+ *     the slave image (and debugging :-)
+ */
+
+static ssize_t stli_memread(struct file *fp, char __user *buf, size_t count, loff_t *offp)
+{
+       unsigned long flags;
+       void __iomem *memptr;
+       struct stlibrd *brdp;
+       unsigned int brdnr;
+       int size, n;
+       void *p;
+       loff_t off = *offp;
+
+       brdnr = iminor(fp->f_path.dentry->d_inode);
+       if (brdnr >= stli_nrbrds)
+               return -ENODEV;
+       brdp = stli_brds[brdnr];
+       if (brdp == NULL)
+               return -ENODEV;
+       if (brdp->state == 0)
+               return -ENODEV;
+       if (off >= brdp->memsize || off + count < off)
+               return 0;
+
+       size = min(count, (size_t)(brdp->memsize - off));
+
+       /*
+        *      Copy the data a page at a time
+        */
+
+       p = (void *)__get_free_page(GFP_KERNEL);
+       if(p == NULL)
+               return -ENOMEM;
+
+       while (size > 0) {
+               spin_lock_irqsave(&brd_lock, flags);
+               EBRDENABLE(brdp);
+               memptr = EBRDGETMEMPTR(brdp, off);
+               n = min(size, (int)(brdp->pagesize - (((unsigned long) off) % brdp->pagesize)));
+               n = min(n, (int)PAGE_SIZE);
+               memcpy_fromio(p, memptr, n);
+               EBRDDISABLE(brdp);
+               spin_unlock_irqrestore(&brd_lock, flags);
+               if (copy_to_user(buf, p, n)) {
+                       count = -EFAULT;
+                       goto out;
+               }
+               off += n;
+               buf += n;
+               size -= n;
+       }
+out:
+       *offp = off;
+       free_page((unsigned long)p);
+       return count;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Code to handle an "staliomem" write operation. This device is the 
+ *     contents of the board shared memory. It is used for down loading
+ *     the slave image (and debugging :-)
+ *
+ *     FIXME: copy under lock
+ */
+
+static ssize_t stli_memwrite(struct file *fp, const char __user *buf, size_t count, loff_t *offp)
+{
+       unsigned long flags;
+       void __iomem *memptr;
+       struct stlibrd *brdp;
+       char __user *chbuf;
+       unsigned int brdnr;
+       int size, n;
+       void *p;
+       loff_t off = *offp;
+
+       brdnr = iminor(fp->f_path.dentry->d_inode);
+
+       if (brdnr >= stli_nrbrds)
+               return -ENODEV;
+       brdp = stli_brds[brdnr];
+       if (brdp == NULL)
+               return -ENODEV;
+       if (brdp->state == 0)
+               return -ENODEV;
+       if (off >= brdp->memsize || off + count < off)
+               return 0;
+
+       chbuf = (char __user *) buf;
+       size = min(count, (size_t)(brdp->memsize - off));
+
+       /*
+        *      Copy the data a page at a time
+        */
+
+       p = (void *)__get_free_page(GFP_KERNEL);
+       if(p == NULL)
+               return -ENOMEM;
+
+       while (size > 0) {
+               n = min(size, (int)(brdp->pagesize - (((unsigned long) off) % brdp->pagesize)));
+               n = min(n, (int)PAGE_SIZE);
+               if (copy_from_user(p, chbuf, n)) {
+                       if (count == 0)
+                               count = -EFAULT;
+                       goto out;
+               }
+               spin_lock_irqsave(&brd_lock, flags);
+               EBRDENABLE(brdp);
+               memptr = EBRDGETMEMPTR(brdp, off);
+               memcpy_toio(memptr, p, n);
+               EBRDDISABLE(brdp);
+               spin_unlock_irqrestore(&brd_lock, flags);
+               off += n;
+               chbuf += n;
+               size -= n;
+       }
+out:
+       free_page((unsigned long) p);
+       *offp = off;
+       return count;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the board stats structure to user app.
+ */
+
+static int stli_getbrdstats(combrd_t __user *bp)
+{
+       struct stlibrd *brdp;
+       unsigned int i;
+
+       if (copy_from_user(&stli_brdstats, bp, sizeof(combrd_t)))
+               return -EFAULT;
+       if (stli_brdstats.brd >= STL_MAXBRDS)
+               return -ENODEV;
+       brdp = stli_brds[stli_brdstats.brd];
+       if (brdp == NULL)
+               return -ENODEV;
+
+       memset(&stli_brdstats, 0, sizeof(combrd_t));
+
+       stli_brdstats.brd = brdp->brdnr;
+       stli_brdstats.type = brdp->brdtype;
+       stli_brdstats.hwid = 0;
+       stli_brdstats.state = brdp->state;
+       stli_brdstats.ioaddr = brdp->iobase;
+       stli_brdstats.memaddr = brdp->memaddr;
+       stli_brdstats.nrpanels = brdp->nrpanels;
+       stli_brdstats.nrports = brdp->nrports;
+       for (i = 0; (i < brdp->nrpanels); i++) {
+               stli_brdstats.panels[i].panel = i;
+               stli_brdstats.panels[i].hwid = brdp->panelids[i];
+               stli_brdstats.panels[i].nrports = brdp->panels[i];
+       }
+
+       if (copy_to_user(bp, &stli_brdstats, sizeof(combrd_t)))
+               return -EFAULT;
+       return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Resolve the referenced port number into a port struct pointer.
+ */
+
+static struct stliport *stli_getport(unsigned int brdnr, unsigned int panelnr,
+               unsigned int portnr)
+{
+       struct stlibrd *brdp;
+       unsigned int i;
+
+       if (brdnr >= STL_MAXBRDS)
+               return NULL;
+       brdp = stli_brds[brdnr];
+       if (brdp == NULL)
+               return NULL;
+       for (i = 0; (i < panelnr); i++)
+               portnr += brdp->panels[i];
+       if (portnr >= brdp->nrports)
+               return NULL;
+       return brdp->ports[portnr];
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the port stats structure to user app. A NULL port struct
+ *     pointer passed in means that we need to find out from the app
+ *     what port to get stats for (used through board control device).
+ */
+
+static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp)
+{
+       unsigned long   flags;
+       struct stlibrd  *brdp;
+       int             rc;
+
+       memset(&stli_comstats, 0, sizeof(comstats_t));
+
+       if (portp == NULL)
+               return -ENODEV;
+       brdp = stli_brds[portp->brdnr];
+       if (brdp == NULL)
+               return -ENODEV;
+
+       mutex_lock(&portp->port.mutex);
+       if (test_bit(BST_STARTED, &brdp->state)) {
+               if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS,
+                   &stli_cdkstats, sizeof(asystats_t), 1)) < 0) {
+                       mutex_unlock(&portp->port.mutex);
+                       return rc;
+               }
+       } else {
+               memset(&stli_cdkstats, 0, sizeof(asystats_t));
+       }
+
+       stli_comstats.brd = portp->brdnr;
+       stli_comstats.panel = portp->panelnr;
+       stli_comstats.port = portp->portnr;
+       stli_comstats.state = portp->state;
+       stli_comstats.flags = portp->port.flags;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       if (tty != NULL) {
+               if (portp->port.tty == tty) {
+                       stli_comstats.ttystate = tty->flags;
+                       stli_comstats.rxbuffered = -1;
+                       if (tty->termios != NULL) {
+                               stli_comstats.cflags = tty->termios->c_cflag;
+                               stli_comstats.iflags = tty->termios->c_iflag;
+                               stli_comstats.oflags = tty->termios->c_oflag;
+                               stli_comstats.lflags = tty->termios->c_lflag;
+                       }
+               }
+       }
+       spin_unlock_irqrestore(&brd_lock, flags);
+
+       stli_comstats.txtotal = stli_cdkstats.txchars;
+       stli_comstats.rxtotal = stli_cdkstats.rxchars + stli_cdkstats.ringover;
+       stli_comstats.txbuffered = stli_cdkstats.txringq;
+       stli_comstats.rxbuffered += stli_cdkstats.rxringq;
+       stli_comstats.rxoverrun = stli_cdkstats.overruns;
+       stli_comstats.rxparity = stli_cdkstats.parity;
+       stli_comstats.rxframing = stli_cdkstats.framing;
+       stli_comstats.rxlost = stli_cdkstats.ringover;
+       stli_comstats.rxbreaks = stli_cdkstats.rxbreaks;
+       stli_comstats.txbreaks = stli_cdkstats.txbreaks;
+       stli_comstats.txxon = stli_cdkstats.txstart;
+       stli_comstats.txxoff = stli_cdkstats.txstop;
+       stli_comstats.rxxon = stli_cdkstats.rxstart;
+       stli_comstats.rxxoff = stli_cdkstats.rxstop;
+       stli_comstats.rxrtsoff = stli_cdkstats.rtscnt / 2;
+       stli_comstats.rxrtson = stli_cdkstats.rtscnt - stli_comstats.rxrtsoff;
+       stli_comstats.modem = stli_cdkstats.dcdcnt;
+       stli_comstats.hwid = stli_cdkstats.hwid;
+       stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals);
+       mutex_unlock(&portp->port.mutex);
+
+       return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the port stats structure to user app. A NULL port struct
+ *     pointer passed in means that we need to find out from the app
+ *     what port to get stats for (used through board control device).
+ */
+
+static int stli_getportstats(struct tty_struct *tty, struct stliport *portp,
+                                                       comstats_t __user *cp)
+{
+       struct stlibrd *brdp;
+       int rc;
+
+       if (!portp) {
+               if (copy_from_user(&stli_comstats, cp, sizeof(comstats_t)))
+                       return -EFAULT;
+               portp = stli_getport(stli_comstats.brd, stli_comstats.panel,
+                       stli_comstats.port);
+               if (!portp)
+                       return -ENODEV;
+       }
+
+       brdp = stli_brds[portp->brdnr];
+       if (!brdp)
+               return -ENODEV;
+
+       if ((rc = stli_portcmdstats(tty, portp)) < 0)
+               return rc;
+
+       return copy_to_user(cp, &stli_comstats, sizeof(comstats_t)) ?
+                       -EFAULT : 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Clear the port stats structure. We also return it zeroed out...
+ */
+
+static int stli_clrportstats(struct stliport *portp, comstats_t __user *cp)
+{
+       struct stlibrd *brdp;
+       int rc;
+
+       if (!portp) {
+               if (copy_from_user(&stli_comstats, cp, sizeof(comstats_t)))
+                       return -EFAULT;
+               portp = stli_getport(stli_comstats.brd, stli_comstats.panel,
+                       stli_comstats.port);
+               if (!portp)
+                       return -ENODEV;
+       }
+
+       brdp = stli_brds[portp->brdnr];
+       if (!brdp)
+               return -ENODEV;
+
+       mutex_lock(&portp->port.mutex);
+
+       if (test_bit(BST_STARTED, &brdp->state)) {
+               if ((rc = stli_cmdwait(brdp, portp, A_CLEARSTATS, NULL, 0, 0)) < 0) {
+                       mutex_unlock(&portp->port.mutex);
+                       return rc;
+               }
+       }
+
+       memset(&stli_comstats, 0, sizeof(comstats_t));
+       stli_comstats.brd = portp->brdnr;
+       stli_comstats.panel = portp->panelnr;
+       stli_comstats.port = portp->portnr;
+       mutex_unlock(&portp->port.mutex);
+
+       if (copy_to_user(cp, &stli_comstats, sizeof(comstats_t)))
+               return -EFAULT;
+       return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the entire driver ports structure to a user app.
+ */
+
+static int stli_getportstruct(struct stliport __user *arg)
+{
+       struct stliport stli_dummyport;
+       struct stliport *portp;
+
+       if (copy_from_user(&stli_dummyport, arg, sizeof(struct stliport)))
+               return -EFAULT;
+       portp = stli_getport(stli_dummyport.brdnr, stli_dummyport.panelnr,
+                stli_dummyport.portnr);
+       if (!portp)
+               return -ENODEV;
+       if (copy_to_user(arg, portp, sizeof(struct stliport)))
+               return -EFAULT;
+       return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the entire driver board structure to a user app.
+ */
+
+static int stli_getbrdstruct(struct stlibrd __user *arg)
+{
+       struct stlibrd stli_dummybrd;
+       struct stlibrd *brdp;
+
+       if (copy_from_user(&stli_dummybrd, arg, sizeof(struct stlibrd)))
+               return -EFAULT;
+       if (stli_dummybrd.brdnr >= STL_MAXBRDS)
+               return -ENODEV;
+       brdp = stli_brds[stli_dummybrd.brdnr];
+       if (!brdp)
+               return -ENODEV;
+       if (copy_to_user(arg, brdp, sizeof(struct stlibrd)))
+               return -EFAULT;
+       return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     The "staliomem" device is also required to do some special operations on
+ *     the board. We need to be able to send an interrupt to the board,
+ *     reset it, and start/stop it.
+ */
+
+static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+       struct stlibrd *brdp;
+       int brdnr, rc, done;
+       void __user *argp = (void __user *)arg;
+
+/*
+ *     First up handle the board independent ioctls.
+ */
+       done = 0;
+       rc = 0;
+
+       switch (cmd) {
+       case COM_GETPORTSTATS:
+               rc = stli_getportstats(NULL, NULL, argp);
+               done++;
+               break;
+       case COM_CLRPORTSTATS:
+               rc = stli_clrportstats(NULL, argp);
+               done++;
+               break;
+       case COM_GETBRDSTATS:
+               rc = stli_getbrdstats(argp);
+               done++;
+               break;
+       case COM_READPORT:
+               rc = stli_getportstruct(argp);
+               done++;
+               break;
+       case COM_READBOARD:
+               rc = stli_getbrdstruct(argp);
+               done++;
+               break;
+       }
+       if (done)
+               return rc;
+
+/*
+ *     Now handle the board specific ioctls. These all depend on the
+ *     minor number of the device they were called from.
+ */
+       brdnr = iminor(fp->f_dentry->d_inode);
+       if (brdnr >= STL_MAXBRDS)
+               return -ENODEV;
+       brdp = stli_brds[brdnr];
+       if (!brdp)
+               return -ENODEV;
+       if (brdp->state == 0)
+               return -ENODEV;
+
+       switch (cmd) {
+       case STL_BINTR:
+               EBRDINTR(brdp);
+               break;
+       case STL_BSTART:
+               rc = stli_startbrd(brdp);
+               break;
+       case STL_BSTOP:
+               clear_bit(BST_STARTED, &brdp->state);
+               break;
+       case STL_BRESET:
+               clear_bit(BST_STARTED, &brdp->state);
+               EBRDRESET(brdp);
+               if (stli_shared == 0) {
+                       if (brdp->reenable != NULL)
+                               (* brdp->reenable)(brdp);
+               }
+               break;
+       default:
+               rc = -ENOIOCTLCMD;
+               break;
+       }
+       return rc;
+}
+
+static const struct tty_operations stli_ops = {
+       .open = stli_open,
+       .close = stli_close,
+       .write = stli_write,
+       .put_char = stli_putchar,
+       .flush_chars = stli_flushchars,
+       .write_room = stli_writeroom,
+       .chars_in_buffer = stli_charsinbuffer,
+       .ioctl = stli_ioctl,
+       .set_termios = stli_settermios,
+       .throttle = stli_throttle,
+       .unthrottle = stli_unthrottle,
+       .stop = stli_stop,
+       .start = stli_start,
+       .hangup = stli_hangup,
+       .flush_buffer = stli_flushbuffer,
+       .break_ctl = stli_breakctl,
+       .wait_until_sent = stli_waituntilsent,
+       .send_xchar = stli_sendxchar,
+       .tiocmget = stli_tiocmget,
+       .tiocmset = stli_tiocmset,
+       .proc_fops = &stli_proc_fops,
+};
+
+static const struct tty_port_operations stli_port_ops = {
+       .carrier_raised = stli_carrier_raised,
+       .dtr_rts = stli_dtr_rts,
+       .activate = stli_activate,
+       .shutdown = stli_shutdown,
+};
+
+/*****************************************************************************/
+/*
+ *     Loadable module initialization stuff.
+ */
+
+static void istallion_cleanup_isa(void)
+{
+       struct stlibrd  *brdp;
+       unsigned int j;
+
+       for (j = 0; (j < stli_nrbrds); j++) {
+               if ((brdp = stli_brds[j]) == NULL ||
+                               test_bit(BST_PROBED, &brdp->state))
+                       continue;
+
+               stli_cleanup_ports(brdp);
+
+               iounmap(brdp->membase);
+               if (brdp->iosize > 0)
+                       release_region(brdp->iobase, brdp->iosize);
+               kfree(brdp);
+               stli_brds[j] = NULL;
+       }
+}
+
+static int __init istallion_module_init(void)
+{
+       unsigned int i;
+       int retval;
+
+       printk(KERN_INFO "%s: version %s\n", stli_drvtitle, stli_drvversion);
+
+       spin_lock_init(&stli_lock);
+       spin_lock_init(&brd_lock);
+
+       stli_txcookbuf = kmalloc(STLI_TXBUFSIZE, GFP_KERNEL);
+       if (!stli_txcookbuf) {
+               printk(KERN_ERR "istallion: failed to allocate memory "
+                               "(size=%d)\n", STLI_TXBUFSIZE);
+               retval = -ENOMEM;
+               goto err;
+       }
+
+       stli_serial = alloc_tty_driver(STL_MAXBRDS * STL_MAXPORTS);
+       if (!stli_serial) {
+               retval = -ENOMEM;
+               goto err_free;
+       }
+
+       stli_serial->owner = THIS_MODULE;
+       stli_serial->driver_name = stli_drvname;
+       stli_serial->name = stli_serialname;
+       stli_serial->major = STL_SERIALMAJOR;
+       stli_serial->minor_start = 0;
+       stli_serial->type = TTY_DRIVER_TYPE_SERIAL;
+       stli_serial->subtype = SERIAL_TYPE_NORMAL;
+       stli_serial->init_termios = stli_deftermios;
+       stli_serial->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+       tty_set_operations(stli_serial, &stli_ops);
+
+       retval = tty_register_driver(stli_serial);
+       if (retval) {
+               printk(KERN_ERR "istallion: failed to register serial driver\n");
+               goto err_ttyput;
+       }
+
+       retval = stli_initbrds();
+       if (retval)
+               goto err_ttyunr;
+
+/*
+ *     Set up a character driver for the shared memory region. We need this
+ *     to down load the slave code image. Also it is a useful debugging tool.
+ */
+       retval = register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem);
+       if (retval) {
+               printk(KERN_ERR "istallion: failed to register serial memory "
+                               "device\n");
+               goto err_deinit;
+       }
+
+       istallion_class = class_create(THIS_MODULE, "staliomem");
+       for (i = 0; i < 4; i++)
+               device_create(istallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i),
+                             NULL, "staliomem%d", i);
+
+       return 0;
+err_deinit:
+       pci_unregister_driver(&stli_pcidriver);
+       istallion_cleanup_isa();
+err_ttyunr:
+       tty_unregister_driver(stli_serial);
+err_ttyput:
+       put_tty_driver(stli_serial);
+err_free:
+       kfree(stli_txcookbuf);
+err:
+       return retval;
+}
+
+/*****************************************************************************/
+
+static void __exit istallion_module_exit(void)
+{
+       unsigned int j;
+
+       printk(KERN_INFO "Unloading %s: version %s\n", stli_drvtitle,
+               stli_drvversion);
+
+       if (stli_timeron) {
+               stli_timeron = 0;
+               del_timer_sync(&stli_timerlist);
+       }
+
+       unregister_chrdev(STL_SIOMEMMAJOR, "staliomem");
+
+       for (j = 0; j < 4; j++)
+               device_destroy(istallion_class, MKDEV(STL_SIOMEMMAJOR, j));
+       class_destroy(istallion_class);
+
+       pci_unregister_driver(&stli_pcidriver);
+       istallion_cleanup_isa();
+
+       tty_unregister_driver(stli_serial);
+       put_tty_driver(stli_serial);
+
+       kfree(stli_txcookbuf);
+}
+
+module_init(istallion_module_init);
+module_exit(istallion_module_exit);
diff --git a/drivers/staging/tty/riscom8.c b/drivers/staging/tty/riscom8.c
new file mode 100644 (file)
index 0000000..602643a
--- /dev/null
@@ -0,0 +1,1560 @@
+/*
+ *      linux/drivers/char/riscom.c  -- RISCom/8 multiport serial driver.
+ *
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (pgmdsg@ibi.com)
+ *
+ *      This code is loosely based on the Linux serial driver, written by
+ *      Linus Torvalds, Theodore T'so and others. The RISCom/8 card
+ *      programming info was obtained from various drivers for other OSes
+ *     (FreeBSD, ISC, etc), but no source code from those drivers were
+ *     directly included in this driver.
+ *
+ *
+ *      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.
+ *
+ *     Revision 1.1
+ *
+ *     ChangeLog:
+ *     Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 27-Jun-2001
+ *     - get rid of check_region and several cleanups
+ */
+
+#include <linux/module.h>
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/mm.h>
+#include <linux/serial.h>
+#include <linux/fcntl.h>
+#include <linux/major.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/tty_flip.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+
+#include <linux/uaccess.h>
+
+#include "riscom8.h"
+#include "riscom8_reg.h"
+
+/* Am I paranoid or not ? ;-) */
+#define RISCOM_PARANOIA_CHECK
+
+/*
+ * Crazy InteliCom/8 boards sometimes have swapped CTS & DSR signals.
+ * You can slightly speed up things by #undefing the following option,
+ * if you are REALLY sure that your board is correct one.
+ */
+
+#define RISCOM_BRAIN_DAMAGED_CTS
+
+/*
+ * The following defines are mostly for testing purposes. But if you need
+ * some nice reporting in your syslog, you can define them also.
+ */
+#undef RC_REPORT_FIFO
+#undef RC_REPORT_OVERRUN
+
+
+#define RISCOM_LEGAL_FLAGS \
+       (ASYNC_HUP_NOTIFY   | ASYNC_SAK          | ASYNC_SPLIT_TERMIOS   | \
+        ASYNC_SPD_HI       | ASYNC_SPEED_VHI    | ASYNC_SESSION_LOCKOUT | \
+        ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
+
+static struct tty_driver *riscom_driver;
+
+static DEFINE_SPINLOCK(riscom_lock);
+
+static struct riscom_board rc_board[RC_NBOARD] =  {
+       {
+               .base   = RC_IOBASE1,
+       },
+       {
+               .base   = RC_IOBASE2,
+       },
+       {
+               .base   = RC_IOBASE3,
+       },
+       {
+               .base   = RC_IOBASE4,
+       },
+};
+
+static struct riscom_port rc_port[RC_NBOARD * RC_NPORT];
+
+/* RISCom/8 I/O ports addresses (without address translation) */
+static unsigned short rc_ioport[] =  {
+#if 1
+       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0c,
+#else
+       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0c, 0x10,
+       0x11, 0x12, 0x18, 0x28, 0x31, 0x32, 0x39, 0x3a, 0x40, 0x41, 0x61, 0x62,
+       0x63, 0x64, 0x6b, 0x70, 0x71, 0x78, 0x7a, 0x7b, 0x7f, 0x100, 0x101
+#endif
+};
+#define RC_NIOPORT     ARRAY_SIZE(rc_ioport)
+
+
+static int rc_paranoia_check(struct riscom_port const *port,
+                                   char *name, const char *routine)
+{
+#ifdef RISCOM_PARANOIA_CHECK
+       static const char badmagic[] = KERN_INFO
+               "rc: Warning: bad riscom port magic number for device %s in %s\n";
+       static const char badinfo[] = KERN_INFO
+               "rc: Warning: null riscom port for device %s in %s\n";
+
+       if (!port) {
+               printk(badinfo, name, routine);
+               return 1;
+       }
+       if (port->magic != RISCOM8_MAGIC) {
+               printk(badmagic, name, routine);
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+/*
+ *
+ *  Service functions for RISCom/8 driver.
+ *
+ */
+
+/* Get board number from pointer */
+static inline int board_No(struct riscom_board const *bp)
+{
+       return bp - rc_board;
+}
+
+/* Get port number from pointer */
+static inline int port_No(struct riscom_port const *port)
+{
+       return RC_PORT(port - rc_port);
+}
+
+/* Get pointer to board from pointer to port */
+static inline struct riscom_board *port_Board(struct riscom_port const *port)
+{
+       return &rc_board[RC_BOARD(port - rc_port)];
+}
+
+/* Input Byte from CL CD180 register */
+static inline unsigned char rc_in(struct riscom_board const *bp,
+                                                       unsigned short reg)
+{
+       return inb(bp->base + RC_TO_ISA(reg));
+}
+
+/* Output Byte to CL CD180 register */
+static inline void rc_out(struct riscom_board const *bp, unsigned short reg,
+                         unsigned char val)
+{
+       outb(val, bp->base + RC_TO_ISA(reg));
+}
+
+/* Wait for Channel Command Register ready */
+static void rc_wait_CCR(struct riscom_board const *bp)
+{
+       unsigned long delay;
+
+       /* FIXME: need something more descriptive then 100000 :) */
+       for (delay = 100000; delay; delay--)
+               if (!rc_in(bp, CD180_CCR))
+                       return;
+
+       printk(KERN_INFO "rc%d: Timeout waiting for CCR.\n", board_No(bp));
+}
+
+/*
+ *  RISCom/8 probe functions.
+ */
+
+static int rc_request_io_range(struct riscom_board * const bp)
+{
+       int i;
+
+       for (i = 0; i < RC_NIOPORT; i++)
+               if (!request_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1,
+                                  "RISCom/8"))  {
+                       goto out_release;
+               }
+       return 0;
+out_release:
+       printk(KERN_INFO "rc%d: Skipping probe at 0x%03x. IO address in use.\n",
+                        board_No(bp), bp->base);
+       while (--i >= 0)
+               release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1);
+       return 1;
+}
+
+static void rc_release_io_range(struct riscom_board * const bp)
+{
+       int i;
+
+       for (i = 0; i < RC_NIOPORT; i++)
+               release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1);
+}
+
+/* Reset and setup CD180 chip */
+static void __init rc_init_CD180(struct riscom_board const *bp)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&riscom_lock, flags);
+
+       rc_out(bp, RC_CTOUT, 0);                   /* Clear timeout        */
+       rc_wait_CCR(bp);                           /* Wait for CCR ready   */
+       rc_out(bp, CD180_CCR, CCR_HARDRESET);      /* Reset CD180 chip     */
+       spin_unlock_irqrestore(&riscom_lock, flags);
+       msleep(50);                                /* Delay 0.05 sec       */
+       spin_lock_irqsave(&riscom_lock, flags);
+       rc_out(bp, CD180_GIVR, RC_ID);             /* Set ID for this chip */
+       rc_out(bp, CD180_GICR, 0);                 /* Clear all bits       */
+       rc_out(bp, CD180_PILR1, RC_ACK_MINT);      /* Prio for modem intr  */
+       rc_out(bp, CD180_PILR2, RC_ACK_TINT);      /* Prio for tx intr     */
+       rc_out(bp, CD180_PILR3, RC_ACK_RINT);      /* Prio for rx intr     */
+
+       /* Setting up prescaler. We need 4 ticks per 1 ms */
+       rc_out(bp, CD180_PPRH, (RC_OSCFREQ/(1000000/RISCOM_TPS)) >> 8);
+       rc_out(bp, CD180_PPRL, (RC_OSCFREQ/(1000000/RISCOM_TPS)) & 0xff);
+
+       spin_unlock_irqrestore(&riscom_lock, flags);
+}
+
+/* Main probing routine, also sets irq. */
+static int __init rc_probe(struct riscom_board *bp)
+{
+       unsigned char val1, val2;
+       int irqs = 0;
+       int retries;
+
+       bp->irq = 0;
+
+       if (rc_request_io_range(bp))
+               return 1;
+
+       /* Are the I/O ports here ? */
+       rc_out(bp, CD180_PPRL, 0x5a);
+       outb(0xff, 0x80);
+       val1 = rc_in(bp, CD180_PPRL);
+       rc_out(bp, CD180_PPRL, 0xa5);
+       outb(0x00, 0x80);
+       val2 = rc_in(bp, CD180_PPRL);
+
+       if ((val1 != 0x5a) || (val2 != 0xa5))  {
+               printk(KERN_ERR "rc%d: RISCom/8 Board at 0x%03x not found.\n",
+                      board_No(bp), bp->base);
+               goto out_release;
+       }
+
+       /* It's time to find IRQ for this board */
+       for (retries = 0; retries < 5 && irqs <= 0; retries++) {
+               irqs = probe_irq_on();
+               rc_init_CD180(bp);               /* Reset CD180 chip         */
+               rc_out(bp, CD180_CAR, 2);        /* Select port 2            */
+               rc_wait_CCR(bp);
+               rc_out(bp, CD180_CCR, CCR_TXEN); /* Enable transmitter       */
+               rc_out(bp, CD180_IER, IER_TXRDY);/* Enable tx empty intr     */
+               msleep(50);
+               irqs = probe_irq_off(irqs);
+               val1 = rc_in(bp, RC_BSR);       /* Get Board Status reg      */
+               val2 = rc_in(bp, RC_ACK_TINT);  /* ACK interrupt             */
+               rc_init_CD180(bp);              /* Reset CD180 again         */
+
+               if ((val1 & RC_BSR_TINT) || (val2 != (RC_ID | GIVR_IT_TX)))  {
+                       printk(KERN_ERR "rc%d: RISCom/8 Board at 0x%03x not "
+                                       "found.\n", board_No(bp), bp->base);
+                       goto out_release;
+               }
+       }
+
+       if (irqs <= 0)  {
+               printk(KERN_ERR "rc%d: Can't find IRQ for RISCom/8 board "
+                               "at 0x%03x.\n", board_No(bp), bp->base);
+               goto out_release;
+       }
+       bp->irq = irqs;
+       bp->flags |= RC_BOARD_PRESENT;
+
+       printk(KERN_INFO "rc%d: RISCom/8 Rev. %c board detected at "
+                        "0x%03x, IRQ %d.\n",
+              board_No(bp),
+              (rc_in(bp, CD180_GFRCR) & 0x0f) + 'A',   /* Board revision */
+              bp->base, bp->irq);
+
+       return 0;
+out_release:
+       rc_release_io_range(bp);
+       return 1;
+}
+
+/*
+ *
+ *  Interrupt processing routines.
+ *
+ */
+
+static struct riscom_port *rc_get_port(struct riscom_board const *bp,
+                                              unsigned char const *what)
+{
+       unsigned char channel;
+       struct riscom_port *port;
+
+       channel = rc_in(bp, CD180_GICR) >> GICR_CHAN_OFF;
+       if (channel < CD180_NCH)  {
+               port = &rc_port[board_No(bp) * RC_NPORT + channel];
+               if (port->port.flags & ASYNC_INITIALIZED)
+                       return port;
+       }
+       printk(KERN_ERR "rc%d: %s interrupt from invalid port %d\n",
+              board_No(bp), what, channel);
+       return NULL;
+}
+
+static void rc_receive_exc(struct riscom_board const *bp)
+{
+       struct riscom_port *port;
+       struct tty_struct *tty;
+       unsigned char status;
+       unsigned char ch, flag;
+
+       port = rc_get_port(bp, "Receive");
+       if (port == NULL)
+               return;
+
+       tty = tty_port_tty_get(&port->port);
+
+#ifdef RC_REPORT_OVERRUN
+       status = rc_in(bp, CD180_RCSR);
+       if (status & RCSR_OE)
+               port->overrun++;
+       status &= port->mark_mask;
+#else
+       status = rc_in(bp, CD180_RCSR) & port->mark_mask;
+#endif
+       ch = rc_in(bp, CD180_RDR);
+       if (!status)
+               goto out;
+       if (status & RCSR_TOUT)  {
+               printk(KERN_WARNING "rc%d: port %d: Receiver timeout. "
+                                   "Hardware problems ?\n",
+                      board_No(bp), port_No(port));
+               goto out;
+
+       } else if (status & RCSR_BREAK)  {
+               printk(KERN_INFO "rc%d: port %d: Handling break...\n",
+                      board_No(bp), port_No(port));
+               flag = TTY_BREAK;
+               if (tty && (port->port.flags & ASYNC_SAK))
+                       do_SAK(tty);
+
+       } else if (status & RCSR_PE)
+               flag = TTY_PARITY;
+
+       else if (status & RCSR_FE)
+               flag = TTY_FRAME;
+
+       else if (status & RCSR_OE)
+               flag = TTY_OVERRUN;
+       else
+               flag = TTY_NORMAL;
+
+       if (tty) {
+               tty_insert_flip_char(tty, ch, flag);
+               tty_flip_buffer_push(tty);
+       }
+out:
+       tty_kref_put(tty);
+}
+
+static void rc_receive(struct riscom_board const *bp)
+{
+       struct riscom_port *port;
+       struct tty_struct *tty;
+       unsigned char count;
+
+       port = rc_get_port(bp, "Receive");
+       if (port == NULL)
+               return;
+
+       tty = tty_port_tty_get(&port->port);
+
+       count = rc_in(bp, CD180_RDCR);
+
+#ifdef RC_REPORT_FIFO
+       port->hits[count > 8 ? 9 : count]++;
+#endif
+
+       while (count--)  {
+               u8 ch = rc_in(bp, CD180_RDR);
+               if (tty)
+                       tty_insert_flip_char(tty, ch, TTY_NORMAL);
+       }
+       if (tty) {
+               tty_flip_buffer_push(tty);
+               tty_kref_put(tty);
+       }
+}
+
+static void rc_transmit(struct riscom_board const *bp)
+{
+       struct riscom_port *port;
+       struct tty_struct *tty;
+       unsigned char count;
+
+       port = rc_get_port(bp, "Transmit");
+       if (port == NULL)
+               return;
+
+       tty = tty_port_tty_get(&port->port);
+
+       if (port->IER & IER_TXEMPTY) {
+               /* FIFO drained */
+               rc_out(bp, CD180_CAR, port_No(port));
+               port->IER &= ~IER_TXEMPTY;
+               rc_out(bp, CD180_IER, port->IER);
+               goto out;
+       }
+
+       if ((port->xmit_cnt <= 0 && !port->break_length)
+           || (tty && (tty->stopped || tty->hw_stopped)))  {
+               rc_out(bp, CD180_CAR, port_No(port));
+               port->IER &= ~IER_TXRDY;
+               rc_out(bp, CD180_IER, port->IER);
+               goto out;
+       }
+
+       if (port->break_length)  {
+               if (port->break_length > 0)  {
+                       if (port->COR2 & COR2_ETC)  {
+                               rc_out(bp, CD180_TDR, CD180_C_ESC);
+                               rc_out(bp, CD180_TDR, CD180_C_SBRK);
+                               port->COR2 &= ~COR2_ETC;
+                       }
+                       count = min_t(int, port->break_length, 0xff);
+                       rc_out(bp, CD180_TDR, CD180_C_ESC);
+                       rc_out(bp, CD180_TDR, CD180_C_DELAY);
+                       rc_out(bp, CD180_TDR, count);
+                       port->break_length -= count;
+                       if (port->break_length == 0)
+                               port->break_length--;
+               } else  {
+                       rc_out(bp, CD180_TDR, CD180_C_ESC);
+                       rc_out(bp, CD180_TDR, CD180_C_EBRK);
+                       rc_out(bp, CD180_COR2, port->COR2);
+                       rc_wait_CCR(bp);
+                       rc_out(bp, CD180_CCR, CCR_CORCHG2);
+                       port->break_length = 0;
+               }
+               goto out;
+       }
+
+       count = CD180_NFIFO;
+       do {
+               rc_out(bp, CD180_TDR, port->port.xmit_buf[port->xmit_tail++]);
+               port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
+               if (--port->xmit_cnt <= 0)
+                       break;
+       } while (--count > 0);
+
+       if (port->xmit_cnt <= 0)  {
+               rc_out(bp, CD180_CAR, port_No(port));
+               port->IER &= ~IER_TXRDY;
+               rc_out(bp, CD180_IER, port->IER);
+       }
+       if (tty && port->xmit_cnt <= port->wakeup_chars)
+               tty_wakeup(tty);
+out:
+       tty_kref_put(tty);
+}
+
+static void rc_check_modem(struct riscom_board const *bp)
+{
+       struct riscom_port *port;
+       struct tty_struct *tty;
+       unsigned char mcr;
+
+       port = rc_get_port(bp, "Modem");
+       if (port == NULL)
+               return;
+
+       tty = tty_port_tty_get(&port->port);
+
+       mcr = rc_in(bp, CD180_MCR);
+       if (mcr & MCR_CDCHG) {
+               if (rc_in(bp, CD180_MSVR) & MSVR_CD)
+                       wake_up_interruptible(&port->port.open_wait);
+               else if (tty)
+                       tty_hangup(tty);
+       }
+
+#ifdef RISCOM_BRAIN_DAMAGED_CTS
+       if (mcr & MCR_CTSCHG)  {
+               if (rc_in(bp, CD180_MSVR) & MSVR_CTS)  {
+                       port->IER |= IER_TXRDY;
+                       if (tty) {
+                               tty->hw_stopped = 0;
+                               if (port->xmit_cnt <= port->wakeup_chars)
+                                       tty_wakeup(tty);
+                       }
+               } else  {
+                       if (tty)
+                               tty->hw_stopped = 1;
+                       port->IER &= ~IER_TXRDY;
+               }
+               rc_out(bp, CD180_IER, port->IER);
+       }
+       if (mcr & MCR_DSRCHG)  {
+               if (rc_in(bp, CD180_MSVR) & MSVR_DSR)  {
+                       port->IER |= IER_TXRDY;
+                       if (tty) {
+                               tty->hw_stopped = 0;
+                               if (port->xmit_cnt <= port->wakeup_chars)
+                                       tty_wakeup(tty);
+                       }
+               } else  {
+                       if (tty)
+                               tty->hw_stopped = 1;
+                       port->IER &= ~IER_TXRDY;
+               }
+               rc_out(bp, CD180_IER, port->IER);
+       }
+#endif /* RISCOM_BRAIN_DAMAGED_CTS */
+
+       /* Clear change bits */
+       rc_out(bp, CD180_MCR, 0);
+       tty_kref_put(tty);
+}
+
+/* The main interrupt processing routine */
+static irqreturn_t rc_interrupt(int dummy, void *dev_id)
+{
+       unsigned char status;
+       unsigned char ack;
+       struct riscom_board *bp = dev_id;
+       unsigned long loop = 0;
+       int handled = 0;
+
+       if (!(bp->flags & RC_BOARD_ACTIVE))
+               return IRQ_NONE;
+
+       while ((++loop < 16) && ((status = ~(rc_in(bp, RC_BSR))) &
+                                (RC_BSR_TOUT | RC_BSR_TINT |
+                                 RC_BSR_MINT | RC_BSR_RINT))) {
+               handled = 1;
+               if (status & RC_BSR_TOUT)
+                       printk(KERN_WARNING "rc%d: Got timeout. Hardware "
+                                           "error?\n", board_No(bp));
+               else if (status & RC_BSR_RINT) {
+                       ack = rc_in(bp, RC_ACK_RINT);
+                       if (ack == (RC_ID | GIVR_IT_RCV))
+                               rc_receive(bp);
+                       else if (ack == (RC_ID | GIVR_IT_REXC))
+                               rc_receive_exc(bp);
+                       else
+                               printk(KERN_WARNING "rc%d: Bad receive ack "
+                                                   "0x%02x.\n",
+                                      board_No(bp), ack);
+               } else if (status & RC_BSR_TINT) {
+                       ack = rc_in(bp, RC_ACK_TINT);
+                       if (ack == (RC_ID | GIVR_IT_TX))
+                               rc_transmit(bp);
+                       else
+                               printk(KERN_WARNING "rc%d: Bad transmit ack "
+                                                   "0x%02x.\n",
+                                      board_No(bp), ack);
+               } else /* if (status & RC_BSR_MINT) */ {
+                       ack = rc_in(bp, RC_ACK_MINT);
+                       if (ack == (RC_ID | GIVR_IT_MODEM))
+                               rc_check_modem(bp);
+                       else
+                               printk(KERN_WARNING "rc%d: Bad modem ack "
+                                                   "0x%02x.\n",
+                                      board_No(bp), ack);
+               }
+               rc_out(bp, CD180_EOIR, 0);   /* Mark end of interrupt */
+               rc_out(bp, RC_CTOUT, 0);     /* Clear timeout flag    */
+       }
+       return IRQ_RETVAL(handled);
+}
+
+/*
+ *  Routines for open & close processing.
+ */
+
+/* Called with disabled interrupts */
+static int rc_setup_board(struct riscom_board *bp)
+{
+       int error;
+
+       if (bp->flags & RC_BOARD_ACTIVE)
+               return 0;
+
+       error = request_irq(bp->irq, rc_interrupt, IRQF_DISABLED,
+                           "RISCom/8", bp);
+       if (error)
+               return error;
+
+       rc_out(bp, RC_CTOUT, 0);                /* Just in case         */
+       bp->DTR = ~0;
+       rc_out(bp, RC_DTR, bp->DTR);            /* Drop DTR on all ports */
+
+       bp->flags |= RC_BOARD_ACTIVE;
+
+       return 0;
+}
+
+/* Called with disabled interrupts */
+static void rc_shutdown_board(struct riscom_board *bp)
+{
+       if (!(bp->flags & RC_BOARD_ACTIVE))
+               return;
+
+       bp->flags &= ~RC_BOARD_ACTIVE;
+
+       free_irq(bp->irq, NULL);
+
+       bp->DTR = ~0;
+       rc_out(bp, RC_DTR, bp->DTR);           /* Drop DTR on all ports */
+
+}
+
+/*
+ * Setting up port characteristics.
+ * Must be called with disabled interrupts
+ */
+static void rc_change_speed(struct tty_struct *tty, struct riscom_board *bp,
+                                               struct riscom_port *port)
+{
+       unsigned long baud;
+       long tmp;
+       unsigned char cor1 = 0, cor3 = 0;
+       unsigned char mcor1 = 0, mcor2 = 0;
+
+       port->IER  = 0;
+       port->COR2 = 0;
+       port->MSVR = MSVR_RTS;
+
+       baud = tty_get_baud_rate(tty);
+
+       /* Select port on the board */
+       rc_out(bp, CD180_CAR, port_No(port));
+
+       if (!baud)  {
+               /* Drop DTR & exit */
+               bp->DTR |= (1u << port_No(port));
+               rc_out(bp, RC_DTR, bp->DTR);
+               return;
+       } else  {
+               /* Set DTR on */
+               bp->DTR &= ~(1u << port_No(port));
+               rc_out(bp, RC_DTR, bp->DTR);
+       }
+
+       /*
+        * Now we must calculate some speed depended things
+        */
+
+       /* Set baud rate for port */
+       tmp = (((RC_OSCFREQ + baud/2) / baud +
+               CD180_TPC/2) / CD180_TPC);
+
+       rc_out(bp, CD180_RBPRH, (tmp >> 8) & 0xff);
+       rc_out(bp, CD180_TBPRH, (tmp >> 8) & 0xff);
+       rc_out(bp, CD180_RBPRL, tmp & 0xff);
+       rc_out(bp, CD180_TBPRL, tmp & 0xff);
+
+       baud = (baud + 5) / 10;   /* Estimated CPS */
+
+       /* Two timer ticks seems enough to wakeup something like SLIP driver */
+       tmp = ((baud + HZ/2) / HZ) * 2 - CD180_NFIFO;
+       port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
+                                             SERIAL_XMIT_SIZE - 1 : tmp);
+
+       /* Receiver timeout will be transmission time for 1.5 chars */
+       tmp = (RISCOM_TPS + RISCOM_TPS/2 + baud/2) / baud;
+       tmp = (tmp > 0xff) ? 0xff : tmp;
+       rc_out(bp, CD180_RTPR, tmp);
+
+       switch (C_CSIZE(tty)) {
+       case CS5:
+               cor1 |= COR1_5BITS;
+               break;
+       case CS6:
+               cor1 |= COR1_6BITS;
+               break;
+       case CS7:
+               cor1 |= COR1_7BITS;
+               break;
+       case CS8:
+               cor1 |= COR1_8BITS;
+               break;
+       }
+       if (C_CSTOPB(tty))
+               cor1 |= COR1_2SB;
+
+       cor1 |= COR1_IGNORE;
+       if (C_PARENB(tty)) {
+               cor1 |= COR1_NORMPAR;
+               if (C_PARODD(tty))
+                       cor1 |= COR1_ODDP;
+               if (I_INPCK(tty))
+                       cor1 &= ~COR1_IGNORE;
+       }
+       /* Set marking of some errors */
+       port->mark_mask = RCSR_OE | RCSR_TOUT;
+       if (I_INPCK(tty))
+               port->mark_mask |= RCSR_FE | RCSR_PE;
+       if (I_BRKINT(tty) || I_PARMRK(tty))
+               port->mark_mask |= RCSR_BREAK;
+       if (I_IGNPAR(tty))
+               port->mark_mask &= ~(RCSR_FE | RCSR_PE);
+       if (I_IGNBRK(tty)) {
+               port->mark_mask &= ~RCSR_BREAK;
+               if (I_IGNPAR(tty))
+                       /* Real raw mode. Ignore all */
+                       port->mark_mask &= ~RCSR_OE;
+       }
+       /* Enable Hardware Flow Control */
+       if (C_CRTSCTS(tty))  {
+#ifdef RISCOM_BRAIN_DAMAGED_CTS
+               port->IER |= IER_DSR | IER_CTS;
+               mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
+               mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
+               tty->hw_stopped = !(rc_in(bp, CD180_MSVR) &
+                                               (MSVR_CTS|MSVR_DSR));
+#else
+               port->COR2 |= COR2_CTSAE;
+#endif
+       }
+       /* Enable Software Flow Control. FIXME: I'm not sure about this */
+       /* Some people reported that it works, but I still doubt */
+       if (I_IXON(tty))  {
+               port->COR2 |= COR2_TXIBE;
+               cor3 |= (COR3_FCT | COR3_SCDE);
+               if (I_IXANY(tty))
+                       port->COR2 |= COR2_IXM;
+               rc_out(bp, CD180_SCHR1, START_CHAR(tty));
+               rc_out(bp, CD180_SCHR2, STOP_CHAR(tty));
+               rc_out(bp, CD180_SCHR3, START_CHAR(tty));
+               rc_out(bp, CD180_SCHR4, STOP_CHAR(tty));
+       }
+       if (!C_CLOCAL(tty))  {
+               /* Enable CD check */
+               port->IER |= IER_CD;
+               mcor1 |= MCOR1_CDZD;
+               mcor2 |= MCOR2_CDOD;
+       }
+
+       if (C_CREAD(tty))
+               /* Enable receiver */
+               port->IER |= IER_RXD;
+
+       /* Set input FIFO size (1-8 bytes) */
+       cor3 |= RISCOM_RXFIFO;
+       /* Setting up CD180 channel registers */
+       rc_out(bp, CD180_COR1, cor1);
+       rc_out(bp, CD180_COR2, port->COR2);
+       rc_out(bp, CD180_COR3, cor3);
+       /* Make CD180 know about registers change */
+       rc_wait_CCR(bp);
+       rc_out(bp, CD180_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
+       /* Setting up modem option registers */
+       rc_out(bp, CD180_MCOR1, mcor1);
+       rc_out(bp, CD180_MCOR2, mcor2);
+       /* Enable CD180 transmitter & receiver */
+       rc_wait_CCR(bp);
+       rc_out(bp, CD180_CCR, CCR_TXEN | CCR_RXEN);
+       /* Enable interrupts */
+       rc_out(bp, CD180_IER, port->IER);
+       /* And finally set RTS on */
+       rc_out(bp, CD180_MSVR, port->MSVR);
+}
+
+/* Must be called with interrupts enabled */
+static int rc_activate_port(struct tty_port *port, struct tty_struct *tty)
+{
+       struct riscom_port *rp = container_of(port, struct riscom_port, port);
+       struct riscom_board *bp = port_Board(rp);
+       unsigned long flags;
+
+       if (tty_port_alloc_xmit_buf(port) < 0)
+               return -ENOMEM;
+
+       spin_lock_irqsave(&riscom_lock, flags);
+
+       clear_bit(TTY_IO_ERROR, &tty->flags);
+       bp->count++;
+       rp->xmit_cnt = rp->xmit_head = rp->xmit_tail = 0;
+       rc_change_speed(tty, bp, rp);
+       spin_unlock_irqrestore(&riscom_lock, flags);
+       return 0;
+}
+
+/* Must be called with interrupts disabled */
+static void rc_shutdown_port(struct tty_struct *tty,
+                       struct riscom_board *bp, struct riscom_port *port)
+{
+#ifdef RC_REPORT_OVERRUN
+       printk(KERN_INFO "rc%d: port %d: Total %ld overruns were detected.\n",
+              board_No(bp), port_No(port), port->overrun);
+#endif
+#ifdef RC_REPORT_FIFO
+       {
+               int i;
+
+               printk(KERN_INFO "rc%d: port %d: FIFO hits [ ",
+                      board_No(bp), port_No(port));
+               for (i = 0; i < 10; i++)
+                       printk("%ld ", port->hits[i]);
+               printk("].\n");
+       }
+#endif
+       tty_port_free_xmit_buf(&port->port);
+
+       /* Select port */
+       rc_out(bp, CD180_CAR, port_No(port));
+       /* Reset port */
+       rc_wait_CCR(bp);
+       rc_out(bp, CD180_CCR, CCR_SOFTRESET);
+       /* Disable all interrupts from this port */
+       port->IER = 0;
+       rc_out(bp, CD180_IER, port->IER);
+
+       set_bit(TTY_IO_ERROR, &tty->flags);
+
+       if (--bp->count < 0)  {
+               printk(KERN_INFO "rc%d: rc_shutdown_port: "
+                                "bad board count: %d\n",
+                      board_No(bp), bp->count);
+               bp->count = 0;
+       }
+       /*
+        * If this is the last opened port on the board
+        * shutdown whole board
+        */
+       if (!bp->count)
+               rc_shutdown_board(bp);
+}
+
+static int carrier_raised(struct tty_port *port)
+{
+       struct riscom_port *p = container_of(port, struct riscom_port, port);
+       struct riscom_board *bp = port_Board(p);
+       unsigned long flags;
+       int CD;
+       
+       spin_lock_irqsave(&riscom_lock, flags);
+       rc_out(bp, CD180_CAR, port_No(p));
+       CD = rc_in(bp, CD180_MSVR) & MSVR_CD;
+       rc_out(bp, CD180_MSVR, MSVR_RTS);
+       bp->DTR &= ~(1u << port_No(p));
+       rc_out(bp, RC_DTR, bp->DTR);
+       spin_unlock_irqrestore(&riscom_lock, flags);
+       return CD;
+}
+
+static void dtr_rts(struct tty_port *port, int onoff)
+{
+       struct riscom_port *p = container_of(port, struct riscom_port, port);
+       struct riscom_board *bp = port_Board(p);
+       unsigned long flags;
+
+       spin_lock_irqsave(&riscom_lock, flags);
+       bp->DTR &= ~(1u << port_No(p));
+       if (onoff == 0)
+               bp->DTR |= (1u << port_No(p));
+       rc_out(bp, RC_DTR, bp->DTR);
+       spin_unlock_irqrestore(&riscom_lock, flags);
+}
+
+static int rc_open(struct tty_struct *tty, struct file *filp)
+{
+       int board;
+       int error;
+       struct riscom_port *port;
+       struct riscom_board *bp;
+
+       board = RC_BOARD(tty->index);
+       if (board >= RC_NBOARD || !(rc_board[board].flags & RC_BOARD_PRESENT))
+               return -ENODEV;
+
+       bp = &rc_board[board];
+       port = rc_port + board * RC_NPORT + RC_PORT(tty->index);
+       if (rc_paranoia_check(port, tty->name, "rc_open"))
+               return -ENODEV;
+
+       error = rc_setup_board(bp);
+       if (error)
+               return error;
+
+       tty->driver_data = port;
+       return tty_port_open(&port->port, tty, filp);
+}
+
+static void rc_flush_buffer(struct tty_struct *tty)
+{
+       struct riscom_port *port = tty->driver_data;
+       unsigned long flags;
+
+       if (rc_paranoia_check(port, tty->name, "rc_flush_buffer"))
+               return;
+
+       spin_lock_irqsave(&riscom_lock, flags);
+       port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+       spin_unlock_irqrestore(&riscom_lock, flags);
+
+       tty_wakeup(tty);
+}
+
+static void rc_close_port(struct tty_port *port)
+{
+       unsigned long flags;
+       struct riscom_port *rp = container_of(port, struct riscom_port, port);
+       struct riscom_board *bp = port_Board(rp);
+       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.
+        */
+
+       spin_lock_irqsave(&riscom_lock, flags);
+       rp->IER &= ~IER_RXD;
+
+       rp->IER &= ~IER_TXRDY;
+       rp->IER |= IER_TXEMPTY;
+       rc_out(bp, CD180_CAR, port_No(rp));
+       rc_out(bp, CD180_IER, rp->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 (rp->IER & IER_TXEMPTY) {
+               spin_unlock_irqrestore(&riscom_lock, flags);
+               msleep_interruptible(jiffies_to_msecs(rp->timeout));
+               spin_lock_irqsave(&riscom_lock, flags);
+               if (time_after(jiffies, timeout))
+                       break;
+       }
+       rc_shutdown_port(port->tty, bp, rp);
+       spin_unlock_irqrestore(&riscom_lock, flags);
+}
+
+static void rc_close(struct tty_struct *tty, struct file *filp)
+{
+       struct riscom_port *port = tty->driver_data;
+
+       if (!port || rc_paranoia_check(port, tty->name, "close"))
+               return;
+       tty_port_close(&port->port, tty, filp);
+}
+
+static int rc_write(struct tty_struct *tty,
+                   const unsigned char *buf, int count)
+{
+       struct riscom_port *port = tty->driver_data;
+       struct riscom_board *bp;
+       int c, total = 0;
+       unsigned long flags;
+
+       if (rc_paranoia_check(port, tty->name, "rc_write"))
+               return 0;
+
+       bp = port_Board(port);
+
+       while (1) {
+               spin_lock_irqsave(&riscom_lock, flags);
+
+               c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+                                         SERIAL_XMIT_SIZE - port->xmit_head));
+               if (c <= 0)
+                       break;  /* lock continues to be held */
+
+               memcpy(port->port.xmit_buf + port->xmit_head, buf, c);
+               port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+               port->xmit_cnt += c;
+
+               spin_unlock_irqrestore(&riscom_lock, flags);
+
+               buf += c;
+               count -= c;
+               total += c;
+       }
+
+       if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+           !(port->IER & IER_TXRDY)) {
+               port->IER |= IER_TXRDY;
+               rc_out(bp, CD180_CAR, port_No(port));
+               rc_out(bp, CD180_IER, port->IER);
+       }
+
+       spin_unlock_irqrestore(&riscom_lock, flags);
+
+       return total;
+}
+
+static int rc_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct riscom_port *port = tty->driver_data;
+       unsigned long flags;
+       int ret = 0;
+
+       if (rc_paranoia_check(port, tty->name, "rc_put_char"))
+               return 0;
+
+       spin_lock_irqsave(&riscom_lock, flags);
+
+       if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1)
+               goto out;
+
+       port->port.xmit_buf[port->xmit_head++] = ch;
+       port->xmit_head &= SERIAL_XMIT_SIZE - 1;
+       port->xmit_cnt++;
+       ret = 1;
+
+out:
+       spin_unlock_irqrestore(&riscom_lock, flags);
+       return ret;
+}
+
+static void rc_flush_chars(struct tty_struct *tty)
+{
+       struct riscom_port *port = tty->driver_data;
+       unsigned long flags;
+
+       if (rc_paranoia_check(port, tty->name, "rc_flush_chars"))
+               return;
+
+       if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped)
+               return;
+
+       spin_lock_irqsave(&riscom_lock, flags);
+
+       port->IER |= IER_TXRDY;
+       rc_out(port_Board(port), CD180_CAR, port_No(port));
+       rc_out(port_Board(port), CD180_IER, port->IER);
+
+       spin_unlock_irqrestore(&riscom_lock, flags);
+}
+
+static int rc_write_room(struct tty_struct *tty)
+{
+       struct riscom_port *port = tty->driver_data;
+       int     ret;
+
+       if (rc_paranoia_check(port, tty->name, "rc_write_room"))
+               return 0;
+
+       ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
+       if (ret < 0)
+               ret = 0;
+       return ret;
+}
+
+static int rc_chars_in_buffer(struct tty_struct *tty)
+{
+       struct riscom_port *port = tty->driver_data;
+
+       if (rc_paranoia_check(port, tty->name, "rc_chars_in_buffer"))
+               return 0;
+
+       return port->xmit_cnt;
+}
+
+static int rc_tiocmget(struct tty_struct *tty)
+{
+       struct riscom_port *port = tty->driver_data;
+       struct riscom_board *bp;
+       unsigned char status;
+       unsigned int result;
+       unsigned long flags;
+
+       if (rc_paranoia_check(port, tty->name, __func__))
+               return -ENODEV;
+
+       bp = port_Board(port);
+
+       spin_lock_irqsave(&riscom_lock, flags);
+
+       rc_out(bp, CD180_CAR, port_No(port));
+       status = rc_in(bp, CD180_MSVR);
+       result = rc_in(bp, RC_RI) & (1u << port_No(port)) ? 0 : TIOCM_RNG;
+
+       spin_unlock_irqrestore(&riscom_lock, flags);
+
+       result |= ((status & MSVR_RTS) ? TIOCM_RTS : 0)
+               | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
+               | ((status & MSVR_CD)  ? TIOCM_CAR : 0)
+               | ((status & MSVR_DSR) ? TIOCM_DSR : 0)
+               | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
+       return result;
+}
+
+static int rc_tiocmset(struct tty_struct *tty,
+                                      unsigned int set, unsigned int clear)
+{
+       struct riscom_port *port = tty->driver_data;
+       unsigned long flags;
+       struct riscom_board *bp;
+
+       if (rc_paranoia_check(port, tty->name, __func__))
+               return -ENODEV;
+
+       bp = port_Board(port);
+
+       spin_lock_irqsave(&riscom_lock, flags);
+
+       if (set & TIOCM_RTS)
+               port->MSVR |= MSVR_RTS;
+       if (set & TIOCM_DTR)
+               bp->DTR &= ~(1u << port_No(port));
+
+       if (clear & TIOCM_RTS)
+               port->MSVR &= ~MSVR_RTS;
+       if (clear & TIOCM_DTR)
+               bp->DTR |= (1u << port_No(port));
+
+       rc_out(bp, CD180_CAR, port_No(port));
+       rc_out(bp, CD180_MSVR, port->MSVR);
+       rc_out(bp, RC_DTR, bp->DTR);
+
+       spin_unlock_irqrestore(&riscom_lock, flags);
+
+       return 0;
+}
+
+static int rc_send_break(struct tty_struct *tty, int length)
+{
+       struct riscom_port *port = tty->driver_data;
+       struct riscom_board *bp = port_Board(port);
+       unsigned long flags;
+
+       if (length == 0 || length == -1)
+               return -EOPNOTSUPP;
+
+       spin_lock_irqsave(&riscom_lock, flags);
+
+       port->break_length = RISCOM_TPS / HZ * length;
+       port->COR2 |= COR2_ETC;
+       port->IER  |= IER_TXRDY;
+       rc_out(bp, CD180_CAR, port_No(port));
+       rc_out(bp, CD180_COR2, port->COR2);
+       rc_out(bp, CD180_IER, port->IER);
+       rc_wait_CCR(bp);
+       rc_out(bp, CD180_CCR, CCR_CORCHG2);
+       rc_wait_CCR(bp);
+
+       spin_unlock_irqrestore(&riscom_lock, flags);
+       return 0;
+}
+
+static int rc_set_serial_info(struct tty_struct *tty, struct riscom_port *port,
+                                    struct serial_struct __user *newinfo)
+{
+       struct serial_struct tmp;
+       struct riscom_board *bp = port_Board(port);
+       int change_speed;
+
+       if (copy_from_user(&tmp, newinfo, sizeof(tmp)))
+               return -EFAULT;
+
+       mutex_lock(&port->port.mutex);
+       change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
+                       (tmp.flags & ASYNC_SPD_MASK));
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               if ((tmp.close_delay != port->port.close_delay) ||
+                   (tmp.closing_wait != port->port.closing_wait) ||
+                   ((tmp.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) |
+                              (tmp.flags & ASYNC_USR_MASK));
+       } else  {
+               port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
+                              (tmp.flags & ASYNC_FLAGS));
+               port->port.close_delay = tmp.close_delay;
+               port->port.closing_wait = tmp.closing_wait;
+       }
+       if (change_speed)  {
+               unsigned long flags;
+
+               spin_lock_irqsave(&riscom_lock, flags);
+               rc_change_speed(tty, bp, port);
+               spin_unlock_irqrestore(&riscom_lock, flags);
+       }
+       mutex_unlock(&port->port.mutex);
+       return 0;
+}
+
+static int rc_get_serial_info(struct riscom_port *port,
+                                    struct serial_struct __user *retinfo)
+{
+       struct serial_struct tmp;
+       struct riscom_board *bp = port_Board(port);
+
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.type = PORT_CIRRUS;
+       tmp.line = port - rc_port;
+
+       mutex_lock(&port->port.mutex);
+       tmp.port = bp->base;
+       tmp.irq  = bp->irq;
+       tmp.flags = port->port.flags;
+       tmp.baud_base = (RC_OSCFREQ + CD180_TPC/2) / CD180_TPC;
+       tmp.close_delay = port->port.close_delay * HZ/100;
+       tmp.closing_wait = port->port.closing_wait * HZ/100;
+       mutex_unlock(&port->port.mutex);
+       tmp.xmit_fifo_size = CD180_NFIFO;
+       return copy_to_user(retinfo, &tmp, sizeof(tmp)) ? -EFAULT : 0;
+}
+
+static int rc_ioctl(struct tty_struct *tty,
+                   unsigned int cmd, unsigned long arg)
+{
+       struct riscom_port *port = tty->driver_data;
+       void __user *argp = (void __user *)arg;
+       int retval;
+
+       if (rc_paranoia_check(port, tty->name, "rc_ioctl"))
+               return -ENODEV;
+
+       switch (cmd) {
+       case TIOCGSERIAL:
+               retval = rc_get_serial_info(port, argp);
+               break;
+       case TIOCSSERIAL:
+               retval = rc_set_serial_info(tty, port, argp);
+               break;
+       default:
+               retval = -ENOIOCTLCMD;
+       }
+       return retval;
+}
+
+static void rc_throttle(struct tty_struct *tty)
+{
+       struct riscom_port *port = tty->driver_data;
+       struct riscom_board *bp;
+       unsigned long flags;
+
+       if (rc_paranoia_check(port, tty->name, "rc_throttle"))
+               return;
+       bp = port_Board(port);
+
+       spin_lock_irqsave(&riscom_lock, flags);
+       port->MSVR &= ~MSVR_RTS;
+       rc_out(bp, CD180_CAR, port_No(port));
+       if (I_IXOFF(tty)) {
+               rc_wait_CCR(bp);
+               rc_out(bp, CD180_CCR, CCR_SSCH2);
+               rc_wait_CCR(bp);
+       }
+       rc_out(bp, CD180_MSVR, port->MSVR);
+       spin_unlock_irqrestore(&riscom_lock, flags);
+}
+
+static void rc_unthrottle(struct tty_struct *tty)
+{
+       struct riscom_port *port = tty->driver_data;
+       struct riscom_board *bp;
+       unsigned long flags;
+
+       if (rc_paranoia_check(port, tty->name, "rc_unthrottle"))
+               return;
+       bp = port_Board(port);
+
+       spin_lock_irqsave(&riscom_lock, flags);
+       port->MSVR |= MSVR_RTS;
+       rc_out(bp, CD180_CAR, port_No(port));
+       if (I_IXOFF(tty))  {
+               rc_wait_CCR(bp);
+               rc_out(bp, CD180_CCR, CCR_SSCH1);
+               rc_wait_CCR(bp);
+       }
+       rc_out(bp, CD180_MSVR, port->MSVR);
+       spin_unlock_irqrestore(&riscom_lock, flags);
+}
+
+static void rc_stop(struct tty_struct *tty)
+{
+       struct riscom_port *port = tty->driver_data;
+       struct riscom_board *bp;
+       unsigned long flags;
+
+       if (rc_paranoia_check(port, tty->name, "rc_stop"))
+               return;
+
+       bp = port_Board(port);
+
+       spin_lock_irqsave(&riscom_lock, flags);
+       port->IER &= ~IER_TXRDY;
+       rc_out(bp, CD180_CAR, port_No(port));
+       rc_out(bp, CD180_IER, port->IER);
+       spin_unlock_irqrestore(&riscom_lock, flags);
+}
+
+static void rc_start(struct tty_struct *tty)
+{
+       struct riscom_port *port = tty->driver_data;
+       struct riscom_board *bp;
+       unsigned long flags;
+
+       if (rc_paranoia_check(port, tty->name, "rc_start"))
+               return;
+
+       bp = port_Board(port);
+
+       spin_lock_irqsave(&riscom_lock, flags);
+
+       if (port->xmit_cnt && port->port.xmit_buf && !(port->IER & IER_TXRDY)) {
+               port->IER |= IER_TXRDY;
+               rc_out(bp, CD180_CAR, port_No(port));
+               rc_out(bp, CD180_IER, port->IER);
+       }
+       spin_unlock_irqrestore(&riscom_lock, flags);
+}
+
+static void rc_hangup(struct tty_struct *tty)
+{
+       struct riscom_port *port = tty->driver_data;
+
+       if (rc_paranoia_check(port, tty->name, "rc_hangup"))
+               return;
+
+       tty_port_hangup(&port->port);
+}
+
+static void rc_set_termios(struct tty_struct *tty,
+                                       struct ktermios *old_termios)
+{
+       struct riscom_port *port = tty->driver_data;
+       unsigned long flags;
+
+       if (rc_paranoia_check(port, tty->name, "rc_set_termios"))
+               return;
+
+       spin_lock_irqsave(&riscom_lock, flags);
+       rc_change_speed(tty, port_Board(port), port);
+       spin_unlock_irqrestore(&riscom_lock, flags);
+
+       if ((old_termios->c_cflag & CRTSCTS) &&
+           !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               rc_start(tty);
+       }
+}
+
+static const struct tty_operations riscom_ops = {
+       .open  = rc_open,
+       .close = rc_close,
+       .write = rc_write,
+       .put_char = rc_put_char,
+       .flush_chars = rc_flush_chars,
+       .write_room = rc_write_room,
+       .chars_in_buffer = rc_chars_in_buffer,
+       .flush_buffer = rc_flush_buffer,
+       .ioctl = rc_ioctl,
+       .throttle = rc_throttle,
+       .unthrottle = rc_unthrottle,
+       .set_termios = rc_set_termios,
+       .stop = rc_stop,
+       .start = rc_start,
+       .hangup = rc_hangup,
+       .tiocmget = rc_tiocmget,
+       .tiocmset = rc_tiocmset,
+       .break_ctl = rc_send_break,
+};
+
+static const struct tty_port_operations riscom_port_ops = {
+       .carrier_raised = carrier_raised,
+       .dtr_rts = dtr_rts,
+       .shutdown = rc_close_port,
+       .activate = rc_activate_port,
+};
+
+
+static int __init rc_init_drivers(void)
+{
+       int error;
+       int i;
+
+       riscom_driver = alloc_tty_driver(RC_NBOARD * RC_NPORT);
+       if (!riscom_driver)
+               return -ENOMEM;
+
+       riscom_driver->owner = THIS_MODULE;
+       riscom_driver->name = "ttyL";
+       riscom_driver->major = RISCOM8_NORMAL_MAJOR;
+       riscom_driver->type = TTY_DRIVER_TYPE_SERIAL;
+       riscom_driver->subtype = SERIAL_TYPE_NORMAL;
+       riscom_driver->init_termios = tty_std_termios;
+       riscom_driver->init_termios.c_cflag =
+               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       riscom_driver->init_termios.c_ispeed = 9600;
+       riscom_driver->init_termios.c_ospeed = 9600;
+       riscom_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_HARDWARE_BREAK;
+       tty_set_operations(riscom_driver, &riscom_ops);
+       error = tty_register_driver(riscom_driver);
+       if (error != 0) {
+               put_tty_driver(riscom_driver);
+               printk(KERN_ERR "rc: Couldn't register RISCom/8 driver, "
+                               "error = %d\n", error);
+               return 1;
+       }
+       memset(rc_port, 0, sizeof(rc_port));
+       for (i = 0; i < RC_NPORT * RC_NBOARD; i++)  {
+               tty_port_init(&rc_port[i].port);
+               rc_port[i].port.ops = &riscom_port_ops;
+               rc_port[i].magic = RISCOM8_MAGIC;
+       }
+       return 0;
+}
+
+static void rc_release_drivers(void)
+{
+       tty_unregister_driver(riscom_driver);
+       put_tty_driver(riscom_driver);
+}
+
+#ifndef MODULE
+/*
+ * Called at boot time.
+ *
+ * You can specify IO base for up to RC_NBOARD cards,
+ * using line "riscom8=0xiobase1,0xiobase2,.." at LILO prompt.
+ * Note that there will be no probing at default
+ * addresses in this case.
+ *
+ */
+static int __init riscom8_setup(char *str)
+{
+       int ints[RC_NBOARD];
+       int i;
+
+       str = get_options(str, ARRAY_SIZE(ints), ints);
+
+       for (i = 0; i < RC_NBOARD; i++) {
+               if (i < ints[0])
+                       rc_board[i].base = ints[i+1];
+               else
+                       rc_board[i].base = 0;
+       }
+       return 1;
+}
+
+__setup("riscom8=", riscom8_setup);
+#endif
+
+static char banner[] __initdata =
+       KERN_INFO "rc: SDL RISCom/8 card driver v1.1, (c) D.Gorodchanin "
+                 "1994-1996.\n";
+static char no_boards_msg[] __initdata =
+       KERN_INFO "rc: No RISCom/8 boards detected.\n";
+
+/*
+ * This routine must be called by kernel at boot time
+ */
+static int __init riscom8_init(void)
+{
+       int i;
+       int found = 0;
+
+       printk(banner);
+
+       if (rc_init_drivers())
+               return -EIO;
+
+       for (i = 0; i < RC_NBOARD; i++)
+               if (rc_board[i].base && !rc_probe(&rc_board[i]))
+                       found++;
+       if (!found)  {
+               rc_release_drivers();
+               printk(no_boards_msg);
+               return -EIO;
+       }
+       return 0;
+}
+
+#ifdef MODULE
+static int iobase;
+static int iobase1;
+static int iobase2;
+static int iobase3;
+module_param(iobase, int, 0);
+module_param(iobase1, int, 0);
+module_param(iobase2, int, 0);
+module_param(iobase3, int, 0);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(RISCOM8_NORMAL_MAJOR);
+#endif /* MODULE */
+
+/*
+ * You can setup up to 4 boards (current value of RC_NBOARD)
+ * by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter.
+ *
+ */
+static int __init riscom8_init_module(void)
+{
+#ifdef MODULE
+       int i;
+
+       if (iobase || iobase1 || iobase2 || iobase3) {
+               for (i = 0; i < RC_NBOARD; i++)
+                       rc_board[i].base = 0;
+       }
+
+       if (iobase)
+               rc_board[0].base = iobase;
+       if (iobase1)
+               rc_board[1].base = iobase1;
+       if (iobase2)
+               rc_board[2].base = iobase2;
+       if (iobase3)
+               rc_board[3].base = iobase3;
+#endif /* MODULE */
+
+       return riscom8_init();
+}
+
+static void __exit riscom8_exit_module(void)
+{
+       int i;
+
+       rc_release_drivers();
+       for (i = 0; i < RC_NBOARD; i++)
+               if (rc_board[i].flags & RC_BOARD_PRESENT)
+                       rc_release_io_range(&rc_board[i]);
+
+}
+
+module_init(riscom8_init_module);
+module_exit(riscom8_exit_module);
diff --git a/drivers/staging/tty/riscom8.h b/drivers/staging/tty/riscom8.h
new file mode 100644 (file)
index 0000000..c9876b3
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *      linux/drivers/char/riscom8.h  -- RISCom/8 multiport serial driver.
+ *
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (pgmdsg@ibi.com)
+ *
+ *      This code is loosely based on the Linux serial driver, written by
+ *      Linus Torvalds, Theodore T'so and others. The RISCom/8 card 
+ *      programming info was obtained from various drivers for other OSes 
+ *     (FreeBSD, ISC, etc), but no source code from those drivers were 
+ *     directly included in this driver.
+ *
+ *
+ *      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.
+ */
+
+#ifndef __LINUX_RISCOM8_H
+#define __LINUX_RISCOM8_H
+
+#include <linux/serial.h>
+
+#ifdef __KERNEL__
+
+#define RC_NBOARD              4
+/* NOTE: RISCom decoder recognizes 16 addresses... */
+#define RC_NPORT               8  
+#define RC_BOARD(line)         (((line) >> 3) & 0x07)
+#define RC_PORT(line)          ((line) & (RC_NPORT - 1))
+
+/* Ticks per sec. Used for setting receiver timeout and break length */
+#define RISCOM_TPS             4000
+
+/* Yeah, after heavy testing I decided it must be 6.
+ * Sure, You can change it if needed.
+ */
+#define RISCOM_RXFIFO          6       /* Max. receiver FIFO size (1-8) */
+
+#define RISCOM8_MAGIC          0x0907
+
+#define RC_IOBASE1     0x220
+#define RC_IOBASE2     0x240
+#define RC_IOBASE3     0x250
+#define RC_IOBASE4     0x260
+
+struct riscom_board {
+       unsigned long   flags;
+       unsigned short  base;
+       unsigned char   irq;
+       signed   char   count;
+       unsigned char   DTR;
+};
+
+#define RC_BOARD_PRESENT       0x00000001
+#define RC_BOARD_ACTIVE                0x00000002
+       
+struct riscom_port {
+       int                     magic;
+       struct                  tty_port port;
+       int                     baud_base;
+       int                     timeout;
+       int                     custom_divisor;
+       int                     xmit_head;
+       int                     xmit_tail;
+       int                     xmit_cnt;
+       short                   wakeup_chars;
+       short                   break_length;
+       unsigned char           mark_mask;
+       unsigned char           IER;
+       unsigned char           MSVR;
+       unsigned char           COR2;
+#ifdef RC_REPORT_OVERRUN
+       unsigned long           overrun;
+#endif 
+#ifdef RC_REPORT_FIFO
+       unsigned long           hits[10];
+#endif
+};
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_RISCOM8_H */
diff --git a/drivers/staging/tty/riscom8_reg.h b/drivers/staging/tty/riscom8_reg.h
new file mode 100644 (file)
index 0000000..a32475e
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ *      linux/drivers/char/riscom8_reg.h  -- RISCom/8 multiport serial driver.
+ */
+
+/*
+ * Definitions for RISCom/8 Async Mux card by SDL Communications, Inc.
+ */
+
+/*
+ * Address mapping between Cirrus Logic CD180 chip internal registers
+ * and ISA port addresses:
+ *
+ *      CL-CD180                A6  A5   A4  A3                      A2 A1 A0
+ *      ISA             A15 A14 A13 A12  A11 A10 A9 A8  A7 A6 A5 A4  A3 A2 A1 A0
+ */
+#define RC_TO_ISA(r)    ((((r)&0x07)<<1) | (((r)&~0x07)<<7))
+
+
+/* RISCom/8 On-Board Registers (assuming address translation) */
+
+#define RC_RI           0x100   /* Ring Indicator Register (R/O)           */
+#define RC_DTR          0x100   /* DTR Register (W/O)                      */
+#define RC_BSR          0x101   /* Board Status Register (R/O)             */
+#define RC_CTOUT        0x101   /* Clear Timeout (W/O)                     */
+
+
+/* Board Status Register */
+
+#define RC_BSR_TOUT     0x08     /* Hardware Timeout                       */
+#define RC_BSR_RINT     0x04     /* Receiver Interrupt                     */
+#define RC_BSR_TINT     0x02     /* Transmitter Interrupt                  */
+#define RC_BSR_MINT     0x01     /* Modem Ctl Interrupt                    */
+
+
+/* On-board oscillator frequency (in Hz) */
+#define RC_OSCFREQ      9830400
+
+/* Values of choice for Interrupt ACKs */
+#define RC_ACK_MINT     0x81    /* goes to PILR1                           */
+#define RC_ACK_RINT     0x82    /* goes to PILR3                           */
+#define RC_ACK_TINT     0x84    /* goes to PILR2                           */
+
+/* Chip ID (sorry, only one chip now) */
+#define RC_ID           0x10
+
+/* Definitions for Cirrus Logic CL-CD180 8-port async mux chip */
+#define CD180_NCH       8       /* Total number of channels                */
+#define CD180_TPC       16      /* Ticks per character                     */
+#define CD180_NFIFO    8       /* TX FIFO size                            */
+
+
+/* Global registers */
+
+#define CD180_GIVR      0x40    /* Global Interrupt Vector Register        */
+#define CD180_GICR      0x41    /* Global Interrupting Channel Register    */
+#define CD180_PILR1     0x61    /* Priority Interrupt Level Register 1     */
+#define CD180_PILR2     0x62    /* Priority Interrupt Level Register 2     */
+#define CD180_PILR3     0x63    /* Priority Interrupt Level Register 3     */
+#define CD180_CAR       0x64    /* Channel Access Register                 */
+#define CD180_GFRCR     0x6b    /* Global Firmware Revision Code Register  */
+#define CD180_PPRH      0x70    /* Prescaler Period Register High          */
+#define CD180_PPRL      0x71    /* Prescaler Period Register Low           */
+#define CD180_RDR       0x78    /* Receiver Data Register                  */
+#define CD180_RCSR      0x7a    /* Receiver Character Status Register      */
+#define CD180_TDR       0x7b    /* Transmit Data Register                  */
+#define CD180_EOIR      0x7f    /* End of Interrupt Register               */
+
+
+/* Channel Registers */
+
+#define CD180_CCR       0x01    /* Channel Command Register                */
+#define CD180_IER       0x02    /* Interrupt Enable Register               */
+#define CD180_COR1      0x03    /* Channel Option Register 1               */
+#define CD180_COR2      0x04    /* Channel Option Register 2               */
+#define CD180_COR3      0x05    /* Channel Option Register 3               */
+#define CD180_CCSR      0x06    /* Channel Control Status Register         */
+#define CD180_RDCR      0x07    /* Receive Data Count Register             */
+#define CD180_SCHR1     0x09    /* Special Character Register 1            */
+#define CD180_SCHR2     0x0a    /* Special Character Register 2            */
+#define CD180_SCHR3     0x0b    /* Special Character Register 3            */
+#define CD180_SCHR4     0x0c    /* Special Character Register 4            */
+#define CD180_MCOR1     0x10    /* Modem Change Option 1 Register          */
+#define CD180_MCOR2     0x11    /* Modem Change Option 2 Register          */
+#define CD180_MCR       0x12    /* Modem Change Register                   */
+#define CD180_RTPR      0x18    /* Receive Timeout Period Register         */
+#define CD180_MSVR      0x28    /* Modem Signal Value Register             */
+#define CD180_RBPRH     0x31    /* Receive Baud Rate Period Register High  */
+#define CD180_RBPRL     0x32    /* Receive Baud Rate Period Register Low   */
+#define CD180_TBPRH     0x39    /* Transmit Baud Rate Period Register High */
+#define CD180_TBPRL     0x3a    /* Transmit Baud Rate Period Register Low  */
+
+
+/* Global Interrupt Vector Register (R/W) */
+
+#define GIVR_ITMASK     0x07     /* Interrupt type mask                     */
+#define  GIVR_IT_MODEM   0x01    /* Modem Signal Change Interrupt           */
+#define  GIVR_IT_TX      0x02    /* Transmit Data Interrupt                 */
+#define  GIVR_IT_RCV     0x03    /* Receive Good Data Interrupt             */
+#define  GIVR_IT_REXC    0x07    /* Receive Exception Interrupt             */
+
+
+/* Global Interrupt Channel Register (R/W) */
+#define GICR_CHAN       0x1c    /* Channel Number Mask                     */
+#define GICR_CHAN_OFF   2       /* Channel Number Offset                   */
+
+
+/* Channel Address Register (R/W) */
+
+#define CAR_CHAN        0x07    /* Channel Number Mask                     */
+#define CAR_A7          0x08    /* A7 Address Extension (unused)           */
+
+
+/* Receive Character Status Register (R/O) */
+
+#define RCSR_TOUT       0x80    /* Rx Timeout                              */
+#define RCSR_SCDET      0x70    /* Special Character Detected Mask         */
+#define  RCSR_NO_SC      0x00   /* No Special Characters Detected          */
+#define  RCSR_SC_1       0x10   /* Special Char 1 (or 1 & 3) Detected      */
+#define  RCSR_SC_2       0x20   /* Special Char 2 (or 2 & 4) Detected      */
+#define  RCSR_SC_3       0x30   /* Special Char 3 Detected                 */
+#define  RCSR_SC_4       0x40   /* Special Char 4 Detected                 */
+#define RCSR_BREAK      0x08    /* Break has been detected                 */
+#define RCSR_PE         0x04    /* Parity Error                            */
+#define RCSR_FE         0x02    /* Frame Error                             */
+#define RCSR_OE         0x01    /* Overrun Error                           */
+
+
+/* Channel Command Register (R/W) (commands in groups can be OR-ed) */
+
+#define CCR_HARDRESET   0x81    /* Reset the chip                          */
+
+#define CCR_SOFTRESET   0x80    /* Soft Channel Reset                      */
+
+#define CCR_CORCHG1     0x42    /* Channel Option Register 1 Changed       */
+#define CCR_CORCHG2     0x44    /* Channel Option Register 2 Changed       */
+#define CCR_CORCHG3     0x48    /* Channel Option Register 3 Changed       */
+
+#define CCR_SSCH1       0x21    /* Send Special Character 1                */
+
+#define CCR_SSCH2       0x22    /* Send Special Character 2                */
+
+#define CCR_SSCH3       0x23    /* Send Special Character 3                */
+
+#define CCR_SSCH4       0x24    /* Send Special Character 4                */
+
+#define CCR_TXEN        0x18    /* Enable Transmitter                      */
+#define CCR_RXEN        0x12    /* Enable Receiver                         */
+
+#define CCR_TXDIS       0x14    /* Disable Transmitter                     */
+#define CCR_RXDIS       0x11    /* Disable Receiver                        */
+
+
+/* Interrupt Enable Register (R/W) */
+
+#define IER_DSR         0x80    /* Enable interrupt on DSR change          */
+#define IER_CD          0x40    /* Enable interrupt on CD change           */
+#define IER_CTS         0x20    /* Enable interrupt on CTS change          */
+#define IER_RXD         0x10    /* Enable interrupt on Receive Data        */
+#define IER_RXSC        0x08    /* Enable interrupt on Receive Spec. Char  */
+#define IER_TXRDY       0x04    /* Enable interrupt on TX FIFO empty       */
+#define IER_TXEMPTY     0x02    /* Enable interrupt on TX completely empty */
+#define IER_RET         0x01    /* Enable interrupt on RX Exc. Timeout     */
+
+
+/* Channel Option Register 1 (R/W) */
+
+#define COR1_ODDP       0x80    /* Odd Parity                              */
+#define COR1_PARMODE    0x60    /* Parity Mode mask                        */
+#define  COR1_NOPAR      0x00   /* No Parity                               */
+#define  COR1_FORCEPAR   0x20   /* Force Parity                            */
+#define  COR1_NORMPAR    0x40   /* Normal Parity                           */
+#define COR1_IGNORE     0x10    /* Ignore Parity on RX                     */
+#define COR1_STOPBITS   0x0c    /* Number of Stop Bits                     */
+#define  COR1_1SB        0x00   /* 1 Stop Bit                              */
+#define  COR1_15SB       0x04   /* 1.5 Stop Bits                           */
+#define  COR1_2SB        0x08   /* 2 Stop Bits                             */
+#define COR1_CHARLEN    0x03    /* Character Length                        */
+#define  COR1_5BITS      0x00   /* 5 bits                                  */
+#define  COR1_6BITS      0x01   /* 6 bits                                  */
+#define  COR1_7BITS      0x02   /* 7 bits                                  */
+#define  COR1_8BITS      0x03   /* 8 bits                                  */
+
+
+/* Channel Option Register 2 (R/W) */
+
+#define COR2_IXM        0x80    /* Implied XON mode                        */
+#define COR2_TXIBE      0x40    /* Enable In-Band (XON/XOFF) Flow Control  */
+#define COR2_ETC        0x20    /* Embedded Tx Commands Enable             */
+#define COR2_LLM        0x10    /* Local Loopback Mode                     */
+#define COR2_RLM        0x08    /* Remote Loopback Mode                    */
+#define COR2_RTSAO      0x04    /* RTS Automatic Output Enable             */
+#define COR2_CTSAE      0x02    /* CTS Automatic Enable                    */
+#define COR2_DSRAE      0x01    /* DSR Automatic Enable                    */
+
+
+/* Channel Option Register 3 (R/W) */
+
+#define COR3_XONCH      0x80    /* XON is a pair of characters (1 & 3)     */
+#define COR3_XOFFCH     0x40    /* XOFF is a pair of characters (2 & 4)    */
+#define COR3_FCT        0x20    /* Flow-Control Transparency Mode          */
+#define COR3_SCDE       0x10    /* Special Character Detection Enable      */
+#define COR3_RXTH       0x0f    /* RX FIFO Threshold value (1-8)           */
+
+
+/* Channel Control Status Register (R/O) */
+
+#define CCSR_RXEN       0x80    /* Receiver Enabled                        */
+#define CCSR_RXFLOFF    0x40    /* Receive Flow Off (XOFF was sent)        */
+#define CCSR_RXFLON     0x20    /* Receive Flow On (XON was sent)          */
+#define CCSR_TXEN       0x08    /* Transmitter Enabled                     */
+#define CCSR_TXFLOFF    0x04    /* Transmit Flow Off (got XOFF)            */
+#define CCSR_TXFLON     0x02    /* Transmit Flow On (got XON)              */
+
+
+/* Modem Change Option Register 1 (R/W) */
+
+#define MCOR1_DSRZD     0x80    /* Detect 0->1 transition of DSR           */
+#define MCOR1_CDZD      0x40    /* Detect 0->1 transition of CD            */
+#define MCOR1_CTSZD     0x20    /* Detect 0->1 transition of CTS           */
+#define MCOR1_DTRTH     0x0f    /* Auto DTR flow control Threshold (1-8)   */
+#define  MCOR1_NODTRFC   0x0     /* Automatic DTR flow control disabled     */
+
+
+/* Modem Change Option Register 2 (R/W) */
+
+#define MCOR2_DSROD     0x80    /* Detect 1->0 transition of DSR           */
+#define MCOR2_CDOD      0x40    /* Detect 1->0 transition of CD            */
+#define MCOR2_CTSOD     0x20    /* Detect 1->0 transition of CTS           */
+
+
+/* Modem Change Register (R/W) */
+
+#define MCR_DSRCHG      0x80    /* DSR Changed                             */
+#define MCR_CDCHG       0x40    /* CD Changed                              */
+#define MCR_CTSCHG      0x20    /* CTS Changed                             */
+
+
+/* Modem Signal Value Register (R/W) */
+
+#define MSVR_DSR        0x80    /* Current state of DSR input              */
+#define MSVR_CD         0x40    /* Current state of CD input               */
+#define MSVR_CTS        0x20    /* Current state of CTS input              */
+#define MSVR_DTR        0x02    /* Current state of DTR output             */
+#define MSVR_RTS        0x01    /* Current state of RTS output             */
+
+
+/* Escape characters */
+
+#define CD180_C_ESC     0x00    /* Escape character                        */
+#define CD180_C_SBRK    0x81    /* Start sending BREAK                     */
+#define CD180_C_DELAY   0x82    /* Delay output                            */
+#define CD180_C_EBRK    0x83    /* Stop sending BREAK                      */
diff --git a/drivers/staging/tty/serial167.c b/drivers/staging/tty/serial167.c
new file mode 100644 (file)
index 0000000..674af69
--- /dev/null
@@ -0,0 +1,2489 @@
+/*
+ * linux/drivers/char/serial167.c
+ *
+ * Driver for MVME166/7 board serial ports, which are via a CD2401.
+ * Based very much on cyclades.c.
+ *
+ * MVME166/7 work by Richard Hirst [richard@sleepie.demon.co.uk]
+ *
+ * ==============================================================
+ *
+ * static char rcsid[] =
+ * "$Revision: 1.36.1.4 $$Date: 1995/03/29 06:14:14 $";
+ *
+ *  linux/kernel/cyclades.c
+ *
+ * Maintained by Marcio Saito (cyclades@netcom.com) and
+ * Randolph Bentson (bentson@grieg.seaslug.org)
+ *
+ * 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.
+ *
+ * This version does not support shared irq's.
+ *
+ * $Log: cyclades.c,v $
+ * Revision 1.36.1.4  1995/03/29  06:14:14  bentson
+ * disambiguate between Cyclom-16Y and Cyclom-32Ye;
+ *
+ * Changes:
+ *
+ * 200 lines of changes record removed - RGH 11-10-95, starting work on
+ * converting this to drive serial ports on mvme166 (cd2401).
+ *
+ * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 2000/08/25
+ * - get rid of verify_area
+ * - use get_user to access memory from userspace in set_threshold,
+ *   set_default_threshold and set_timeout
+ * - don't use the panic function in serial167_init
+ * - do resource release on failure on serial167_init
+ * - include missing restore_flags in mvme167_serial_console_setup
+ *
+ * Kars de Jong <jongk@linux-m68k.org> - 2004/09/06
+ * - replace bottom half handler with task queue handler
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/interrupt.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/serial167.h>
+#include <linux/delay.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/tty_flip.h>
+#include <linux/gfp.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/mvme16xhw.h>
+#include <asm/bootinfo.h>
+#include <asm/setup.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <asm/uaccess.h>
+#include <linux/init.h>
+
+#define SERIAL_PARANOIA_CHECK
+#undef  SERIAL_DEBUG_OPEN
+#undef  SERIAL_DEBUG_THROTTLE
+#undef  SERIAL_DEBUG_OTHER
+#undef  SERIAL_DEBUG_IO
+#undef  SERIAL_DEBUG_COUNT
+#undef  SERIAL_DEBUG_DTR
+#undef  CYCLOM_16Y_HACK
+#define  CYCLOM_ENABLE_MONITORING
+
+#define WAKEUP_CHARS 256
+
+#define STD_COM_FLAGS (0)
+
+static struct tty_driver *cy_serial_driver;
+extern int serial_console;
+static struct cyclades_port *serial_console_info = NULL;
+static unsigned int serial_console_cflag = 0;
+u_char initial_console_speed;
+
+/* Base address of cd2401 chip on mvme166/7 */
+
+#define BASE_ADDR (0xfff45000)
+#define pcc2chip       ((volatile u_char *)0xfff42000)
+#define PccSCCMICR     0x1d
+#define PccSCCTICR     0x1e
+#define PccSCCRICR     0x1f
+#define PccTPIACKR     0x25
+#define PccRPIACKR     0x27
+#define PccIMLR                0x3f
+
+/* This is the per-port data structure */
+struct cyclades_port cy_port[] = {
+       /* CARD#  */
+       {-1},                   /* ttyS0 */
+       {-1},                   /* ttyS1 */
+       {-1},                   /* ttyS2 */
+       {-1},                   /* ttyS3 */
+};
+
+#define NR_PORTS        ARRAY_SIZE(cy_port)
+
+/*
+ * 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->flags.
+ *         0,     1,     2,     3,     4,     5,     6,     7,     8,     9,
+ *        10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
+ *                                                  HI            VHI
+ */
+static int baud_table[] = {
+       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
+       1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000,
+       0
+};
+
+#if 0
+static char baud_co[] = {      /* 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 char baud_bpr[] = {     /* 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
+};
+#endif
+
+/* I think 166 brd clocks 2401 at 20MHz.... */
+
+/* These values are written directly to tcor, and >> 5 for writing to rcor */
+static u_char baud_co[] = {    /* 20 MHz clock option table */
+       0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x60, 0x60, 0x40,
+       0x40, 0x40, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* These values written directly to tbpr/rbpr */
+static u_char baud_bpr[] = {   /* 20 MHz baud rate period table */
+       0x00, 0xc0, 0x80, 0x58, 0x6c, 0x40, 0xc0, 0x81, 0x40, 0x81,
+       0x57, 0x40, 0x81, 0x40, 0x81, 0x40, 0x2b, 0x20, 0x15, 0x10
+};
+
+static u_char baud_cor4[] = {  /* receive threshold */
+       0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+       0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07
+};
+
+static void shutdown(struct cyclades_port *);
+static int startup(struct cyclades_port *);
+static void cy_throttle(struct tty_struct *);
+static void cy_unthrottle(struct tty_struct *);
+static void config_setup(struct cyclades_port *);
+#ifdef CYCLOM_SHOW_STATUS
+static void show_status(int);
+#endif
+
+/*
+ * I have my own version of udelay(), as it is needed when initialising
+ * the chip, before the delay loop has been calibrated.  Should probably
+ * reference one of the vmechip2 or pccchip2 counter for an accurate
+ * delay, but this wild guess will do for now.
+ */
+
+void my_udelay(long us)
+{
+       u_char x;
+       volatile u_char *p = &x;
+       int i;
+
+       while (us--)
+               for (i = 100; i; i--)
+                       x |= *p;
+}
+
+static inline int serial_paranoia_check(struct cyclades_port *info, char *name,
+               const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+       if (!info) {
+               printk("Warning: null cyclades_port for (%s) in %s\n", name,
+                               routine);
+               return 1;
+       }
+
+       if (info < &cy_port[0] || info >= &cy_port[NR_PORTS]) {
+               printk("Warning: cyclades_port out of range for (%s) in %s\n",
+                               name, routine);
+               return 1;
+       }
+
+       if (info->magic != CYCLADES_MAGIC) {
+               printk("Warning: bad magic number for serial struct (%s) in "
+                               "%s\n", name, routine);
+               return 1;
+       }
+#endif
+       return 0;
+}                              /* serial_paranoia_check */
+
+#if 0
+/* The following diagnostic routines allow the driver to spew
+   information on the screen, even (especially!) during interrupts.
+ */
+void SP(char *data)
+{
+       unsigned long flags;
+       local_irq_save(flags);
+       printk(KERN_EMERG "%s", data);
+       local_irq_restore(flags);
+}
+
+char scrn[2];
+void CP(char data)
+{
+       unsigned long flags;
+       local_irq_save(flags);
+       scrn[0] = data;
+       printk(KERN_EMERG "%c", scrn);
+       local_irq_restore(flags);
+}                              /* CP */
+
+void CP1(int data)
+{
+       (data < 10) ? CP(data + '0') : CP(data + 'A' - 10);
+}                              /* CP1 */
+void CP2(int data)
+{
+       CP1((data >> 4) & 0x0f);
+       CP1(data & 0x0f);
+}                              /* CP2 */
+void CP4(int data)
+{
+       CP2((data >> 8) & 0xff);
+       CP2(data & 0xff);
+}                              /* CP4 */
+void CP8(long data)
+{
+       CP4((data >> 16) & 0xffff);
+       CP4(data & 0xffff);
+}                              /* CP8 */
+#endif
+
+/* 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.
+ */
+u_short write_cy_cmd(volatile u_char * base_addr, u_char cmd)
+{
+       unsigned long flags;
+       volatile int i;
+
+       local_irq_save(flags);
+       /* Check to see that the previous command has completed */
+       for (i = 0; i < 100; i++) {
+               if (base_addr[CyCCR] == 0) {
+                       break;
+               }
+               my_udelay(10L);
+       }
+       /* if the CCR never cleared, the previous command
+          didn't finish within the "reasonable time" */
+       if (i == 10) {
+               local_irq_restore(flags);
+               return (-1);
+       }
+
+       /* Issue the new command */
+       base_addr[CyCCR] = cmd;
+       local_irq_restore(flags);
+       return (0);
+}                              /* write_cy_cmd */
+
+/* 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_port *info = tty->driver_data;
+       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+       int channel;
+       unsigned long flags;
+
+#ifdef SERIAL_DEBUG_OTHER
+       printk("cy_stop %s\n", tty->name);      /* */
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_stop"))
+               return;
+
+       channel = info->line;
+
+       local_irq_save(flags);
+       base_addr[CyCAR] = (u_char) (channel);  /* index channel */
+       base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
+       local_irq_restore(flags);
+}                              /* cy_stop */
+
+static void cy_start(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+       int channel;
+       unsigned long flags;
+
+#ifdef SERIAL_DEBUG_OTHER
+       printk("cy_start %s\n", tty->name);     /* */
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_start"))
+               return;
+
+       channel = info->line;
+
+       local_irq_save(flags);
+       base_addr[CyCAR] = (u_char) (channel);
+       base_addr[CyIER] |= CyTxMpty;
+       local_irq_restore(flags);
+}                              /* cy_start */
+
+/* The real interrupt service routines are called
+   whenever the card wants its hand held--chars
+   received, out buffer empty, modem change, etc.
+ */
+static irqreturn_t cd2401_rxerr_interrupt(int irq, void *dev_id)
+{
+       struct tty_struct *tty;
+       struct cyclades_port *info;
+       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+       unsigned char err, rfoc;
+       int channel;
+       char data;
+
+       /* determine the channel and change to that context */
+       channel = (u_short) (base_addr[CyLICR] >> 2);
+       info = &cy_port[channel];
+       info->last_active = jiffies;
+
+       if ((err = base_addr[CyRISR]) & CyTIMEOUT) {
+               /* This is a receive timeout interrupt, ignore it */
+               base_addr[CyREOIR] = CyNOTRANS;
+               return IRQ_HANDLED;
+       }
+
+       /* Read a byte of data if there is any - assume the error
+        * is associated with this character */
+
+       if ((rfoc = base_addr[CyRFOC]) != 0)
+               data = base_addr[CyRDR];
+       else
+               data = 0;
+
+       /* if there is nowhere to put the data, discard it */
+       if (info->tty == 0) {
+               base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;
+               return IRQ_HANDLED;
+       } else {                /* there is an open port for this data */
+               tty = info->tty;
+               if (err & info->ignore_status_mask) {
+                       base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;
+                       return IRQ_HANDLED;
+               }
+               if (tty_buffer_request_room(tty, 1) != 0) {
+                       if (err & info->read_status_mask) {
+                               if (err & CyBREAK) {
+                                       tty_insert_flip_char(tty, data,
+                                                            TTY_BREAK);
+                                       if (info->flags & ASYNC_SAK) {
+                                               do_SAK(tty);
+                                       }
+                               } else if (err & CyFRAME) {
+                                       tty_insert_flip_char(tty, data,
+                                                            TTY_FRAME);
+                               } else if (err & CyPARITY) {
+                                       tty_insert_flip_char(tty, data,
+                                                            TTY_PARITY);
+                               } else if (err & CyOVERRUN) {
+                                       tty_insert_flip_char(tty, 0,
+                                                            TTY_OVERRUN);
+                                       /*
+                                          If the flip buffer itself is
+                                          overflowing, we still lose
+                                          the next incoming character.
+                                        */
+                                       if (tty_buffer_request_room(tty, 1) !=
+                                           0) {
+                                               tty_insert_flip_char(tty, data,
+                                                                    TTY_FRAME);
+                                       }
+                                       /* 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);
+                               }
+                       } else {
+                               tty_insert_flip_char(tty, data, TTY_NORMAL);
+                       }
+               } else {
+                       /* there was a software buffer overrun
+                          and nothing could be done about it!!! */
+               }
+       }
+       tty_schedule_flip(tty);
+       /* end of service */
+       base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;
+       return IRQ_HANDLED;
+}                              /* cy_rxerr_interrupt */
+
+static irqreturn_t cd2401_modem_interrupt(int irq, void *dev_id)
+{
+       struct cyclades_port *info;
+       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+       int channel;
+       int mdm_change;
+       int mdm_status;
+
+       /* determine the channel and change to that context */
+       channel = (u_short) (base_addr[CyLICR] >> 2);
+       info = &cy_port[channel];
+       info->last_active = jiffies;
+
+       mdm_change = base_addr[CyMISR];
+       mdm_status = base_addr[CyMSVR1];
+
+       if (info->tty == 0) {   /* nowhere to put the data, ignore it */
+               ;
+       } else {
+               if ((mdm_change & CyDCD)
+                   && (info->flags & ASYNC_CHECK_CD)) {
+                       if (mdm_status & CyDCD) {
+/* CP('!'); */
+                               wake_up_interruptible(&info->open_wait);
+                       } else {
+/* CP('@'); */
+                               tty_hangup(info->tty);
+                               wake_up_interruptible(&info->open_wait);
+                               info->flags &= ~ASYNC_NORMAL_ACTIVE;
+                       }
+               }
+               if ((mdm_change & CyCTS)
+                   && (info->flags & ASYNC_CTS_FLOW)) {
+                       if (info->tty->stopped) {
+                               if (mdm_status & CyCTS) {
+                                       /* !!! cy_start isn't used because... */
+                                       info->tty->stopped = 0;
+                                       base_addr[CyIER] |= CyTxMpty;
+                                       tty_wakeup(info->tty);
+                               }
+                       } else {
+                               if (!(mdm_status & CyCTS)) {
+                                       /* !!! cy_stop isn't used because... */
+                                       info->tty->stopped = 1;
+                                       base_addr[CyIER] &=
+                                           ~(CyTxMpty | CyTxRdy);
+                               }
+                       }
+               }
+               if (mdm_status & CyDSR) {
+               }
+       }
+       base_addr[CyMEOIR] = 0;
+       return IRQ_HANDLED;
+}                              /* cy_modem_interrupt */
+
+static irqreturn_t cd2401_tx_interrupt(int irq, void *dev_id)
+{
+       struct cyclades_port *info;
+       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+       int channel;
+       int char_count, saved_cnt;
+       int outch;
+
+       /* determine the channel and change to that context */
+       channel = (u_short) (base_addr[CyLICR] >> 2);
+
+       /* validate the port number (as configured and open) */
+       if ((channel < 0) || (NR_PORTS <= channel)) {
+               base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
+               base_addr[CyTEOIR] = CyNOTRANS;
+               return IRQ_HANDLED;
+       }
+       info = &cy_port[channel];
+       info->last_active = jiffies;
+       if (info->tty == 0) {
+               base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
+               base_addr[CyTEOIR] = CyNOTRANS;
+               return IRQ_HANDLED;
+       }
+
+       /* load the on-chip space available for outbound data */
+       saved_cnt = char_count = base_addr[CyTFTC];
+
+       if (info->x_char) {     /* send special char */
+               outch = info->x_char;
+               base_addr[CyTDR] = outch;
+               char_count--;
+               info->x_char = 0;
+       }
+
+       if (info->x_break) {
+               /*  The Cirrus chip requires the "Embedded Transmit
+                  Commands" of start break, delay, and end break
+                  sequences to be sent.  The duration of the
+                  break is given in TICs, which runs at HZ
+                  (typically 100) and the PPR runs at 200 Hz,
+                  so the delay is duration * 200/HZ, and thus a
+                  break can run from 1/100 sec to about 5/4 sec.
+                  Need to check these values - RGH 141095.
+                */
+               base_addr[CyTDR] = 0;   /* start break */
+               base_addr[CyTDR] = 0x81;
+               base_addr[CyTDR] = 0;   /* delay a bit */
+               base_addr[CyTDR] = 0x82;
+               base_addr[CyTDR] = info->x_break * 200 / HZ;
+               base_addr[CyTDR] = 0;   /* terminate break */
+               base_addr[CyTDR] = 0x83;
+               char_count -= 7;
+               info->x_break = 0;
+       }
+
+       while (char_count > 0) {
+               if (!info->xmit_cnt) {
+                       base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
+                       break;
+               }
+               if (info->xmit_buf == 0) {
+                       base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
+                       break;
+               }
+               if (info->tty->stopped || info->tty->hw_stopped) {
+                       base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
+                       break;
+               }
+               /* 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->xmit_buf[info->xmit_tail];
+               if (outch) {
+                       info->xmit_cnt--;
+                       info->xmit_tail = (info->xmit_tail + 1)
+                           & (PAGE_SIZE - 1);
+                       base_addr[CyTDR] = outch;
+                       char_count--;
+               } else {
+                       if (char_count > 1) {
+                               info->xmit_cnt--;
+                               info->xmit_tail = (info->xmit_tail + 1)
+                                   & (PAGE_SIZE - 1);
+                               base_addr[CyTDR] = outch;
+                               base_addr[CyTDR] = 0;
+                               char_count--;
+                               char_count--;
+                       } else {
+                               break;
+                       }
+               }
+       }
+
+       if (info->xmit_cnt < WAKEUP_CHARS)
+               tty_wakeup(info->tty);
+
+       base_addr[CyTEOIR] = (char_count != saved_cnt) ? 0 : CyNOTRANS;
+       return IRQ_HANDLED;
+}                              /* cy_tx_interrupt */
+
+static irqreturn_t cd2401_rx_interrupt(int irq, void *dev_id)
+{
+       struct tty_struct *tty;
+       struct cyclades_port *info;
+       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+       int channel;
+       char data;
+       int char_count;
+       int save_cnt;
+
+       /* determine the channel and change to that context */
+       channel = (u_short) (base_addr[CyLICR] >> 2);
+       info = &cy_port[channel];
+       info->last_active = jiffies;
+       save_cnt = char_count = base_addr[CyRFOC];
+
+               /* if there is nowhere to put the data, discard it */
+       if (info->tty == 0) {
+               while (char_count--) {
+                       data = base_addr[CyRDR];
+               }
+       } else {                /* there is an open port for this data */
+               tty = info->tty;
+               /* load # characters available from the chip */
+
+#ifdef CYCLOM_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
+               while (char_count--) {
+                       data = base_addr[CyRDR];
+                       tty_insert_flip_char(tty, data, TTY_NORMAL);
+#ifdef CYCLOM_16Y_HACK
+                       udelay(10L);
+#endif
+               }
+               tty_schedule_flip(tty);
+       }
+       /* end of service */
+       base_addr[CyREOIR] = save_cnt ? 0 : CyNOTRANS;
+       return IRQ_HANDLED;
+}                              /* cy_rx_interrupt */
+
+/* This is called whenever a port becomes active;
+   interrupts are enabled and DTR & RTS are turned on.
+ */
+static int startup(struct cyclades_port *info)
+{
+       unsigned long flags;
+       volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+       int channel;
+
+       if (info->flags & ASYNC_INITIALIZED) {
+               return 0;
+       }
+
+       if (!info->type) {
+               if (info->tty) {
+                       set_bit(TTY_IO_ERROR, &info->tty->flags);
+               }
+               return 0;
+       }
+       if (!info->xmit_buf) {
+               info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
+               if (!info->xmit_buf) {
+                       return -ENOMEM;
+               }
+       }
+
+       config_setup(info);
+
+       channel = info->line;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("startup channel %d\n", channel);
+#endif
+
+       local_irq_save(flags);
+       base_addr[CyCAR] = (u_char) channel;
+       write_cy_cmd(base_addr, CyENB_RCVR | CyENB_XMTR);
+
+       base_addr[CyCAR] = (u_char) channel;    /* !!! Is this needed? */
+       base_addr[CyMSVR1] = CyRTS;
+/* CP('S');CP('1'); */
+       base_addr[CyMSVR2] = CyDTR;
+
+#ifdef SERIAL_DEBUG_DTR
+       printk("cyc: %d: raising DTR\n", __LINE__);
+       printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
+              base_addr[CyMSVR2]);
+#endif
+
+       base_addr[CyIER] |= CyRxData;
+       info->flags |= ASYNC_INITIALIZED;
+
+       if (info->tty) {
+               clear_bit(TTY_IO_ERROR, &info->tty->flags);
+       }
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+       local_irq_restore(flags);
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk(" done\n");
+#endif
+       return 0;
+}                              /* startup */
+
+void start_xmit(struct cyclades_port *info)
+{
+       unsigned long flags;
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       int channel;
+
+       channel = info->line;
+       local_irq_save(flags);
+       base_addr[CyCAR] = channel;
+       base_addr[CyIER] |= CyTxMpty;
+       local_irq_restore(flags);
+}                              /* 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 shutdown(struct cyclades_port *info)
+{
+       unsigned long flags;
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       int channel;
+
+       if (!(info->flags & ASYNC_INITIALIZED)) {
+/* CP('$'); */
+               return;
+       }
+
+       channel = info->line;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("shutdown channel %d\n", channel);
+#endif
+
+       /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE
+          SENT BEFORE DROPPING THE LINE !!!  (Perhaps
+          set some flag that is read when XMTY happens.)
+          Other choices are to delay some fixed interval
+          or schedule some later processing.
+        */
+       local_irq_save(flags);
+       if (info->xmit_buf) {
+               free_page((unsigned long)info->xmit_buf);
+               info->xmit_buf = NULL;
+       }
+
+       base_addr[CyCAR] = (u_char) channel;
+       if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+               base_addr[CyMSVR1] = 0;
+/* CP('C');CP('1'); */
+               base_addr[CyMSVR2] = 0;
+#ifdef SERIAL_DEBUG_DTR
+               printk("cyc: %d: dropping DTR\n", __LINE__);
+               printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
+                      base_addr[CyMSVR2]);
+#endif
+       }
+       write_cy_cmd(base_addr, CyDIS_RCVR);
+       /* it may be appropriate to clear _XMIT at
+          some later date (after testing)!!! */
+
+       if (info->tty) {
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+       }
+       info->flags &= ~ASYNC_INITIALIZED;
+       local_irq_restore(flags);
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk(" done\n");
+#endif
+}                              /* shutdown */
+
+/*
+ * This routine finds or computes the various line characteristics.
+ */
+static void config_setup(struct cyclades_port *info)
+{
+       unsigned long flags;
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       int channel;
+       unsigned cflag;
+       int i;
+       unsigned char ti, need_init_chan = 0;
+
+       if (!info->tty || !info->tty->termios) {
+               return;
+       }
+       if (info->line == -1) {
+               return;
+       }
+       cflag = info->tty->termios->c_cflag;
+
+       /* baud rate */
+       i = cflag & CBAUD;
+#ifdef CBAUDEX
+/* Starting with kernel 1.1.65, there is direct support for
+   higher baud rates.  The following code supports those
+   changes.  The conditional aspect allows this driver to be
+   used for earlier as well as later kernel versions.  (The
+   mapping is slightly different from serial.c because there
+   is still the possibility of supporting 75 kbit/sec with
+   the Cyclades board.)
+ */
+       if (i & CBAUDEX) {
+               if (i == B57600)
+                       i = 16;
+               else if (i == B115200)
+                       i = 18;
+#ifdef B78600
+               else if (i == B78600)
+                       i = 17;
+#endif
+               else
+                       info->tty->termios->c_cflag &= ~CBAUDEX;
+       }
+#endif
+       if (i == 15) {
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                       i += 1;
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                       i += 3;
+       }
+       /* Don't ever change the speed of the console port.  It will
+        * run at the speed specified in bootinfo, or at 19.2K */
+       /* Actually, it should run at whatever speed 166Bug was using */
+       /* Note info->timeout isn't used at present */
+       if (info != serial_console_info) {
+               info->tbpr = baud_bpr[i];       /* Tx BPR */
+               info->tco = baud_co[i]; /* Tx CO */
+               info->rbpr = baud_bpr[i];       /* Rx BPR */
+               info->rco = baud_co[i] >> 5;    /* Rx CO */
+               if (baud_table[i] == 134) {
+                       info->timeout =
+                           (info->xmit_fifo_size * HZ * 30 / 269) + 2;
+                       /* get it right for 134.5 baud */
+               } 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->cor7 = 0;
+       info->cor6 = 0;
+       info->cor5 = 0;
+       info->cor4 = (info->default_threshold ? info->default_threshold : baud_cor4[i]);        /* receive threshold */
+       /* Following two lines added 101295, RGH. */
+       /* It is obviously wrong to access CyCORx, and not info->corx here,
+        * try and remember to fix it later! */
+       channel = info->line;
+       base_addr[CyCAR] = (u_char) channel;
+       if (C_CLOCAL(info->tty)) {
+               if (base_addr[CyIER] & CyMdmCh)
+                       base_addr[CyIER] &= ~CyMdmCh;   /* without modem intr */
+               /* ignore 1->0 modem transitions */
+               if (base_addr[CyCOR4] & (CyDSR | CyCTS | CyDCD))
+                       base_addr[CyCOR4] &= ~(CyDSR | CyCTS | CyDCD);
+               /* ignore 0->1 modem transitions */
+               if (base_addr[CyCOR5] & (CyDSR | CyCTS | CyDCD))
+                       base_addr[CyCOR5] &= ~(CyDSR | CyCTS | CyDCD);
+       } else {
+               if ((base_addr[CyIER] & CyMdmCh) != CyMdmCh)
+                       base_addr[CyIER] |= CyMdmCh;    /* with modem intr */
+               /* act on 1->0 modem transitions */
+               if ((base_addr[CyCOR4] & (CyDSR | CyCTS | CyDCD)) !=
+                   (CyDSR | CyCTS | CyDCD))
+                       base_addr[CyCOR4] |= CyDSR | CyCTS | CyDCD;
+               /* act on 0->1 modem transitions */
+               if ((base_addr[CyCOR5] & (CyDSR | CyCTS | CyDCD)) !=
+                   (CyDSR | CyCTS | CyDCD))
+                       base_addr[CyCOR5] |= CyDSR | CyCTS | CyDCD;
+       }
+       info->cor3 = (cflag & CSTOPB) ? Cy_2_STOP : Cy_1_STOP;
+       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 & PARENB) {
+               if (cflag & PARODD) {
+                       info->cor1 |= CyPARITY_O;
+               } else {
+                       info->cor1 |= CyPARITY_E;
+               }
+       } else {
+               info->cor1 |= CyPARITY_NONE;
+       }
+
+       /* CTS flow control flag */
+#if 0
+       /* Don't complcate matters for now! RGH 141095 */
+       if (cflag & CRTSCTS) {
+               info->flags |= ASYNC_CTS_FLOW;
+               info->cor2 |= CyCtsAE;
+       } else {
+               info->flags &= ~ASYNC_CTS_FLOW;
+               info->cor2 &= ~CyCtsAE;
+       }
+#endif
+       if (cflag & CLOCAL)
+               info->flags &= ~ASYNC_CHECK_CD;
+       else
+               info->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 = info->line;
+
+       local_irq_save(flags);
+       base_addr[CyCAR] = (u_char) channel;
+
+       /* CyCMR set once only in mvme167_init_serial() */
+       if (base_addr[CyLICR] != channel << 2)
+               base_addr[CyLICR] = channel << 2;
+       if (base_addr[CyLIVR] != 0x5c)
+               base_addr[CyLIVR] = 0x5c;
+
+       /* tx and rx baud rate */
+
+       if (base_addr[CyCOR1] != info->cor1)
+               need_init_chan = 1;
+       if (base_addr[CyTCOR] != info->tco)
+               base_addr[CyTCOR] = info->tco;
+       if (base_addr[CyTBPR] != info->tbpr)
+               base_addr[CyTBPR] = info->tbpr;
+       if (base_addr[CyRCOR] != info->rco)
+               base_addr[CyRCOR] = info->rco;
+       if (base_addr[CyRBPR] != info->rbpr)
+               base_addr[CyRBPR] = info->rbpr;
+
+       /* set line characteristics  according configuration */
+
+       if (base_addr[CySCHR1] != START_CHAR(info->tty))
+               base_addr[CySCHR1] = START_CHAR(info->tty);
+       if (base_addr[CySCHR2] != STOP_CHAR(info->tty))
+               base_addr[CySCHR2] = STOP_CHAR(info->tty);
+       if (base_addr[CySCRL] != START_CHAR(info->tty))
+               base_addr[CySCRL] = START_CHAR(info->tty);
+       if (base_addr[CySCRH] != START_CHAR(info->tty))
+               base_addr[CySCRH] = START_CHAR(info->tty);
+       if (base_addr[CyCOR1] != info->cor1)
+               base_addr[CyCOR1] = info->cor1;
+       if (base_addr[CyCOR2] != info->cor2)
+               base_addr[CyCOR2] = info->cor2;
+       if (base_addr[CyCOR3] != info->cor3)
+               base_addr[CyCOR3] = info->cor3;
+       if (base_addr[CyCOR4] != info->cor4)
+               base_addr[CyCOR4] = info->cor4;
+       if (base_addr[CyCOR5] != info->cor5)
+               base_addr[CyCOR5] = info->cor5;
+       if (base_addr[CyCOR6] != info->cor6)
+               base_addr[CyCOR6] = info->cor6;
+       if (base_addr[CyCOR7] != info->cor7)
+               base_addr[CyCOR7] = info->cor7;
+
+       if (need_init_chan)
+               write_cy_cmd(base_addr, CyINIT_CHAN);
+
+       base_addr[CyCAR] = (u_char) channel;    /* !!! Is this needed? */
+
+       /* 2ms default rx timeout */
+       ti = info->default_timeout ? info->default_timeout : 0x02;
+       if (base_addr[CyRTPRL] != ti)
+               base_addr[CyRTPRL] = ti;
+       if (base_addr[CyRTPRH] != 0)
+               base_addr[CyRTPRH] = 0;
+
+       /* Set up RTS here also ????? RGH 141095 */
+       if (i == 0) {           /* baud rate is zero, turn off line */
+               if ((base_addr[CyMSVR2] & CyDTR) == CyDTR)
+                       base_addr[CyMSVR2] = 0;
+#ifdef SERIAL_DEBUG_DTR
+               printk("cyc: %d: dropping DTR\n", __LINE__);
+               printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
+                      base_addr[CyMSVR2]);
+#endif
+       } else {
+               if ((base_addr[CyMSVR2] & CyDTR) != CyDTR)
+                       base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+               printk("cyc: %d: raising DTR\n", __LINE__);
+               printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
+                      base_addr[CyMSVR2]);
+#endif
+       }
+
+       if (info->tty) {
+               clear_bit(TTY_IO_ERROR, &info->tty->flags);
+       }
+
+       local_irq_restore(flags);
+
+}                              /* config_setup */
+
+static int cy_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct cyclades_port *info = tty->driver_data;
+       unsigned long flags;
+
+#ifdef SERIAL_DEBUG_IO
+       printk("cy_put_char %s(0x%02x)\n", tty->name, ch);
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_put_char"))
+               return 0;
+
+       if (!info->xmit_buf)
+               return 0;
+
+       local_irq_save(flags);
+       if (info->xmit_cnt >= PAGE_SIZE - 1) {
+               local_irq_restore(flags);
+               return 0;
+       }
+
+       info->xmit_buf[info->xmit_head++] = ch;
+       info->xmit_head &= PAGE_SIZE - 1;
+       info->xmit_cnt++;
+       local_irq_restore(flags);
+       return 1;
+}                              /* cy_put_char */
+
+static void cy_flush_chars(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+       unsigned long flags;
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       int channel;
+
+#ifdef SERIAL_DEBUG_IO
+       printk("cy_flush_chars %s\n", tty->name);       /* */
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_flush_chars"))
+               return;
+
+       if (info->xmit_cnt <= 0 || tty->stopped
+           || tty->hw_stopped || !info->xmit_buf)
+               return;
+
+       channel = info->line;
+
+       local_irq_save(flags);
+       base_addr[CyCAR] = channel;
+       base_addr[CyIER] |= CyTxMpty;
+       local_irq_restore(flags);
+}                              /* cy_flush_chars */
+
+/* This routine gets called when tty_write has put something into
+    the write_queue.  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, total = 0;
+
+#ifdef SERIAL_DEBUG_IO
+       printk("cy_write %s\n", tty->name);     /* */
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_write")) {
+               return 0;
+       }
+
+       if (!info->xmit_buf) {
+               return 0;
+       }
+
+       while (1) {
+               local_irq_save(flags);
+               c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                                         SERIAL_XMIT_SIZE - info->xmit_head));
+               if (c <= 0) {
+                       local_irq_restore(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;
+               local_irq_restore(flags);
+
+               buf += c;
+               count -= c;
+               total += c;
+       }
+
+       if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
+               start_xmit(info);
+       }
+       return total;
+}                              /* cy_write */
+
+static int cy_write_room(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+       int ret;
+
+#ifdef SERIAL_DEBUG_IO
+       printk("cy_write_room %s\n", tty->name);        /* */
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_write_room"))
+               return 0;
+       ret = PAGE_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;
+
+#ifdef SERIAL_DEBUG_IO
+       printk("cy_chars_in_buffer %s %d\n", tty->name, info->xmit_cnt);        /* */
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer"))
+               return 0;
+
+       return info->xmit_cnt;
+}                              /* cy_chars_in_buffer */
+
+static void cy_flush_buffer(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+       unsigned long flags;
+
+#ifdef SERIAL_DEBUG_IO
+       printk("cy_flush_buffer %s\n", tty->name);      /* */
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_flush_buffer"))
+               return;
+       local_irq_save(flags);
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       local_irq_restore(flags);
+       tty_wakeup(tty);
+}                              /* cy_flush_buffer */
+
+/* This routine is called by the upper-layer tty layer to signal
+   that incoming characters should be throttled or that the
+   throttle should be released.
+ */
+static void cy_throttle(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+       unsigned long flags;
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       int channel;
+
+#ifdef SERIAL_DEBUG_THROTTLE
+       char buf[64];
+
+       printk("throttle %s: %d....\n", tty_name(tty, buf),
+              tty->ldisc.chars_in_buffer(tty));
+       printk("cy_throttle %s\n", tty->name);
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_nthrottle")) {
+               return;
+       }
+
+       if (I_IXOFF(tty)) {
+               info->x_char = STOP_CHAR(tty);
+               /* Should use the "Send Special Character" feature!!! */
+       }
+
+       channel = info->line;
+
+       local_irq_save(flags);
+       base_addr[CyCAR] = (u_char) channel;
+       base_addr[CyMSVR1] = 0;
+       local_irq_restore(flags);
+}                              /* cy_throttle */
+
+static void cy_unthrottle(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+       unsigned long flags;
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       int channel;
+
+#ifdef SERIAL_DEBUG_THROTTLE
+       char buf[64];
+
+       printk("throttle %s: %d....\n", tty_name(tty, buf),
+              tty->ldisc.chars_in_buffer(tty));
+       printk("cy_unthrottle %s\n", tty->name);
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_nthrottle")) {
+               return;
+       }
+
+       if (I_IXOFF(tty)) {
+               info->x_char = START_CHAR(tty);
+               /* Should use the "Send Special Character" feature!!! */
+       }
+
+       channel = info->line;
+
+       local_irq_save(flags);
+       base_addr[CyCAR] = (u_char) channel;
+       base_addr[CyMSVR1] = CyRTS;
+       local_irq_restore(flags);
+}                              /* cy_unthrottle */
+
+static int
+get_serial_info(struct cyclades_port *info,
+               struct serial_struct __user * retinfo)
+{
+       struct serial_struct tmp;
+
+/* CP('g'); */
+       if (!retinfo)
+               return -EFAULT;
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.type = info->type;
+       tmp.line = info->line;
+       tmp.port = info->line;
+       tmp.irq = 0;
+       tmp.flags = info->flags;
+       tmp.baud_base = 0;      /*!!! */
+       tmp.close_delay = info->close_delay;
+       tmp.custom_divisor = 0; /*!!! */
+       tmp.hub6 = 0;           /*!!! */
+       return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;
+}                              /* get_serial_info */
+
+static int
+set_serial_info(struct cyclades_port *info,
+               struct serial_struct __user * new_info)
+{
+       struct serial_struct new_serial;
+       struct cyclades_port old_info;
+
+/* CP('s'); */
+       if (!new_info)
+               return -EFAULT;
+       if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
+               return -EFAULT;
+       old_info = *info;
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               if ((new_serial.close_delay != info->close_delay) ||
+                   ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) !=
+                    (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)))
+                       return -EPERM;
+               info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+                              (new_serial.flags & ASYNC_USR_MASK));
+               goto check_and_exit;
+       }
+
+       /*
+        * OK, past this point, all the error checking has been done.
+        * At this point, we start making changes.....
+        */
+
+       info->flags = ((info->flags & ~ASYNC_FLAGS) |
+                      (new_serial.flags & ASYNC_FLAGS));
+       info->close_delay = new_serial.close_delay;
+
+check_and_exit:
+       if (info->flags & ASYNC_INITIALIZED) {
+               config_setup(info);
+               return 0;
+       }
+       return startup(info);
+}                              /* set_serial_info */
+
+static int cy_tiocmget(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+       int channel;
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       unsigned long flags;
+       unsigned char status;
+
+       channel = info->line;
+
+       local_irq_save(flags);
+       base_addr[CyCAR] = (u_char) channel;
+       status = base_addr[CyMSVR1] | base_addr[CyMSVR2];
+       local_irq_restore(flags);
+
+       return ((status & CyRTS) ? TIOCM_RTS : 0)
+           | ((status & CyDTR) ? TIOCM_DTR : 0)
+           | ((status & CyDCD) ? TIOCM_CAR : 0)
+           | ((status & CyDSR) ? TIOCM_DSR : 0)
+           | ((status & CyCTS) ? TIOCM_CTS : 0);
+}                              /* cy_tiocmget */
+
+static int
+cy_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear)
+{
+       struct cyclades_port *info = tty->driver_data;
+       int channel;
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       unsigned long flags;
+
+       channel = info->line;
+
+       if (set & TIOCM_RTS) {
+               local_irq_save(flags);
+               base_addr[CyCAR] = (u_char) channel;
+               base_addr[CyMSVR1] = CyRTS;
+               local_irq_restore(flags);
+       }
+       if (set & TIOCM_DTR) {
+               local_irq_save(flags);
+               base_addr[CyCAR] = (u_char) channel;
+/* CP('S');CP('2'); */
+               base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+               printk("cyc: %d: raising DTR\n", __LINE__);
+               printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
+                      base_addr[CyMSVR2]);
+#endif
+               local_irq_restore(flags);
+       }
+
+       if (clear & TIOCM_RTS) {
+               local_irq_save(flags);
+               base_addr[CyCAR] = (u_char) channel;
+               base_addr[CyMSVR1] = 0;
+               local_irq_restore(flags);
+       }
+       if (clear & TIOCM_DTR) {
+               local_irq_save(flags);
+               base_addr[CyCAR] = (u_char) channel;
+/* CP('C');CP('2'); */
+               base_addr[CyMSVR2] = 0;
+#ifdef SERIAL_DEBUG_DTR
+               printk("cyc: %d: dropping DTR\n", __LINE__);
+               printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
+                      base_addr[CyMSVR2]);
+#endif
+               local_irq_restore(flags);
+       }
+
+       return 0;
+}                              /* set_modem_info */
+
+static void send_break(struct cyclades_port *info, int duration)
+{                              /* Let the transmit ISR take care of this (since it
+                                  requires stuffing characters into the output stream).
+                                */
+       info->x_break = duration;
+       if (!info->xmit_cnt) {
+               start_xmit(info);
+       }
+}                              /* send_break */
+
+static int
+get_mon_info(struct cyclades_port *info, struct cyclades_monitor __user * mon)
+{
+
+       if (copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor)))
+               return -EFAULT;
+       info->mon.int_count = 0;
+       info->mon.char_count = 0;
+       info->mon.char_max = 0;
+       info->mon.char_last = 0;
+       return 0;
+}
+
+static int set_threshold(struct cyclades_port *info, unsigned long __user * arg)
+{
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       unsigned long value;
+       int channel;
+
+       if (get_user(value, arg))
+               return -EFAULT;
+
+       channel = info->line;
+       info->cor4 &= ~CyREC_FIFO;
+       info->cor4 |= value & CyREC_FIFO;
+       base_addr[CyCOR4] = info->cor4;
+       return 0;
+}
+
+static int
+get_threshold(struct cyclades_port *info, unsigned long __user * value)
+{
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       int channel;
+       unsigned long tmp;
+
+       channel = info->line;
+
+       tmp = base_addr[CyCOR4] & CyREC_FIFO;
+       return put_user(tmp, value);
+}
+
+static int
+set_default_threshold(struct cyclades_port *info, unsigned long __user * arg)
+{
+       unsigned long value;
+
+       if (get_user(value, arg))
+               return -EFAULT;
+
+       info->default_threshold = value & 0x0f;
+       return 0;
+}
+
+static int
+get_default_threshold(struct cyclades_port *info, unsigned long __user * value)
+{
+       return put_user(info->default_threshold, value);
+}
+
+static int set_timeout(struct cyclades_port *info, unsigned long __user * arg)
+{
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       int channel;
+       unsigned long value;
+
+       if (get_user(value, arg))
+               return -EFAULT;
+
+       channel = info->line;
+
+       base_addr[CyRTPRL] = value & 0xff;
+       base_addr[CyRTPRH] = (value >> 8) & 0xff;
+       return 0;
+}
+
+static int get_timeout(struct cyclades_port *info, unsigned long __user * value)
+{
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       int channel;
+       unsigned long tmp;
+
+       channel = info->line;
+
+       tmp = base_addr[CyRTPRL];
+       return put_user(tmp, value);
+}
+
+static int set_default_timeout(struct cyclades_port *info, unsigned long value)
+{
+       info->default_timeout = value & 0xff;
+       return 0;
+}
+
+static int
+get_default_timeout(struct cyclades_port *info, unsigned long __user * value)
+{
+       return put_user(info->default_timeout, value);
+}
+
+static int
+cy_ioctl(struct tty_struct *tty,
+        unsigned int cmd, unsigned long arg)
+{
+       struct cyclades_port *info = tty->driver_data;
+       int ret_val = 0;
+       void __user *argp = (void __user *)arg;
+
+#ifdef SERIAL_DEBUG_OTHER
+       printk("cy_ioctl %s, cmd = %x arg = %lx\n", tty->name, cmd, arg);       /* */
+#endif
+
+       tty_lock();
+
+       switch (cmd) {
+       case CYGETMON:
+               ret_val = get_mon_info(info, argp);
+               break;
+       case CYGETTHRESH:
+               ret_val = get_threshold(info, argp);
+               break;
+       case CYSETTHRESH:
+               ret_val = set_threshold(info, argp);
+               break;
+       case CYGETDEFTHRESH:
+               ret_val = get_default_threshold(info, argp);
+               break;
+       case CYSETDEFTHRESH:
+               ret_val = set_default_threshold(info, argp);
+               break;
+       case CYGETTIMEOUT:
+               ret_val = get_timeout(info, argp);
+               break;
+       case CYSETTIMEOUT:
+               ret_val = set_timeout(info, argp);
+               break;
+       case CYGETDEFTIMEOUT:
+               ret_val = get_default_timeout(info, argp);
+               break;
+       case CYSETDEFTIMEOUT:
+               ret_val = set_default_timeout(info, (unsigned long)arg);
+               break;
+       case TCSBRK:            /* SVID version: non-zero arg --> no break */
+               ret_val = tty_check_change(tty);
+               if (ret_val)
+                       break;
+               tty_wait_until_sent(tty, 0);
+               if (!arg)
+                       send_break(info, HZ / 4);       /* 1/4 second */
+               break;
+       case TCSBRKP:           /* support for POSIX tcsendbreak() */
+               ret_val = tty_check_change(tty);
+               if (ret_val)
+                       break;
+               tty_wait_until_sent(tty, 0);
+               send_break(info, arg ? arg * (HZ / 10) : HZ / 4);
+               break;
+
+/* The following commands are incompletely implemented!!! */
+       case TIOCGSERIAL:
+               ret_val = get_serial_info(info, argp);
+               break;
+       case TIOCSSERIAL:
+               ret_val = set_serial_info(info, argp);
+               break;
+       default:
+               ret_val = -ENOIOCTLCMD;
+       }
+       tty_unlock();
+
+#ifdef SERIAL_DEBUG_OTHER
+       printk("cy_ioctl done\n");
+#endif
+
+       return ret_val;
+}                              /* cy_ioctl */
+
+static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+       struct cyclades_port *info = tty->driver_data;
+
+#ifdef SERIAL_DEBUG_OTHER
+       printk("cy_set_termios %s\n", tty->name);
+#endif
+
+       if (tty->termios->c_cflag == old_termios->c_cflag)
+               return;
+       config_setup(info);
+
+       if ((old_termios->c_cflag & CRTSCTS) &&
+           !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->stopped = 0;
+               cy_start(tty);
+       }
+#ifdef tytso_patch_94Nov25_1726
+       if (!(old_termios->c_cflag & CLOCAL) &&
+           (tty->termios->c_cflag & CLOCAL))
+               wake_up_interruptible(&info->open_wait);
+#endif
+}                              /* cy_set_termios */
+
+static void cy_close(struct tty_struct *tty, struct file *filp)
+{
+       struct cyclades_port *info = tty->driver_data;
+
+/* CP('C'); */
+#ifdef SERIAL_DEBUG_OTHER
+       printk("cy_close %s\n", tty->name);
+#endif
+
+       if (!info || serial_paranoia_check(info, tty->name, "cy_close")) {
+               return;
+       }
+#ifdef SERIAL_DEBUG_OPEN
+       printk("cy_close %s, count = %d\n", tty->name, info->count);
+#endif
+
+       if ((tty->count == 1) && (info->count != 1)) {
+               /*
+                * Uh, oh.  tty->count is 1, which means that the tty
+                * structure will be freed.  Info->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("cy_close: bad serial port count; tty->count is 1, "
+                      "info->count is %d\n", info->count);
+               info->count = 1;
+       }
+#ifdef SERIAL_DEBUG_COUNT
+       printk("cyc: %d: decrementing count to %d\n", __LINE__,
+              info->count - 1);
+#endif
+       if (--info->count < 0) {
+               printk("cy_close: bad serial port count for ttys%d: %d\n",
+                      info->line, info->count);
+#ifdef SERIAL_DEBUG_COUNT
+               printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+               info->count = 0;
+       }
+       if (info->count)
+               return;
+       info->flags |= ASYNC_CLOSING;
+       if (info->flags & ASYNC_INITIALIZED)
+               tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
+       shutdown(info);
+       cy_flush_buffer(tty);
+       tty_ldisc_flush(tty);
+       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);
+
+#ifdef SERIAL_DEBUG_OTHER
+       printk("cy_close done\n");
+#endif
+}                              /* cy_close */
+
+/*
+ * cy_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void cy_hangup(struct tty_struct *tty)
+{
+       struct cyclades_port *info = tty->driver_data;
+
+#ifdef SERIAL_DEBUG_OTHER
+       printk("cy_hangup %s\n", tty->name);    /* */
+#endif
+
+       if (serial_paranoia_check(info, tty->name, "cy_hangup"))
+               return;
+
+       shutdown(info);
+#if 0
+       info->event = 0;
+       info->count = 0;
+#ifdef SERIAL_DEBUG_COUNT
+       printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+       info->tty = 0;
+#endif
+       info->flags &= ~ASYNC_NORMAL_ACTIVE;
+       wake_up_interruptible(&info->open_wait);
+}                              /* cy_hangup */
+
+/*
+ * ------------------------------------------------------------
+ * cy_open() and friends
+ * ------------------------------------------------------------
+ */
+
+static int
+block_til_ready(struct tty_struct *tty, struct file *filp,
+               struct cyclades_port *info)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       unsigned long flags;
+       int channel;
+       int retval;
+       volatile u_char *base_addr = (u_char *) BASE_ADDR;
+
+       /*
+        * If the device is in the middle of being closed, then block
+        * until it's done, and then try again.
+        */
+       if (info->flags & ASYNC_CLOSING) {
+               interruptible_sleep_on(&info->close_wait);
+               if (info->flags & ASYNC_HUP_NOTIFY) {
+                       return -EAGAIN;
+               } else {
+                       return -ERESTARTSYS;
+               }
+       }
+
+       /*
+        * If non-blocking mode is set, then make the check up front
+        * and then exit.
+        */
+       if (filp->f_flags & O_NONBLOCK) {
+               info->flags |= ASYNC_NORMAL_ACTIVE;
+               return 0;
+       }
+
+       /*
+        * Block waiting for the carrier detect and the line to become
+        * free (i.e., not in use by the callout).  While we are in
+        * this loop, info->count is dropped by one, so that
+        * cy_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: %s, count = %d\n",
+              tty->name, info->count);
+       /**/
+#endif
+           info->count--;
+#ifdef SERIAL_DEBUG_COUNT
+       printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count);
+#endif
+       info->blocked_open++;
+
+       channel = info->line;
+
+       while (1) {
+               local_irq_save(flags);
+               base_addr[CyCAR] = (u_char) channel;
+               base_addr[CyMSVR1] = CyRTS;
+/* CP('S');CP('4'); */
+               base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+               printk("cyc: %d: raising DTR\n", __LINE__);
+               printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
+                      base_addr[CyMSVR2]);
+#endif
+               local_irq_restore(flags);
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (tty_hung_up_p(filp)
+                   || !(info->flags & ASYNC_INITIALIZED)) {
+                       if (info->flags & ASYNC_HUP_NOTIFY) {
+                               retval = -EAGAIN;
+                       } else {
+                               retval = -ERESTARTSYS;
+                       }
+                       break;
+               }
+               local_irq_save(flags);
+               base_addr[CyCAR] = (u_char) channel;
+/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */
+               if (!(info->flags & ASYNC_CLOSING)
+                   && (C_CLOCAL(tty)
+                       || (base_addr[CyMSVR1] & CyDCD))) {
+                       local_irq_restore(flags);
+                       break;
+               }
+               local_irq_restore(flags);
+               if (signal_pending(current)) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+#ifdef SERIAL_DEBUG_OPEN
+               printk("block_til_ready blocking: %s, count = %d\n",
+                      tty->name, info->count);
+               /**/
+#endif
+               tty_unlock();
+               schedule();
+               tty_lock();
+       }
+       __set_current_state(TASK_RUNNING);
+       remove_wait_queue(&info->open_wait, &wait);
+       if (!tty_hung_up_p(filp)) {
+               info->count++;
+#ifdef SERIAL_DEBUG_COUNT
+               printk("cyc: %d: incrementing count to %d\n", __LINE__,
+                      info->count);
+#endif
+       }
+       info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+       printk("block_til_ready after blocking: %s, count = %d\n",
+              tty->name, info->count);
+       /**/
+#endif
+           if (retval)
+               return retval;
+       info->flags |= ASYNC_NORMAL_ACTIVE;
+       return 0;
+}                              /* block_til_ready */
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * performs the serial-specific initialization for the tty structure.
+ */
+int cy_open(struct tty_struct *tty, struct file *filp)
+{
+       struct cyclades_port *info;
+       int retval, line;
+
+/* CP('O'); */
+       line = tty->index;
+       if ((line < 0) || (NR_PORTS <= line)) {
+               return -ENODEV;
+       }
+       info = &cy_port[line];
+       if (info->line < 0) {
+               return -ENODEV;
+       }
+#ifdef SERIAL_DEBUG_OTHER
+       printk("cy_open %s\n", tty->name);      /* */
+#endif
+       if (serial_paranoia_check(info, tty->name, "cy_open")) {
+               return -ENODEV;
+       }
+#ifdef SERIAL_DEBUG_OPEN
+       printk("cy_open %s, count = %d\n", tty->name, info->count);
+       /**/
+#endif
+           info->count++;
+#ifdef SERIAL_DEBUG_COUNT
+       printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count);
+#endif
+       tty->driver_data = info;
+       info->tty = tty;
+
+       /*
+        * 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("cy_open returning after block_til_ready with %d\n",
+                      retval);
+#endif
+               return retval;
+       }
+#ifdef SERIAL_DEBUG_OPEN
+       printk("cy_open done\n");
+       /**/
+#endif
+           return 0;
+}                              /* cy_open */
+
+/*
+ * ---------------------------------------------------------------------
+ * serial167_init() and friends
+ *
+ * serial167_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_version(void)
+{
+       printk("MVME166/167 cd2401 driver\n");
+}                              /* show_version */
+
+/* initialize chips on card -- return number of valid
+   chips (which is number of ports/4) */
+
+/*
+ * This initialises the hardware to a reasonable state.  It should
+ * probe the chip first so as to copy 166-Bug setup as a default for
+ * port 0.  It initialises CMR to CyASYNC; that is never done again, so
+ * as to limit the number of CyINIT_CHAN commands in normal running.
+ *
+ * ... I wonder what I should do if this fails ...
+ */
+
+void mvme167_serial_console_setup(int cflag)
+{
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       int ch;
+       u_char spd;
+       u_char rcor, rbpr, badspeed = 0;
+       unsigned long flags;
+
+       local_irq_save(flags);
+
+       /*
+        * First probe channel zero of the chip, to see what speed has
+        * been selected.
+        */
+
+       base_addr[CyCAR] = 0;
+
+       rcor = base_addr[CyRCOR] << 5;
+       rbpr = base_addr[CyRBPR];
+
+       for (spd = 0; spd < sizeof(baud_bpr); spd++)
+               if (rbpr == baud_bpr[spd] && rcor == baud_co[spd])
+                       break;
+       if (spd >= sizeof(baud_bpr)) {
+               spd = 14;       /* 19200 */
+               badspeed = 1;   /* Failed to identify speed */
+       }
+       initial_console_speed = spd;
+
+       /* OK, we have chosen a speed, now reset and reinitialise */
+
+       my_udelay(20000L);      /* Allow time for any active o/p to complete */
+       if (base_addr[CyCCR] != 0x00) {
+               local_irq_restore(flags);
+               /* printk(" chip is never idle (CCR != 0)\n"); */
+               return;
+       }
+
+       base_addr[CyCCR] = CyCHIP_RESET;        /* Reset the chip */
+       my_udelay(1000L);
+
+       if (base_addr[CyGFRCR] == 0x00) {
+               local_irq_restore(flags);
+               /* printk(" chip is not responding (GFRCR stayed 0)\n"); */
+               return;
+       }
+
+       /*
+        * System clock is 20Mhz, divided by 2048, so divide by 10 for a 1.0ms
+        * tick
+        */
+
+       base_addr[CyTPR] = 10;
+
+       base_addr[CyPILR1] = 0x01;      /* Interrupt level for modem change */
+       base_addr[CyPILR2] = 0x02;      /* Interrupt level for tx ints */
+       base_addr[CyPILR3] = 0x03;      /* Interrupt level for rx ints */
+
+       /*
+        * Attempt to set up all channels to something reasonable, and
+        * bang out a INIT_CHAN command.  We should then be able to limit
+        * the amount of fiddling we have to do in normal running.
+        */
+
+       for (ch = 3; ch >= 0; ch--) {
+               base_addr[CyCAR] = (u_char) ch;
+               base_addr[CyIER] = 0;
+               base_addr[CyCMR] = CyASYNC;
+               base_addr[CyLICR] = (u_char) ch << 2;
+               base_addr[CyLIVR] = 0x5c;
+               base_addr[CyTCOR] = baud_co[spd];
+               base_addr[CyTBPR] = baud_bpr[spd];
+               base_addr[CyRCOR] = baud_co[spd] >> 5;
+               base_addr[CyRBPR] = baud_bpr[spd];
+               base_addr[CySCHR1] = 'Q' & 0x1f;
+               base_addr[CySCHR2] = 'X' & 0x1f;
+               base_addr[CySCRL] = 0;
+               base_addr[CySCRH] = 0;
+               base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE;
+               base_addr[CyCOR2] = 0;
+               base_addr[CyCOR3] = Cy_1_STOP;
+               base_addr[CyCOR4] = baud_cor4[spd];
+               base_addr[CyCOR5] = 0;
+               base_addr[CyCOR6] = 0;
+               base_addr[CyCOR7] = 0;
+               base_addr[CyRTPRL] = 2;
+               base_addr[CyRTPRH] = 0;
+               base_addr[CyMSVR1] = 0;
+               base_addr[CyMSVR2] = 0;
+               write_cy_cmd(base_addr, CyINIT_CHAN | CyDIS_RCVR | CyDIS_XMTR);
+       }
+
+       /*
+        * Now do specials for channel zero....
+        */
+
+       base_addr[CyMSVR1] = CyRTS;
+       base_addr[CyMSVR2] = CyDTR;
+       base_addr[CyIER] = CyRxData;
+       write_cy_cmd(base_addr, CyENB_RCVR | CyENB_XMTR);
+
+       local_irq_restore(flags);
+
+       my_udelay(20000L);      /* Let it all settle down */
+
+       printk("CD2401 initialised,  chip is rev 0x%02x\n", base_addr[CyGFRCR]);
+       if (badspeed)
+               printk
+                   ("  WARNING:  Failed to identify line speed, rcor=%02x,rbpr=%02x\n",
+                    rcor >> 5, rbpr);
+}                              /* serial_console_init */
+
+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,
+       .tiocmget = cy_tiocmget,
+       .tiocmset = cy_tiocmset,
+};
+
+/* 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 64 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 int __init serial167_init(void)
+{
+       struct cyclades_port *info;
+       int ret = 0;
+       int good_ports = 0;
+       int port_num = 0;
+       int index;
+       int DefSpeed;
+#ifdef notyet
+       struct sigaction sa;
+#endif
+
+       if (!(mvme16x_config & MVME16x_CONFIG_GOT_CD2401))
+               return 0;
+
+       cy_serial_driver = alloc_tty_driver(NR_PORTS);
+       if (!cy_serial_driver)
+               return -ENOMEM;
+
+#if 0
+       scrn[1] = '\0';
+#endif
+
+       show_version();
+
+       /* Has "console=0,9600n8" been used in bootinfo to change speed? */
+       if (serial_console_cflag)
+               DefSpeed = serial_console_cflag & 0017;
+       else {
+               DefSpeed = initial_console_speed;
+               serial_console_info = &cy_port[0];
+               serial_console_cflag = DefSpeed | CS8;
+#if 0
+               serial_console = 64;    /*callout_driver.minor_start */
+#endif
+       }
+
+       /* Initialize the tty_driver structure */
+
+       cy_serial_driver->owner = THIS_MODULE;
+       cy_serial_driver->name = "ttyS";
+       cy_serial_driver->major = TTY_MAJOR;
+       cy_serial_driver->minor_start = 64;
+       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_set_operations(cy_serial_driver, &cy_ops);
+
+       ret = tty_register_driver(cy_serial_driver);
+       if (ret) {
+               printk(KERN_ERR "Couldn't register MVME166/7 serial driver\n");
+               put_tty_driver(cy_serial_driver);
+               return ret;
+       }
+
+       port_num = 0;
+       info = cy_port;
+       for (index = 0; index < 1; index++) {
+
+               good_ports = 4;
+
+               if (port_num < NR_PORTS) {
+                       while (good_ports-- && port_num < NR_PORTS) {
+               /*** initialize port ***/
+                               info->magic = CYCLADES_MAGIC;
+                               info->type = PORT_CIRRUS;
+                               info->card = index;
+                               info->line = port_num;
+                               info->flags = STD_COM_FLAGS;
+                               info->tty = NULL;
+                               info->xmit_fifo_size = 12;
+                               info->cor1 = CyPARITY_NONE | Cy_8_BITS;
+                               info->cor2 = CyETC;
+                               info->cor3 = Cy_1_STOP;
+                               info->cor4 = 0x08;      /* _very_ small receive threshold */
+                               info->cor5 = 0;
+                               info->cor6 = 0;
+                               info->cor7 = 0;
+                               info->tbpr = baud_bpr[DefSpeed];        /* Tx BPR */
+                               info->tco = baud_co[DefSpeed];  /* Tx CO */
+                               info->rbpr = baud_bpr[DefSpeed];        /* Rx BPR */
+                               info->rco = baud_co[DefSpeed] >> 5;     /* Rx CO */
+                               info->close_delay = 0;
+                               info->x_char = 0;
+                               info->count = 0;
+#ifdef SERIAL_DEBUG_COUNT
+                               printk("cyc: %d: setting count to 0\n",
+                                      __LINE__);
+#endif
+                               info->blocked_open = 0;
+                               info->default_threshold = 0;
+                               info->default_timeout = 0;
+                               init_waitqueue_head(&info->open_wait);
+                               init_waitqueue_head(&info->close_wait);
+                               /* info->session */
+                               /* info->pgrp */
+/*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/
+                               info->read_status_mask =
+                                   CyTIMEOUT | CySPECHAR | CyBREAK | CyPARITY |
+                                   CyFRAME | CyOVERRUN;
+                               /* info->timeout */
+
+                               printk("ttyS%d ", info->line);
+                               port_num++;
+                               info++;
+                               if (!(port_num & 7)) {
+                                       printk("\n               ");
+                               }
+                       }
+               }
+               printk("\n");
+       }
+       while (port_num < NR_PORTS) {
+               info->line = -1;
+               port_num++;
+               info++;
+       }
+
+       ret = request_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt, 0,
+                         "cd2401_errors", cd2401_rxerr_interrupt);
+       if (ret) {
+               printk(KERN_ERR "Could't get cd2401_errors IRQ");
+               goto cleanup_serial_driver;
+       }
+
+       ret = request_irq(MVME167_IRQ_SER_MODEM, cd2401_modem_interrupt, 0,
+                         "cd2401_modem", cd2401_modem_interrupt);
+       if (ret) {
+               printk(KERN_ERR "Could't get cd2401_modem IRQ");
+               goto cleanup_irq_cd2401_errors;
+       }
+
+       ret = request_irq(MVME167_IRQ_SER_TX, cd2401_tx_interrupt, 0,
+                         "cd2401_txints", cd2401_tx_interrupt);
+       if (ret) {
+               printk(KERN_ERR "Could't get cd2401_txints IRQ");
+               goto cleanup_irq_cd2401_modem;
+       }
+
+       ret = request_irq(MVME167_IRQ_SER_RX, cd2401_rx_interrupt, 0,
+                         "cd2401_rxints", cd2401_rx_interrupt);
+       if (ret) {
+               printk(KERN_ERR "Could't get cd2401_rxints IRQ");
+               goto cleanup_irq_cd2401_txints;
+       }
+
+       /* Now we have registered the interrupt handlers, allow the interrupts */
+
+       pcc2chip[PccSCCMICR] = 0x15;    /* Serial ints are level 5 */
+       pcc2chip[PccSCCTICR] = 0x15;
+       pcc2chip[PccSCCRICR] = 0x15;
+
+       pcc2chip[PccIMLR] = 3;  /* Allow PCC2 ints above 3!? */
+
+       return 0;
+cleanup_irq_cd2401_txints:
+       free_irq(MVME167_IRQ_SER_TX, cd2401_tx_interrupt);
+cleanup_irq_cd2401_modem:
+       free_irq(MVME167_IRQ_SER_MODEM, cd2401_modem_interrupt);
+cleanup_irq_cd2401_errors:
+       free_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt);
+cleanup_serial_driver:
+       if (tty_unregister_driver(cy_serial_driver))
+               printk(KERN_ERR
+                      "Couldn't unregister MVME166/7 serial driver\n");
+       put_tty_driver(cy_serial_driver);
+       return ret;
+}                              /* serial167_init */
+
+module_init(serial167_init);
+
+#ifdef CYCLOM_SHOW_STATUS
+static void show_status(int line_num)
+{
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       int channel;
+       struct cyclades_port *info;
+       unsigned long flags;
+
+       info = &cy_port[line_num];
+       channel = info->line;
+       printk("  channel %d\n", channel);
+       /**/ printk(" cy_port\n");
+       printk("  card line flags = %d %d %x\n",
+              info->card, info->line, info->flags);
+       printk
+           ("  *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n",
+            (long)info->tty, info->read_status_mask, info->timeout,
+            info->xmit_fifo_size);
+       printk("  cor1,cor2,cor3,cor4,cor5,cor6,cor7 = %x %x %x %x %x %x %x\n",
+              info->cor1, info->cor2, info->cor3, info->cor4, info->cor5,
+              info->cor6, info->cor7);
+       printk("  tbpr,tco,rbpr,rco = %d %d %d %d\n", info->tbpr, info->tco,
+              info->rbpr, info->rco);
+       printk("  close_delay event count = %d %d %d\n", info->close_delay,
+              info->event, info->count);
+       printk("  x_char blocked_open = %x %x\n", info->x_char,
+              info->blocked_open);
+       printk("  open_wait = %lx %lx %lx\n", (long)info->open_wait);
+
+       local_irq_save(flags);
+
+/* Global Registers */
+
+       printk(" CyGFRCR %x\n", base_addr[CyGFRCR]);
+       printk(" CyCAR %x\n", base_addr[CyCAR]);
+       printk(" CyRISR %x\n", base_addr[CyRISR]);
+       printk(" CyTISR %x\n", base_addr[CyTISR]);
+       printk(" CyMISR %x\n", base_addr[CyMISR]);
+       printk(" CyRIR %x\n", base_addr[CyRIR]);
+       printk(" CyTIR %x\n", base_addr[CyTIR]);
+       printk(" CyMIR %x\n", base_addr[CyMIR]);
+       printk(" CyTPR %x\n", base_addr[CyTPR]);
+
+       base_addr[CyCAR] = (u_char) channel;
+
+/* Virtual Registers */
+
+#if 0
+       printk(" CyRIVR %x\n", base_addr[CyRIVR]);
+       printk(" CyTIVR %x\n", base_addr[CyTIVR]);
+       printk(" CyMIVR %x\n", base_addr[CyMIVR]);
+       printk(" CyMISR %x\n", base_addr[CyMISR]);
+#endif
+
+/* Channel Registers */
+
+       printk(" CyCCR %x\n", base_addr[CyCCR]);
+       printk(" CyIER %x\n", base_addr[CyIER]);
+       printk(" CyCOR1 %x\n", base_addr[CyCOR1]);
+       printk(" CyCOR2 %x\n", base_addr[CyCOR2]);
+       printk(" CyCOR3 %x\n", base_addr[CyCOR3]);
+       printk(" CyCOR4 %x\n", base_addr[CyCOR4]);
+       printk(" CyCOR5 %x\n", base_addr[CyCOR5]);
+#if 0
+       printk(" CyCCSR %x\n", base_addr[CyCCSR]);
+       printk(" CyRDCR %x\n", base_addr[CyRDCR]);
+#endif
+       printk(" CySCHR1 %x\n", base_addr[CySCHR1]);
+       printk(" CySCHR2 %x\n", base_addr[CySCHR2]);
+#if 0
+       printk(" CySCHR3 %x\n", base_addr[CySCHR3]);
+       printk(" CySCHR4 %x\n", base_addr[CySCHR4]);
+       printk(" CySCRL %x\n", base_addr[CySCRL]);
+       printk(" CySCRH %x\n", base_addr[CySCRH]);
+       printk(" CyLNC %x\n", base_addr[CyLNC]);
+       printk(" CyMCOR1 %x\n", base_addr[CyMCOR1]);
+       printk(" CyMCOR2 %x\n", base_addr[CyMCOR2]);
+#endif
+       printk(" CyRTPRL %x\n", base_addr[CyRTPRL]);
+       printk(" CyRTPRH %x\n", base_addr[CyRTPRH]);
+       printk(" CyMSVR1 %x\n", base_addr[CyMSVR1]);
+       printk(" CyMSVR2 %x\n", base_addr[CyMSVR2]);
+       printk(" CyRBPR %x\n", base_addr[CyRBPR]);
+       printk(" CyRCOR %x\n", base_addr[CyRCOR]);
+       printk(" CyTBPR %x\n", base_addr[CyTBPR]);
+       printk(" CyTCOR %x\n", base_addr[CyTCOR]);
+
+       local_irq_restore(flags);
+}                              /* show_status */
+#endif
+
+#if 0
+/* Dummy routine in mvme16x/config.c for now */
+
+/* Serial console setup. Called from linux/init/main.c */
+
+void console_setup(char *str, int *ints)
+{
+       char *s;
+       int baud, bits, parity;
+       int cflag = 0;
+
+       /* Sanity check. */
+       if (ints[0] > 3 || ints[1] > 3)
+               return;
+
+       /* Get baud, bits and parity */
+       baud = 2400;
+       bits = 8;
+       parity = 'n';
+       if (ints[2])
+               baud = ints[2];
+       if ((s = strchr(str, ','))) {
+               do {
+                       s++;
+               } while (*s >= '0' && *s <= '9');
+               if (*s)
+                       parity = *s++;
+               if (*s)
+                       bits = *s - '0';
+       }
+
+       /* Now construct a cflag setting. */
+       switch (baud) {
+       case 1200:
+               cflag |= B1200;
+               break;
+       case 9600:
+               cflag |= B9600;
+               break;
+       case 19200:
+               cflag |= B19200;
+               break;
+       case 38400:
+               cflag |= B38400;
+               break;
+       case 2400:
+       default:
+               cflag |= B2400;
+               break;
+       }
+       switch (bits) {
+       case 7:
+               cflag |= CS7;
+               break;
+       default:
+       case 8:
+               cflag |= CS8;
+               break;
+       }
+       switch (parity) {
+       case 'o':
+       case 'O':
+               cflag |= PARODD;
+               break;
+       case 'e':
+       case 'E':
+               cflag |= PARENB;
+               break;
+       }
+
+       serial_console_info = &cy_port[ints[1]];
+       serial_console_cflag = cflag;
+       serial_console = ints[1] + 64;  /*callout_driver.minor_start */
+}
+#endif
+
+/*
+ * The following is probably out of date for 2.1.x serial console stuff.
+ *
+ * The console is registered early on from arch/m68k/kernel/setup.c, and
+ * it therefore relies on the chip being setup correctly by 166-Bug.  This
+ * seems reasonable, as the serial port has been used to invoke the system
+ * boot.  It also means that this function must not rely on any data
+ * initialisation performed by serial167_init() etc.
+ *
+ * Of course, once the console has been registered, we had better ensure
+ * that serial167_init() doesn't leave the chip non-functional.
+ *
+ * The console must be locked when we get here.
+ */
+
+void serial167_console_write(struct console *co, const char *str,
+                            unsigned count)
+{
+       volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
+       unsigned long flags;
+       volatile u_char sink;
+       u_char ier;
+       int port;
+       u_char do_lf = 0;
+       int i = 0;
+
+       local_irq_save(flags);
+
+       /* Ensure transmitter is enabled! */
+
+       port = 0;
+       base_addr[CyCAR] = (u_char) port;
+       while (base_addr[CyCCR])
+               ;
+       base_addr[CyCCR] = CyENB_XMTR;
+
+       ier = base_addr[CyIER];
+       base_addr[CyIER] = CyTxMpty;
+
+       while (1) {
+               if (pcc2chip[PccSCCTICR] & 0x20) {
+                       /* We have a Tx int. Acknowledge it */
+                       sink = pcc2chip[PccTPIACKR];
+                       if ((base_addr[CyLICR] >> 2) == port) {
+                               if (i == count) {
+                                       /* Last char of string is now output */
+                                       base_addr[CyTEOIR] = CyNOTRANS;
+                                       break;
+                               }
+                               if (do_lf) {
+                                       base_addr[CyTDR] = '\n';
+                                       str++;
+                                       i++;
+                                       do_lf = 0;
+                               } else if (*str == '\n') {
+                                       base_addr[CyTDR] = '\r';
+                                       do_lf = 1;
+                               } else {
+                                       base_addr[CyTDR] = *str++;
+                                       i++;
+                               }
+                               base_addr[CyTEOIR] = 0;
+                       } else
+                               base_addr[CyTEOIR] = CyNOTRANS;
+               }
+       }
+
+       base_addr[CyIER] = ier;
+
+       local_irq_restore(flags);
+}
+
+static struct tty_driver *serial167_console_device(struct console *c,
+                                                  int *index)
+{
+       *index = c->index;
+       return cy_serial_driver;
+}
+
+static struct console sercons = {
+       .name = "ttyS",
+       .write = serial167_console_write,
+       .device = serial167_console_device,
+       .flags = CON_PRINTBUFFER,
+       .index = -1,
+};
+
+static int __init serial167_console_init(void)
+{
+       if (vme_brdtype == VME_TYPE_MVME166 ||
+           vme_brdtype == VME_TYPE_MVME167 ||
+           vme_brdtype == VME_TYPE_MVME177) {
+               mvme167_serial_console_setup(0);
+               register_console(&sercons);
+       }
+       return 0;
+}
+
+console_initcall(serial167_console_init);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/tty/specialix.c b/drivers/staging/tty/specialix.c
new file mode 100644 (file)
index 0000000..47e5753
--- /dev/null
@@ -0,0 +1,2368 @@
+/*
+ *      specialix.c  -- specialix IO8+ multiport serial driver.
+ *
+ *      Copyright (C) 1997  Roger Wolff (R.E.Wolff@BitWizard.nl)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (pgmdsg@ibi.com)
+ *
+ *      Specialix pays for the development and support of this driver.
+ *      Please DO contact io8-linux@specialix.co.uk if you require
+ *      support. But please read the documentation (specialix.txt)
+ *      first.
+ *
+ *      This driver was developped in the BitWizard linux device
+ *      driver service. If you require a linux device driver for your
+ *      product, please contact devices@BitWizard.nl for a quote.
+ *
+ *      This code is firmly based on the riscom/8 serial driver,
+ *      written by Dmitry Gorodchanin. The specialix IO8+ card
+ *      programming information was obtained from the CL-CD1865 Data
+ *      Book, and Specialix document number 6200059: IO8+ Hardware
+ *      Functional Specification.
+ *
+ *      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.
+ *
+ * Revision history:
+ *
+ * Revision 1.0:  April 1st 1997.
+ *                Initial release for alpha testing.
+ * Revision 1.1:  April 14th 1997.
+ *                Incorporated Richard Hudsons suggestions,
+ *                removed some debugging printk's.
+ * Revision 1.2:  April 15th 1997.
+ *                Ported to 2.1.x kernels.
+ * Revision 1.3:  April 17th 1997
+ *                Backported to 2.0. (Compatibility macros).
+ * Revision 1.4:  April 18th 1997
+ *                Fixed DTR/RTS bug that caused the card to indicate
+ *                "don't send data" to a modem after the password prompt.
+ *                Fixed bug for premature (fake) interrupts.
+ * Revision 1.5:  April 19th 1997
+ *                fixed a minor typo in the header file, cleanup a little.
+ *                performance warnings are now MAXed at once per minute.
+ * Revision 1.6:  May 23 1997
+ *                Changed the specialix=... format to include interrupt.
+ * Revision 1.7:  May 27 1997
+ *                Made many more debug printk's a compile time option.
+ * Revision 1.8:  Jul 1  1997
+ *                port to linux-2.1.43 kernel.
+ * Revision 1.9:  Oct 9  1998
+ *                Added stuff for the IO8+/PCI version.
+ * Revision 1.10: Oct 22  1999 / Jan 21 2000.
+ *                Added stuff for setserial.
+ *                Nicolas Mailhot (Nicolas.Mailhot@email.enst.fr)
+ *
+ */
+
+#define VERSION "1.11"
+
+
+/*
+ * There is a bunch of documentation about the card, jumpers, config
+ * settings, restrictions, cables, device names and numbers in
+ * Documentation/serial/specialix.txt
+ */
+
+#include <linux/module.h>
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/mm.h>
+#include <linux/serial.h>
+#include <linux/fcntl.h>
+#include <linux/major.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/gfp.h>
+
+#include "specialix_io8.h"
+#include "cd1865.h"
+
+
+/*
+   This driver can spew a whole lot of debugging output at you. If you
+   need maximum performance, you should disable the DEBUG define. To
+   aid in debugging in the field, I'm leaving the compile-time debug
+   features enabled, and disable them "runtime". That allows me to
+   instruct people with problems to enable debugging without requiring
+   them to recompile...
+*/
+#define DEBUG
+
+static int sx_debug;
+static int sx_rxfifo = SPECIALIX_RXFIFO;
+static int sx_rtscts;
+
+#ifdef DEBUG
+#define dprintk(f, str...) if (sx_debug & f) printk(str)
+#else
+#define dprintk(f, str...) /* nothing */
+#endif
+
+#define SX_DEBUG_FLOW    0x0001
+#define SX_DEBUG_DATA    0x0002
+#define SX_DEBUG_PROBE   0x0004
+#define SX_DEBUG_CHAN    0x0008
+#define SX_DEBUG_INIT    0x0010
+#define SX_DEBUG_RX      0x0020
+#define SX_DEBUG_TX      0x0040
+#define SX_DEBUG_IRQ     0x0080
+#define SX_DEBUG_OPEN    0x0100
+#define SX_DEBUG_TERMIOS 0x0200
+#define SX_DEBUG_SIGNALS 0x0400
+#define SX_DEBUG_FIFO    0x0800
+
+
+#define func_enter() dprintk(SX_DEBUG_FLOW, "io8: enter %s\n", __func__)
+#define func_exit()  dprintk(SX_DEBUG_FLOW, "io8: exit  %s\n", __func__)
+
+
+/* Configurable options: */
+
+/* Am I paranoid or not ? ;-) */
+#define SPECIALIX_PARANOIA_CHECK
+
+/*
+ * The following defines are mostly for testing purposes. But if you need
+ * some nice reporting in your syslog, you can define them also.
+ */
+#undef SX_REPORT_FIFO
+#undef SX_REPORT_OVERRUN
+
+
+
+
+#define SPECIALIX_LEGAL_FLAGS \
+       (ASYNC_HUP_NOTIFY   | ASYNC_SAK          | ASYNC_SPLIT_TERMIOS   | \
+        ASYNC_SPD_HI       | ASYNC_SPEED_VHI    | ASYNC_SESSION_LOCKOUT | \
+        ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
+
+static struct tty_driver *specialix_driver;
+
+static struct specialix_board sx_board[SX_NBOARD] =  {
+       { 0, SX_IOBASE1,  9, },
+       { 0, SX_IOBASE2, 11, },
+       { 0, SX_IOBASE3, 12, },
+       { 0, SX_IOBASE4, 15, },
+};
+
+static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
+
+
+static int sx_paranoia_check(struct specialix_port const *port,
+                                   char *name, const char *routine)
+{
+#ifdef SPECIALIX_PARANOIA_CHECK
+       static const char *badmagic = KERN_ERR
+         "sx: Warning: bad specialix port magic number for device %s in %s\n";
+       static const char *badinfo = KERN_ERR
+         "sx: Warning: null specialix port for device %s in %s\n";
+
+       if (!port) {
+               printk(badinfo, name, routine);
+               return 1;
+       }
+       if (port->magic != SPECIALIX_MAGIC) {
+               printk(badmagic, name, routine);
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+
+/*
+ *
+ *  Service functions for specialix IO8+ driver.
+ *
+ */
+
+/* Get board number from pointer */
+static inline int board_No(struct specialix_board *bp)
+{
+       return bp - sx_board;
+}
+
+
+/* Get port number from pointer */
+static inline int port_No(struct specialix_port const *port)
+{
+       return SX_PORT(port - sx_port);
+}
+
+
+/* Get pointer to board from pointer to port */
+static inline struct specialix_board *port_Board(
+                                       struct specialix_port const *port)
+{
+       return &sx_board[SX_BOARD(port - sx_port)];
+}
+
+
+/* Input Byte from CL CD186x register */
+static inline unsigned char sx_in(struct specialix_board *bp,
+                                                       unsigned short reg)
+{
+       bp->reg = reg | 0x80;
+       outb(reg | 0x80, bp->base + SX_ADDR_REG);
+       return inb(bp->base + SX_DATA_REG);
+}
+
+
+/* Output Byte to CL CD186x register */
+static inline void sx_out(struct specialix_board *bp, unsigned short reg,
+                         unsigned char val)
+{
+       bp->reg = reg | 0x80;
+       outb(reg | 0x80, bp->base + SX_ADDR_REG);
+       outb(val, bp->base + SX_DATA_REG);
+}
+
+
+/* Input Byte from CL CD186x register */
+static inline unsigned char sx_in_off(struct specialix_board *bp,
+                               unsigned short reg)
+{
+       bp->reg = reg;
+       outb(reg, bp->base + SX_ADDR_REG);
+       return inb(bp->base + SX_DATA_REG);
+}
+
+
+/* Output Byte to CL CD186x register */
+static inline void sx_out_off(struct specialix_board  *bp,
+                               unsigned short reg, unsigned char val)
+{
+       bp->reg = reg;
+       outb(reg, bp->base + SX_ADDR_REG);
+       outb(val, bp->base + SX_DATA_REG);
+}
+
+
+/* Wait for Channel Command Register ready */
+static void sx_wait_CCR(struct specialix_board  *bp)
+{
+       unsigned long delay, flags;
+       unsigned char ccr;
+
+       for (delay = SX_CCR_TIMEOUT; delay; delay--) {
+               spin_lock_irqsave(&bp->lock, flags);
+               ccr = sx_in(bp, CD186x_CCR);
+               spin_unlock_irqrestore(&bp->lock, flags);
+               if (!ccr)
+                       return;
+               udelay(1);
+       }
+
+       printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
+}
+
+
+/* Wait for Channel Command Register ready */
+static void sx_wait_CCR_off(struct specialix_board  *bp)
+{
+       unsigned long delay;
+       unsigned char crr;
+       unsigned long flags;
+
+       for (delay = SX_CCR_TIMEOUT; delay; delay--) {
+               spin_lock_irqsave(&bp->lock, flags);
+               crr = sx_in_off(bp, CD186x_CCR);
+               spin_unlock_irqrestore(&bp->lock, flags);
+               if (!crr)
+                       return;
+               udelay(1);
+       }
+
+       printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
+}
+
+
+/*
+ *  specialix IO8+ IO range functions.
+ */
+
+static int sx_request_io_range(struct specialix_board *bp)
+{
+       return request_region(bp->base,
+               bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
+               "specialix IO8+") == NULL;
+}
+
+
+static void sx_release_io_range(struct specialix_board *bp)
+{
+       release_region(bp->base, bp->flags & SX_BOARD_IS_PCI ?
+                                       SX_PCI_IO_SPACE : SX_IO_SPACE);
+}
+
+
+/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
+static int sx_set_irq(struct specialix_board *bp)
+{
+       int virq;
+       int i;
+       unsigned long flags;
+
+       if (bp->flags & SX_BOARD_IS_PCI)
+               return 1;
+       switch (bp->irq) {
+       /* In the same order as in the docs... */
+       case 15:
+               virq = 0;
+               break;
+       case 12:
+               virq = 1;
+               break;
+       case 11:
+               virq = 2;
+               break;
+       case 9:
+               virq = 3;
+               break;
+       default:printk(KERN_ERR
+                           "Speclialix: cannot set irq to %d.\n", bp->irq);
+               return 0;
+       }
+       spin_lock_irqsave(&bp->lock, flags);
+       for (i = 0; i < 2; i++) {
+               sx_out(bp, CD186x_CAR, i);
+               sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
+       }
+       spin_unlock_irqrestore(&bp->lock, flags);
+       return 1;
+}
+
+
+/* Reset and setup CD186x chip */
+static int sx_init_CD186x(struct specialix_board  *bp)
+{
+       unsigned long flags;
+       int scaler;
+       int rv = 1;
+
+       func_enter();
+       sx_wait_CCR_off(bp);                       /* Wait for CCR ready        */
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out_off(bp, CD186x_CCR, CCR_HARDRESET);      /* Reset CD186x chip          */
+       spin_unlock_irqrestore(&bp->lock, flags);
+       msleep(50);                                     /* Delay 0.05 sec            */
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out_off(bp, CD186x_GIVR, SX_ID);             /* Set ID for this chip      */
+       sx_out_off(bp, CD186x_GICR, 0);                 /* Clear all bits            */
+       sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT);      /* Prio for modem intr       */
+       sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT);      /* Prio for transmitter intr */
+       sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT);      /* Prio for receiver intr    */
+       /* Set RegAckEn */
+       sx_out_off(bp, CD186x_SRCR, sx_in(bp, CD186x_SRCR) | SRCR_REGACKEN);
+
+       /* Setting up prescaler. We need 4 ticks per 1 ms */
+       scaler =  SX_OSCFREQ/SPECIALIX_TPS;
+
+       sx_out_off(bp, CD186x_PPRH, scaler >> 8);
+       sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
+       spin_unlock_irqrestore(&bp->lock, flags);
+
+       if (!sx_set_irq(bp)) {
+               /* Figure out how to pass this along... */
+               printk(KERN_ERR "Cannot set irq to %d.\n", bp->irq);
+               rv = 0;
+       }
+
+       func_exit();
+       return rv;
+}
+
+
+static int read_cross_byte(struct specialix_board *bp, int reg, int bit)
+{
+       int i;
+       int t;
+       unsigned long flags;
+
+       spin_lock_irqsave(&bp->lock, flags);
+       for (i = 0, t = 0; i < 8; i++) {
+               sx_out_off(bp, CD186x_CAR, i);
+               if (sx_in_off(bp, reg) & bit)
+                       t |= 1 << i;
+       }
+       spin_unlock_irqrestore(&bp->lock, flags);
+
+       return t;
+}
+
+
+/* Main probing routine, also sets irq. */
+static int sx_probe(struct specialix_board *bp)
+{
+       unsigned char val1, val2;
+       int rev;
+       int chip;
+
+       func_enter();
+
+       if (sx_request_io_range(bp)) {
+               func_exit();
+               return 1;
+       }
+
+       /* Are the I/O ports here ? */
+       sx_out_off(bp, CD186x_PPRL, 0x5a);
+       udelay(1);
+       val1 = sx_in_off(bp, CD186x_PPRL);
+
+       sx_out_off(bp, CD186x_PPRL, 0xa5);
+       udelay(1);
+       val2 = sx_in_off(bp, CD186x_PPRL);
+
+
+       if (val1 != 0x5a || val2 != 0xa5) {
+               printk(KERN_INFO
+                       "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
+                                               board_No(bp), bp->base);
+               sx_release_io_range(bp);
+               func_exit();
+               return 1;
+       }
+
+       /* Check the DSR lines that Specialix uses as board
+          identification */
+       val1 = read_cross_byte(bp, CD186x_MSVR, MSVR_DSR);
+       val2 = read_cross_byte(bp, CD186x_MSVR, MSVR_RTS);
+       dprintk(SX_DEBUG_INIT,
+                       "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
+                                       board_No(bp), val1, val2);
+
+       /* They managed to switch the bit order between the docs and
+          the IO8+ card. The new PCI card now conforms to old docs.
+          They changed the PCI docs to reflect the situation on the
+          old card. */
+       val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
+       if (val1 != val2) {
+               printk(KERN_INFO
+                 "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
+                      board_No(bp), val2, bp->base, val1);
+               sx_release_io_range(bp);
+               func_exit();
+               return 1;
+       }
+
+
+       /* Reset CD186x again  */
+       if (!sx_init_CD186x(bp)) {
+               sx_release_io_range(bp);
+               func_exit();
+               return 1;
+       }
+
+       sx_request_io_range(bp);
+       bp->flags |= SX_BOARD_PRESENT;
+
+       /* Chip           revcode   pkgtype
+                         GFRCR     SRCR bit 7
+          CD180 rev B    0x81      0
+          CD180 rev C    0x82      0
+          CD1864 rev A   0x82      1
+          CD1865 rev A   0x83      1  -- Do not use!!! Does not work.
+          CD1865 rev B   0x84      1
+        -- Thanks to Gwen Wang, Cirrus Logic.
+        */
+
+       switch (sx_in_off(bp, CD186x_GFRCR)) {
+       case 0x82:
+               chip = 1864;
+               rev = 'A';
+               break;
+       case 0x83:
+               chip = 1865;
+               rev = 'A';
+               break;
+       case 0x84:
+               chip = 1865;
+               rev = 'B';
+               break;
+       case 0x85:
+               chip = 1865;
+               rev = 'C';
+               break; /* Does not exist at this time */
+       default:
+               chip = -1;
+               rev = 'x';
+       }
+
+       dprintk(SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR));
+
+       printk(KERN_INFO
+    "sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
+                               board_No(bp), bp->base, bp->irq, chip, rev);
+
+       func_exit();
+       return 0;
+}
+
+/*
+ *
+ *  Interrupt processing routines.
+ * */
+
+static struct specialix_port *sx_get_port(struct specialix_board *bp,
+                                              unsigned char const *what)
+{
+       unsigned char channel;
+       struct specialix_port *port = NULL;
+
+       channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
+       dprintk(SX_DEBUG_CHAN, "channel: %d\n", channel);
+       if (channel < CD186x_NCH) {
+               port = &sx_port[board_No(bp) * SX_NPORT + channel];
+               dprintk(SX_DEBUG_CHAN, "port: %d %p flags: 0x%lx\n",
+                       board_No(bp) * SX_NPORT + channel,  port,
+                       port->port.flags & ASYNC_INITIALIZED);
+
+               if (port->port.flags & ASYNC_INITIALIZED) {
+                       dprintk(SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
+                       func_exit();
+                       return port;
+               }
+       }
+       printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
+              board_No(bp), what, channel);
+       return NULL;
+}
+
+
+static void sx_receive_exc(struct specialix_board *bp)
+{
+       struct specialix_port *port;
+       struct tty_struct *tty;
+       unsigned char status;
+       unsigned char ch, flag;
+
+       func_enter();
+
+       port = sx_get_port(bp, "Receive");
+       if (!port) {
+               dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
+               func_exit();
+               return;
+       }
+       tty = port->port.tty;
+
+       status = sx_in(bp, CD186x_RCSR);
+
+       dprintk(SX_DEBUG_RX, "status: 0x%x\n", status);
+       if (status & RCSR_OE) {
+               port->overrun++;
+               dprintk(SX_DEBUG_FIFO,
+                       "sx%d: port %d: Overrun. Total %ld overruns.\n",
+                               board_No(bp), port_No(port), port->overrun);
+       }
+       status &= port->mark_mask;
+
+       /* This flip buffer check needs to be below the reading of the
+          status register to reset the chip's IRQ.... */
+       if (tty_buffer_request_room(tty, 1) == 0) {
+               dprintk(SX_DEBUG_FIFO,
+                   "sx%d: port %d: Working around flip buffer overflow.\n",
+                                       board_No(bp), port_No(port));
+               func_exit();
+               return;
+       }
+
+       ch = sx_in(bp, CD186x_RDR);
+       if (!status) {
+               func_exit();
+               return;
+       }
+       if (status & RCSR_TOUT) {
+               printk(KERN_INFO
+                   "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
+                                       board_No(bp), port_No(port));
+               func_exit();
+               return;
+
+       } else if (status & RCSR_BREAK) {
+               dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
+                      board_No(bp), port_No(port));
+               flag = TTY_BREAK;
+               if (port->port.flags & ASYNC_SAK)
+                       do_SAK(tty);
+
+       } else if (status & RCSR_PE)
+               flag = TTY_PARITY;
+
+       else if (status & RCSR_FE)
+               flag = TTY_FRAME;
+
+       else if (status & RCSR_OE)
+               flag = TTY_OVERRUN;
+
+       else
+               flag = TTY_NORMAL;
+
+       if (tty_insert_flip_char(tty, ch, flag))
+               tty_flip_buffer_push(tty);
+       func_exit();
+}
+
+
+static void sx_receive(struct specialix_board *bp)
+{
+       struct specialix_port *port;
+       struct tty_struct *tty;
+       unsigned char count;
+
+       func_enter();
+
+       port = sx_get_port(bp, "Receive");
+       if (port == NULL) {
+               dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
+               func_exit();
+               return;
+       }
+       tty = port->port.tty;
+
+       count = sx_in(bp, CD186x_RDCR);
+       dprintk(SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
+       port->hits[count > 8 ? 9 : count]++;
+
+       while (count--)
+               tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
+       tty_flip_buffer_push(tty);
+       func_exit();
+}
+
+
+static void sx_transmit(struct specialix_board *bp)
+{
+       struct specialix_port *port;
+       struct tty_struct *tty;
+       unsigned char count;
+
+       func_enter();
+       port = sx_get_port(bp, "Transmit");
+       if (port == NULL) {
+               func_exit();
+               return;
+       }
+       dprintk(SX_DEBUG_TX, "port: %p\n", port);
+       tty = port->port.tty;
+
+       if (port->IER & IER_TXEMPTY) {
+               /* FIFO drained */
+               sx_out(bp, CD186x_CAR, port_No(port));
+               port->IER &= ~IER_TXEMPTY;
+               sx_out(bp, CD186x_IER, port->IER);
+               func_exit();
+               return;
+       }
+
+       if ((port->xmit_cnt <= 0 && !port->break_length)
+           || tty->stopped || tty->hw_stopped) {
+               sx_out(bp, CD186x_CAR, port_No(port));
+               port->IER &= ~IER_TXRDY;
+               sx_out(bp, CD186x_IER, port->IER);
+               func_exit();
+               return;
+       }
+
+       if (port->break_length) {
+               if (port->break_length > 0) {
+                       if (port->COR2 & COR2_ETC) {
+                               sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+                               sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
+                               port->COR2 &= ~COR2_ETC;
+                       }
+                       count = min_t(int, port->break_length, 0xff);
+                       sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+                       sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
+                       sx_out(bp, CD186x_TDR, count);
+                       port->break_length -= count;
+                       if (port->break_length == 0)
+                               port->break_length--;
+               } else {
+                       sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+                       sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
+                       sx_out(bp, CD186x_COR2, port->COR2);
+                       sx_wait_CCR(bp);
+                       sx_out(bp, CD186x_CCR, CCR_CORCHG2);
+                       port->break_length = 0;
+               }
+
+               func_exit();
+               return;
+       }
+
+       count = CD186x_NFIFO;
+       do {
+               sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
+               port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
+               if (--port->xmit_cnt <= 0)
+                       break;
+       } while (--count > 0);
+
+       if (port->xmit_cnt <= 0) {
+               sx_out(bp, CD186x_CAR, port_No(port));
+               port->IER &= ~IER_TXRDY;
+               sx_out(bp, CD186x_IER, port->IER);
+       }
+       if (port->xmit_cnt <= port->wakeup_chars)
+               tty_wakeup(tty);
+
+       func_exit();
+}
+
+
+static void sx_check_modem(struct specialix_board *bp)
+{
+       struct specialix_port *port;
+       struct tty_struct *tty;
+       unsigned char mcr;
+       int msvr_cd;
+
+       dprintk(SX_DEBUG_SIGNALS, "Modem intr. ");
+       port = sx_get_port(bp, "Modem");
+       if (port == NULL)
+               return;
+
+       tty = port->port.tty;
+
+       mcr = sx_in(bp, CD186x_MCR);
+
+       if ((mcr & MCR_CDCHG)) {
+               dprintk(SX_DEBUG_SIGNALS, "CD just changed... ");
+               msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
+               if (msvr_cd) {
+                       dprintk(SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
+                       wake_up_interruptible(&port->port.open_wait);
+               } else {
+                       dprintk(SX_DEBUG_SIGNALS, "Sending HUP.\n");
+                       tty_hangup(tty);
+               }
+       }
+
+#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
+       if (mcr & MCR_CTSCHG) {
+               if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
+                       tty->hw_stopped = 0;
+                       port->IER |= IER_TXRDY;
+                       if (port->xmit_cnt <= port->wakeup_chars)
+                               tty_wakeup(tty);
+               } else {
+                       tty->hw_stopped = 1;
+                       port->IER &= ~IER_TXRDY;
+               }
+               sx_out(bp, CD186x_IER, port->IER);
+       }
+       if (mcr & MCR_DSSXHG) {
+               if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
+                       tty->hw_stopped = 0;
+                       port->IER |= IER_TXRDY;
+                       if (port->xmit_cnt <= port->wakeup_chars)
+                               tty_wakeup(tty);
+               } else {
+                       tty->hw_stopped = 1;
+                       port->IER &= ~IER_TXRDY;
+               }
+               sx_out(bp, CD186x_IER, port->IER);
+       }
+#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
+
+       /* Clear change bits */
+       sx_out(bp, CD186x_MCR, 0);
+}
+
+
+/* The main interrupt processing routine */
+static irqreturn_t sx_interrupt(int dummy, void *dev_id)
+{
+       unsigned char status;
+       unsigned char ack;
+       struct specialix_board *bp = dev_id;
+       unsigned long loop = 0;
+       int saved_reg;
+       unsigned long flags;
+
+       func_enter();
+
+       spin_lock_irqsave(&bp->lock, flags);
+
+       dprintk(SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __func__,
+               port_No(sx_get_port(bp, "INT")),
+               SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1);
+       if (!(bp->flags & SX_BOARD_ACTIVE)) {
+               dprintk(SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n",
+                                                               bp->irq);
+               spin_unlock_irqrestore(&bp->lock, flags);
+               func_exit();
+               return IRQ_NONE;
+       }
+
+       saved_reg = bp->reg;
+
+       while (++loop < 16) {
+               status = sx_in(bp, CD186x_SRSR) &
+                               (SRSR_RREQint | SRSR_TREQint | SRSR_MREQint);
+               if (status == 0)
+                       break;
+               if (status & SRSR_RREQint) {
+                       ack = sx_in(bp, CD186x_RRAR);
+
+                       if (ack == (SX_ID | GIVR_IT_RCV))
+                               sx_receive(bp);
+                       else if (ack == (SX_ID | GIVR_IT_REXC))
+                               sx_receive_exc(bp);
+                       else
+                               printk(KERN_ERR
+                               "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
+                                               board_No(bp), status, ack);
+
+               } else if (status & SRSR_TREQint) {
+                       ack = sx_in(bp, CD186x_TRAR);
+
+                       if (ack == (SX_ID | GIVR_IT_TX))
+                               sx_transmit(bp);
+                       else
+                               printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
+                                       board_No(bp), status, ack,
+                                       port_No(sx_get_port(bp, "Int")));
+               } else if (status & SRSR_MREQint) {
+                       ack = sx_in(bp, CD186x_MRAR);
+
+                       if (ack == (SX_ID | GIVR_IT_MODEM))
+                               sx_check_modem(bp);
+                       else
+                               printk(KERN_ERR
+                                 "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
+                                      board_No(bp), status, ack);
+
+               }
+
+               sx_out(bp, CD186x_EOIR, 0);   /* Mark end of interrupt */
+       }
+       bp->reg = saved_reg;
+       outb(bp->reg, bp->base + SX_ADDR_REG);
+       spin_unlock_irqrestore(&bp->lock, flags);
+       func_exit();
+       return IRQ_HANDLED;
+}
+
+
+/*
+ *  Routines for open & close processing.
+ */
+
+static void turn_ints_off(struct specialix_board *bp)
+{
+       unsigned long flags;
+
+       func_enter();
+       spin_lock_irqsave(&bp->lock, flags);
+       (void) sx_in_off(bp, 0); /* Turn off interrupts. */
+       spin_unlock_irqrestore(&bp->lock, flags);
+
+       func_exit();
+}
+
+static void turn_ints_on(struct specialix_board *bp)
+{
+       unsigned long flags;
+
+       func_enter();
+
+       spin_lock_irqsave(&bp->lock, flags);
+       (void) sx_in(bp, 0); /* Turn ON interrupts. */
+       spin_unlock_irqrestore(&bp->lock, flags);
+
+       func_exit();
+}
+
+
+/* Called with disabled interrupts */
+static int sx_setup_board(struct specialix_board *bp)
+{
+       int error;
+
+       if (bp->flags & SX_BOARD_ACTIVE)
+               return 0;
+
+       if (bp->flags & SX_BOARD_IS_PCI)
+               error = request_irq(bp->irq, sx_interrupt,
+                       IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
+       else
+               error = request_irq(bp->irq, sx_interrupt,
+                       IRQF_DISABLED, "specialix IO8+", bp);
+
+       if (error)
+               return error;
+
+       turn_ints_on(bp);
+       bp->flags |= SX_BOARD_ACTIVE;
+
+       return 0;
+}
+
+
+/* Called with disabled interrupts */
+static void sx_shutdown_board(struct specialix_board *bp)
+{
+       func_enter();
+
+       if (!(bp->flags & SX_BOARD_ACTIVE)) {
+               func_exit();
+               return;
+       }
+
+       bp->flags &= ~SX_BOARD_ACTIVE;
+
+       dprintk(SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
+                bp->irq, board_No(bp));
+       free_irq(bp->irq, bp);
+       turn_ints_off(bp);
+       func_exit();
+}
+
+static unsigned int sx_crtscts(struct tty_struct *tty)
+{
+       if (sx_rtscts)
+               return C_CRTSCTS(tty);
+       return 1;
+}
+
+/*
+ * Setting up port characteristics.
+ * Must be called with disabled interrupts
+ */
+static void sx_change_speed(struct specialix_board *bp,
+                                               struct specialix_port *port)
+{
+       struct tty_struct *tty;
+       unsigned long baud;
+       long tmp;
+       unsigned char cor1 = 0, cor3 = 0;
+       unsigned char mcor1 = 0, mcor2 = 0;
+       static unsigned long again;
+       unsigned long flags;
+
+       func_enter();
+
+       tty = port->port.tty;
+       if (!tty || !tty->termios) {
+               func_exit();
+               return;
+       }
+
+       port->IER  = 0;
+       port->COR2 = 0;
+       /* Select port on the board */
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out(bp, CD186x_CAR, port_No(port));
+
+       /* The Specialix board doens't implement the RTS lines.
+          They are used to set the IRQ level. Don't touch them. */
+       if (sx_crtscts(tty))
+               port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
+       else
+               port->MSVR =  (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
+       spin_unlock_irqrestore(&bp->lock, flags);
+       dprintk(SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
+       baud = tty_get_baud_rate(tty);
+
+       if (baud == 38400) {
+               if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                       baud = 57600;
+               if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                       baud = 115200;
+       }
+
+       if (!baud) {
+               /* Drop DTR & exit */
+               dprintk(SX_DEBUG_TERMIOS, "Dropping DTR...  Hmm....\n");
+               if (!sx_crtscts(tty)) {
+                       port->MSVR &= ~MSVR_DTR;
+                       spin_lock_irqsave(&bp->lock, flags);
+                       sx_out(bp, CD186x_MSVR, port->MSVR);
+                       spin_unlock_irqrestore(&bp->lock, flags);
+               } else
+                       dprintk(SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
+               return;
+       } else {
+               /* Set DTR on */
+               if (!sx_crtscts(tty))
+                       port->MSVR |= MSVR_DTR;
+       }
+
+       /*
+        * Now we must calculate some speed depended things
+        */
+
+       /* Set baud rate for port */
+       tmp = port->custom_divisor ;
+       if (tmp)
+               printk(KERN_INFO
+                       "sx%d: Using custom baud rate divisor %ld. \n"
+                       "This is an untested option, please be careful.\n",
+                                                       port_No(port), tmp);
+       else
+               tmp = (((SX_OSCFREQ + baud/2) / baud + CD186x_TPC/2) /
+                                                               CD186x_TPC);
+
+       if (tmp < 0x10 && time_before(again, jiffies)) {
+               again = jiffies + HZ * 60;
+               /* Page 48 of version 2.0 of the CL-CD1865 databook */
+               if (tmp >= 12) {
+                       printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
+                               "Performance degradation is possible.\n"
+                               "Read specialix.txt for more info.\n",
+                                               port_No(port), tmp);
+               } else {
+                       printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
+               "Warning: overstressing Cirrus chip. This might not work.\n"
+               "Read specialix.txt for more info.\n", port_No(port), tmp);
+               }
+       }
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
+       sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
+       sx_out(bp, CD186x_RBPRL, tmp & 0xff);
+       sx_out(bp, CD186x_TBPRL, tmp & 0xff);
+       spin_unlock_irqrestore(&bp->lock, flags);
+       if (port->custom_divisor)
+               baud = (SX_OSCFREQ + port->custom_divisor/2) /
+                                                       port->custom_divisor;
+       baud = (baud + 5) / 10;         /* Estimated CPS */
+
+       /* Two timer ticks seems enough to wakeup something like SLIP driver */
+       tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
+       port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
+                                             SERIAL_XMIT_SIZE - 1 : tmp);
+
+       /* Receiver timeout will be transmission time for 1.5 chars */
+       tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
+       tmp = (tmp > 0xff) ? 0xff : tmp;
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out(bp, CD186x_RTPR, tmp);
+       spin_unlock_irqrestore(&bp->lock, flags);
+       switch (C_CSIZE(tty)) {
+       case CS5:
+               cor1 |= COR1_5BITS;
+               break;
+       case CS6:
+               cor1 |= COR1_6BITS;
+               break;
+       case CS7:
+               cor1 |= COR1_7BITS;
+               break;
+       case CS8:
+               cor1 |= COR1_8BITS;
+               break;
+       }
+
+       if (C_CSTOPB(tty))
+               cor1 |= COR1_2SB;
+
+       cor1 |= COR1_IGNORE;
+       if (C_PARENB(tty)) {
+               cor1 |= COR1_NORMPAR;
+               if (C_PARODD(tty))
+                       cor1 |= COR1_ODDP;
+               if (I_INPCK(tty))
+                       cor1 &= ~COR1_IGNORE;
+       }
+       /* Set marking of some errors */
+       port->mark_mask = RCSR_OE | RCSR_TOUT;
+       if (I_INPCK(tty))
+               port->mark_mask |= RCSR_FE | RCSR_PE;
+       if (I_BRKINT(tty) || I_PARMRK(tty))
+               port->mark_mask |= RCSR_BREAK;
+       if (I_IGNPAR(tty))
+               port->mark_mask &= ~(RCSR_FE | RCSR_PE);
+       if (I_IGNBRK(tty)) {
+               port->mark_mask &= ~RCSR_BREAK;
+               if (I_IGNPAR(tty))
+                       /* Real raw mode. Ignore all */
+                       port->mark_mask &= ~RCSR_OE;
+       }
+       /* Enable Hardware Flow Control */
+       if (C_CRTSCTS(tty)) {
+#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
+               port->IER |= IER_DSR | IER_CTS;
+               mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
+               mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
+               spin_lock_irqsave(&bp->lock, flags);
+               tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) &
+                                                       (MSVR_CTS|MSVR_DSR));
+               spin_unlock_irqrestore(&bp->lock, flags);
+#else
+               port->COR2 |= COR2_CTSAE;
+#endif
+       }
+       /* Enable Software Flow Control. FIXME: I'm not sure about this */
+       /* Some people reported that it works, but I still doubt it */
+       if (I_IXON(tty)) {
+               port->COR2 |= COR2_TXIBE;
+               cor3 |= (COR3_FCT | COR3_SCDE);
+               if (I_IXANY(tty))
+                       port->COR2 |= COR2_IXM;
+               spin_lock_irqsave(&bp->lock, flags);
+               sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
+               sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
+               sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
+               sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
+               spin_unlock_irqrestore(&bp->lock, flags);
+       }
+       if (!C_CLOCAL(tty)) {
+               /* Enable CD check */
+               port->IER |= IER_CD;
+               mcor1 |= MCOR1_CDZD;
+               mcor2 |= MCOR2_CDOD;
+       }
+
+       if (C_CREAD(tty))
+               /* Enable receiver */
+               port->IER |= IER_RXD;
+
+       /* Set input FIFO size (1-8 bytes) */
+       cor3 |= sx_rxfifo;
+       /* Setting up CD186x channel registers */
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out(bp, CD186x_COR1, cor1);
+       sx_out(bp, CD186x_COR2, port->COR2);
+       sx_out(bp, CD186x_COR3, cor3);
+       spin_unlock_irqrestore(&bp->lock, flags);
+       /* Make CD186x know about registers change */
+       sx_wait_CCR(bp);
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
+       /* Setting up modem option registers */
+       dprintk(SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n",
+                                                               mcor1, mcor2);
+       sx_out(bp, CD186x_MCOR1, mcor1);
+       sx_out(bp, CD186x_MCOR2, mcor2);
+       spin_unlock_irqrestore(&bp->lock, flags);
+       /* Enable CD186x transmitter & receiver */
+       sx_wait_CCR(bp);
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
+       /* Enable interrupts */
+       sx_out(bp, CD186x_IER, port->IER);
+       /* And finally set the modem lines... */
+       sx_out(bp, CD186x_MSVR, port->MSVR);
+       spin_unlock_irqrestore(&bp->lock, flags);
+
+       func_exit();
+}
+
+
+/* Must be called with interrupts enabled */
+static int sx_setup_port(struct specialix_board *bp,
+                                               struct specialix_port *port)
+{
+       unsigned long flags;
+
+       func_enter();
+
+       if (port->port.flags & ASYNC_INITIALIZED) {
+               func_exit();
+               return 0;
+       }
+
+       if (!port->xmit_buf) {
+               /* We may sleep in get_zeroed_page() */
+               unsigned long tmp;
+
+               tmp = get_zeroed_page(GFP_KERNEL);
+               if (tmp == 0L) {
+                       func_exit();
+                       return -ENOMEM;
+               }
+
+               if (port->xmit_buf) {
+                       free_page(tmp);
+                       func_exit();
+                       return -ERESTARTSYS;
+               }
+               port->xmit_buf = (unsigned char *) tmp;
+       }
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       if (port->port.tty)
+               clear_bit(TTY_IO_ERROR, &port->port.tty->flags);
+
+       port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+       sx_change_speed(bp, port);
+       port->port.flags |= ASYNC_INITIALIZED;
+
+       spin_unlock_irqrestore(&port->lock, flags);
+
+
+       func_exit();
+       return 0;
+}
+
+
+/* Must be called with interrupts disabled */
+static void sx_shutdown_port(struct specialix_board *bp,
+                                               struct specialix_port *port)
+{
+       struct tty_struct *tty;
+       int i;
+       unsigned long flags;
+
+       func_enter();
+
+       if (!(port->port.flags & ASYNC_INITIALIZED)) {
+               func_exit();
+               return;
+       }
+
+       if (sx_debug & SX_DEBUG_FIFO) {
+               dprintk(SX_DEBUG_FIFO,
+                       "sx%d: port %d: %ld overruns, FIFO hits [ ",
+                               board_No(bp), port_No(port), port->overrun);
+               for (i = 0; i < 10; i++)
+                       dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
+               dprintk(SX_DEBUG_FIFO, "].\n");
+       }
+
+       if (port->xmit_buf) {
+               free_page((unsigned long) port->xmit_buf);
+               port->xmit_buf = NULL;
+       }
+
+       /* Select port */
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out(bp, CD186x_CAR, port_No(port));
+
+       tty = port->port.tty;
+       if (tty == NULL || C_HUPCL(tty)) {
+               /* Drop DTR */
+               sx_out(bp, CD186x_MSVDTR, 0);
+       }
+       spin_unlock_irqrestore(&bp->lock, flags);
+       /* Reset port */
+       sx_wait_CCR(bp);
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
+       /* Disable all interrupts from this port */
+       port->IER = 0;
+       sx_out(bp, CD186x_IER, port->IER);
+       spin_unlock_irqrestore(&bp->lock, flags);
+       if (tty)
+               set_bit(TTY_IO_ERROR, &tty->flags);
+       port->port.flags &= ~ASYNC_INITIALIZED;
+
+       if (!bp->count)
+               sx_shutdown_board(bp);
+       func_exit();
+}
+
+
+static int block_til_ready(struct tty_struct *tty, struct file *filp,
+                                               struct specialix_port *port)
+{
+       DECLARE_WAITQUEUE(wait,  current);
+       struct specialix_board *bp = port_Board(port);
+       int    retval;
+       int    do_clocal = 0;
+       int    CD;
+       unsigned long flags;
+
+       func_enter();
+
+       /*
+        * 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) || port->port.flags & ASYNC_CLOSING) {
+               interruptible_sleep_on(&port->port.close_wait);
+               if (port->port.flags & ASYNC_HUP_NOTIFY) {
+                       func_exit();
+                       return -EAGAIN;
+               } else {
+                       func_exit();
+                       return -ERESTARTSYS;
+               }
+       }
+
+       /*
+        * 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))) {
+               port->port.flags |= ASYNC_NORMAL_ACTIVE;
+               func_exit();
+               return 0;
+       }
+
+       if (C_CLOCAL(tty))
+               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, info->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(&port->port.open_wait, &wait);
+       spin_lock_irqsave(&port->lock, flags);
+       if (!tty_hung_up_p(filp))
+               port->port.count--;
+       spin_unlock_irqrestore(&port->lock, flags);
+       port->port.blocked_open++;
+       while (1) {
+               spin_lock_irqsave(&bp->lock, flags);
+               sx_out(bp, CD186x_CAR, port_No(port));
+               CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
+               if (sx_crtscts(tty)) {
+                       /* Activate RTS */
+                       port->MSVR |= MSVR_DTR;         /* WTF? */
+                       sx_out(bp, CD186x_MSVR, port->MSVR);
+               } else {
+                       /* Activate DTR */
+                       port->MSVR |= MSVR_DTR;
+                       sx_out(bp, CD186x_MSVR, port->MSVR);
+               }
+               spin_unlock_irqrestore(&bp->lock, flags);
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (tty_hung_up_p(filp) ||
+                   !(port->port.flags & ASYNC_INITIALIZED)) {
+                       if (port->port.flags & ASYNC_HUP_NOTIFY)
+                               retval = -EAGAIN;
+                       else
+                               retval = -ERESTARTSYS;
+                       break;
+               }
+               if (!(port->port.flags & ASYNC_CLOSING) &&
+                   (do_clocal || CD))
+                       break;
+               if (signal_pending(current)) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+               tty_unlock();
+               schedule();
+               tty_lock();
+       }
+
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&port->port.open_wait, &wait);
+       spin_lock_irqsave(&port->lock, flags);
+       if (!tty_hung_up_p(filp))
+               port->port.count++;
+       port->port.blocked_open--;
+       spin_unlock_irqrestore(&port->lock, flags);
+       if (retval) {
+               func_exit();
+               return retval;
+       }
+
+       port->port.flags |= ASYNC_NORMAL_ACTIVE;
+       func_exit();
+       return 0;
+}
+
+
+static int sx_open(struct tty_struct *tty, struct file *filp)
+{
+       int board;
+       int error;
+       struct specialix_port *port;
+       struct specialix_board *bp;
+       int i;
+       unsigned long flags;
+
+       func_enter();
+
+       board = SX_BOARD(tty->index);
+
+       if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
+               func_exit();
+               return -ENODEV;
+       }
+
+       bp = &sx_board[board];
+       port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
+       port->overrun = 0;
+       for (i = 0; i < 10; i++)
+               port->hits[i] = 0;
+
+       dprintk(SX_DEBUG_OPEN,
+                       "Board = %d, bp = %p, port = %p, portno = %d.\n",
+                                board, bp, port, SX_PORT(tty->index));
+
+       if (sx_paranoia_check(port, tty->name, "sx_open")) {
+               func_enter();
+               return -ENODEV;
+       }
+
+       error = sx_setup_board(bp);
+       if (error) {
+               func_exit();
+               return error;
+       }
+
+       spin_lock_irqsave(&bp->lock, flags);
+       port->port.count++;
+       bp->count++;
+       tty->driver_data = port;
+       port->port.tty = tty;
+       spin_unlock_irqrestore(&bp->lock, flags);
+
+       error = sx_setup_port(bp, port);
+       if (error) {
+               func_enter();
+               return error;
+       }
+
+       error = block_til_ready(tty, filp, port);
+       if (error) {
+               func_enter();
+               return error;
+       }
+
+       func_exit();
+       return 0;
+}
+
+static void sx_flush_buffer(struct tty_struct *tty)
+{
+       struct specialix_port *port = tty->driver_data;
+       unsigned long flags;
+       struct specialix_board  *bp;
+
+       func_enter();
+
+       if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
+               func_exit();
+               return;
+       }
+
+       bp = port_Board(port);
+       spin_lock_irqsave(&port->lock, flags);
+       port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+       spin_unlock_irqrestore(&port->lock, flags);
+       tty_wakeup(tty);
+
+       func_exit();
+}
+
+static void sx_close(struct tty_struct *tty, struct file *filp)
+{
+       struct specialix_port *port = tty->driver_data;
+       struct specialix_board *bp;
+       unsigned long flags;
+       unsigned long timeout;
+
+       func_enter();
+       if (!port || sx_paranoia_check(port, tty->name, "close")) {
+               func_exit();
+               return;
+       }
+       spin_lock_irqsave(&port->lock, flags);
+
+       if (tty_hung_up_p(filp)) {
+               spin_unlock_irqrestore(&port->lock, flags);
+               func_exit();
+               return;
+       }
+
+       bp = port_Board(port);
+       if (tty->count == 1 && port->port.count != 1) {
+               printk(KERN_ERR "sx%d: sx_close: bad port count;"
+                      " tty->count is 1, port count is %d\n",
+                      board_No(bp), port->port.count);
+               port->port.count = 1;
+       }
+
+       if (port->port.count > 1) {
+               port->port.count--;
+               bp->count--;
+
+               spin_unlock_irqrestore(&port->lock, flags);
+
+               func_exit();
+               return;
+       }
+       port->port.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;
+       spin_unlock_irqrestore(&port->lock, flags);
+       dprintk(SX_DEBUG_OPEN, "Closing\n");
+       if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
+               tty_wait_until_sent(tty, port->port.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.
+        */
+       dprintk(SX_DEBUG_OPEN, "Closed\n");
+       port->IER &= ~IER_RXD;
+       if (port->port.flags & ASYNC_INITIALIZED) {
+               port->IER &= ~IER_TXRDY;
+               port->IER |= IER_TXEMPTY;
+               spin_lock_irqsave(&bp->lock, flags);
+               sx_out(bp, CD186x_CAR, port_No(port));
+               sx_out(bp, CD186x_IER, port->IER);
+               spin_unlock_irqrestore(&bp->lock, flags);
+               /*
+                * 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 (port->IER & IER_TXEMPTY) {
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       msleep_interruptible(jiffies_to_msecs(port->timeout));
+                       if (time_after(jiffies, timeout)) {
+                               printk(KERN_INFO "Timeout waiting for close\n");
+                               break;
+                       }
+               }
+
+       }
+
+       if (--bp->count < 0) {
+               printk(KERN_ERR
+                   "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
+                               board_No(bp), bp->count, tty->index);
+               bp->count = 0;
+       }
+       if (--port->port.count < 0) {
+               printk(KERN_ERR
+                       "sx%d: sx_close: bad port count for tty%d: %d\n",
+                               board_No(bp), port_No(port), port->port.count);
+               port->port.count = 0;
+       }
+
+       sx_shutdown_port(bp, port);
+       sx_flush_buffer(tty);
+       tty_ldisc_flush(tty);
+       spin_lock_irqsave(&port->lock, flags);
+       tty->closing = 0;
+       port->port.tty = NULL;
+       spin_unlock_irqrestore(&port->lock, flags);
+       if (port->port.blocked_open) {
+               if (port->port.close_delay)
+                       msleep_interruptible(
+                               jiffies_to_msecs(port->port.close_delay));
+               wake_up_interruptible(&port->port.open_wait);
+       }
+       port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
+       wake_up_interruptible(&port->port.close_wait);
+
+       func_exit();
+}
+
+
+static int sx_write(struct tty_struct *tty,
+                                       const unsigned char *buf, int count)
+{
+       struct specialix_port *port = tty->driver_data;
+       struct specialix_board *bp;
+       int c, total = 0;
+       unsigned long flags;
+
+       func_enter();
+       if (sx_paranoia_check(port, tty->name, "sx_write")) {
+               func_exit();
+               return 0;
+       }
+
+       bp = port_Board(port);
+
+       if (!port->xmit_buf) {
+               func_exit();
+               return 0;
+       }
+
+       while (1) {
+               spin_lock_irqsave(&port->lock, flags);
+               c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+                                  SERIAL_XMIT_SIZE - port->xmit_head));
+               if (c <= 0) {
+                       spin_unlock_irqrestore(&port->lock, flags);
+                       break;
+               }
+               memcpy(port->xmit_buf + port->xmit_head, buf, c);
+               port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+               port->xmit_cnt += c;
+               spin_unlock_irqrestore(&port->lock, flags);
+
+               buf += c;
+               count -= c;
+               total += c;
+       }
+
+       spin_lock_irqsave(&bp->lock, flags);
+       if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+           !(port->IER & IER_TXRDY)) {
+               port->IER |= IER_TXRDY;
+               sx_out(bp, CD186x_CAR, port_No(port));
+               sx_out(bp, CD186x_IER, port->IER);
+       }
+       spin_unlock_irqrestore(&bp->lock, flags);
+       func_exit();
+
+       return total;
+}
+
+
+static int sx_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct specialix_port *port = tty->driver_data;
+       unsigned long flags;
+       struct specialix_board  *bp;
+
+       func_enter();
+
+       if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
+               func_exit();
+               return 0;
+       }
+       dprintk(SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
+       if (!port->xmit_buf) {
+               func_exit();
+               return 0;
+       }
+       bp = port_Board(port);
+       spin_lock_irqsave(&port->lock, flags);
+
+       dprintk(SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n",
+                                       port->xmit_cnt, port->xmit_buf);
+       if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1 || !port->xmit_buf) {
+               spin_unlock_irqrestore(&port->lock, flags);
+               dprintk(SX_DEBUG_TX, "Exit size\n");
+               func_exit();
+               return 0;
+       }
+       dprintk(SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
+       port->xmit_buf[port->xmit_head++] = ch;
+       port->xmit_head &= SERIAL_XMIT_SIZE - 1;
+       port->xmit_cnt++;
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       func_exit();
+       return 1;
+}
+
+
+static void sx_flush_chars(struct tty_struct *tty)
+{
+       struct specialix_port *port = tty->driver_data;
+       unsigned long flags;
+       struct specialix_board  *bp = port_Board(port);
+
+       func_enter();
+
+       if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
+               func_exit();
+               return;
+       }
+       if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+           !port->xmit_buf) {
+               func_exit();
+               return;
+       }
+       spin_lock_irqsave(&bp->lock, flags);
+       port->IER |= IER_TXRDY;
+       sx_out(port_Board(port), CD186x_CAR, port_No(port));
+       sx_out(port_Board(port), CD186x_IER, port->IER);
+       spin_unlock_irqrestore(&bp->lock, flags);
+
+       func_exit();
+}
+
+
+static int sx_write_room(struct tty_struct *tty)
+{
+       struct specialix_port *port = tty->driver_data;
+       int     ret;
+
+       func_enter();
+
+       if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
+               func_exit();
+               return 0;
+       }
+
+       ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
+       if (ret < 0)
+               ret = 0;
+
+       func_exit();
+       return ret;
+}
+
+
+static int sx_chars_in_buffer(struct tty_struct *tty)
+{
+       struct specialix_port *port = tty->driver_data;
+
+       func_enter();
+
+       if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
+               func_exit();
+               return 0;
+       }
+       func_exit();
+       return port->xmit_cnt;
+}
+
+static int sx_tiocmget(struct tty_struct *tty)
+{
+       struct specialix_port *port = tty->driver_data;
+       struct specialix_board *bp;
+       unsigned char status;
+       unsigned int result;
+       unsigned long flags;
+
+       func_enter();
+
+       if (sx_paranoia_check(port, tty->name, __func__)) {
+               func_exit();
+               return -ENODEV;
+       }
+
+       bp = port_Board(port);
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out(bp, CD186x_CAR, port_No(port));
+       status = sx_in(bp, CD186x_MSVR);
+       spin_unlock_irqrestore(&bp->lock, flags);
+       dprintk(SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
+                       port_No(port), status, sx_in(bp, CD186x_CAR));
+       dprintk(SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
+       if (sx_crtscts(port->port.tty)) {
+               result  = TIOCM_DTR | TIOCM_DSR
+                         |   ((status & MSVR_DTR) ? TIOCM_RTS : 0)
+                         |   ((status & MSVR_CD)  ? TIOCM_CAR : 0)
+                         |   ((status & MSVR_CTS) ? TIOCM_CTS : 0);
+       } else {
+               result  = TIOCM_RTS | TIOCM_DSR
+                         |   ((status & MSVR_DTR) ? TIOCM_DTR : 0)
+                         |   ((status & MSVR_CD)  ? TIOCM_CAR : 0)
+                         |   ((status & MSVR_CTS) ? TIOCM_CTS : 0);
+       }
+
+       func_exit();
+
+       return result;
+}
+
+
+static int sx_tiocmset(struct tty_struct *tty,
+                      unsigned int set, unsigned int clear)
+{
+       struct specialix_port *port = tty->driver_data;
+       unsigned long flags;
+       struct specialix_board *bp;
+
+       func_enter();
+
+       if (sx_paranoia_check(port, tty->name, __func__)) {
+               func_exit();
+               return -ENODEV;
+       }
+
+       bp = port_Board(port);
+
+       spin_lock_irqsave(&port->lock, flags);
+       if (sx_crtscts(port->port.tty)) {
+               if (set & TIOCM_RTS)
+                       port->MSVR |= MSVR_DTR;
+       } else {
+               if (set & TIOCM_DTR)
+                       port->MSVR |= MSVR_DTR;
+       }
+       if (sx_crtscts(port->port.tty)) {
+               if (clear & TIOCM_RTS)
+                       port->MSVR &= ~MSVR_DTR;
+       } else {
+               if (clear & TIOCM_DTR)
+                       port->MSVR &= ~MSVR_DTR;
+       }
+       spin_lock(&bp->lock);
+       sx_out(bp, CD186x_CAR, port_No(port));
+       sx_out(bp, CD186x_MSVR, port->MSVR);
+       spin_unlock(&bp->lock);
+       spin_unlock_irqrestore(&port->lock, flags);
+       func_exit();
+       return 0;
+}
+
+
+static int sx_send_break(struct tty_struct *tty, int length)
+{
+       struct specialix_port *port = tty->driver_data;
+       struct specialix_board *bp = port_Board(port);
+       unsigned long flags;
+
+       func_enter();
+       if (length == 0 || length == -1)
+               return -EOPNOTSUPP;
+
+       spin_lock_irqsave(&port->lock, flags);
+       port->break_length = SPECIALIX_TPS / HZ * length;
+       port->COR2 |= COR2_ETC;
+       port->IER  |= IER_TXRDY;
+       spin_lock(&bp->lock);
+       sx_out(bp, CD186x_CAR, port_No(port));
+       sx_out(bp, CD186x_COR2, port->COR2);
+       sx_out(bp, CD186x_IER, port->IER);
+       spin_unlock(&bp->lock);
+       spin_unlock_irqrestore(&port->lock, flags);
+       sx_wait_CCR(bp);
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out(bp, CD186x_CCR, CCR_CORCHG2);
+       spin_unlock_irqrestore(&bp->lock, flags);
+       sx_wait_CCR(bp);
+
+       func_exit();
+       return 0;
+}
+
+
+static int sx_set_serial_info(struct specialix_port *port,
+                                       struct serial_struct __user *newinfo)
+{
+       struct serial_struct tmp;
+       struct specialix_board *bp = port_Board(port);
+       int change_speed;
+
+       func_enter();
+
+       if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
+               func_enter();
+               return -EFAULT;
+       }
+
+       mutex_lock(&port->port.mutex);
+       change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
+                       (tmp.flags & ASYNC_SPD_MASK));
+       change_speed |= (tmp.custom_divisor != port->custom_divisor);
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               if ((tmp.close_delay != port->port.close_delay) ||
+                   (tmp.closing_wait != port->port.closing_wait) ||
+                   ((tmp.flags & ~ASYNC_USR_MASK) !=
+                    (port->port.flags & ~ASYNC_USR_MASK))) {
+                       func_exit();
+                       mutex_unlock(&port->port.mutex);
+                       return -EPERM;
+               }
+               port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
+                                               (tmp.flags & ASYNC_USR_MASK));
+               port->custom_divisor = tmp.custom_divisor;
+       } else {
+               port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
+                                               (tmp.flags & ASYNC_FLAGS));
+               port->port.close_delay = tmp.close_delay;
+               port->port.closing_wait = tmp.closing_wait;
+               port->custom_divisor = tmp.custom_divisor;
+       }
+       if (change_speed)
+               sx_change_speed(bp, port);
+
+       func_exit();
+       mutex_unlock(&port->port.mutex);
+       return 0;
+}
+
+
+static int sx_get_serial_info(struct specialix_port *port,
+                                    struct serial_struct __user *retinfo)
+{
+       struct serial_struct tmp;
+       struct specialix_board *bp = port_Board(port);
+
+       func_enter();
+
+       memset(&tmp, 0, sizeof(tmp));
+       mutex_lock(&port->port.mutex);
+       tmp.type = PORT_CIRRUS;
+       tmp.line = port - sx_port;
+       tmp.port = bp->base;
+       tmp.irq  = bp->irq;
+       tmp.flags = port->port.flags;
+       tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
+       tmp.close_delay = port->port.close_delay * HZ/100;
+       tmp.closing_wait = port->port.closing_wait * HZ/100;
+       tmp.custom_divisor =  port->custom_divisor;
+       tmp.xmit_fifo_size = CD186x_NFIFO;
+       mutex_unlock(&port->port.mutex);
+       if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
+               func_exit();
+               return -EFAULT;
+       }
+
+       func_exit();
+       return 0;
+}
+
+
+static int sx_ioctl(struct tty_struct *tty,
+                               unsigned int cmd, unsigned long arg)
+{
+       struct specialix_port *port = tty->driver_data;
+       void __user *argp = (void __user *)arg;
+
+       func_enter();
+
+       if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
+               func_exit();
+               return -ENODEV;
+       }
+
+       switch (cmd) {
+       case TIOCGSERIAL:
+               func_exit();
+               return sx_get_serial_info(port, argp);
+       case TIOCSSERIAL:
+               func_exit();
+               return sx_set_serial_info(port, argp);
+       default:
+               func_exit();
+               return -ENOIOCTLCMD;
+       }
+       func_exit();
+       return 0;
+}
+
+
+static void sx_throttle(struct tty_struct *tty)
+{
+       struct specialix_port *port = tty->driver_data;
+       struct specialix_board *bp;
+       unsigned long flags;
+
+       func_enter();
+
+       if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
+               func_exit();
+               return;
+       }
+
+       bp = port_Board(port);
+
+       /* Use DTR instead of RTS ! */
+       if (sx_crtscts(tty))
+               port->MSVR &= ~MSVR_DTR;
+       else {
+               /* Auch!!! I think the system shouldn't call this then. */
+               /* Or maybe we're supposed (allowed?) to do our side of hw
+                  handshake anyway, even when hardware handshake is off.
+                  When you see this in your logs, please report.... */
+               printk(KERN_ERR
+                  "sx%d: Need to throttle, but can't (hardware hs is off)\n",
+                                                       port_No(port));
+       }
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out(bp, CD186x_CAR, port_No(port));
+       spin_unlock_irqrestore(&bp->lock, flags);
+       if (I_IXOFF(tty)) {
+               sx_wait_CCR(bp);
+               spin_lock_irqsave(&bp->lock, flags);
+               sx_out(bp, CD186x_CCR, CCR_SSCH2);
+               spin_unlock_irqrestore(&bp->lock, flags);
+               sx_wait_CCR(bp);
+       }
+       spin_lock_irqsave(&bp->lock, flags);
+       sx_out(bp, CD186x_MSVR, port->MSVR);
+       spin_unlock_irqrestore(&bp->lock, flags);
+
+       func_exit();
+}
+
+
+static void sx_unthrottle(struct tty_struct *tty)
+{
+       struct specialix_port *port = tty->driver_data;
+       struct specialix_board *bp;
+       unsigned long flags;
+
+       func_enter();
+
+       if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
+               func_exit();
+               return;
+       }
+
+       bp = port_Board(port);
+
+       spin_lock_irqsave(&port->lock, flags);
+       /* XXXX Use DTR INSTEAD???? */
+       if (sx_crtscts(tty))
+               port->MSVR |= MSVR_DTR;
+       /* Else clause: see remark in "sx_throttle"... */
+       spin_lock(&bp->lock);
+       sx_out(bp, CD186x_CAR, port_No(port));
+       spin_unlock(&bp->lock);
+       if (I_IXOFF(tty)) {
+               spin_unlock_irqrestore(&port->lock, flags);
+               sx_wait_CCR(bp);
+               spin_lock_irqsave(&bp->lock, flags);
+               sx_out(bp, CD186x_CCR, CCR_SSCH1);
+               spin_unlock_irqrestore(&bp->lock, flags);
+               sx_wait_CCR(bp);
+               spin_lock_irqsave(&port->lock, flags);
+       }
+       spin_lock(&bp->lock);
+       sx_out(bp, CD186x_MSVR, port->MSVR);
+       spin_unlock(&bp->lock);
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       func_exit();
+}
+
+
+static void sx_stop(struct tty_struct *tty)
+{
+       struct specialix_port *port = tty->driver_data;
+       struct specialix_board *bp;
+       unsigned long flags;
+
+       func_enter();
+
+       if (sx_paranoia_check(port, tty->name, "sx_stop")) {
+               func_exit();
+               return;
+       }
+
+       bp = port_Board(port);
+
+       spin_lock_irqsave(&port->lock, flags);
+       port->IER &= ~IER_TXRDY;
+       spin_lock(&bp->lock);
+       sx_out(bp, CD186x_CAR, port_No(port));
+       sx_out(bp, CD186x_IER, port->IER);
+       spin_unlock(&bp->lock);
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       func_exit();
+}
+
+
+static void sx_start(struct tty_struct *tty)
+{
+       struct specialix_port *port = tty->driver_data;
+       struct specialix_board *bp;
+       unsigned long flags;
+
+       func_enter();
+
+       if (sx_paranoia_check(port, tty->name, "sx_start")) {
+               func_exit();
+               return;
+       }
+
+       bp = port_Board(port);
+
+       spin_lock_irqsave(&port->lock, flags);
+       if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
+               port->IER |= IER_TXRDY;
+               spin_lock(&bp->lock);
+               sx_out(bp, CD186x_CAR, port_No(port));
+               sx_out(bp, CD186x_IER, port->IER);
+               spin_unlock(&bp->lock);
+       }
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       func_exit();
+}
+
+static void sx_hangup(struct tty_struct *tty)
+{
+       struct specialix_port *port = tty->driver_data;
+       struct specialix_board *bp;
+       unsigned long flags;
+
+       func_enter();
+
+       if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
+               func_exit();
+               return;
+       }
+
+       bp = port_Board(port);
+
+       sx_shutdown_port(bp, port);
+       spin_lock_irqsave(&port->lock, flags);
+       bp->count -= port->port.count;
+       if (bp->count < 0) {
+               printk(KERN_ERR
+                       "sx%d: sx_hangup: bad board count: %d port: %d\n",
+                                       board_No(bp), bp->count, tty->index);
+               bp->count = 0;
+       }
+       port->port.count = 0;
+       port->port.flags &= ~ASYNC_NORMAL_ACTIVE;
+       port->port.tty = NULL;
+       spin_unlock_irqrestore(&port->lock, flags);
+       wake_up_interruptible(&port->port.open_wait);
+
+       func_exit();
+}
+
+
+static void sx_set_termios(struct tty_struct *tty,
+                                       struct ktermios *old_termios)
+{
+       struct specialix_port *port = tty->driver_data;
+       unsigned long flags;
+       struct specialix_board  *bp;
+
+       if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
+               return;
+
+       bp = port_Board(port);
+       spin_lock_irqsave(&port->lock, flags);
+       sx_change_speed(port_Board(port), port);
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       if ((old_termios->c_cflag & CRTSCTS) &&
+           !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               sx_start(tty);
+       }
+}
+
+static const struct tty_operations sx_ops = {
+       .open  = sx_open,
+       .close = sx_close,
+       .write = sx_write,
+       .put_char = sx_put_char,
+       .flush_chars = sx_flush_chars,
+       .write_room = sx_write_room,
+       .chars_in_buffer = sx_chars_in_buffer,
+       .flush_buffer = sx_flush_buffer,
+       .ioctl = sx_ioctl,
+       .throttle = sx_throttle,
+       .unthrottle = sx_unthrottle,
+       .set_termios = sx_set_termios,
+       .stop = sx_stop,
+       .start = sx_start,
+       .hangup = sx_hangup,
+       .tiocmget = sx_tiocmget,
+       .tiocmset = sx_tiocmset,
+       .break_ctl = sx_send_break,
+};
+
+static int sx_init_drivers(void)
+{
+       int error;
+       int i;
+
+       func_enter();
+
+       specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
+       if (!specialix_driver) {
+               printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
+               func_exit();
+               return 1;
+       }
+
+       specialix_driver->owner = THIS_MODULE;
+       specialix_driver->name = "ttyW";
+       specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
+       specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
+       specialix_driver->subtype = SERIAL_TYPE_NORMAL;
+       specialix_driver->init_termios = tty_std_termios;
+       specialix_driver->init_termios.c_cflag =
+               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       specialix_driver->init_termios.c_ispeed = 9600;
+       specialix_driver->init_termios.c_ospeed = 9600;
+       specialix_driver->flags = TTY_DRIVER_REAL_RAW |
+                                               TTY_DRIVER_HARDWARE_BREAK;
+       tty_set_operations(specialix_driver, &sx_ops);
+
+       error = tty_register_driver(specialix_driver);
+       if (error) {
+               put_tty_driver(specialix_driver);
+               printk(KERN_ERR
+                 "sx: Couldn't register specialix IO8+ driver, error = %d\n",
+                                                               error);
+               func_exit();
+               return 1;
+       }
+       memset(sx_port, 0, sizeof(sx_port));
+       for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
+               sx_port[i].magic = SPECIALIX_MAGIC;
+               tty_port_init(&sx_port[i].port);
+               spin_lock_init(&sx_port[i].lock);
+       }
+
+       func_exit();
+       return 0;
+}
+
+static void sx_release_drivers(void)
+{
+       func_enter();
+
+       tty_unregister_driver(specialix_driver);
+       put_tty_driver(specialix_driver);
+       func_exit();
+}
+
+/*
+ * This routine must be called by kernel at boot time
+ */
+static int __init specialix_init(void)
+{
+       int i;
+       int found = 0;
+
+       func_enter();
+
+       printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
+       printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
+       if (sx_rtscts)
+               printk(KERN_INFO
+                       "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
+       else
+               printk(KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
+
+       for (i = 0; i < SX_NBOARD; i++)
+               spin_lock_init(&sx_board[i].lock);
+
+       if (sx_init_drivers()) {
+               func_exit();
+               return -EIO;
+       }
+
+       for (i = 0; i < SX_NBOARD; i++)
+               if (sx_board[i].base && !sx_probe(&sx_board[i]))
+                       found++;
+
+#ifdef CONFIG_PCI
+       {
+               struct pci_dev *pdev = NULL;
+
+               i = 0;
+               while (i < SX_NBOARD) {
+                       if (sx_board[i].flags & SX_BOARD_PRESENT) {
+                               i++;
+                               continue;
+                       }
+                       pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX,
+                                       PCI_DEVICE_ID_SPECIALIX_IO8, pdev);
+                       if (!pdev)
+                               break;
+
+                       if (pci_enable_device(pdev))
+                               continue;
+
+                       sx_board[i].irq = pdev->irq;
+
+                       sx_board[i].base = pci_resource_start(pdev, 2);
+
+                       sx_board[i].flags |= SX_BOARD_IS_PCI;
+                       if (!sx_probe(&sx_board[i]))
+                               found++;
+               }
+               /* May exit pci_get sequence early with lots of boards */
+               if (pdev != NULL)
+                       pci_dev_put(pdev);
+       }
+#endif
+
+       if (!found) {
+               sx_release_drivers();
+               printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
+               func_exit();
+               return -EIO;
+       }
+
+       func_exit();
+       return 0;
+}
+
+static int iobase[SX_NBOARD]  = {0,};
+static int irq[SX_NBOARD] = {0,};
+
+module_param_array(iobase, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param(sx_debug, int, 0);
+module_param(sx_rtscts, int, 0);
+module_param(sx_rxfifo, int, 0);
+
+/*
+ * You can setup up to 4 boards.
+ * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
+ * You should specify the IRQs too in that case "irq=....,...".
+ *
+ * More than 4 boards in one computer is not possible, as the card can
+ * only use 4 different interrupts.
+ *
+ */
+static int __init specialix_init_module(void)
+{
+       int i;
+
+       func_enter();
+
+       if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
+               for (i = 0; i < SX_NBOARD; i++) {
+                       sx_board[i].base = iobase[i];
+                       sx_board[i].irq = irq[i];
+                       sx_board[i].count = 0;
+               }
+       }
+
+       func_exit();
+
+       return specialix_init();
+}
+
+static void __exit specialix_exit_module(void)
+{
+       int i;
+
+       func_enter();
+
+       sx_release_drivers();
+       for (i = 0; i < SX_NBOARD; i++)
+               if (sx_board[i].flags & SX_BOARD_PRESENT)
+                       sx_release_io_range(&sx_board[i]);
+       func_exit();
+}
+
+static struct pci_device_id specialx_pci_tbl[] __devinitdata __used = {
+       { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
+       { }
+};
+MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
+
+module_init(specialix_init_module);
+module_exit(specialix_exit_module);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(SPECIALIX_NORMAL_MAJOR);
diff --git a/drivers/staging/tty/specialix_io8.h b/drivers/staging/tty/specialix_io8.h
new file mode 100644 (file)
index 0000000..c630052
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ *      linux/drivers/char/specialix_io8.h  -- 
+ *                                   Specialix IO8+ multiport serial driver.
+ *
+ *      Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (pgmdsg@ibi.com)
+ *
+ *
+ *      Specialix pays for the development and support of this driver.
+ *      Please DO contact io8-linux@specialix.co.uk if you require
+ *      support.
+ *
+ *      This driver was developped in the BitWizard linux device
+ *      driver service. If you require a linux device driver for your
+ *      product, please contact devices@BitWizard.nl for a quote.
+ *
+ *      This code is firmly based on the riscom/8 serial driver,
+ *      written by Dmitry Gorodchanin. The specialix IO8+ card
+ *      programming information was obtained from the CL-CD1865 Data
+ *      Book, and Specialix document number 6200059: IO8+ Hardware
+ *      Functional Specification.
+ *
+ *      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.
+ * */
+
+#ifndef __LINUX_SPECIALIX_H
+#define __LINUX_SPECIALIX_H
+
+#include <linux/serial.h>
+
+#ifdef __KERNEL__
+
+/* You can have max 4 ISA cards in one PC, and I recommend not much 
+more than a few  PCI versions of the card. */
+
+#define SX_NBOARD              8
+
+/* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */
+#define SX_IO_SPACE             4
+/* The PCI version decodes 8 addresses, but still only 2 are used. */
+#define SX_PCI_IO_SPACE         8
+
+/* eight ports per board. */
+#define SX_NPORT               8
+#define SX_BOARD(line)         ((line) / SX_NPORT)
+#define SX_PORT(line)          ((line) & (SX_NPORT - 1))
+
+
+#define SX_DATA_REG 0     /* Base+0 : Data register */
+#define SX_ADDR_REG 1     /* base+1 : Address register. */
+
+#define MHz *1000000   /* I'm ashamed of myself. */
+
+/* On-board oscillator frequency */
+#define SX_OSCFREQ      (25 MHz/2)
+/* There is a 25MHz crystal on the board, but the chip is in /2 mode */
+
+
+/* Ticks per sec. Used for setting receiver timeout and break length */
+#define SPECIALIX_TPS          4000
+
+/* Yeah, after heavy testing I decided it must be 6.
+ * Sure, You can change it if needed.
+ */
+#define SPECIALIX_RXFIFO       6       /* Max. receiver FIFO size (1-8) */
+
+#define SPECIALIX_MAGIC                0x0907
+
+#define SX_CCR_TIMEOUT 10000   /* CCR timeout. You may need to wait upto
+                                  10 milliseconds before the internal
+                                  processor is available again after
+                                  you give it a command */
+
+#define SX_IOBASE1     0x100
+#define SX_IOBASE2     0x180
+#define SX_IOBASE3     0x250
+#define SX_IOBASE4     0x260
+
+struct specialix_board {
+       unsigned long   flags;
+       unsigned short  base;
+       unsigned char   irq;
+       //signed   char count;
+       int count;
+       unsigned char   DTR;
+        int reg;
+       spinlock_t lock;
+};
+
+#define SX_BOARD_PRESENT       0x00000001
+#define SX_BOARD_ACTIVE                0x00000002
+#define SX_BOARD_IS_PCI                0x00000004
+
+
+struct specialix_port {
+       int                     magic;
+       struct tty_port         port;
+       int                     baud_base;
+       int                     flags;
+       int                     timeout;
+       unsigned char           * xmit_buf;
+       int                     custom_divisor;
+       int                     xmit_head;
+       int                     xmit_tail;
+       int                     xmit_cnt;
+       short                   wakeup_chars;
+       short                   break_length;
+       unsigned char           mark_mask;
+       unsigned char           IER;
+       unsigned char           MSVR;
+       unsigned char           COR2;
+       unsigned long           overrun;
+       unsigned long           hits[10];
+       spinlock_t lock;
+};
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_SPECIALIX_H */
+
+
+
+
+
+
+
+
+
diff --git a/drivers/staging/tty/stallion.c b/drivers/staging/tty/stallion.c
new file mode 100644 (file)
index 0000000..4fff5cd
--- /dev/null
@@ -0,0 +1,4651 @@
+/*****************************************************************************/
+
+/*
+ *     stallion.c  -- stallion multiport serial driver.
+ *
+ *     Copyright (C) 1996-1999  Stallion Technologies
+ *     Copyright (C) 1994-1996  Greg Ungerer.
+ *
+ *     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.
+ *
+ *     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.
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/seq_file.h>
+#include <linux/cd1400.h>
+#include <linux/sc26198.h>
+#include <linux/comstats.h>
+#include <linux/stallion.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include <linux/pci.h>
+
+/*****************************************************************************/
+
+/*
+ *     Define different board types. Use the standard Stallion "assigned"
+ *     board numbers. Boards supported in this driver are abbreviated as
+ *     EIO = EasyIO and ECH = EasyConnection 8/32.
+ */
+#define        BRD_EASYIO      20
+#define        BRD_ECH         21
+#define        BRD_ECHMC       22
+#define        BRD_ECHPCI      26
+#define        BRD_ECH64PCI    27
+#define        BRD_EASYIOPCI   28
+
+struct stlconf {
+       unsigned int    brdtype;
+       int             ioaddr1;
+       int             ioaddr2;
+       unsigned long   memaddr;
+       int             irq;
+       int             irqtype;
+};
+
+static unsigned int stl_nrbrds;
+
+/*****************************************************************************/
+
+/*
+ *     Define some important driver characteristics. Device major numbers
+ *     allocated as per Linux Device Registry.
+ */
+#ifndef        STL_SIOMEMMAJOR
+#define        STL_SIOMEMMAJOR         28
+#endif
+#ifndef        STL_SERIALMAJOR
+#define        STL_SERIALMAJOR         24
+#endif
+#ifndef        STL_CALLOUTMAJOR
+#define        STL_CALLOUTMAJOR        25
+#endif
+
+/*
+ *     Set the TX buffer size. Bigger is better, but we don't want
+ *     to chew too much memory with buffers!
+ */
+#define        STL_TXBUFLOW            512
+#define        STL_TXBUFSIZE           4096
+
+/*****************************************************************************/
+
+/*
+ *     Define our local driver identity first. Set up stuff to deal with
+ *     all the local structures required by a serial tty driver.
+ */
+static char    *stl_drvtitle = "Stallion Multiport Serial Driver";
+static char    *stl_drvname = "stallion";
+static char    *stl_drvversion = "5.6.0";
+
+static struct tty_driver       *stl_serial;
+
+/*
+ *     Define a local default termios struct. All ports will be created
+ *     with this termios initially. Basically all it defines is a raw port
+ *     at 9600, 8 data bits, 1 stop bit.
+ */
+static struct ktermios         stl_deftermios = {
+       .c_cflag        = (B9600 | CS8 | CREAD | HUPCL | CLOCAL),
+       .c_cc           = INIT_C_CC,
+       .c_ispeed       = 9600,
+       .c_ospeed       = 9600,
+};
+
+/*
+ *     Define global place to put buffer overflow characters.
+ */
+static char            stl_unwanted[SC26198_RXFIFOSIZE];
+
+/*****************************************************************************/
+
+static DEFINE_MUTEX(stl_brdslock);
+static struct stlbrd           *stl_brds[STL_MAXBRDS];
+
+static const struct tty_port_operations stl_port_ops;
+
+/*
+ *     Per board state flags. Used with the state field of the board struct.
+ *     Not really much here!
+ */
+#define        BRD_FOUND       0x1
+#define STL_PROBED     0x2
+
+
+/*
+ *     Define the port structure istate flags. These set of flags are
+ *     modified at interrupt time - so setting and reseting them needs
+ *     to be atomic. Use the bit clear/setting routines for this.
+ */
+#define        ASYI_TXBUSY     1
+#define        ASYI_TXLOW      2
+#define        ASYI_TXFLOWED   3
+
+/*
+ *     Define an array of board names as printable strings. Handy for
+ *     referencing boards when printing trace and stuff.
+ */
+static char    *stl_brdnames[] = {
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "EasyIO",
+       "EC8/32-AT",
+       "EC8/32-MC",
+       NULL,
+       NULL,
+       NULL,
+       "EC8/32-PCI",
+       "EC8/64-PCI",
+       "EasyIO-PCI",
+};
+
+/*****************************************************************************/
+
+/*
+ *     Define some string labels for arguments passed from the module
+ *     load line. These allow for easy board definitions, and easy
+ *     modification of the io, memory and irq resoucres.
+ */
+static unsigned int stl_nargs;
+static char    *board0[4];
+static char    *board1[4];
+static char    *board2[4];
+static char    *board3[4];
+
+static char    **stl_brdsp[] = {
+       (char **) &board0,
+       (char **) &board1,
+       (char **) &board2,
+       (char **) &board3
+};
+
+/*
+ *     Define a set of common board names, and types. This is used to
+ *     parse any module arguments.
+ */
+
+static struct {
+       char    *name;
+       int     type;
+} stl_brdstr[] = {
+       { "easyio", BRD_EASYIO },
+       { "eio", BRD_EASYIO },
+       { "20", BRD_EASYIO },
+       { "ec8/32", BRD_ECH },
+       { "ec8/32-at", BRD_ECH },
+       { "ec8/32-isa", BRD_ECH },
+       { "ech", BRD_ECH },
+       { "echat", BRD_ECH },
+       { "21", BRD_ECH },
+       { "ec8/32-mc", BRD_ECHMC },
+       { "ec8/32-mca", BRD_ECHMC },
+       { "echmc", BRD_ECHMC },
+       { "echmca", BRD_ECHMC },
+       { "22", BRD_ECHMC },
+       { "ec8/32-pc", BRD_ECHPCI },
+       { "ec8/32-pci", BRD_ECHPCI },
+       { "26", BRD_ECHPCI },
+       { "ec8/64-pc", BRD_ECH64PCI },
+       { "ec8/64-pci", BRD_ECH64PCI },
+       { "ech-pci", BRD_ECH64PCI },
+       { "echpci", BRD_ECH64PCI },
+       { "echpc", BRD_ECH64PCI },
+       { "27", BRD_ECH64PCI },
+       { "easyio-pc", BRD_EASYIOPCI },
+       { "easyio-pci", BRD_EASYIOPCI },
+       { "eio-pci", BRD_EASYIOPCI },
+       { "eiopci", BRD_EASYIOPCI },
+       { "28", BRD_EASYIOPCI },
+};
+
+/*
+ *     Define the module agruments.
+ */
+
+module_param_array(board0, charp, &stl_nargs, 0);
+MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,ioaddr2][,irq]]");
+module_param_array(board1, charp, &stl_nargs, 0);
+MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,ioaddr2][,irq]]");
+module_param_array(board2, charp, &stl_nargs, 0);
+MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,ioaddr2][,irq]]");
+module_param_array(board3, charp, &stl_nargs, 0);
+MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,ioaddr2][,irq]]");
+
+/*****************************************************************************/
+
+/*
+ *     Hardware ID bits for the EasyIO and ECH boards. These defines apply
+ *     to the directly accessible io ports of these boards (not the uarts -
+ *     they are in cd1400.h and sc26198.h).
+ */
+#define        EIO_8PORTRS     0x04
+#define        EIO_4PORTRS     0x05
+#define        EIO_8PORTDI     0x00
+#define        EIO_8PORTM      0x06
+#define        EIO_MK3         0x03
+#define        EIO_IDBITMASK   0x07
+
+#define        EIO_BRDMASK     0xf0
+#define        ID_BRD4         0x10
+#define        ID_BRD8         0x20
+#define        ID_BRD16        0x30
+
+#define        EIO_INTRPEND    0x08
+#define        EIO_INTEDGE     0x00
+#define        EIO_INTLEVEL    0x08
+#define        EIO_0WS         0x10
+
+#define        ECH_ID          0xa0
+#define        ECH_IDBITMASK   0xe0
+#define        ECH_BRDENABLE   0x08
+#define        ECH_BRDDISABLE  0x00
+#define        ECH_INTENABLE   0x01
+#define        ECH_INTDISABLE  0x00
+#define        ECH_INTLEVEL    0x02
+#define        ECH_INTEDGE     0x00
+#define        ECH_INTRPEND    0x01
+#define        ECH_BRDRESET    0x01
+
+#define        ECHMC_INTENABLE 0x01
+#define        ECHMC_BRDRESET  0x02
+
+#define        ECH_PNLSTATUS   2
+#define        ECH_PNL16PORT   0x20
+#define        ECH_PNLIDMASK   0x07
+#define        ECH_PNLXPID     0x40
+#define        ECH_PNLINTRPEND 0x80
+
+#define        ECH_ADDR2MASK   0x1e0
+
+/*
+ *     Define the vector mapping bits for the programmable interrupt board
+ *     hardware. These bits encode the interrupt for the board to use - it
+ *     is software selectable (except the EIO-8M).
+ */
+static unsigned char   stl_vecmap[] = {
+       0xff, 0xff, 0xff, 0x04, 0x06, 0x05, 0xff, 0x07,
+       0xff, 0xff, 0x00, 0x02, 0x01, 0xff, 0xff, 0x03
+};
+
+/*
+ *     Lock ordering is that you may not take stallion_lock holding
+ *     brd_lock.
+ */
+
+static spinlock_t brd_lock;            /* Guard the board mapping */
+static spinlock_t stallion_lock;       /* Guard the tty driver */
+
+/*
+ *     Set up enable and disable macros for the ECH boards. They require
+ *     the secondary io address space to be activated and deactivated.
+ *     This way all ECH boards can share their secondary io region.
+ *     If this is an ECH-PCI board then also need to set the page pointer
+ *     to point to the correct page.
+ */
+#define        BRDENABLE(brdnr,pagenr)                                         \
+       if (stl_brds[(brdnr)]->brdtype == BRD_ECH)                      \
+               outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDENABLE),    \
+                       stl_brds[(brdnr)]->ioctrl);                     \
+       else if (stl_brds[(brdnr)]->brdtype == BRD_ECHPCI)              \
+               outb((pagenr), stl_brds[(brdnr)]->ioctrl);
+
+#define        BRDDISABLE(brdnr)                                               \
+       if (stl_brds[(brdnr)]->brdtype == BRD_ECH)                      \
+               outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE),   \
+                       stl_brds[(brdnr)]->ioctrl);
+
+#define        STL_CD1400MAXBAUD       230400
+#define        STL_SC26198MAXBAUD      460800
+
+#define        STL_BAUDBASE            115200
+#define        STL_CLOSEDELAY          (5 * HZ / 10)
+
+/*****************************************************************************/
+
+/*
+ *     Define the Stallion PCI vendor and device IDs.
+ */
+#ifndef        PCI_VENDOR_ID_STALLION
+#define        PCI_VENDOR_ID_STALLION          0x124d
+#endif
+#ifndef PCI_DEVICE_ID_ECHPCI832
+#define        PCI_DEVICE_ID_ECHPCI832         0x0000
+#endif
+#ifndef PCI_DEVICE_ID_ECHPCI864
+#define        PCI_DEVICE_ID_ECHPCI864         0x0002
+#endif
+#ifndef PCI_DEVICE_ID_EIOPCI
+#define        PCI_DEVICE_ID_EIOPCI            0x0003
+#endif
+
+/*
+ *     Define structure to hold all Stallion PCI boards.
+ */
+
+static struct pci_device_id stl_pcibrds[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI864),
+               .driver_data = BRD_ECH64PCI },
+       { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_EIOPCI),
+               .driver_data = BRD_EASYIOPCI },
+       { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832),
+               .driver_data = BRD_ECHPCI },
+       { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410),
+               .driver_data = BRD_ECHPCI },
+       { }
+};
+MODULE_DEVICE_TABLE(pci, stl_pcibrds);
+
+/*****************************************************************************/
+
+/*
+ *     Define macros to extract a brd/port number from a minor number.
+ */
+#define        MINOR2BRD(min)          (((min) & 0xc0) >> 6)
+#define        MINOR2PORT(min)         ((min) & 0x3f)
+
+/*
+ *     Define a baud rate table that converts termios baud rate selector
+ *     into the actual baud rate value. All baud rate calculations are
+ *     based on the actual baud rate required.
+ */
+static unsigned int    stl_baudrates[] = {
+       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+       9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
+};
+
+/*****************************************************************************/
+
+/*
+ *     Declare all those functions in this driver!
+ */
+
+static long    stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg);
+static int     stl_brdinit(struct stlbrd *brdp);
+static int     stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp);
+static int     stl_clrportstats(struct stlport *portp, comstats_t __user *cp);
+
+/*
+ *     CD1400 uart specific handling functions.
+ */
+static void    stl_cd1400setreg(struct stlport *portp, int regnr, int value);
+static int     stl_cd1400getreg(struct stlport *portp, int regnr);
+static int     stl_cd1400updatereg(struct stlport *portp, int regnr, int value);
+static int     stl_cd1400panelinit(struct stlbrd *brdp, struct stlpanel *panelp);
+static void    stl_cd1400portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp);
+static void    stl_cd1400setport(struct stlport *portp, struct ktermios *tiosp);
+static int     stl_cd1400getsignals(struct stlport *portp);
+static void    stl_cd1400setsignals(struct stlport *portp, int dtr, int rts);
+static void    stl_cd1400ccrwait(struct stlport *portp);
+static void    stl_cd1400enablerxtx(struct stlport *portp, int rx, int tx);
+static void    stl_cd1400startrxtx(struct stlport *portp, int rx, int tx);
+static void    stl_cd1400disableintrs(struct stlport *portp);
+static void    stl_cd1400sendbreak(struct stlport *portp, int len);
+static void    stl_cd1400flowctrl(struct stlport *portp, int state);
+static void    stl_cd1400sendflow(struct stlport *portp, int state);
+static void    stl_cd1400flush(struct stlport *portp);
+static int     stl_cd1400datastate(struct stlport *portp);
+static void    stl_cd1400eiointr(struct stlpanel *panelp, unsigned int iobase);
+static void    stl_cd1400echintr(struct stlpanel *panelp, unsigned int iobase);
+static void    stl_cd1400txisr(struct stlpanel *panelp, int ioaddr);
+static void    stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr);
+static void    stl_cd1400mdmisr(struct stlpanel *panelp, int ioaddr);
+
+static inline int      stl_cd1400breakisr(struct stlport *portp, int ioaddr);
+
+/*
+ *     SC26198 uart specific handling functions.
+ */
+static void    stl_sc26198setreg(struct stlport *portp, int regnr, int value);
+static int     stl_sc26198getreg(struct stlport *portp, int regnr);
+static int     stl_sc26198updatereg(struct stlport *portp, int regnr, int value);
+static int     stl_sc26198getglobreg(struct stlport *portp, int regnr);
+static int     stl_sc26198panelinit(struct stlbrd *brdp, struct stlpanel *panelp);
+static void    stl_sc26198portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp);
+static void    stl_sc26198setport(struct stlport *portp, struct ktermios *tiosp);
+static int     stl_sc26198getsignals(struct stlport *portp);
+static void    stl_sc26198setsignals(struct stlport *portp, int dtr, int rts);
+static void    stl_sc26198enablerxtx(struct stlport *portp, int rx, int tx);
+static void    stl_sc26198startrxtx(struct stlport *portp, int rx, int tx);
+static void    stl_sc26198disableintrs(struct stlport *portp);
+static void    stl_sc26198sendbreak(struct stlport *portp, int len);
+static void    stl_sc26198flowctrl(struct stlport *portp, int state);
+static void    stl_sc26198sendflow(struct stlport *portp, int state);
+static void    stl_sc26198flush(struct stlport *portp);
+static int     stl_sc26198datastate(struct stlport *portp);
+static void    stl_sc26198wait(struct stlport *portp);
+static void    stl_sc26198txunflow(struct stlport *portp, struct tty_struct *tty);
+static void    stl_sc26198intr(struct stlpanel *panelp, unsigned int iobase);
+static void    stl_sc26198txisr(struct stlport *port);
+static void    stl_sc26198rxisr(struct stlport *port, unsigned int iack);
+static void    stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char ch);
+static void    stl_sc26198rxbadchars(struct stlport *portp);
+static void    stl_sc26198otherisr(struct stlport *port, unsigned int iack);
+
+/*****************************************************************************/
+
+/*
+ *     Generic UART support structure.
+ */
+typedef struct uart {
+       int     (*panelinit)(struct stlbrd *brdp, struct stlpanel *panelp);
+       void    (*portinit)(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp);
+       void    (*setport)(struct stlport *portp, struct ktermios *tiosp);
+       int     (*getsignals)(struct stlport *portp);
+       void    (*setsignals)(struct stlport *portp, int dtr, int rts);
+       void    (*enablerxtx)(struct stlport *portp, int rx, int tx);
+       void    (*startrxtx)(struct stlport *portp, int rx, int tx);
+       void    (*disableintrs)(struct stlport *portp);
+       void    (*sendbreak)(struct stlport *portp, int len);
+       void    (*flowctrl)(struct stlport *portp, int state);
+       void    (*sendflow)(struct stlport *portp, int state);
+       void    (*flush)(struct stlport *portp);
+       int     (*datastate)(struct stlport *portp);
+       void    (*intr)(struct stlpanel *panelp, unsigned int iobase);
+} uart_t;
+
+/*
+ *     Define some macros to make calling these functions nice and clean.
+ */
+#define        stl_panelinit           (* ((uart_t *) panelp->uartp)->panelinit)
+#define        stl_portinit            (* ((uart_t *) portp->uartp)->portinit)
+#define        stl_setport             (* ((uart_t *) portp->uartp)->setport)
+#define        stl_getsignals          (* ((uart_t *) portp->uartp)->getsignals)
+#define        stl_setsignals          (* ((uart_t *) portp->uartp)->setsignals)
+#define        stl_enablerxtx          (* ((uart_t *) portp->uartp)->enablerxtx)
+#define        stl_startrxtx           (* ((uart_t *) portp->uartp)->startrxtx)
+#define        stl_disableintrs        (* ((uart_t *) portp->uartp)->disableintrs)
+#define        stl_sendbreak           (* ((uart_t *) portp->uartp)->sendbreak)
+#define        stl_flowctrl            (* ((uart_t *) portp->uartp)->flowctrl)
+#define        stl_sendflow            (* ((uart_t *) portp->uartp)->sendflow)
+#define        stl_flush               (* ((uart_t *) portp->uartp)->flush)
+#define        stl_datastate           (* ((uart_t *) portp->uartp)->datastate)
+
+/*****************************************************************************/
+
+/*
+ *     CD1400 UART specific data initialization.
+ */
+static uart_t stl_cd1400uart = {
+       stl_cd1400panelinit,
+       stl_cd1400portinit,
+       stl_cd1400setport,
+       stl_cd1400getsignals,
+       stl_cd1400setsignals,
+       stl_cd1400enablerxtx,
+       stl_cd1400startrxtx,
+       stl_cd1400disableintrs,
+       stl_cd1400sendbreak,
+       stl_cd1400flowctrl,
+       stl_cd1400sendflow,
+       stl_cd1400flush,
+       stl_cd1400datastate,
+       stl_cd1400eiointr
+};
+
+/*
+ *     Define the offsets within the register bank of a cd1400 based panel.
+ *     These io address offsets are common to the EasyIO board as well.
+ */
+#define        EREG_ADDR       0
+#define        EREG_DATA       4
+#define        EREG_RXACK      5
+#define        EREG_TXACK      6
+#define        EREG_MDACK      7
+
+#define        EREG_BANKSIZE   8
+
+#define        CD1400_CLK      25000000
+#define        CD1400_CLK8M    20000000
+
+/*
+ *     Define the cd1400 baud rate clocks. These are used when calculating
+ *     what clock and divisor to use for the required baud rate. Also
+ *     define the maximum baud rate allowed, and the default base baud.
+ */
+static int     stl_cd1400clkdivs[] = {
+       CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4
+};
+
+/*****************************************************************************/
+
+/*
+ *     SC26198 UART specific data initization.
+ */
+static uart_t stl_sc26198uart = {
+       stl_sc26198panelinit,
+       stl_sc26198portinit,
+       stl_sc26198setport,
+       stl_sc26198getsignals,
+       stl_sc26198setsignals,
+       stl_sc26198enablerxtx,
+       stl_sc26198startrxtx,
+       stl_sc26198disableintrs,
+       stl_sc26198sendbreak,
+       stl_sc26198flowctrl,
+       stl_sc26198sendflow,
+       stl_sc26198flush,
+       stl_sc26198datastate,
+       stl_sc26198intr
+};
+
+/*
+ *     Define the offsets within the register bank of a sc26198 based panel.
+ */
+#define        XP_DATA         0
+#define        XP_ADDR         1
+#define        XP_MODID        2
+#define        XP_STATUS       2
+#define        XP_IACK         3
+
+#define        XP_BANKSIZE     4
+
+/*
+ *     Define the sc26198 baud rate table. Offsets within the table
+ *     represent the actual baud rate selector of sc26198 registers.
+ */
+static unsigned int    sc26198_baudtable[] = {
+       50, 75, 150, 200, 300, 450, 600, 900, 1200, 1800, 2400, 3600,
+       4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200,
+       230400, 460800, 921600
+};
+
+#define        SC26198_NRBAUDS         ARRAY_SIZE(sc26198_baudtable)
+
+/*****************************************************************************/
+
+/*
+ *     Define the driver info for a user level control device. Used mainly
+ *     to get at port stats - only not using the port device itself.
+ */
+static const struct file_operations    stl_fsiomem = {
+       .owner          = THIS_MODULE,
+       .unlocked_ioctl = stl_memioctl,
+       .llseek         = noop_llseek,
+};
+
+static struct class *stallion_class;
+
+static void stl_cd_change(struct stlport *portp)
+{
+       unsigned int oldsigs = portp->sigs;
+       struct tty_struct *tty = tty_port_tty_get(&portp->port);
+
+       if (!tty)
+               return;
+
+       portp->sigs = stl_getsignals(portp);
+
+       if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0))
+               wake_up_interruptible(&portp->port.open_wait);
+
+       if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0))
+               if (portp->port.flags & ASYNC_CHECK_CD)
+                       tty_hangup(tty);
+       tty_kref_put(tty);
+}
+
+/*
+ *     Check for any arguments passed in on the module load command line.
+ */
+
+/*****************************************************************************/
+
+/*
+ *     Parse the supplied argument string, into the board conf struct.
+ */
+
+static int __init stl_parsebrd(struct stlconf *confp, char **argp)
+{
+       char    *sp;
+       unsigned int i;
+
+       pr_debug("stl_parsebrd(confp=%p,argp=%p)\n", confp, argp);
+
+       if ((argp[0] == NULL) || (*argp[0] == 0))
+               return 0;
+
+       for (sp = argp[0], i = 0; (*sp != 0) && (i < 25); sp++, i++)
+               *sp = tolower(*sp);
+
+       for (i = 0; i < ARRAY_SIZE(stl_brdstr); i++)
+               if (strcmp(stl_brdstr[i].name, argp[0]) == 0)
+                       break;
+
+       if (i == ARRAY_SIZE(stl_brdstr)) {
+               printk("STALLION: unknown board name, %s?\n", argp[0]);
+               return 0;
+       }
+
+       confp->brdtype = stl_brdstr[i].type;
+
+       i = 1;
+       if ((argp[i] != NULL) && (*argp[i] != 0))
+               confp->ioaddr1 = simple_strtoul(argp[i], NULL, 0);
+       i++;
+       if (confp->brdtype == BRD_ECH) {
+               if ((argp[i] != NULL) && (*argp[i] != 0))
+                       confp->ioaddr2 = simple_strtoul(argp[i], NULL, 0);
+               i++;
+       }
+       if ((argp[i] != NULL) && (*argp[i] != 0))
+               confp->irq = simple_strtoul(argp[i], NULL, 0);
+       return 1;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Allocate a new board structure. Fill out the basic info in it.
+ */
+
+static struct stlbrd *stl_allocbrd(void)
+{
+       struct stlbrd   *brdp;
+
+       brdp = kzalloc(sizeof(struct stlbrd), GFP_KERNEL);
+       if (!brdp) {
+               printk("STALLION: failed to allocate memory (size=%Zd)\n",
+                       sizeof(struct stlbrd));
+               return NULL;
+       }
+
+       brdp->magic = STL_BOARDMAGIC;
+       return brdp;
+}
+
+/*****************************************************************************/
+
+static int stl_activate(struct tty_port *port, struct tty_struct *tty)
+{
+       struct stlport *portp = container_of(port, struct stlport, port);
+       if (!portp->tx.buf) {
+               portp->tx.buf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL);
+               if (!portp->tx.buf)
+                       return -ENOMEM;
+               portp->tx.head = portp->tx.buf;
+               portp->tx.tail = portp->tx.buf;
+       }
+       stl_setport(portp, tty->termios);
+       portp->sigs = stl_getsignals(portp);
+       stl_setsignals(portp, 1, 1);
+       stl_enablerxtx(portp, 1, 1);
+       stl_startrxtx(portp, 1, 0);
+       return 0;
+}
+
+static int stl_open(struct tty_struct *tty, struct file *filp)
+{
+       struct stlport  *portp;
+       struct stlbrd   *brdp;
+       unsigned int    minordev, brdnr, panelnr;
+       int             portnr;
+
+       pr_debug("stl_open(tty=%p,filp=%p): device=%s\n", tty, filp, tty->name);
+
+       minordev = tty->index;
+       brdnr = MINOR2BRD(minordev);
+       if (brdnr >= stl_nrbrds)
+               return -ENODEV;
+       brdp = stl_brds[brdnr];
+       if (brdp == NULL)
+               return -ENODEV;
+
+       minordev = MINOR2PORT(minordev);
+       for (portnr = -1, panelnr = 0; panelnr < STL_MAXPANELS; panelnr++) {
+               if (brdp->panels[panelnr] == NULL)
+                       break;
+               if (minordev < brdp->panels[panelnr]->nrports) {
+                       portnr = minordev;
+                       break;
+               }
+               minordev -= brdp->panels[panelnr]->nrports;
+       }
+       if (portnr < 0)
+               return -ENODEV;
+
+       portp = brdp->panels[panelnr]->ports[portnr];
+       if (portp == NULL)
+               return -ENODEV;
+
+       tty->driver_data = portp;
+       return tty_port_open(&portp->port, tty, filp);
+
+}
+
+/*****************************************************************************/
+
+static int stl_carrier_raised(struct tty_port *port)
+{
+       struct stlport *portp = container_of(port, struct stlport, port);
+       return (portp->sigs & TIOCM_CD) ? 1 : 0;
+}
+
+static void stl_dtr_rts(struct tty_port *port, int on)
+{
+       struct stlport *portp = container_of(port, struct stlport, port);
+       /* Takes brd_lock internally */
+       stl_setsignals(portp, on, on);
+}
+
+/*****************************************************************************/
+
+static void stl_flushbuffer(struct tty_struct *tty)
+{
+       struct stlport  *portp;
+
+       pr_debug("stl_flushbuffer(tty=%p)\n", tty);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+
+       stl_flush(portp);
+       tty_wakeup(tty);
+}
+
+/*****************************************************************************/
+
+static void stl_waituntilsent(struct tty_struct *tty, int timeout)
+{
+       struct stlport  *portp;
+       unsigned long   tend;
+
+       pr_debug("stl_waituntilsent(tty=%p,timeout=%d)\n", tty, timeout);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+
+       if (timeout == 0)
+               timeout = HZ;
+       tend = jiffies + timeout;
+
+       while (stl_datastate(portp)) {
+               if (signal_pending(current))
+                       break;
+               msleep_interruptible(20);
+               if (time_after_eq(jiffies, tend))
+                       break;
+       }
+}
+
+/*****************************************************************************/
+
+static void stl_shutdown(struct tty_port *port)
+{
+       struct stlport *portp = container_of(port, struct stlport, port);
+       stl_disableintrs(portp);
+       stl_enablerxtx(portp, 0, 0);
+       stl_flush(portp);
+       portp->istate = 0;
+       if (portp->tx.buf != NULL) {
+               kfree(portp->tx.buf);
+               portp->tx.buf = NULL;
+               portp->tx.head = NULL;
+               portp->tx.tail = NULL;
+       }
+}
+
+static void stl_close(struct tty_struct *tty, struct file *filp)
+{
+       struct stlport*portp;
+       pr_debug("stl_close(tty=%p,filp=%p)\n", tty, filp);
+
+       portp = tty->driver_data;
+       if(portp == NULL)
+               return;
+       tty_port_close(&portp->port, tty, filp);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Write routine. Take data and stuff it in to the TX ring queue.
+ *     If transmit interrupts are not running then start them.
+ */
+
+static int stl_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+       struct stlport  *portp;
+       unsigned int    len, stlen;
+       unsigned char   *chbuf;
+       char            *head, *tail;
+
+       pr_debug("stl_write(tty=%p,buf=%p,count=%d)\n", tty, buf, count);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return 0;
+       if (portp->tx.buf == NULL)
+               return 0;
+
+/*
+ *     If copying direct from user space we must cater for page faults,
+ *     causing us to "sleep" here for a while. To handle this copy in all
+ *     the data we need now, into a local buffer. Then when we got it all
+ *     copy it into the TX buffer.
+ */
+       chbuf = (unsigned char *) buf;
+
+       head = portp->tx.head;
+       tail = portp->tx.tail;
+       if (head >= tail) {
+               len = STL_TXBUFSIZE - (head - tail) - 1;
+               stlen = STL_TXBUFSIZE - (head - portp->tx.buf);
+       } else {
+               len = tail - head - 1;
+               stlen = len;
+       }
+
+       len = min(len, (unsigned int)count);
+       count = 0;
+       while (len > 0) {
+               stlen = min(len, stlen);
+               memcpy(head, chbuf, stlen);
+               len -= stlen;
+               chbuf += stlen;
+               count += stlen;
+               head += stlen;
+               if (head >= (portp->tx.buf + STL_TXBUFSIZE)) {
+                       head = portp->tx.buf;
+                       stlen = tail - head;
+               }
+       }
+       portp->tx.head = head;
+
+       clear_bit(ASYI_TXLOW, &portp->istate);
+       stl_startrxtx(portp, -1, 1);
+
+       return count;
+}
+
+/*****************************************************************************/
+
+static int stl_putchar(struct tty_struct *tty, unsigned char ch)
+{
+       struct stlport  *portp;
+       unsigned int    len;
+       char            *head, *tail;
+
+       pr_debug("stl_putchar(tty=%p,ch=%x)\n", tty, ch);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return -EINVAL;
+       if (portp->tx.buf == NULL)
+               return -EINVAL;
+
+       head = portp->tx.head;
+       tail = portp->tx.tail;
+
+       len = (head >= tail) ? (STL_TXBUFSIZE - (head - tail)) : (tail - head);
+       len--;
+
+       if (len > 0) {
+               *head++ = ch;
+               if (head >= (portp->tx.buf + STL_TXBUFSIZE))
+                       head = portp->tx.buf;
+       }       
+       portp->tx.head = head;
+       return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     If there are any characters in the buffer then make sure that TX
+ *     interrupts are on and get'em out. Normally used after the putchar
+ *     routine has been called.
+ */
+
+static void stl_flushchars(struct tty_struct *tty)
+{
+       struct stlport  *portp;
+
+       pr_debug("stl_flushchars(tty=%p)\n", tty);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+       if (portp->tx.buf == NULL)
+               return;
+
+       stl_startrxtx(portp, -1, 1);
+}
+
+/*****************************************************************************/
+
+static int stl_writeroom(struct tty_struct *tty)
+{
+       struct stlport  *portp;
+       char            *head, *tail;
+
+       pr_debug("stl_writeroom(tty=%p)\n", tty);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return 0;
+       if (portp->tx.buf == NULL)
+               return 0;
+
+       head = portp->tx.head;
+       tail = portp->tx.tail;
+       return (head >= tail) ? (STL_TXBUFSIZE - (head - tail) - 1) : (tail - head - 1);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return number of chars in the TX buffer. Normally we would just
+ *     calculate the number of chars in the buffer and return that, but if
+ *     the buffer is empty and TX interrupts are still on then we return
+ *     that the buffer still has 1 char in it. This way whoever called us
+ *     will not think that ALL chars have drained - since the UART still
+ *     must have some chars in it (we are busy after all).
+ */
+
+static int stl_charsinbuffer(struct tty_struct *tty)
+{
+       struct stlport  *portp;
+       unsigned int    size;
+       char            *head, *tail;
+
+       pr_debug("stl_charsinbuffer(tty=%p)\n", tty);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return 0;
+       if (portp->tx.buf == NULL)
+               return 0;
+
+       head = portp->tx.head;
+       tail = portp->tx.tail;
+       size = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
+       if ((size == 0) && test_bit(ASYI_TXBUSY, &portp->istate))
+               size = 1;
+       return size;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Generate the serial struct info.
+ */
+
+static int stl_getserial(struct stlport *portp, struct serial_struct __user *sp)
+{
+       struct serial_struct    sio;
+       struct stlbrd           *brdp;
+
+       pr_debug("stl_getserial(portp=%p,sp=%p)\n", portp, sp);
+
+       memset(&sio, 0, sizeof(struct serial_struct));
+
+       mutex_lock(&portp->port.mutex);
+       sio.line = portp->portnr;
+       sio.port = portp->ioaddr;
+       sio.flags = portp->port.flags;
+       sio.baud_base = portp->baud_base;
+       sio.close_delay = portp->close_delay;
+       sio.closing_wait = portp->closing_wait;
+       sio.custom_divisor = portp->custom_divisor;
+       sio.hub6 = 0;
+       if (portp->uartp == &stl_cd1400uart) {
+               sio.type = PORT_CIRRUS;
+               sio.xmit_fifo_size = CD1400_TXFIFOSIZE;
+       } else {
+               sio.type = PORT_UNKNOWN;
+               sio.xmit_fifo_size = SC26198_TXFIFOSIZE;
+       }
+
+       brdp = stl_brds[portp->brdnr];
+       if (brdp != NULL)
+               sio.irq = brdp->irq;
+       mutex_unlock(&portp->port.mutex);
+
+       return copy_to_user(sp, &sio, sizeof(struct serial_struct)) ? -EFAULT : 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Set port according to the serial struct info.
+ *     At this point we do not do any auto-configure stuff, so we will
+ *     just quietly ignore any requests to change irq, etc.
+ */
+
+static int stl_setserial(struct tty_struct *tty, struct serial_struct __user *sp)
+{
+       struct stlport *        portp = tty->driver_data;
+       struct serial_struct    sio;
+
+       pr_debug("stl_setserial(portp=%p,sp=%p)\n", portp, sp);
+
+       if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
+               return -EFAULT;
+       mutex_lock(&portp->port.mutex);
+       if (!capable(CAP_SYS_ADMIN)) {
+               if ((sio.baud_base != portp->baud_base) ||
+                   (sio.close_delay != portp->close_delay) ||
+                   ((sio.flags & ~ASYNC_USR_MASK) !=
+                   (portp->port.flags & ~ASYNC_USR_MASK))) {
+                       mutex_unlock(&portp->port.mutex);
+                       return -EPERM;
+               }
+       } 
+
+       portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) |
+               (sio.flags & ASYNC_USR_MASK);
+       portp->baud_base = sio.baud_base;
+       portp->close_delay = sio.close_delay;
+       portp->closing_wait = sio.closing_wait;
+       portp->custom_divisor = sio.custom_divisor;
+       mutex_unlock(&portp->port.mutex);
+       stl_setport(portp, tty->termios);
+       return 0;
+}
+
+/*****************************************************************************/
+
+static int stl_tiocmget(struct tty_struct *tty)
+{
+       struct stlport  *portp;
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return -ENODEV;
+       if (tty->flags & (1 << TTY_IO_ERROR))
+               return -EIO;
+
+       return stl_getsignals(portp);
+}
+
+static int stl_tiocmset(struct tty_struct *tty,
+                       unsigned int set, unsigned int clear)
+{
+       struct stlport  *portp;
+       int rts = -1, dtr = -1;
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return -ENODEV;
+       if (tty->flags & (1 << TTY_IO_ERROR))
+               return -EIO;
+
+       if (set & TIOCM_RTS)
+               rts = 1;
+       if (set & TIOCM_DTR)
+               dtr = 1;
+       if (clear & TIOCM_RTS)
+               rts = 0;
+       if (clear & TIOCM_DTR)
+               dtr = 0;
+
+       stl_setsignals(portp, dtr, rts);
+       return 0;
+}
+
+static int stl_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
+{
+       struct stlport  *portp;
+       int             rc;
+       void __user *argp = (void __user *)arg;
+
+       pr_debug("stl_ioctl(tty=%p,cmd=%x,arg=%lx)\n", tty, cmd, arg);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return -ENODEV;
+
+       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+           (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS))
+               if (tty->flags & (1 << TTY_IO_ERROR))
+                       return -EIO;
+
+       rc = 0;
+
+       switch (cmd) {
+       case TIOCGSERIAL:
+               rc = stl_getserial(portp, argp);
+               break;
+       case TIOCSSERIAL:
+               rc = stl_setserial(tty, argp);
+               break;
+       case COM_GETPORTSTATS:
+               rc = stl_getportstats(tty, portp, argp);
+               break;
+       case COM_CLRPORTSTATS:
+               rc = stl_clrportstats(portp, argp);
+               break;
+       case TIOCSERCONFIG:
+       case TIOCSERGWILD:
+       case TIOCSERSWILD:
+       case TIOCSERGETLSR:
+       case TIOCSERGSTRUCT:
+       case TIOCSERGETMULTI:
+       case TIOCSERSETMULTI:
+       default:
+               rc = -ENOIOCTLCMD;
+               break;
+       }
+       return rc;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Start the transmitter again. Just turn TX interrupts back on.
+ */
+
+static void stl_start(struct tty_struct *tty)
+{
+       struct stlport  *portp;
+
+       pr_debug("stl_start(tty=%p)\n", tty);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+       stl_startrxtx(portp, -1, 1);
+}
+
+/*****************************************************************************/
+
+static void stl_settermios(struct tty_struct *tty, struct ktermios *old)
+{
+       struct stlport  *portp;
+       struct ktermios *tiosp;
+
+       pr_debug("stl_settermios(tty=%p,old=%p)\n", tty, old);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+
+       tiosp = tty->termios;
+       if ((tiosp->c_cflag == old->c_cflag) &&
+           (tiosp->c_iflag == old->c_iflag))
+               return;
+
+       stl_setport(portp, tiosp);
+       stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0),
+               -1);
+       if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) {
+               tty->hw_stopped = 0;
+               stl_start(tty);
+       }
+       if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL))
+               wake_up_interruptible(&portp->port.open_wait);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Attempt to flow control who ever is sending us data. Based on termios
+ *     settings use software or/and hardware flow control.
+ */
+
+static void stl_throttle(struct tty_struct *tty)
+{
+       struct stlport  *portp;
+
+       pr_debug("stl_throttle(tty=%p)\n", tty);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+       stl_flowctrl(portp, 0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Unflow control the device sending us data...
+ */
+
+static void stl_unthrottle(struct tty_struct *tty)
+{
+       struct stlport  *portp;
+
+       pr_debug("stl_unthrottle(tty=%p)\n", tty);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+       stl_flowctrl(portp, 1);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Stop the transmitter. Basically to do this we will just turn TX
+ *     interrupts off.
+ */
+
+static void stl_stop(struct tty_struct *tty)
+{
+       struct stlport  *portp;
+
+       pr_debug("stl_stop(tty=%p)\n", tty);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+       stl_startrxtx(portp, -1, 0);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Hangup this port. This is pretty much like closing the port, only
+ *     a little more brutal. No waiting for data to drain. Shutdown the
+ *     port and maybe drop signals.
+ */
+
+static void stl_hangup(struct tty_struct *tty)
+{
+       struct stlport  *portp = tty->driver_data;
+       pr_debug("stl_hangup(tty=%p)\n", tty);
+
+       if (portp == NULL)
+               return;
+       tty_port_hangup(&portp->port);
+}
+
+/*****************************************************************************/
+
+static int stl_breakctl(struct tty_struct *tty, int state)
+{
+       struct stlport  *portp;
+
+       pr_debug("stl_breakctl(tty=%p,state=%d)\n", tty, state);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return -EINVAL;
+
+       stl_sendbreak(portp, ((state == -1) ? 1 : 2));
+       return 0;
+}
+
+/*****************************************************************************/
+
+static void stl_sendxchar(struct tty_struct *tty, char ch)
+{
+       struct stlport  *portp;
+
+       pr_debug("stl_sendxchar(tty=%p,ch=%x)\n", tty, ch);
+
+       portp = tty->driver_data;
+       if (portp == NULL)
+               return;
+
+       if (ch == STOP_CHAR(tty))
+               stl_sendflow(portp, 0);
+       else if (ch == START_CHAR(tty))
+               stl_sendflow(portp, 1);
+       else
+               stl_putchar(tty, ch);
+}
+
+static void stl_portinfo(struct seq_file *m, struct stlport *portp, int portnr)
+{
+       int     sigs;
+       char sep;
+
+       seq_printf(m, "%d: uart:%s tx:%d rx:%d",
+               portnr, (portp->hwid == 1) ? "SC26198" : "CD1400",
+               (int) portp->stats.txtotal, (int) portp->stats.rxtotal);
+
+       if (portp->stats.rxframing)
+               seq_printf(m, " fe:%d", (int) portp->stats.rxframing);
+       if (portp->stats.rxparity)
+               seq_printf(m, " pe:%d", (int) portp->stats.rxparity);
+       if (portp->stats.rxbreaks)
+               seq_printf(m, " brk:%d", (int) portp->stats.rxbreaks);
+       if (portp->stats.rxoverrun)
+               seq_printf(m, " oe:%d", (int) portp->stats.rxoverrun);
+
+       sigs = stl_getsignals(portp);
+       sep = ' ';
+       if (sigs & TIOCM_RTS) {
+               seq_printf(m, "%c%s", sep, "RTS");
+               sep = '|';
+       }
+       if (sigs & TIOCM_CTS) {
+               seq_printf(m, "%c%s", sep, "CTS");
+               sep = '|';
+       }
+       if (sigs & TIOCM_DTR) {
+               seq_printf(m, "%c%s", sep, "DTR");
+               sep = '|';
+       }
+       if (sigs & TIOCM_CD) {
+               seq_printf(m, "%c%s", sep, "DCD");
+               sep = '|';
+       }
+       if (sigs & TIOCM_DSR) {
+               seq_printf(m, "%c%s", sep, "DSR");
+               sep = '|';
+       }
+       seq_putc(m, '\n');
+}
+
+/*****************************************************************************/
+
+/*
+ *     Port info, read from the /proc file system.
+ */
+
+static int stl_proc_show(struct seq_file *m, void *v)
+{
+       struct stlbrd   *brdp;
+       struct stlpanel *panelp;
+       struct stlport  *portp;
+       unsigned int    brdnr, panelnr, portnr;
+       int             totalport;
+
+       totalport = 0;
+
+       seq_printf(m, "%s: version %s\n", stl_drvtitle, stl_drvversion);
+
+/*
+ *     We scan through for each board, panel and port. The offset is
+ *     calculated on the fly, and irrelevant ports are skipped.
+ */
+       for (brdnr = 0; brdnr < stl_nrbrds; brdnr++) {
+               brdp = stl_brds[brdnr];
+               if (brdp == NULL)
+                       continue;
+               if (brdp->state == 0)
+                       continue;
+
+               totalport = brdnr * STL_MAXPORTS;
+               for (panelnr = 0; panelnr < brdp->nrpanels; panelnr++) {
+                       panelp = brdp->panels[panelnr];
+                       if (panelp == NULL)
+                               continue;
+
+                       for (portnr = 0; portnr < panelp->nrports; portnr++,
+                           totalport++) {
+                               portp = panelp->ports[portnr];
+                               if (portp == NULL)
+                                       continue;
+                               stl_portinfo(m, portp, totalport);
+                       }
+               }
+       }
+       return 0;
+}
+
+static int stl_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, stl_proc_show, NULL);
+}
+
+static const struct file_operations stl_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = stl_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/*****************************************************************************/
+
+/*
+ *     All board interrupts are vectored through here first. This code then
+ *     calls off to the approrpriate board interrupt handlers.
+ */
+
+static irqreturn_t stl_intr(int irq, void *dev_id)
+{
+       struct stlbrd *brdp = dev_id;
+
+       pr_debug("stl_intr(brdp=%p,irq=%d)\n", brdp, brdp->irq);
+
+       return IRQ_RETVAL((* brdp->isr)(brdp));
+}
+
+/*****************************************************************************/
+
+/*
+ *     Interrupt service routine for EasyIO board types.
+ */
+
+static int stl_eiointr(struct stlbrd *brdp)
+{
+       struct stlpanel *panelp;
+       unsigned int    iobase;
+       int             handled = 0;
+
+       spin_lock(&brd_lock);
+       panelp = brdp->panels[0];
+       iobase = panelp->iobase;
+       while (inb(brdp->iostatus) & EIO_INTRPEND) {
+               handled = 1;
+               (* panelp->isr)(panelp, iobase);
+       }
+       spin_unlock(&brd_lock);
+       return handled;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Interrupt service routine for ECH-AT board types.
+ */
+
+static int stl_echatintr(struct stlbrd *brdp)
+{
+       struct stlpanel *panelp;
+       unsigned int    ioaddr, bnknr;
+       int             handled = 0;
+
+       outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
+
+       while (inb(brdp->iostatus) & ECH_INTRPEND) {
+               handled = 1;
+               for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) {
+                       ioaddr = brdp->bnkstataddr[bnknr];
+                       if (inb(ioaddr) & ECH_PNLINTRPEND) {
+                               panelp = brdp->bnk2panel[bnknr];
+                               (* panelp->isr)(panelp, (ioaddr & 0xfffc));
+                       }
+               }
+       }
+
+       outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+
+       return handled;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Interrupt service routine for ECH-MCA board types.
+ */
+
+static int stl_echmcaintr(struct stlbrd *brdp)
+{
+       struct stlpanel *panelp;
+       unsigned int    ioaddr, bnknr;
+       int             handled = 0;
+
+       while (inb(brdp->iostatus) & ECH_INTRPEND) {
+               handled = 1;
+               for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) {
+                       ioaddr = brdp->bnkstataddr[bnknr];
+                       if (inb(ioaddr) & ECH_PNLINTRPEND) {
+                               panelp = brdp->bnk2panel[bnknr];
+                               (* panelp->isr)(panelp, (ioaddr & 0xfffc));
+                       }
+               }
+       }
+       return handled;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Interrupt service routine for ECH-PCI board types.
+ */
+
+static int stl_echpciintr(struct stlbrd *brdp)
+{
+       struct stlpanel *panelp;
+       unsigned int    ioaddr, bnknr, recheck;
+       int             handled = 0;
+
+       while (1) {
+               recheck = 0;
+               for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) {
+                       outb(brdp->bnkpageaddr[bnknr], brdp->ioctrl);
+                       ioaddr = brdp->bnkstataddr[bnknr];
+                       if (inb(ioaddr) & ECH_PNLINTRPEND) {
+                               panelp = brdp->bnk2panel[bnknr];
+                               (* panelp->isr)(panelp, (ioaddr & 0xfffc));
+                               recheck++;
+                               handled = 1;
+                       }
+               }
+               if (! recheck)
+                       break;
+       }
+       return handled;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Interrupt service routine for ECH-8/64-PCI board types.
+ */
+
+static int stl_echpci64intr(struct stlbrd *brdp)
+{
+       struct stlpanel *panelp;
+       unsigned int    ioaddr, bnknr;
+       int             handled = 0;
+
+       while (inb(brdp->ioctrl) & 0x1) {
+               handled = 1;
+               for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) {
+                       ioaddr = brdp->bnkstataddr[bnknr];
+                       if (inb(ioaddr) & ECH_PNLINTRPEND) {
+                               panelp = brdp->bnk2panel[bnknr];
+                               (* panelp->isr)(panelp, (ioaddr & 0xfffc));
+                       }
+               }
+       }
+
+       return handled;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Initialize all the ports on a panel.
+ */
+
+static int __devinit stl_initports(struct stlbrd *brdp, struct stlpanel *panelp)
+{
+       struct stlport *portp;
+       unsigned int i;
+       int chipmask;
+
+       pr_debug("stl_initports(brdp=%p,panelp=%p)\n", brdp, panelp);
+
+       chipmask = stl_panelinit(brdp, panelp);
+
+/*
+ *     All UART's are initialized (if found!). Now go through and setup
+ *     each ports data structures.
+ */
+       for (i = 0; i < panelp->nrports; i++) {
+               portp = kzalloc(sizeof(struct stlport), GFP_KERNEL);
+               if (!portp) {
+                       printk("STALLION: failed to allocate memory "
+                               "(size=%Zd)\n", sizeof(struct stlport));
+                       break;
+               }
+               tty_port_init(&portp->port);
+               portp->port.ops = &stl_port_ops;
+               portp->magic = STL_PORTMAGIC;
+               portp->portnr = i;
+               portp->brdnr = panelp->brdnr;
+               portp->panelnr = panelp->panelnr;
+               portp->uartp = panelp->uartp;
+               portp->clk = brdp->clk;
+               portp->baud_base = STL_BAUDBASE;
+               portp->close_delay = STL_CLOSEDELAY;
+               portp->closing_wait = 30 * HZ;
+               init_waitqueue_head(&portp->port.open_wait);
+               init_waitqueue_head(&portp->port.close_wait);
+               portp->stats.brd = portp->brdnr;
+               portp->stats.panel = portp->panelnr;
+               portp->stats.port = portp->portnr;
+               panelp->ports[i] = portp;
+               stl_portinit(brdp, panelp, portp);
+       }
+
+       return 0;
+}
+
+static void stl_cleanup_panels(struct stlbrd *brdp)
+{
+       struct stlpanel *panelp;
+       struct stlport *portp;
+       unsigned int j, k;
+       struct tty_struct *tty;
+
+       for (j = 0; j < STL_MAXPANELS; j++) {
+               panelp = brdp->panels[j];
+               if (panelp == NULL)
+                       continue;
+               for (k = 0; k < STL_PORTSPERPANEL; k++) {
+                       portp = panelp->ports[k];
+                       if (portp == NULL)
+                               continue;
+                       tty = tty_port_tty_get(&portp->port);
+                       if (tty != NULL) {
+                               stl_hangup(tty);
+                               tty_kref_put(tty);
+                       }
+                       kfree(portp->tx.buf);
+                       kfree(portp);
+               }
+               kfree(panelp);
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Try to find and initialize an EasyIO board.
+ */
+
+static int __devinit stl_initeio(struct stlbrd *brdp)
+{
+       struct stlpanel *panelp;
+       unsigned int    status;
+       char            *name;
+       int             retval;
+
+       pr_debug("stl_initeio(brdp=%p)\n", brdp);
+
+       brdp->ioctrl = brdp->ioaddr1 + 1;
+       brdp->iostatus = brdp->ioaddr1 + 2;
+
+       status = inb(brdp->iostatus);
+       if ((status & EIO_IDBITMASK) == EIO_MK3)
+               brdp->ioctrl++;
+
+/*
+ *     Handle board specific stuff now. The real difference is PCI
+ *     or not PCI.
+ */
+       if (brdp->brdtype == BRD_EASYIOPCI) {
+               brdp->iosize1 = 0x80;
+               brdp->iosize2 = 0x80;
+               name = "serial(EIO-PCI)";
+               outb(0x41, (brdp->ioaddr2 + 0x4c));
+       } else {
+               brdp->iosize1 = 8;
+               name = "serial(EIO)";
+               if ((brdp->irq < 0) || (brdp->irq > 15) ||
+                   (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+                       printk("STALLION: invalid irq=%d for brd=%d\n",
+                               brdp->irq, brdp->brdnr);
+                       retval = -EINVAL;
+                       goto err;
+               }
+               outb((stl_vecmap[brdp->irq] | EIO_0WS |
+                       ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)),
+                       brdp->ioctrl);
+       }
+
+       retval = -EBUSY;
+       if (!request_region(brdp->ioaddr1, brdp->iosize1, name)) {
+               printk(KERN_WARNING "STALLION: Warning, board %d I/O address "
+                       "%x conflicts with another device\n", brdp->brdnr, 
+                       brdp->ioaddr1);
+               goto err;
+       }
+       
+       if (brdp->iosize2 > 0)
+               if (!request_region(brdp->ioaddr2, brdp->iosize2, name)) {
+                       printk(KERN_WARNING "STALLION: Warning, board %d I/O "
+                               "address %x conflicts with another device\n",
+                               brdp->brdnr, brdp->ioaddr2);
+                       printk(KERN_WARNING "STALLION: Warning, also "
+                               "releasing board %d I/O address %x \n", 
+                               brdp->brdnr, brdp->ioaddr1);
+                       goto err_rel1;
+               }
+
+/*
+ *     Everything looks OK, so let's go ahead and probe for the hardware.
+ */
+       brdp->clk = CD1400_CLK;
+       brdp->isr = stl_eiointr;
+
+       retval = -ENODEV;
+       switch (status & EIO_IDBITMASK) {
+       case EIO_8PORTM:
+               brdp->clk = CD1400_CLK8M;
+               /* fall thru */
+       case EIO_8PORTRS:
+       case EIO_8PORTDI:
+               brdp->nrports = 8;
+               break;
+       case EIO_4PORTRS:
+               brdp->nrports = 4;
+               break;
+       case EIO_MK3:
+               switch (status & EIO_BRDMASK) {
+               case ID_BRD4:
+                       brdp->nrports = 4;
+                       break;
+               case ID_BRD8:
+                       brdp->nrports = 8;
+                       break;
+               case ID_BRD16:
+                       brdp->nrports = 16;
+                       break;
+               default:
+                       goto err_rel2;
+               }
+               break;
+       default:
+               goto err_rel2;
+       }
+
+/*
+ *     We have verified that the board is actually present, so now we
+ *     can complete the setup.
+ */
+
+       panelp = kzalloc(sizeof(struct stlpanel), GFP_KERNEL);
+       if (!panelp) {
+               printk(KERN_WARNING "STALLION: failed to allocate memory "
+                       "(size=%Zd)\n", sizeof(struct stlpanel));
+               retval = -ENOMEM;
+               goto err_rel2;
+       }
+
+       panelp->magic = STL_PANELMAGIC;
+       panelp->brdnr = brdp->brdnr;
+       panelp->panelnr = 0;
+       panelp->nrports = brdp->nrports;
+       panelp->iobase = brdp->ioaddr1;
+       panelp->hwid = status;
+       if ((status & EIO_IDBITMASK) == EIO_MK3) {
+               panelp->uartp = &stl_sc26198uart;
+               panelp->isr = stl_sc26198intr;
+       } else {
+               panelp->uartp = &stl_cd1400uart;
+               panelp->isr = stl_cd1400eiointr;
+       }
+
+       brdp->panels[0] = panelp;
+       brdp->nrpanels = 1;
+       brdp->state |= BRD_FOUND;
+       brdp->hwid = status;
+       if (request_irq(brdp->irq, stl_intr, IRQF_SHARED, name, brdp) != 0) {
+               printk("STALLION: failed to register interrupt "
+                   "routine for %s irq=%d\n", name, brdp->irq);
+               retval = -ENODEV;
+               goto err_fr;
+       }
+
+       return 0;
+err_fr:
+       stl_cleanup_panels(brdp);
+err_rel2:
+       if (brdp->iosize2 > 0)
+               release_region(brdp->ioaddr2, brdp->iosize2);
+err_rel1:
+       release_region(brdp->ioaddr1, brdp->iosize1);
+err:
+       return retval;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Try to find an ECH board and initialize it. This code is capable of
+ *     dealing with all types of ECH board.
+ */
+
+static int __devinit stl_initech(struct stlbrd *brdp)
+{
+       struct stlpanel *panelp;
+       unsigned int    status, nxtid, ioaddr, conflict, panelnr, banknr, i;
+       int             retval;
+       char            *name;
+
+       pr_debug("stl_initech(brdp=%p)\n", brdp);
+
+       status = 0;
+       conflict = 0;
+
+/*
+ *     Set up the initial board register contents for boards. This varies a
+ *     bit between the different board types. So we need to handle each
+ *     separately. Also do a check that the supplied IRQ is good.
+ */
+       switch (brdp->brdtype) {
+
+       case BRD_ECH:
+               brdp->isr = stl_echatintr;
+               brdp->ioctrl = brdp->ioaddr1 + 1;
+               brdp->iostatus = brdp->ioaddr1 + 1;
+               status = inb(brdp->iostatus);
+               if ((status & ECH_IDBITMASK) != ECH_ID) {
+                       retval = -ENODEV;
+                       goto err;
+               }
+               if ((brdp->irq < 0) || (brdp->irq > 15) ||
+                   (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+                       printk("STALLION: invalid irq=%d for brd=%d\n",
+                               brdp->irq, brdp->brdnr);
+                       retval = -EINVAL;
+                       goto err;
+               }
+               status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1);
+               status |= (stl_vecmap[brdp->irq] << 1);
+               outb((status | ECH_BRDRESET), brdp->ioaddr1);
+               brdp->ioctrlval = ECH_INTENABLE |
+                       ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE);
+               for (i = 0; i < 10; i++)
+                       outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
+               brdp->iosize1 = 2;
+               brdp->iosize2 = 32;
+               name = "serial(EC8/32)";
+               outb(status, brdp->ioaddr1);
+               break;
+
+       case BRD_ECHMC:
+               brdp->isr = stl_echmcaintr;
+               brdp->ioctrl = brdp->ioaddr1 + 0x20;
+               brdp->iostatus = brdp->ioctrl;
+               status = inb(brdp->iostatus);
+               if ((status & ECH_IDBITMASK) != ECH_ID) {
+                       retval = -ENODEV;
+                       goto err;
+               }
+               if ((brdp->irq < 0) || (brdp->irq > 15) ||
+                   (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+                       printk("STALLION: invalid irq=%d for brd=%d\n",
+                               brdp->irq, brdp->brdnr);
+                       retval = -EINVAL;
+                       goto err;
+               }
+               outb(ECHMC_BRDRESET, brdp->ioctrl);
+               outb(ECHMC_INTENABLE, brdp->ioctrl);
+               brdp->iosize1 = 64;
+               name = "serial(EC8/32-MC)";
+               break;
+
+       case BRD_ECHPCI:
+               brdp->isr = stl_echpciintr;
+               brdp->ioctrl = brdp->ioaddr1 + 2;
+               brdp->iosize1 = 4;
+               brdp->iosize2 = 8;
+               name = "serial(EC8/32-PCI)";
+               break;
+
+       case BRD_ECH64PCI:
+               brdp->isr = stl_echpci64intr;
+               brdp->ioctrl = brdp->ioaddr2 + 0x40;
+               outb(0x43, (brdp->ioaddr1 + 0x4c));
+               brdp->iosize1 = 0x80;
+               brdp->iosize2 = 0x80;
+               name = "serial(EC8/64-PCI)";
+               break;
+
+       default:
+               printk("STALLION: unknown board type=%d\n", brdp->brdtype);
+               retval = -EINVAL;
+               goto err;
+       }
+
+/*
+ *     Check boards for possible IO address conflicts and return fail status 
+ *     if an IO conflict found.
+ */
+       retval = -EBUSY;
+       if (!request_region(brdp->ioaddr1, brdp->iosize1, name)) {
+               printk(KERN_WARNING "STALLION: Warning, board %d I/O address "
+                       "%x conflicts with another device\n", brdp->brdnr, 
+                       brdp->ioaddr1);
+               goto err;
+       }
+       
+       if (brdp->iosize2 > 0)
+               if (!request_region(brdp->ioaddr2, brdp->iosize2, name)) {
+                       printk(KERN_WARNING "STALLION: Warning, board %d I/O "
+                               "address %x conflicts with another device\n",
+                               brdp->brdnr, brdp->ioaddr2);
+                       printk(KERN_WARNING "STALLION: Warning, also "
+                               "releasing board %d I/O address %x \n", 
+                               brdp->brdnr, brdp->ioaddr1);
+                       goto err_rel1;
+               }
+
+/*
+ *     Scan through the secondary io address space looking for panels.
+ *     As we find'em allocate and initialize panel structures for each.
+ */
+       brdp->clk = CD1400_CLK;
+       brdp->hwid = status;
+
+       ioaddr = brdp->ioaddr2;
+       banknr = 0;
+       panelnr = 0;
+       nxtid = 0;
+
+       for (i = 0; i < STL_MAXPANELS; i++) {
+               if (brdp->brdtype == BRD_ECHPCI) {
+                       outb(nxtid, brdp->ioctrl);
+                       ioaddr = brdp->ioaddr2;
+               }
+               status = inb(ioaddr + ECH_PNLSTATUS);
+               if ((status & ECH_PNLIDMASK) != nxtid)
+                       break;
+               panelp = kzalloc(sizeof(struct stlpanel), GFP_KERNEL);
+               if (!panelp) {
+                       printk("STALLION: failed to allocate memory "
+                               "(size=%Zd)\n", sizeof(struct stlpanel));
+                       retval = -ENOMEM;
+                       goto err_fr;
+               }
+               panelp->magic = STL_PANELMAGIC;
+               panelp->brdnr = brdp->brdnr;
+               panelp->panelnr = panelnr;
+               panelp->iobase = ioaddr;
+               panelp->pagenr = nxtid;
+               panelp->hwid = status;
+               brdp->bnk2panel[banknr] = panelp;
+               brdp->bnkpageaddr[banknr] = nxtid;
+               brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS;
+
+               if (status & ECH_PNLXPID) {
+                       panelp->uartp = &stl_sc26198uart;
+                       panelp->isr = stl_sc26198intr;
+                       if (status & ECH_PNL16PORT) {
+                               panelp->nrports = 16;
+                               brdp->bnk2panel[banknr] = panelp;
+                               brdp->bnkpageaddr[banknr] = nxtid;
+                               brdp->bnkstataddr[banknr++] = ioaddr + 4 +
+                                       ECH_PNLSTATUS;
+                       } else
+                               panelp->nrports = 8;
+               } else {
+                       panelp->uartp = &stl_cd1400uart;
+                       panelp->isr = stl_cd1400echintr;
+                       if (status & ECH_PNL16PORT) {
+                               panelp->nrports = 16;
+                               panelp->ackmask = 0x80;
+                               if (brdp->brdtype != BRD_ECHPCI)
+                                       ioaddr += EREG_BANKSIZE;
+                               brdp->bnk2panel[banknr] = panelp;
+                               brdp->bnkpageaddr[banknr] = ++nxtid;
+                               brdp->bnkstataddr[banknr++] = ioaddr +
+                                       ECH_PNLSTATUS;
+                       } else {
+                               panelp->nrports = 8;
+                               panelp->ackmask = 0xc0;
+                       }
+               }
+
+               nxtid++;
+               ioaddr += EREG_BANKSIZE;
+               brdp->nrports += panelp->nrports;
+               brdp->panels[panelnr++] = panelp;
+               if ((brdp->brdtype != BRD_ECHPCI) &&
+                   (ioaddr >= (brdp->ioaddr2 + brdp->iosize2))) {
+                       retval = -EINVAL;
+                       goto err_fr;
+               }
+       }
+
+       brdp->nrpanels = panelnr;
+       brdp->nrbnks = banknr;
+       if (brdp->brdtype == BRD_ECH)
+               outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+
+       brdp->state |= BRD_FOUND;
+       if (request_irq(brdp->irq, stl_intr, IRQF_SHARED, name, brdp) != 0) {
+               printk("STALLION: failed to register interrupt "
+                   "routine for %s irq=%d\n", name, brdp->irq);
+               retval = -ENODEV;
+               goto err_fr;
+       }
+
+       return 0;
+err_fr:
+       stl_cleanup_panels(brdp);
+       if (brdp->iosize2 > 0)
+               release_region(brdp->ioaddr2, brdp->iosize2);
+err_rel1:
+       release_region(brdp->ioaddr1, brdp->iosize1);
+err:
+       return retval;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Initialize and configure the specified board.
+ *     Scan through all the boards in the configuration and see what we
+ *     can find. Handle EIO and the ECH boards a little differently here
+ *     since the initial search and setup is very different.
+ */
+
+static int __devinit stl_brdinit(struct stlbrd *brdp)
+{
+       int i, retval;
+
+       pr_debug("stl_brdinit(brdp=%p)\n", brdp);
+
+       switch (brdp->brdtype) {
+       case BRD_EASYIO:
+       case BRD_EASYIOPCI:
+               retval = stl_initeio(brdp);
+               if (retval)
+                       goto err;
+               break;
+       case BRD_ECH:
+       case BRD_ECHMC:
+       case BRD_ECHPCI:
+       case BRD_ECH64PCI:
+               retval = stl_initech(brdp);
+               if (retval)
+                       goto err;
+               break;
+       default:
+               printk("STALLION: board=%d is unknown board type=%d\n",
+                       brdp->brdnr, brdp->brdtype);
+               retval = -ENODEV;
+               goto err;
+       }
+
+       if ((brdp->state & BRD_FOUND) == 0) {
+               printk("STALLION: %s board not found, board=%d io=%x irq=%d\n",
+                       stl_brdnames[brdp->brdtype], brdp->brdnr,
+                       brdp->ioaddr1, brdp->irq);
+               goto err_free;
+       }
+
+       for (i = 0; i < STL_MAXPANELS; i++)
+               if (brdp->panels[i] != NULL)
+                       stl_initports(brdp, brdp->panels[i]);
+
+       printk("STALLION: %s found, board=%d io=%x irq=%d "
+               "nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype],
+               brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels,
+               brdp->nrports);
+
+       return 0;
+err_free:
+       free_irq(brdp->irq, brdp);
+
+       stl_cleanup_panels(brdp);
+
+       release_region(brdp->ioaddr1, brdp->iosize1);
+       if (brdp->iosize2 > 0)
+               release_region(brdp->ioaddr2, brdp->iosize2);
+err:
+       return retval;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Find the next available board number that is free.
+ */
+
+static int __devinit stl_getbrdnr(void)
+{
+       unsigned int i;
+
+       for (i = 0; i < STL_MAXBRDS; i++)
+               if (stl_brds[i] == NULL) {
+                       if (i >= stl_nrbrds)
+                               stl_nrbrds = i + 1;
+                       return i;
+               }
+
+       return -1;
+}
+
+/*****************************************************************************/
+/*
+ *     We have a Stallion board. Allocate a board structure and
+ *     initialize it. Read its IO and IRQ resources from PCI
+ *     configuration space.
+ */
+
+static int __devinit stl_pciprobe(struct pci_dev *pdev,
+               const struct pci_device_id *ent)
+{
+       struct stlbrd *brdp;
+       unsigned int i, brdtype = ent->driver_data;
+       int brdnr, retval = -ENODEV;
+
+       if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE)
+               goto err;
+
+       retval = pci_enable_device(pdev);
+       if (retval)
+               goto err;
+       brdp = stl_allocbrd();
+       if (brdp == NULL) {
+               retval = -ENOMEM;
+               goto err;
+       }
+       mutex_lock(&stl_brdslock);
+       brdnr = stl_getbrdnr();
+       if (brdnr < 0) {
+               dev_err(&pdev->dev, "too many boards found, "
+                       "maximum supported %d\n", STL_MAXBRDS);
+               mutex_unlock(&stl_brdslock);
+               retval = -ENODEV;
+               goto err_fr;
+       }
+       brdp->brdnr = (unsigned int)brdnr;
+       stl_brds[brdp->brdnr] = brdp;
+       mutex_unlock(&stl_brdslock);
+
+       brdp->brdtype = brdtype;
+       brdp->state |= STL_PROBED;
+
+/*
+ *     We have all resources from the board, so let's setup the actual
+ *     board structure now.
+ */
+       switch (brdtype) {
+       case BRD_ECHPCI:
+               brdp->ioaddr2 = pci_resource_start(pdev, 0);
+               brdp->ioaddr1 = pci_resource_start(pdev, 1);
+               break;
+       case BRD_ECH64PCI:
+               brdp->ioaddr2 = pci_resource_start(pdev, 2);
+               brdp->ioaddr1 = pci_resource_start(pdev, 1);
+               break;
+       case BRD_EASYIOPCI:
+               brdp->ioaddr1 = pci_resource_start(pdev, 2);
+               brdp->ioaddr2 = pci_resource_start(pdev, 1);
+               break;
+       default:
+               dev_err(&pdev->dev, "unknown PCI board type=%u\n", brdtype);
+               break;
+       }
+
+       brdp->irq = pdev->irq;
+       retval = stl_brdinit(brdp);
+       if (retval)
+               goto err_null;
+
+       pci_set_drvdata(pdev, brdp);
+
+       for (i = 0; i < brdp->nrports; i++)
+               tty_register_device(stl_serial,
+                               brdp->brdnr * STL_MAXPORTS + i, &pdev->dev);
+
+       return 0;
+err_null:
+       stl_brds[brdp->brdnr] = NULL;
+err_fr:
+       kfree(brdp);
+err:
+       return retval;
+}
+
+static void __devexit stl_pciremove(struct pci_dev *pdev)
+{
+       struct stlbrd *brdp = pci_get_drvdata(pdev);
+       unsigned int i;
+
+       free_irq(brdp->irq, brdp);
+
+       stl_cleanup_panels(brdp);
+
+       release_region(brdp->ioaddr1, brdp->iosize1);
+       if (brdp->iosize2 > 0)
+               release_region(brdp->ioaddr2, brdp->iosize2);
+
+       for (i = 0; i < brdp->nrports; i++)
+               tty_unregister_device(stl_serial,
+                               brdp->brdnr * STL_MAXPORTS + i);
+
+       stl_brds[brdp->brdnr] = NULL;
+       kfree(brdp);
+}
+
+static struct pci_driver stl_pcidriver = {
+       .name = "stallion",
+       .id_table = stl_pcibrds,
+       .probe = stl_pciprobe,
+       .remove = __devexit_p(stl_pciremove)
+};
+
+/*****************************************************************************/
+
+/*
+ *     Return the board stats structure to user app.
+ */
+
+static int stl_getbrdstats(combrd_t __user *bp)
+{
+       combrd_t        stl_brdstats;
+       struct stlbrd   *brdp;
+       struct stlpanel *panelp;
+       unsigned int i;
+
+       if (copy_from_user(&stl_brdstats, bp, sizeof(combrd_t)))
+               return -EFAULT;
+       if (stl_brdstats.brd >= STL_MAXBRDS)
+               return -ENODEV;
+       brdp = stl_brds[stl_brdstats.brd];
+       if (brdp == NULL)
+               return -ENODEV;
+
+       memset(&stl_brdstats, 0, sizeof(combrd_t));
+       stl_brdstats.brd = brdp->brdnr;
+       stl_brdstats.type = brdp->brdtype;
+       stl_brdstats.hwid = brdp->hwid;
+       stl_brdstats.state = brdp->state;
+       stl_brdstats.ioaddr = brdp->ioaddr1;
+       stl_brdstats.ioaddr2 = brdp->ioaddr2;
+       stl_brdstats.irq = brdp->irq;
+       stl_brdstats.nrpanels = brdp->nrpanels;
+       stl_brdstats.nrports = brdp->nrports;
+       for (i = 0; i < brdp->nrpanels; i++) {
+               panelp = brdp->panels[i];
+               stl_brdstats.panels[i].panel = i;
+               stl_brdstats.panels[i].hwid = panelp->hwid;
+               stl_brdstats.panels[i].nrports = panelp->nrports;
+       }
+
+       return copy_to_user(bp, &stl_brdstats, sizeof(combrd_t)) ? -EFAULT : 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Resolve the referenced port number into a port struct pointer.
+ */
+
+static struct stlport *stl_getport(int brdnr, int panelnr, int portnr)
+{
+       struct stlbrd   *brdp;
+       struct stlpanel *panelp;
+
+       if (brdnr < 0 || brdnr >= STL_MAXBRDS)
+               return NULL;
+       brdp = stl_brds[brdnr];
+       if (brdp == NULL)
+               return NULL;
+       if (panelnr < 0 || (unsigned int)panelnr >= brdp->nrpanels)
+               return NULL;
+       panelp = brdp->panels[panelnr];
+       if (panelp == NULL)
+               return NULL;
+       if (portnr < 0 || (unsigned int)portnr >= panelp->nrports)
+               return NULL;
+       return panelp->ports[portnr];
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the port stats structure to user app. A NULL port struct
+ *     pointer passed in means that we need to find out from the app
+ *     what port to get stats for (used through board control device).
+ */
+
+static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp)
+{
+       comstats_t      stl_comstats;
+       unsigned char   *head, *tail;
+       unsigned long   flags;
+
+       if (!portp) {
+               if (copy_from_user(&stl_comstats, cp, sizeof(comstats_t)))
+                       return -EFAULT;
+               portp = stl_getport(stl_comstats.brd, stl_comstats.panel,
+                       stl_comstats.port);
+               if (portp == NULL)
+                       return -ENODEV;
+       }
+
+       mutex_lock(&portp->port.mutex);
+       portp->stats.state = portp->istate;
+       portp->stats.flags = portp->port.flags;
+       portp->stats.hwid = portp->hwid;
+
+       portp->stats.ttystate = 0;
+       portp->stats.cflags = 0;
+       portp->stats.iflags = 0;
+       portp->stats.oflags = 0;
+       portp->stats.lflags = 0;
+       portp->stats.rxbuffered = 0;
+
+       spin_lock_irqsave(&stallion_lock, flags);
+       if (tty != NULL && portp->port.tty == tty) {
+               portp->stats.ttystate = tty->flags;
+               /* No longer available as a statistic */
+               portp->stats.rxbuffered = 1; /*tty->flip.count; */
+               if (tty->termios != NULL) {
+                       portp->stats.cflags = tty->termios->c_cflag;
+                       portp->stats.iflags = tty->termios->c_iflag;
+                       portp->stats.oflags = tty->termios->c_oflag;
+                       portp->stats.lflags = tty->termios->c_lflag;
+               }
+       }
+       spin_unlock_irqrestore(&stallion_lock, flags);
+
+       head = portp->tx.head;
+       tail = portp->tx.tail;
+       portp->stats.txbuffered = (head >= tail) ? (head - tail) :
+               (STL_TXBUFSIZE - (tail - head));
+
+       portp->stats.signals = (unsigned long) stl_getsignals(portp);
+       mutex_unlock(&portp->port.mutex);
+
+       return copy_to_user(cp, &portp->stats,
+                           sizeof(comstats_t)) ? -EFAULT : 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Clear the port stats structure. We also return it zeroed out...
+ */
+
+static int stl_clrportstats(struct stlport *portp, comstats_t __user *cp)
+{
+       comstats_t      stl_comstats;
+
+       if (!portp) {
+               if (copy_from_user(&stl_comstats, cp, sizeof(comstats_t)))
+                       return -EFAULT;
+               portp = stl_getport(stl_comstats.brd, stl_comstats.panel,
+                       stl_comstats.port);
+               if (portp == NULL)
+                       return -ENODEV;
+       }
+
+       mutex_lock(&portp->port.mutex);
+       memset(&portp->stats, 0, sizeof(comstats_t));
+       portp->stats.brd = portp->brdnr;
+       portp->stats.panel = portp->panelnr;
+       portp->stats.port = portp->portnr;
+       mutex_unlock(&portp->port.mutex);
+       return copy_to_user(cp, &portp->stats,
+                           sizeof(comstats_t)) ? -EFAULT : 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the entire driver ports structure to a user app.
+ */
+
+static int stl_getportstruct(struct stlport __user *arg)
+{
+       struct stlport  stl_dummyport;
+       struct stlport  *portp;
+
+       if (copy_from_user(&stl_dummyport, arg, sizeof(struct stlport)))
+               return -EFAULT;
+       portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr,
+                stl_dummyport.portnr);
+       if (!portp)
+               return -ENODEV;
+       return copy_to_user(arg, portp, sizeof(struct stlport)) ? -EFAULT : 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the entire driver board structure to a user app.
+ */
+
+static int stl_getbrdstruct(struct stlbrd __user *arg)
+{
+       struct stlbrd   stl_dummybrd;
+       struct stlbrd   *brdp;
+
+       if (copy_from_user(&stl_dummybrd, arg, sizeof(struct stlbrd)))
+               return -EFAULT;
+       if (stl_dummybrd.brdnr >= STL_MAXBRDS)
+               return -ENODEV;
+       brdp = stl_brds[stl_dummybrd.brdnr];
+       if (!brdp)
+               return -ENODEV;
+       return copy_to_user(arg, brdp, sizeof(struct stlbrd)) ? -EFAULT : 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     The "staliomem" device is also required to do some special operations
+ *     on the board and/or ports. In this driver it is mostly used for stats
+ *     collection.
+ */
+
+static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+       int     brdnr, rc;
+       void __user *argp = (void __user *)arg;
+
+       pr_debug("stl_memioctl(fp=%p,cmd=%x,arg=%lx)\n", fp, cmd,arg);
+
+       brdnr = iminor(fp->f_dentry->d_inode);
+       if (brdnr >= STL_MAXBRDS)
+               return -ENODEV;
+       rc = 0;
+
+       switch (cmd) {
+       case COM_GETPORTSTATS:
+               rc = stl_getportstats(NULL, NULL, argp);
+               break;
+       case COM_CLRPORTSTATS:
+               rc = stl_clrportstats(NULL, argp);
+               break;
+       case COM_GETBRDSTATS:
+               rc = stl_getbrdstats(argp);
+               break;
+       case COM_READPORT:
+               rc = stl_getportstruct(argp);
+               break;
+       case COM_READBOARD:
+               rc = stl_getbrdstruct(argp);
+               break;
+       default:
+               rc = -ENOIOCTLCMD;
+               break;
+       }
+       return rc;
+}
+
+static const struct tty_operations stl_ops = {
+       .open = stl_open,
+       .close = stl_close,
+       .write = stl_write,
+       .put_char = stl_putchar,
+       .flush_chars = stl_flushchars,
+       .write_room = stl_writeroom,
+       .chars_in_buffer = stl_charsinbuffer,
+       .ioctl = stl_ioctl,
+       .set_termios = stl_settermios,
+       .throttle = stl_throttle,
+       .unthrottle = stl_unthrottle,
+       .stop = stl_stop,
+       .start = stl_start,
+       .hangup = stl_hangup,
+       .flush_buffer = stl_flushbuffer,
+       .break_ctl = stl_breakctl,
+       .wait_until_sent = stl_waituntilsent,
+       .send_xchar = stl_sendxchar,
+       .tiocmget = stl_tiocmget,
+       .tiocmset = stl_tiocmset,
+       .proc_fops = &stl_proc_fops,
+};
+
+static const struct tty_port_operations stl_port_ops = {
+       .carrier_raised = stl_carrier_raised,
+       .dtr_rts = stl_dtr_rts,
+       .activate = stl_activate,
+       .shutdown = stl_shutdown,
+};
+
+/*****************************************************************************/
+/*                       CD1400 HARDWARE FUNCTIONS                           */
+/*****************************************************************************/
+
+/*
+ *     These functions get/set/update the registers of the cd1400 UARTs.
+ *     Access to the cd1400 registers is via an address/data io port pair.
+ *     (Maybe should make this inline...)
+ */
+
+static int stl_cd1400getreg(struct stlport *portp, int regnr)
+{
+       outb((regnr + portp->uartaddr), portp->ioaddr);
+       return inb(portp->ioaddr + EREG_DATA);
+}
+
+static void stl_cd1400setreg(struct stlport *portp, int regnr, int value)
+{
+       outb(regnr + portp->uartaddr, portp->ioaddr);
+       outb(value, portp->ioaddr + EREG_DATA);
+}
+
+static int stl_cd1400updatereg(struct stlport *portp, int regnr, int value)
+{
+       outb(regnr + portp->uartaddr, portp->ioaddr);
+       if (inb(portp->ioaddr + EREG_DATA) != value) {
+               outb(value, portp->ioaddr + EREG_DATA);
+               return 1;
+       }
+       return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Inbitialize the UARTs in a panel. We don't care what sort of board
+ *     these ports are on - since the port io registers are almost
+ *     identical when dealing with ports.
+ */
+
+static int stl_cd1400panelinit(struct stlbrd *brdp, struct stlpanel *panelp)
+{
+       unsigned int    gfrcr;
+       int             chipmask, i, j;
+       int             nrchips, uartaddr, ioaddr;
+       unsigned long   flags;
+
+       pr_debug("stl_panelinit(brdp=%p,panelp=%p)\n", brdp, panelp);
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(panelp->brdnr, panelp->pagenr);
+
+/*
+ *     Check that each chip is present and started up OK.
+ */
+       chipmask = 0;
+       nrchips = panelp->nrports / CD1400_PORTS;
+       for (i = 0; i < nrchips; i++) {
+               if (brdp->brdtype == BRD_ECHPCI) {
+                       outb((panelp->pagenr + (i >> 1)), brdp->ioctrl);
+                       ioaddr = panelp->iobase;
+               } else
+                       ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1));
+               uartaddr = (i & 0x01) ? 0x080 : 0;
+               outb((GFRCR + uartaddr), ioaddr);
+               outb(0, (ioaddr + EREG_DATA));
+               outb((CCR + uartaddr), ioaddr);
+               outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
+               outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
+               outb((GFRCR + uartaddr), ioaddr);
+               for (j = 0; j < CCR_MAXWAIT; j++)
+                       if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0)
+                               break;
+
+               if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) {
+                       printk("STALLION: cd1400 not responding, "
+                               "brd=%d panel=%d chip=%d\n",
+                               panelp->brdnr, panelp->panelnr, i);
+                       continue;
+               }
+               chipmask |= (0x1 << i);
+               outb((PPR + uartaddr), ioaddr);
+               outb(PPR_SCALAR, (ioaddr + EREG_DATA));
+       }
+
+       BRDDISABLE(panelp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+       return chipmask;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Initialize hardware specific port registers.
+ */
+
+static void stl_cd1400portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp)
+{
+       unsigned long flags;
+       pr_debug("stl_cd1400portinit(brdp=%p,panelp=%p,portp=%p)\n", brdp,
+                       panelp, portp);
+
+       if ((brdp == NULL) || (panelp == NULL) ||
+           (portp == NULL))
+               return;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) ||
+               (portp->portnr < 8)) ? 0 : EREG_BANKSIZE);
+       portp->uartaddr = (portp->portnr & 0x04) << 5;
+       portp->pagenr = panelp->pagenr + (portp->portnr >> 3);
+
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+       stl_cd1400setreg(portp, LIVR, (portp->portnr << 3));
+       portp->hwid = stl_cd1400getreg(portp, GFRCR);
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Wait for the command register to be ready. We will poll this,
+ *     since it won't usually take too long to be ready.
+ */
+
+static void stl_cd1400ccrwait(struct stlport *portp)
+{
+       int     i;
+
+       for (i = 0; i < CCR_MAXWAIT; i++)
+               if (stl_cd1400getreg(portp, CCR) == 0)
+                       return;
+
+       printk("STALLION: cd1400 not responding, port=%d panel=%d brd=%d\n",
+               portp->portnr, portp->panelnr, portp->brdnr);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Set up the cd1400 registers for a port based on the termios port
+ *     settings.
+ */
+
+static void stl_cd1400setport(struct stlport *portp, struct ktermios *tiosp)
+{
+       struct stlbrd   *brdp;
+       unsigned long   flags;
+       unsigned int    clkdiv, baudrate;
+       unsigned char   cor1, cor2, cor3;
+       unsigned char   cor4, cor5, ccr;
+       unsigned char   srer, sreron, sreroff;
+       unsigned char   mcor1, mcor2, rtpr;
+       unsigned char   clk, div;
+
+       cor1 = 0;
+       cor2 = 0;
+       cor3 = 0;
+       cor4 = 0;
+       cor5 = 0;
+       ccr = 0;
+       rtpr = 0;
+       clk = 0;
+       div = 0;
+       mcor1 = 0;
+       mcor2 = 0;
+       sreron = 0;
+       sreroff = 0;
+
+       brdp = stl_brds[portp->brdnr];
+       if (brdp == NULL)
+               return;
+
+/*
+ *     Set up the RX char ignore mask with those RX error types we
+ *     can ignore. We can get the cd1400 to help us out a little here,
+ *     it will ignore parity errors and breaks for us.
+ */
+       portp->rxignoremsk = 0;
+       if (tiosp->c_iflag & IGNPAR) {
+               portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN);
+               cor1 |= COR1_PARIGNORE;
+       }
+       if (tiosp->c_iflag & IGNBRK) {
+               portp->rxignoremsk |= ST_BREAK;
+               cor4 |= COR4_IGNBRK;
+       }
+
+       portp->rxmarkmsk = ST_OVERRUN;
+       if (tiosp->c_iflag & (INPCK | PARMRK))
+               portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING);
+       if (tiosp->c_iflag & BRKINT)
+               portp->rxmarkmsk |= ST_BREAK;
+
+/*
+ *     Go through the char size, parity and stop bits and set all the
+ *     option register appropriately.
+ */
+       switch (tiosp->c_cflag & CSIZE) {
+       case CS5:
+               cor1 |= COR1_CHL5;
+               break;
+       case CS6:
+               cor1 |= COR1_CHL6;
+               break;
+       case CS7:
+               cor1 |= COR1_CHL7;
+               break;
+       default:
+               cor1 |= COR1_CHL8;
+               break;
+       }
+
+       if (tiosp->c_cflag & CSTOPB)
+               cor1 |= COR1_STOP2;
+       else
+               cor1 |= COR1_STOP1;
+
+       if (tiosp->c_cflag & PARENB) {
+               if (tiosp->c_cflag & PARODD)
+                       cor1 |= (COR1_PARENB | COR1_PARODD);
+               else
+                       cor1 |= (COR1_PARENB | COR1_PAREVEN);
+       } else {
+               cor1 |= COR1_PARNONE;
+       }
+
+/*
+ *     Set the RX FIFO threshold at 6 chars. This gives a bit of breathing
+ *     space for hardware flow control and the like. This should be set to
+ *     VMIN. Also here we will set the RX data timeout to 10ms - this should
+ *     really be based on VTIME.
+ */
+       cor3 |= FIFO_RXTHRESHOLD;
+       rtpr = 2;
+
+/*
+ *     Calculate the baud rate timers. For now we will just assume that
+ *     the input and output baud are the same. Could have used a baud
+ *     table here, but this way we can generate virtually any baud rate
+ *     we like!
+ */
+       baudrate = tiosp->c_cflag & CBAUD;
+       if (baudrate & CBAUDEX) {
+               baudrate &= ~CBAUDEX;
+               if ((baudrate < 1) || (baudrate > 4))
+                       tiosp->c_cflag &= ~CBAUDEX;
+               else
+                       baudrate += 15;
+       }
+       baudrate = stl_baudrates[baudrate];
+       if ((tiosp->c_cflag & CBAUD) == B38400) {
+               if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                       baudrate = 57600;
+               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                       baudrate = 115200;
+               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                       baudrate = 230400;
+               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                       baudrate = 460800;
+               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+                       baudrate = (portp->baud_base / portp->custom_divisor);
+       }
+       if (baudrate > STL_CD1400MAXBAUD)
+               baudrate = STL_CD1400MAXBAUD;
+
+       if (baudrate > 0) {
+               for (clk = 0; clk < CD1400_NUMCLKS; clk++) {
+                       clkdiv = (portp->clk / stl_cd1400clkdivs[clk]) / baudrate;
+                       if (clkdiv < 0x100)
+                               break;
+               }
+               div = (unsigned char) clkdiv;
+       }
+
+/*
+ *     Check what form of modem signaling is required and set it up.
+ */
+       if ((tiosp->c_cflag & CLOCAL) == 0) {
+               mcor1 |= MCOR1_DCD;
+               mcor2 |= MCOR2_DCD;
+               sreron |= SRER_MODEM;
+               portp->port.flags |= ASYNC_CHECK_CD;
+       } else
+               portp->port.flags &= ~ASYNC_CHECK_CD;
+
+/*
+ *     Setup cd1400 enhanced modes if we can. In particular we want to
+ *     handle as much of the flow control as possible automatically. As
+ *     well as saving a few CPU cycles it will also greatly improve flow
+ *     control reliability.
+ */
+       if (tiosp->c_iflag & IXON) {
+               cor2 |= COR2_TXIBE;
+               cor3 |= COR3_SCD12;
+               if (tiosp->c_iflag & IXANY)
+                       cor2 |= COR2_IXM;
+       }
+
+       if (tiosp->c_cflag & CRTSCTS) {
+               cor2 |= COR2_CTSAE;
+               mcor1 |= FIFO_RTSTHRESHOLD;
+       }
+
+/*
+ *     All cd1400 register values calculated so go through and set
+ *     them all up.
+ */
+
+       pr_debug("SETPORT: portnr=%d panelnr=%d brdnr=%d\n",
+               portp->portnr, portp->panelnr, portp->brdnr);
+       pr_debug("    cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n",
+               cor1, cor2, cor3, cor4, cor5);
+       pr_debug("    mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n",
+               mcor1, mcor2, rtpr, sreron, sreroff);
+       pr_debug("    tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div);
+       pr_debug("    schr1=%x schr2=%x schr3=%x schr4=%x\n",
+               tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP],
+               tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x3));
+       srer = stl_cd1400getreg(portp, SRER);
+       stl_cd1400setreg(portp, SRER, 0);
+       if (stl_cd1400updatereg(portp, COR1, cor1))
+               ccr = 1;
+       if (stl_cd1400updatereg(portp, COR2, cor2))
+               ccr = 1;
+       if (stl_cd1400updatereg(portp, COR3, cor3))
+               ccr = 1;
+       if (ccr) {
+               stl_cd1400ccrwait(portp);
+               stl_cd1400setreg(portp, CCR, CCR_CORCHANGE);
+       }
+       stl_cd1400setreg(portp, COR4, cor4);
+       stl_cd1400setreg(portp, COR5, cor5);
+       stl_cd1400setreg(portp, MCOR1, mcor1);
+       stl_cd1400setreg(portp, MCOR2, mcor2);
+       if (baudrate > 0) {
+               stl_cd1400setreg(portp, TCOR, clk);
+               stl_cd1400setreg(portp, TBPR, div);
+               stl_cd1400setreg(portp, RCOR, clk);
+               stl_cd1400setreg(portp, RBPR, div);
+       }
+       stl_cd1400setreg(portp, SCHR1, tiosp->c_cc[VSTART]);
+       stl_cd1400setreg(portp, SCHR2, tiosp->c_cc[VSTOP]);
+       stl_cd1400setreg(portp, SCHR3, tiosp->c_cc[VSTART]);
+       stl_cd1400setreg(portp, SCHR4, tiosp->c_cc[VSTOP]);
+       stl_cd1400setreg(portp, RTPR, rtpr);
+       mcor1 = stl_cd1400getreg(portp, MSVR1);
+       if (mcor1 & MSVR1_DCD)
+               portp->sigs |= TIOCM_CD;
+       else
+               portp->sigs &= ~TIOCM_CD;
+       stl_cd1400setreg(portp, SRER, ((srer & ~sreroff) | sreron));
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Set the state of the DTR and RTS signals.
+ */
+
+static void stl_cd1400setsignals(struct stlport *portp, int dtr, int rts)
+{
+       unsigned char   msvr1, msvr2;
+       unsigned long   flags;
+
+       pr_debug("stl_cd1400setsignals(portp=%p,dtr=%d,rts=%d)\n",
+                       portp, dtr, rts);
+
+       msvr1 = 0;
+       msvr2 = 0;
+       if (dtr > 0)
+               msvr1 = MSVR1_DTR;
+       if (rts > 0)
+               msvr2 = MSVR2_RTS;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+       if (rts >= 0)
+               stl_cd1400setreg(portp, MSVR2, msvr2);
+       if (dtr >= 0)
+               stl_cd1400setreg(portp, MSVR1, msvr1);
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the state of the signals.
+ */
+
+static int stl_cd1400getsignals(struct stlport *portp)
+{
+       unsigned char   msvr1, msvr2;
+       unsigned long   flags;
+       int             sigs;
+
+       pr_debug("stl_cd1400getsignals(portp=%p)\n", portp);
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+       msvr1 = stl_cd1400getreg(portp, MSVR1);
+       msvr2 = stl_cd1400getreg(portp, MSVR2);
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+
+       sigs = 0;
+       sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0;
+       sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0;
+       sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0;
+       sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0;
+#if 0
+       sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0;
+       sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0;
+#else
+       sigs |= TIOCM_DSR;
+#endif
+       return sigs;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Enable/Disable the Transmitter and/or Receiver.
+ */
+
+static void stl_cd1400enablerxtx(struct stlport *portp, int rx, int tx)
+{
+       unsigned char   ccr;
+       unsigned long   flags;
+
+       pr_debug("stl_cd1400enablerxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx, tx);
+
+       ccr = 0;
+
+       if (tx == 0)
+               ccr |= CCR_TXDISABLE;
+       else if (tx > 0)
+               ccr |= CCR_TXENABLE;
+       if (rx == 0)
+               ccr |= CCR_RXDISABLE;
+       else if (rx > 0)
+               ccr |= CCR_RXENABLE;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+       stl_cd1400ccrwait(portp);
+       stl_cd1400setreg(portp, CCR, ccr);
+       stl_cd1400ccrwait(portp);
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Start/stop the Transmitter and/or Receiver.
+ */
+
+static void stl_cd1400startrxtx(struct stlport *portp, int rx, int tx)
+{
+       unsigned char   sreron, sreroff;
+       unsigned long   flags;
+
+       pr_debug("stl_cd1400startrxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx, tx);
+
+       sreron = 0;
+       sreroff = 0;
+       if (tx == 0)
+               sreroff |= (SRER_TXDATA | SRER_TXEMPTY);
+       else if (tx == 1)
+               sreron |= SRER_TXDATA;
+       else if (tx >= 2)
+               sreron |= SRER_TXEMPTY;
+       if (rx == 0)
+               sreroff |= SRER_RXDATA;
+       else if (rx > 0)
+               sreron |= SRER_RXDATA;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+       stl_cd1400setreg(portp, SRER,
+               ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron));
+       BRDDISABLE(portp->brdnr);
+       if (tx > 0)
+               set_bit(ASYI_TXBUSY, &portp->istate);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Disable all interrupts from this port.
+ */
+
+static void stl_cd1400disableintrs(struct stlport *portp)
+{
+       unsigned long   flags;
+
+       pr_debug("stl_cd1400disableintrs(portp=%p)\n", portp);
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+       stl_cd1400setreg(portp, SRER, 0);
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+static void stl_cd1400sendbreak(struct stlport *portp, int len)
+{
+       unsigned long   flags;
+
+       pr_debug("stl_cd1400sendbreak(portp=%p,len=%d)\n", portp, len);
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+       stl_cd1400setreg(portp, SRER,
+               ((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) |
+               SRER_TXEMPTY));
+       BRDDISABLE(portp->brdnr);
+       portp->brklen = len;
+       if (len == 1)
+               portp->stats.txbreaks++;
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Take flow control actions...
+ */
+
+static void stl_cd1400flowctrl(struct stlport *portp, int state)
+{
+       struct tty_struct       *tty;
+       unsigned long           flags;
+
+       pr_debug("stl_cd1400flowctrl(portp=%p,state=%x)\n", portp, state);
+
+       if (portp == NULL)
+               return;
+       tty = tty_port_tty_get(&portp->port);
+       if (tty == NULL)
+               return;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+
+       if (state) {
+               if (tty->termios->c_iflag & IXOFF) {
+                       stl_cd1400ccrwait(portp);
+                       stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1);
+                       portp->stats.rxxon++;
+                       stl_cd1400ccrwait(portp);
+               }
+/*
+ *             Question: should we return RTS to what it was before? It may
+ *             have been set by an ioctl... Suppose not, since if you have
+ *             hardware flow control set then it is pretty silly to go and
+ *             set the RTS line by hand.
+ */
+               if (tty->termios->c_cflag & CRTSCTS) {
+                       stl_cd1400setreg(portp, MCOR1,
+                               (stl_cd1400getreg(portp, MCOR1) |
+                               FIFO_RTSTHRESHOLD));
+                       stl_cd1400setreg(portp, MSVR2, MSVR2_RTS);
+                       portp->stats.rxrtson++;
+               }
+       } else {
+               if (tty->termios->c_iflag & IXOFF) {
+                       stl_cd1400ccrwait(portp);
+                       stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2);
+                       portp->stats.rxxoff++;
+                       stl_cd1400ccrwait(portp);
+               }
+               if (tty->termios->c_cflag & CRTSCTS) {
+                       stl_cd1400setreg(portp, MCOR1,
+                               (stl_cd1400getreg(portp, MCOR1) & 0xf0));
+                       stl_cd1400setreg(portp, MSVR2, 0);
+                       portp->stats.rxrtsoff++;
+               }
+       }
+
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+       tty_kref_put(tty);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Send a flow control character...
+ */
+
+static void stl_cd1400sendflow(struct stlport *portp, int state)
+{
+       struct tty_struct       *tty;
+       unsigned long           flags;
+
+       pr_debug("stl_cd1400sendflow(portp=%p,state=%x)\n", portp, state);
+
+       if (portp == NULL)
+               return;
+       tty = tty_port_tty_get(&portp->port);
+       if (tty == NULL)
+               return;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+       if (state) {
+               stl_cd1400ccrwait(portp);
+               stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1);
+               portp->stats.rxxon++;
+               stl_cd1400ccrwait(portp);
+       } else {
+               stl_cd1400ccrwait(portp);
+               stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2);
+               portp->stats.rxxoff++;
+               stl_cd1400ccrwait(portp);
+       }
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+       tty_kref_put(tty);
+}
+
+/*****************************************************************************/
+
+static void stl_cd1400flush(struct stlport *portp)
+{
+       unsigned long   flags;
+
+       pr_debug("stl_cd1400flush(portp=%p)\n", portp);
+
+       if (portp == NULL)
+               return;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+       stl_cd1400ccrwait(portp);
+       stl_cd1400setreg(portp, CCR, CCR_TXFLUSHFIFO);
+       stl_cd1400ccrwait(portp);
+       portp->tx.tail = portp->tx.head;
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the current state of data flow on this port. This is only
+ *     really interesting when determining if data has fully completed
+ *     transmission or not... This is easy for the cd1400, it accurately
+ *     maintains the busy port flag.
+ */
+
+static int stl_cd1400datastate(struct stlport *portp)
+{
+       pr_debug("stl_cd1400datastate(portp=%p)\n", portp);
+
+       if (portp == NULL)
+               return 0;
+
+       return test_bit(ASYI_TXBUSY, &portp->istate) ? 1 : 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Interrupt service routine for cd1400 EasyIO boards.
+ */
+
+static void stl_cd1400eiointr(struct stlpanel *panelp, unsigned int iobase)
+{
+       unsigned char   svrtype;
+
+       pr_debug("stl_cd1400eiointr(panelp=%p,iobase=%x)\n", panelp, iobase);
+
+       spin_lock(&brd_lock);
+       outb(SVRR, iobase);
+       svrtype = inb(iobase + EREG_DATA);
+       if (panelp->nrports > 4) {
+               outb((SVRR + 0x80), iobase);
+               svrtype |= inb(iobase + EREG_DATA);
+       }
+
+       if (svrtype & SVRR_RX)
+               stl_cd1400rxisr(panelp, iobase);
+       else if (svrtype & SVRR_TX)
+               stl_cd1400txisr(panelp, iobase);
+       else if (svrtype & SVRR_MDM)
+               stl_cd1400mdmisr(panelp, iobase);
+
+       spin_unlock(&brd_lock);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Interrupt service routine for cd1400 panels.
+ */
+
+static void stl_cd1400echintr(struct stlpanel *panelp, unsigned int iobase)
+{
+       unsigned char   svrtype;
+
+       pr_debug("stl_cd1400echintr(panelp=%p,iobase=%x)\n", panelp, iobase);
+
+       outb(SVRR, iobase);
+       svrtype = inb(iobase + EREG_DATA);
+       outb((SVRR + 0x80), iobase);
+       svrtype |= inb(iobase + EREG_DATA);
+       if (svrtype & SVRR_RX)
+               stl_cd1400rxisr(panelp, iobase);
+       else if (svrtype & SVRR_TX)
+               stl_cd1400txisr(panelp, iobase);
+       else if (svrtype & SVRR_MDM)
+               stl_cd1400mdmisr(panelp, iobase);
+}
+
+
+/*****************************************************************************/
+
+/*
+ *     Unfortunately we need to handle breaks in the TX data stream, since
+ *     this is the only way to generate them on the cd1400.
+ */
+
+static int stl_cd1400breakisr(struct stlport *portp, int ioaddr)
+{
+       if (portp->brklen == 1) {
+               outb((COR2 + portp->uartaddr), ioaddr);
+               outb((inb(ioaddr + EREG_DATA) | COR2_ETC),
+                       (ioaddr + EREG_DATA));
+               outb((TDR + portp->uartaddr), ioaddr);
+               outb(ETC_CMD, (ioaddr + EREG_DATA));
+               outb(ETC_STARTBREAK, (ioaddr + EREG_DATA));
+               outb((SRER + portp->uartaddr), ioaddr);
+               outb((inb(ioaddr + EREG_DATA) & ~(SRER_TXDATA | SRER_TXEMPTY)),
+                       (ioaddr + EREG_DATA));
+               return 1;
+       } else if (portp->brklen > 1) {
+               outb((TDR + portp->uartaddr), ioaddr);
+               outb(ETC_CMD, (ioaddr + EREG_DATA));
+               outb(ETC_STOPBREAK, (ioaddr + EREG_DATA));
+               portp->brklen = -1;
+               return 1;
+       } else {
+               outb((COR2 + portp->uartaddr), ioaddr);
+               outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC),
+                       (ioaddr + EREG_DATA));
+               portp->brklen = 0;
+       }
+       return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Transmit interrupt handler. This has gotta be fast!  Handling TX
+ *     chars is pretty simple, stuff as many as possible from the TX buffer
+ *     into the cd1400 FIFO. Must also handle TX breaks here, since they
+ *     are embedded as commands in the data stream. Oh no, had to use a goto!
+ *     This could be optimized more, will do when I get time...
+ *     In practice it is possible that interrupts are enabled but that the
+ *     port has been hung up. Need to handle not having any TX buffer here,
+ *     this is done by using the side effect that head and tail will also
+ *     be NULL if the buffer has been freed.
+ */
+
+static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr)
+{
+       struct stlport  *portp;
+       int             len, stlen;
+       char            *head, *tail;
+       unsigned char   ioack, srer;
+       struct tty_struct *tty;
+
+       pr_debug("stl_cd1400txisr(panelp=%p,ioaddr=%x)\n", panelp, ioaddr);
+
+       ioack = inb(ioaddr + EREG_TXACK);
+       if (((ioack & panelp->ackmask) != 0) ||
+           ((ioack & ACK_TYPMASK) != ACK_TYPTX)) {
+               printk("STALLION: bad TX interrupt ack value=%x\n", ioack);
+               return;
+       }
+       portp = panelp->ports[(ioack >> 3)];
+
+/*
+ *     Unfortunately we need to handle breaks in the data stream, since
+ *     this is the only way to generate them on the cd1400. Do it now if
+ *     a break is to be sent.
+ */
+       if (portp->brklen != 0)
+               if (stl_cd1400breakisr(portp, ioaddr))
+                       goto stl_txalldone;
+
+       head = portp->tx.head;
+       tail = portp->tx.tail;
+       len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
+       if ((len == 0) || ((len < STL_TXBUFLOW) &&
+           (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
+               set_bit(ASYI_TXLOW, &portp->istate);
+               tty = tty_port_tty_get(&portp->port);
+               if (tty) {
+                       tty_wakeup(tty);
+                       tty_kref_put(tty);
+               }
+       }
+
+       if (len == 0) {
+               outb((SRER + portp->uartaddr), ioaddr);
+               srer = inb(ioaddr + EREG_DATA);
+               if (srer & SRER_TXDATA) {
+                       srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY;
+               } else {
+                       srer &= ~(SRER_TXDATA | SRER_TXEMPTY);
+                       clear_bit(ASYI_TXBUSY, &portp->istate);
+               }
+               outb(srer, (ioaddr + EREG_DATA));
+       } else {
+               len = min(len, CD1400_TXFIFOSIZE);
+               portp->stats.txtotal += len;
+               stlen = min_t(unsigned int, len,
+                               (portp->tx.buf + STL_TXBUFSIZE) - tail);
+               outb((TDR + portp->uartaddr), ioaddr);
+               outsb((ioaddr + EREG_DATA), tail, stlen);
+               len -= stlen;
+               tail += stlen;
+               if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
+                       tail = portp->tx.buf;
+               if (len > 0) {
+                       outsb((ioaddr + EREG_DATA), tail, len);
+                       tail += len;
+               }
+               portp->tx.tail = tail;
+       }
+
+stl_txalldone:
+       outb((EOSRR + portp->uartaddr), ioaddr);
+       outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+
+/*
+ *     Receive character interrupt handler. Determine if we have good chars
+ *     or bad chars and then process appropriately. Good chars are easy
+ *     just shove the lot into the RX buffer and set all status byte to 0.
+ *     If a bad RX char then process as required. This routine needs to be
+ *     fast!  In practice it is possible that we get an interrupt on a port
+ *     that is closed. This can happen on hangups - since they completely
+ *     shutdown a port not in user context. Need to handle this case.
+ */
+
+static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr)
+{
+       struct stlport          *portp;
+       struct tty_struct       *tty;
+       unsigned int            ioack, len, buflen;
+       unsigned char           status;
+       char                    ch;
+
+       pr_debug("stl_cd1400rxisr(panelp=%p,ioaddr=%x)\n", panelp, ioaddr);
+
+       ioack = inb(ioaddr + EREG_RXACK);
+       if ((ioack & panelp->ackmask) != 0) {
+               printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+               return;
+       }
+       portp = panelp->ports[(ioack >> 3)];
+       tty = tty_port_tty_get(&portp->port);
+
+       if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) {
+               outb((RDCR + portp->uartaddr), ioaddr);
+               len = inb(ioaddr + EREG_DATA);
+               if (tty == NULL || (buflen = tty_buffer_request_room(tty, len)) == 0) {
+                       len = min_t(unsigned int, len, sizeof(stl_unwanted));
+                       outb((RDSR + portp->uartaddr), ioaddr);
+                       insb((ioaddr + EREG_DATA), &stl_unwanted[0], len);
+                       portp->stats.rxlost += len;
+                       portp->stats.rxtotal += len;
+               } else {
+                       len = min(len, buflen);
+                       if (len > 0) {
+                               unsigned char *ptr;
+                               outb((RDSR + portp->uartaddr), ioaddr);
+                               tty_prepare_flip_string(tty, &ptr, len);
+                               insb((ioaddr + EREG_DATA), ptr, len);
+                               tty_schedule_flip(tty);
+                               portp->stats.rxtotal += len;
+                       }
+               }
+       } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) {
+               outb((RDSR + portp->uartaddr), ioaddr);
+               status = inb(ioaddr + EREG_DATA);
+               ch = inb(ioaddr + EREG_DATA);
+               if (status & ST_PARITY)
+                       portp->stats.rxparity++;
+               if (status & ST_FRAMING)
+                       portp->stats.rxframing++;
+               if (status & ST_OVERRUN)
+                       portp->stats.rxoverrun++;
+               if (status & ST_BREAK)
+                       portp->stats.rxbreaks++;
+               if (status & ST_SCHARMASK) {
+                       if ((status & ST_SCHARMASK) == ST_SCHAR1)
+                               portp->stats.txxon++;
+                       if ((status & ST_SCHARMASK) == ST_SCHAR2)
+                               portp->stats.txxoff++;
+                       goto stl_rxalldone;
+               }
+               if (tty != NULL && (portp->rxignoremsk & status) == 0) {
+                       if (portp->rxmarkmsk & status) {
+                               if (status & ST_BREAK) {
+                                       status = TTY_BREAK;
+                                       if (portp->port.flags & ASYNC_SAK) {
+                                               do_SAK(tty);
+                                               BRDENABLE(portp->brdnr, portp->pagenr);
+                                       }
+                               } else if (status & ST_PARITY)
+                                       status = TTY_PARITY;
+                               else if (status & ST_FRAMING)
+                                       status = TTY_FRAME;
+                               else if(status & ST_OVERRUN)
+                                       status = TTY_OVERRUN;
+                               else
+                                       status = 0;
+                       } else
+                               status = 0;
+                       tty_insert_flip_char(tty, ch, status);
+                       tty_schedule_flip(tty);
+               }
+       } else {
+               printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+               tty_kref_put(tty);
+               return;
+       }
+
+stl_rxalldone:
+       tty_kref_put(tty);
+       outb((EOSRR + portp->uartaddr), ioaddr);
+       outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+
+/*
+ *     Modem interrupt handler. The is called when the modem signal line
+ *     (DCD) has changed state. Leave most of the work to the off-level
+ *     processing routine.
+ */
+
+static void stl_cd1400mdmisr(struct stlpanel *panelp, int ioaddr)
+{
+       struct stlport  *portp;
+       unsigned int    ioack;
+       unsigned char   misr;
+
+       pr_debug("stl_cd1400mdmisr(panelp=%p)\n", panelp);
+
+       ioack = inb(ioaddr + EREG_MDACK);
+       if (((ioack & panelp->ackmask) != 0) ||
+           ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) {
+               printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack);
+               return;
+       }
+       portp = panelp->ports[(ioack >> 3)];
+
+       outb((MISR + portp->uartaddr), ioaddr);
+       misr = inb(ioaddr + EREG_DATA);
+       if (misr & MISR_DCD) {
+               stl_cd_change(portp);
+               portp->stats.modem++;
+       }
+
+       outb((EOSRR + portp->uartaddr), ioaddr);
+       outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+/*                      SC26198 HARDWARE FUNCTIONS                           */
+/*****************************************************************************/
+
+/*
+ *     These functions get/set/update the registers of the sc26198 UARTs.
+ *     Access to the sc26198 registers is via an address/data io port pair.
+ *     (Maybe should make this inline...)
+ */
+
+static int stl_sc26198getreg(struct stlport *portp, int regnr)
+{
+       outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
+       return inb(portp->ioaddr + XP_DATA);
+}
+
+static void stl_sc26198setreg(struct stlport *portp, int regnr, int value)
+{
+       outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
+       outb(value, (portp->ioaddr + XP_DATA));
+}
+
+static int stl_sc26198updatereg(struct stlport *portp, int regnr, int value)
+{
+       outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
+       if (inb(portp->ioaddr + XP_DATA) != value) {
+               outb(value, (portp->ioaddr + XP_DATA));
+               return 1;
+       }
+       return 0;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Functions to get and set the sc26198 global registers.
+ */
+
+static int stl_sc26198getglobreg(struct stlport *portp, int regnr)
+{
+       outb(regnr, (portp->ioaddr + XP_ADDR));
+       return inb(portp->ioaddr + XP_DATA);
+}
+
+#if 0
+static void stl_sc26198setglobreg(struct stlport *portp, int regnr, int value)
+{
+       outb(regnr, (portp->ioaddr + XP_ADDR));
+       outb(value, (portp->ioaddr + XP_DATA));
+}
+#endif
+
+/*****************************************************************************/
+
+/*
+ *     Inbitialize the UARTs in a panel. We don't care what sort of board
+ *     these ports are on - since the port io registers are almost
+ *     identical when dealing with ports.
+ */
+
+static int stl_sc26198panelinit(struct stlbrd *brdp, struct stlpanel *panelp)
+{
+       int     chipmask, i;
+       int     nrchips, ioaddr;
+
+       pr_debug("stl_sc26198panelinit(brdp=%p,panelp=%p)\n", brdp, panelp);
+
+       BRDENABLE(panelp->brdnr, panelp->pagenr);
+
+/*
+ *     Check that each chip is present and started up OK.
+ */
+       chipmask = 0;
+       nrchips = (panelp->nrports + 4) / SC26198_PORTS;
+       if (brdp->brdtype == BRD_ECHPCI)
+               outb(panelp->pagenr, brdp->ioctrl);
+
+       for (i = 0; i < nrchips; i++) {
+               ioaddr = panelp->iobase + (i * 4); 
+               outb(SCCR, (ioaddr + XP_ADDR));
+               outb(CR_RESETALL, (ioaddr + XP_DATA));
+               outb(TSTR, (ioaddr + XP_ADDR));
+               if (inb(ioaddr + XP_DATA) != 0) {
+                       printk("STALLION: sc26198 not responding, "
+                               "brd=%d panel=%d chip=%d\n",
+                               panelp->brdnr, panelp->panelnr, i);
+                       continue;
+               }
+               chipmask |= (0x1 << i);
+               outb(GCCR, (ioaddr + XP_ADDR));
+               outb(GCCR_IVRTYPCHANACK, (ioaddr + XP_DATA));
+               outb(WDTRCR, (ioaddr + XP_ADDR));
+               outb(0xff, (ioaddr + XP_DATA));
+       }
+
+       BRDDISABLE(panelp->brdnr);
+       return chipmask;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Initialize hardware specific port registers.
+ */
+
+static void stl_sc26198portinit(struct stlbrd *brdp, struct stlpanel *panelp, struct stlport *portp)
+{
+       pr_debug("stl_sc26198portinit(brdp=%p,panelp=%p,portp=%p)\n", brdp,
+                       panelp, portp);
+
+       if ((brdp == NULL) || (panelp == NULL) ||
+           (portp == NULL))
+               return;
+
+       portp->ioaddr = panelp->iobase + ((portp->portnr < 8) ? 0 : 4);
+       portp->uartaddr = (portp->portnr & 0x07) << 4;
+       portp->pagenr = panelp->pagenr;
+       portp->hwid = 0x1;
+
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_sc26198setreg(portp, IOPCR, IOPCR_SETSIGS);
+       BRDDISABLE(portp->brdnr);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Set up the sc26198 registers for a port based on the termios port
+ *     settings.
+ */
+
+static void stl_sc26198setport(struct stlport *portp, struct ktermios *tiosp)
+{
+       struct stlbrd   *brdp;
+       unsigned long   flags;
+       unsigned int    baudrate;
+       unsigned char   mr0, mr1, mr2, clk;
+       unsigned char   imron, imroff, iopr, ipr;
+
+       mr0 = 0;
+       mr1 = 0;
+       mr2 = 0;
+       clk = 0;
+       iopr = 0;
+       imron = 0;
+       imroff = 0;
+
+       brdp = stl_brds[portp->brdnr];
+       if (brdp == NULL)
+               return;
+
+/*
+ *     Set up the RX char ignore mask with those RX error types we
+ *     can ignore.
+ */
+       portp->rxignoremsk = 0;
+       if (tiosp->c_iflag & IGNPAR)
+               portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING |
+                       SR_RXOVERRUN);
+       if (tiosp->c_iflag & IGNBRK)
+               portp->rxignoremsk |= SR_RXBREAK;
+
+       portp->rxmarkmsk = SR_RXOVERRUN;
+       if (tiosp->c_iflag & (INPCK | PARMRK))
+               portp->rxmarkmsk |= (SR_RXPARITY | SR_RXFRAMING);
+       if (tiosp->c_iflag & BRKINT)
+               portp->rxmarkmsk |= SR_RXBREAK;
+
+/*
+ *     Go through the char size, parity and stop bits and set all the
+ *     option register appropriately.
+ */
+       switch (tiosp->c_cflag & CSIZE) {
+       case CS5:
+               mr1 |= MR1_CS5;
+               break;
+       case CS6:
+               mr1 |= MR1_CS6;
+               break;
+       case CS7:
+               mr1 |= MR1_CS7;
+               break;
+       default:
+               mr1 |= MR1_CS8;
+               break;
+       }
+
+       if (tiosp->c_cflag & CSTOPB)
+               mr2 |= MR2_STOP2;
+       else
+               mr2 |= MR2_STOP1;
+
+       if (tiosp->c_cflag & PARENB) {
+               if (tiosp->c_cflag & PARODD)
+                       mr1 |= (MR1_PARENB | MR1_PARODD);
+               else
+                       mr1 |= (MR1_PARENB | MR1_PAREVEN);
+       } else
+               mr1 |= MR1_PARNONE;
+
+       mr1 |= MR1_ERRBLOCK;
+
+/*
+ *     Set the RX FIFO threshold at 8 chars. This gives a bit of breathing
+ *     space for hardware flow control and the like. This should be set to
+ *     VMIN.
+ */
+       mr2 |= MR2_RXFIFOHALF;
+
+/*
+ *     Calculate the baud rate timers. For now we will just assume that
+ *     the input and output baud are the same. The sc26198 has a fixed
+ *     baud rate table, so only discrete baud rates possible.
+ */
+       baudrate = tiosp->c_cflag & CBAUD;
+       if (baudrate & CBAUDEX) {
+               baudrate &= ~CBAUDEX;
+               if ((baudrate < 1) || (baudrate > 4))
+                       tiosp->c_cflag &= ~CBAUDEX;
+               else
+                       baudrate += 15;
+       }
+       baudrate = stl_baudrates[baudrate];
+       if ((tiosp->c_cflag & CBAUD) == B38400) {
+               if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                       baudrate = 57600;
+               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                       baudrate = 115200;
+               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                       baudrate = 230400;
+               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                       baudrate = 460800;
+               else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+                       baudrate = (portp->baud_base / portp->custom_divisor);
+       }
+       if (baudrate > STL_SC26198MAXBAUD)
+               baudrate = STL_SC26198MAXBAUD;
+
+       if (baudrate > 0)
+               for (clk = 0; clk < SC26198_NRBAUDS; clk++)
+                       if (baudrate <= sc26198_baudtable[clk])
+                               break;
+
+/*
+ *     Check what form of modem signaling is required and set it up.
+ */
+       if (tiosp->c_cflag & CLOCAL) {
+               portp->port.flags &= ~ASYNC_CHECK_CD;
+       } else {
+               iopr |= IOPR_DCDCOS;
+               imron |= IR_IOPORT;
+               portp->port.flags |= ASYNC_CHECK_CD;
+       }
+
+/*
+ *     Setup sc26198 enhanced modes if we can. In particular we want to
+ *     handle as much of the flow control as possible automatically. As
+ *     well as saving a few CPU cycles it will also greatly improve flow
+ *     control reliability.
+ */
+       if (tiosp->c_iflag & IXON) {
+               mr0 |= MR0_SWFTX | MR0_SWFT;
+               imron |= IR_XONXOFF;
+       } else
+               imroff |= IR_XONXOFF;
+
+       if (tiosp->c_iflag & IXOFF)
+               mr0 |= MR0_SWFRX;
+
+       if (tiosp->c_cflag & CRTSCTS) {
+               mr2 |= MR2_AUTOCTS;
+               mr1 |= MR1_AUTORTS;
+       }
+
+/*
+ *     All sc26198 register values calculated so go through and set
+ *     them all up.
+ */
+
+       pr_debug("SETPORT: portnr=%d panelnr=%d brdnr=%d\n",
+               portp->portnr, portp->panelnr, portp->brdnr);
+       pr_debug("    mr0=%x mr1=%x mr2=%x clk=%x\n", mr0, mr1, mr2, clk);
+       pr_debug("    iopr=%x imron=%x imroff=%x\n", iopr, imron, imroff);
+       pr_debug("    schr1=%x schr2=%x schr3=%x schr4=%x\n",
+               tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP],
+               tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_sc26198setreg(portp, IMR, 0);
+       stl_sc26198updatereg(portp, MR0, mr0);
+       stl_sc26198updatereg(portp, MR1, mr1);
+       stl_sc26198setreg(portp, SCCR, CR_RXERRBLOCK);
+       stl_sc26198updatereg(portp, MR2, mr2);
+       stl_sc26198updatereg(portp, IOPIOR,
+               ((stl_sc26198getreg(portp, IOPIOR) & ~IPR_CHANGEMASK) | iopr));
+
+       if (baudrate > 0) {
+               stl_sc26198setreg(portp, TXCSR, clk);
+               stl_sc26198setreg(portp, RXCSR, clk);
+       }
+
+       stl_sc26198setreg(portp, XONCR, tiosp->c_cc[VSTART]);
+       stl_sc26198setreg(portp, XOFFCR, tiosp->c_cc[VSTOP]);
+
+       ipr = stl_sc26198getreg(portp, IPR);
+       if (ipr & IPR_DCD)
+               portp->sigs &= ~TIOCM_CD;
+       else
+               portp->sigs |= TIOCM_CD;
+
+       portp->imr = (portp->imr & ~imroff) | imron;
+       stl_sc26198setreg(portp, IMR, portp->imr);
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Set the state of the DTR and RTS signals.
+ */
+
+static void stl_sc26198setsignals(struct stlport *portp, int dtr, int rts)
+{
+       unsigned char   iopioron, iopioroff;
+       unsigned long   flags;
+
+       pr_debug("stl_sc26198setsignals(portp=%p,dtr=%d,rts=%d)\n", portp,
+                       dtr, rts);
+
+       iopioron = 0;
+       iopioroff = 0;
+       if (dtr == 0)
+               iopioroff |= IPR_DTR;
+       else if (dtr > 0)
+               iopioron |= IPR_DTR;
+       if (rts == 0)
+               iopioroff |= IPR_RTS;
+       else if (rts > 0)
+               iopioron |= IPR_RTS;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_sc26198setreg(portp, IOPIOR,
+               ((stl_sc26198getreg(portp, IOPIOR) & ~iopioroff) | iopioron));
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the state of the signals.
+ */
+
+static int stl_sc26198getsignals(struct stlport *portp)
+{
+       unsigned char   ipr;
+       unsigned long   flags;
+       int             sigs;
+
+       pr_debug("stl_sc26198getsignals(portp=%p)\n", portp);
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       ipr = stl_sc26198getreg(portp, IPR);
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+
+       sigs = 0;
+       sigs |= (ipr & IPR_DCD) ? 0 : TIOCM_CD;
+       sigs |= (ipr & IPR_CTS) ? 0 : TIOCM_CTS;
+       sigs |= (ipr & IPR_DTR) ? 0: TIOCM_DTR;
+       sigs |= (ipr & IPR_RTS) ? 0: TIOCM_RTS;
+       sigs |= TIOCM_DSR;
+       return sigs;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Enable/Disable the Transmitter and/or Receiver.
+ */
+
+static void stl_sc26198enablerxtx(struct stlport *portp, int rx, int tx)
+{
+       unsigned char   ccr;
+       unsigned long   flags;
+
+       pr_debug("stl_sc26198enablerxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx,tx);
+
+       ccr = portp->crenable;
+       if (tx == 0)
+               ccr &= ~CR_TXENABLE;
+       else if (tx > 0)
+               ccr |= CR_TXENABLE;
+       if (rx == 0)
+               ccr &= ~CR_RXENABLE;
+       else if (rx > 0)
+               ccr |= CR_RXENABLE;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_sc26198setreg(portp, SCCR, ccr);
+       BRDDISABLE(portp->brdnr);
+       portp->crenable = ccr;
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Start/stop the Transmitter and/or Receiver.
+ */
+
+static void stl_sc26198startrxtx(struct stlport *portp, int rx, int tx)
+{
+       unsigned char   imr;
+       unsigned long   flags;
+
+       pr_debug("stl_sc26198startrxtx(portp=%p,rx=%d,tx=%d)\n", portp, rx, tx);
+
+       imr = portp->imr;
+       if (tx == 0)
+               imr &= ~IR_TXRDY;
+       else if (tx == 1)
+               imr |= IR_TXRDY;
+       if (rx == 0)
+               imr &= ~(IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG);
+       else if (rx > 0)
+               imr |= IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_sc26198setreg(portp, IMR, imr);
+       BRDDISABLE(portp->brdnr);
+       portp->imr = imr;
+       if (tx > 0)
+               set_bit(ASYI_TXBUSY, &portp->istate);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Disable all interrupts from this port.
+ */
+
+static void stl_sc26198disableintrs(struct stlport *portp)
+{
+       unsigned long   flags;
+
+       pr_debug("stl_sc26198disableintrs(portp=%p)\n", portp);
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       portp->imr = 0;
+       stl_sc26198setreg(portp, IMR, 0);
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+static void stl_sc26198sendbreak(struct stlport *portp, int len)
+{
+       unsigned long   flags;
+
+       pr_debug("stl_sc26198sendbreak(portp=%p,len=%d)\n", portp, len);
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       if (len == 1) {
+               stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK);
+               portp->stats.txbreaks++;
+       } else
+               stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK);
+
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Take flow control actions...
+ */
+
+static void stl_sc26198flowctrl(struct stlport *portp, int state)
+{
+       struct tty_struct       *tty;
+       unsigned long           flags;
+       unsigned char           mr0;
+
+       pr_debug("stl_sc26198flowctrl(portp=%p,state=%x)\n", portp, state);
+
+       if (portp == NULL)
+               return;
+       tty = tty_port_tty_get(&portp->port);
+       if (tty == NULL)
+               return;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+
+       if (state) {
+               if (tty->termios->c_iflag & IXOFF) {
+                       mr0 = stl_sc26198getreg(portp, MR0);
+                       stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+                       stl_sc26198setreg(portp, SCCR, CR_TXSENDXON);
+                       mr0 |= MR0_SWFRX;
+                       portp->stats.rxxon++;
+                       stl_sc26198wait(portp);
+                       stl_sc26198setreg(portp, MR0, mr0);
+               }
+/*
+ *             Question: should we return RTS to what it was before? It may
+ *             have been set by an ioctl... Suppose not, since if you have
+ *             hardware flow control set then it is pretty silly to go and
+ *             set the RTS line by hand.
+ */
+               if (tty->termios->c_cflag & CRTSCTS) {
+                       stl_sc26198setreg(portp, MR1,
+                               (stl_sc26198getreg(portp, MR1) | MR1_AUTORTS));
+                       stl_sc26198setreg(portp, IOPIOR,
+                               (stl_sc26198getreg(portp, IOPIOR) | IOPR_RTS));
+                       portp->stats.rxrtson++;
+               }
+       } else {
+               if (tty->termios->c_iflag & IXOFF) {
+                       mr0 = stl_sc26198getreg(portp, MR0);
+                       stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+                       stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF);
+                       mr0 &= ~MR0_SWFRX;
+                       portp->stats.rxxoff++;
+                       stl_sc26198wait(portp);
+                       stl_sc26198setreg(portp, MR0, mr0);
+               }
+               if (tty->termios->c_cflag & CRTSCTS) {
+                       stl_sc26198setreg(portp, MR1,
+                               (stl_sc26198getreg(portp, MR1) & ~MR1_AUTORTS));
+                       stl_sc26198setreg(portp, IOPIOR,
+                               (stl_sc26198getreg(portp, IOPIOR) & ~IOPR_RTS));
+                       portp->stats.rxrtsoff++;
+               }
+       }
+
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+       tty_kref_put(tty);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Send a flow control character.
+ */
+
+static void stl_sc26198sendflow(struct stlport *portp, int state)
+{
+       struct tty_struct       *tty;
+       unsigned long           flags;
+       unsigned char           mr0;
+
+       pr_debug("stl_sc26198sendflow(portp=%p,state=%x)\n", portp, state);
+
+       if (portp == NULL)
+               return;
+       tty = tty_port_tty_get(&portp->port);
+       if (tty == NULL)
+               return;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       if (state) {
+               mr0 = stl_sc26198getreg(portp, MR0);
+               stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+               stl_sc26198setreg(portp, SCCR, CR_TXSENDXON);
+               mr0 |= MR0_SWFRX;
+               portp->stats.rxxon++;
+               stl_sc26198wait(portp);
+               stl_sc26198setreg(portp, MR0, mr0);
+       } else {
+               mr0 = stl_sc26198getreg(portp, MR0);
+               stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+               stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF);
+               mr0 &= ~MR0_SWFRX;
+               portp->stats.rxxoff++;
+               stl_sc26198wait(portp);
+               stl_sc26198setreg(portp, MR0, mr0);
+       }
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+       tty_kref_put(tty);
+}
+
+/*****************************************************************************/
+
+static void stl_sc26198flush(struct stlport *portp)
+{
+       unsigned long   flags;
+
+       pr_debug("stl_sc26198flush(portp=%p)\n", portp);
+
+       if (portp == NULL)
+               return;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       stl_sc26198setreg(portp, SCCR, CR_TXRESET);
+       stl_sc26198setreg(portp, SCCR, portp->crenable);
+       BRDDISABLE(portp->brdnr);
+       portp->tx.tail = portp->tx.head;
+       spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Return the current state of data flow on this port. This is only
+ *     really interesting when determining if data has fully completed
+ *     transmission or not... The sc26198 interrupt scheme cannot
+ *     determine when all data has actually drained, so we need to
+ *     check the port statusy register to be sure.
+ */
+
+static int stl_sc26198datastate(struct stlport *portp)
+{
+       unsigned long   flags;
+       unsigned char   sr;
+
+       pr_debug("stl_sc26198datastate(portp=%p)\n", portp);
+
+       if (portp == NULL)
+               return 0;
+       if (test_bit(ASYI_TXBUSY, &portp->istate))
+               return 1;
+
+       spin_lock_irqsave(&brd_lock, flags);
+       BRDENABLE(portp->brdnr, portp->pagenr);
+       sr = stl_sc26198getreg(portp, SR);
+       BRDDISABLE(portp->brdnr);
+       spin_unlock_irqrestore(&brd_lock, flags);
+
+       return (sr & SR_TXEMPTY) ? 0 : 1;
+}
+
+/*****************************************************************************/
+
+/*
+ *     Delay for a small amount of time, to give the sc26198 a chance
+ *     to process a command...
+ */
+
+static void stl_sc26198wait(struct stlport *portp)
+{
+       int     i;
+
+       pr_debug("stl_sc26198wait(portp=%p)\n", portp);
+
+       if (portp == NULL)
+               return;
+
+       for (i = 0; i < 20; i++)
+               stl_sc26198getglobreg(portp, TSTR);
+}
+
+/*****************************************************************************/
+
+/*
+ *     If we are TX flow controlled and in IXANY mode then we may
+ *     need to unflow control here. We gotta do this because of the
+ *     automatic flow control modes of the sc26198.
+ */
+
+static void stl_sc26198txunflow(struct stlport *portp, struct tty_struct *tty)
+{
+       unsigned char   mr0;
+
+       mr0 = stl_sc26198getreg(portp, MR0);
+       stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+       stl_sc26198setreg(portp, SCCR, CR_HOSTXON);
+       stl_sc26198wait(portp);
+       stl_sc26198setreg(portp, MR0, mr0);
+       clear_bit(ASYI_TXFLOWED, &portp->istate);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Interrupt service routine for sc26198 panels.
+ */
+
+static void stl_sc26198intr(struct stlpanel *panelp, unsigned int iobase)
+{
+       struct stlport  *portp;
+       unsigned int    iack;
+
+       spin_lock(&brd_lock);
+
+/* 
+ *     Work around bug in sc26198 chip... Cannot have A6 address
+ *     line of UART high, else iack will be returned as 0.
+ */
+       outb(0, (iobase + 1));
+
+       iack = inb(iobase + XP_IACK);
+       portp = panelp->ports[(iack & IVR_CHANMASK) + ((iobase & 0x4) << 1)];
+
+       if (iack & IVR_RXDATA)
+               stl_sc26198rxisr(portp, iack);
+       else if (iack & IVR_TXDATA)
+               stl_sc26198txisr(portp);
+       else
+               stl_sc26198otherisr(portp, iack);
+
+       spin_unlock(&brd_lock);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Transmit interrupt handler. This has gotta be fast!  Handling TX
+ *     chars is pretty simple, stuff as many as possible from the TX buffer
+ *     into the sc26198 FIFO.
+ *     In practice it is possible that interrupts are enabled but that the
+ *     port has been hung up. Need to handle not having any TX buffer here,
+ *     this is done by using the side effect that head and tail will also
+ *     be NULL if the buffer has been freed.
+ */
+
+static void stl_sc26198txisr(struct stlport *portp)
+{
+       struct tty_struct *tty;
+       unsigned int    ioaddr;
+       unsigned char   mr0;
+       int             len, stlen;
+       char            *head, *tail;
+
+       pr_debug("stl_sc26198txisr(portp=%p)\n", portp);
+
+       ioaddr = portp->ioaddr;
+       head = portp->tx.head;
+       tail = portp->tx.tail;
+       len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
+       if ((len == 0) || ((len < STL_TXBUFLOW) &&
+           (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
+               set_bit(ASYI_TXLOW, &portp->istate);
+               tty = tty_port_tty_get(&portp->port);
+               if (tty) {
+                       tty_wakeup(tty);
+                       tty_kref_put(tty);
+               }
+       }
+
+       if (len == 0) {
+               outb((MR0 | portp->uartaddr), (ioaddr + XP_ADDR));
+               mr0 = inb(ioaddr + XP_DATA);
+               if ((mr0 & MR0_TXMASK) == MR0_TXEMPTY) {
+                       portp->imr &= ~IR_TXRDY;
+                       outb((IMR | portp->uartaddr), (ioaddr + XP_ADDR));
+                       outb(portp->imr, (ioaddr + XP_DATA));
+                       clear_bit(ASYI_TXBUSY, &portp->istate);
+               } else {
+                       mr0 |= ((mr0 & ~MR0_TXMASK) | MR0_TXEMPTY);
+                       outb(mr0, (ioaddr + XP_DATA));
+               }
+       } else {
+               len = min(len, SC26198_TXFIFOSIZE);
+               portp->stats.txtotal += len;
+               stlen = min_t(unsigned int, len,
+                               (portp->tx.buf + STL_TXBUFSIZE) - tail);
+               outb(GTXFIFO, (ioaddr + XP_ADDR));
+               outsb((ioaddr + XP_DATA), tail, stlen);
+               len -= stlen;
+               tail += stlen;
+               if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
+                       tail = portp->tx.buf;
+               if (len > 0) {
+                       outsb((ioaddr + XP_DATA), tail, len);
+                       tail += len;
+               }
+               portp->tx.tail = tail;
+       }
+}
+
+/*****************************************************************************/
+
+/*
+ *     Receive character interrupt handler. Determine if we have good chars
+ *     or bad chars and then process appropriately. Good chars are easy
+ *     just shove the lot into the RX buffer and set all status byte to 0.
+ *     If a bad RX char then process as required. This routine needs to be
+ *     fast!  In practice it is possible that we get an interrupt on a port
+ *     that is closed. This can happen on hangups - since they completely
+ *     shutdown a port not in user context. Need to handle this case.
+ */
+
+static void stl_sc26198rxisr(struct stlport *portp, unsigned int iack)
+{
+       struct tty_struct       *tty;
+       unsigned int            len, buflen, ioaddr;
+
+       pr_debug("stl_sc26198rxisr(portp=%p,iack=%x)\n", portp, iack);
+
+       tty = tty_port_tty_get(&portp->port);
+       ioaddr = portp->ioaddr;
+       outb(GIBCR, (ioaddr + XP_ADDR));
+       len = inb(ioaddr + XP_DATA) + 1;
+
+       if ((iack & IVR_TYPEMASK) == IVR_RXDATA) {
+               if (tty == NULL || (buflen = tty_buffer_request_room(tty, len)) == 0) {
+                       len = min_t(unsigned int, len, sizeof(stl_unwanted));
+                       outb(GRXFIFO, (ioaddr + XP_ADDR));
+                       insb((ioaddr + XP_DATA), &stl_unwanted[0], len);
+                       portp->stats.rxlost += len;
+                       portp->stats.rxtotal += len;
+               } else {
+                       len = min(len, buflen);
+                       if (len > 0) {
+                               unsigned char *ptr;
+                               outb(GRXFIFO, (ioaddr + XP_ADDR));
+                               tty_prepare_flip_string(tty, &ptr, len);
+                               insb((ioaddr + XP_DATA), ptr, len);
+                               tty_schedule_flip(tty);
+                               portp->stats.rxtotal += len;
+                       }
+               }
+       } else {
+               stl_sc26198rxbadchars(portp);
+       }
+
+/*
+ *     If we are TX flow controlled and in IXANY mode then we may need
+ *     to unflow control here. We gotta do this because of the automatic
+ *     flow control modes of the sc26198.
+ */
+       if (test_bit(ASYI_TXFLOWED, &portp->istate)) {
+               if ((tty != NULL) &&
+                   (tty->termios != NULL) &&
+                   (tty->termios->c_iflag & IXANY)) {
+                       stl_sc26198txunflow(portp, tty);
+               }
+       }
+       tty_kref_put(tty);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Process an RX bad character.
+ */
+
+static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char ch)
+{
+       struct tty_struct       *tty;
+       unsigned int            ioaddr;
+
+       tty = tty_port_tty_get(&portp->port);
+       ioaddr = portp->ioaddr;
+
+       if (status & SR_RXPARITY)
+               portp->stats.rxparity++;
+       if (status & SR_RXFRAMING)
+               portp->stats.rxframing++;
+       if (status & SR_RXOVERRUN)
+               portp->stats.rxoverrun++;
+       if (status & SR_RXBREAK)
+               portp->stats.rxbreaks++;
+
+       if ((tty != NULL) &&
+           ((portp->rxignoremsk & status) == 0)) {
+               if (portp->rxmarkmsk & status) {
+                       if (status & SR_RXBREAK) {
+                               status = TTY_BREAK;
+                               if (portp->port.flags & ASYNC_SAK) {
+                                       do_SAK(tty);
+                                       BRDENABLE(portp->brdnr, portp->pagenr);
+                               }
+                       } else if (status & SR_RXPARITY)
+                               status = TTY_PARITY;
+                       else if (status & SR_RXFRAMING)
+                               status = TTY_FRAME;
+                       else if(status & SR_RXOVERRUN)
+                               status = TTY_OVERRUN;
+                       else
+                               status = 0;
+               } else
+                       status = 0;
+
+               tty_insert_flip_char(tty, ch, status);
+               tty_schedule_flip(tty);
+
+               if (status == 0)
+                       portp->stats.rxtotal++;
+       }
+       tty_kref_put(tty);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Process all characters in the RX FIFO of the UART. Check all char
+ *     status bytes as well, and process as required. We need to check
+ *     all bytes in the FIFO, in case some more enter the FIFO while we
+ *     are here. To get the exact character error type we need to switch
+ *     into CHAR error mode (that is why we need to make sure we empty
+ *     the FIFO).
+ */
+
+static void stl_sc26198rxbadchars(struct stlport *portp)
+{
+       unsigned char   status, mr1;
+       char            ch;
+
+/*
+ *     To get the precise error type for each character we must switch
+ *     back into CHAR error mode.
+ */
+       mr1 = stl_sc26198getreg(portp, MR1);
+       stl_sc26198setreg(portp, MR1, (mr1 & ~MR1_ERRBLOCK));
+
+       while ((status = stl_sc26198getreg(portp, SR)) & SR_RXRDY) {
+               stl_sc26198setreg(portp, SCCR, CR_CLEARRXERR);
+               ch = stl_sc26198getreg(portp, RXFIFO);
+               stl_sc26198rxbadch(portp, status, ch);
+       }
+
+/*
+ *     To get correct interrupt class we must switch back into BLOCK
+ *     error mode.
+ */
+       stl_sc26198setreg(portp, MR1, mr1);
+}
+
+/*****************************************************************************/
+
+/*
+ *     Other interrupt handler. This includes modem signals, flow
+ *     control actions, etc. Most stuff is left to off-level interrupt
+ *     processing time.
+ */
+
+static void stl_sc26198otherisr(struct stlport *portp, unsigned int iack)
+{
+       unsigned char   cir, ipr, xisr;
+
+       pr_debug("stl_sc26198otherisr(portp=%p,iack=%x)\n", portp, iack);
+
+       cir = stl_sc26198getglobreg(portp, CIR);
+
+       switch (cir & CIR_SUBTYPEMASK) {
+       case CIR_SUBCOS:
+               ipr = stl_sc26198getreg(portp, IPR);
+               if (ipr & IPR_DCDCHANGE) {
+                       stl_cd_change(portp);
+                       portp->stats.modem++;
+               }
+               break;
+       case CIR_SUBXONXOFF:
+               xisr = stl_sc26198getreg(portp, XISR);
+               if (xisr & XISR_RXXONGOT) {
+                       set_bit(ASYI_TXFLOWED, &portp->istate);
+                       portp->stats.txxoff++;
+               }
+               if (xisr & XISR_RXXOFFGOT) {
+                       clear_bit(ASYI_TXFLOWED, &portp->istate);
+                       portp->stats.txxon++;
+               }
+               break;
+       case CIR_SUBBREAK:
+               stl_sc26198setreg(portp, SCCR, CR_BREAKRESET);
+               stl_sc26198rxbadchars(portp);
+               break;
+       default:
+               break;
+       }
+}
+
+static void stl_free_isabrds(void)
+{
+       struct stlbrd *brdp;
+       unsigned int i;
+
+       for (i = 0; i < stl_nrbrds; i++) {
+               if ((brdp = stl_brds[i]) == NULL || (brdp->state & STL_PROBED))
+                       continue;
+
+               free_irq(brdp->irq, brdp);
+
+               stl_cleanup_panels(brdp);
+
+               release_region(brdp->ioaddr1, brdp->iosize1);
+               if (brdp->iosize2 > 0)
+                       release_region(brdp->ioaddr2, brdp->iosize2);
+
+               kfree(brdp);
+               stl_brds[i] = NULL;
+       }
+}
+
+/*
+ *     Loadable module initialization stuff.
+ */
+static int __init stallion_module_init(void)
+{
+       struct stlbrd   *brdp;
+       struct stlconf  conf;
+       unsigned int i, j;
+       int retval;
+
+       printk(KERN_INFO "%s: version %s\n", stl_drvtitle, stl_drvversion);
+
+       spin_lock_init(&stallion_lock);
+       spin_lock_init(&brd_lock);
+
+       stl_serial = alloc_tty_driver(STL_MAXBRDS * STL_MAXPORTS);
+       if (!stl_serial) {
+               retval = -ENOMEM;
+               goto err;
+       }
+
+       stl_serial->owner = THIS_MODULE;
+       stl_serial->driver_name = stl_drvname;
+       stl_serial->name = "ttyE";
+       stl_serial->major = STL_SERIALMAJOR;
+       stl_serial->minor_start = 0;
+       stl_serial->type = TTY_DRIVER_TYPE_SERIAL;
+       stl_serial->subtype = SERIAL_TYPE_NORMAL;
+       stl_serial->init_termios = stl_deftermios;
+       stl_serial->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+       tty_set_operations(stl_serial, &stl_ops);
+
+       retval = tty_register_driver(stl_serial);
+       if (retval) {
+               printk("STALLION: failed to register serial driver\n");
+               goto err_frtty;
+       }
+
+/*
+ *     Find any dynamically supported boards. That is via module load
+ *     line options.
+ */
+       for (i = stl_nrbrds; i < stl_nargs; i++) {
+               memset(&conf, 0, sizeof(conf));
+               if (stl_parsebrd(&conf, stl_brdsp[i]) == 0)
+                       continue;
+               if ((brdp = stl_allocbrd()) == NULL)
+                       continue;
+               brdp->brdnr = i;
+               brdp->brdtype = conf.brdtype;
+               brdp->ioaddr1 = conf.ioaddr1;
+               brdp->ioaddr2 = conf.ioaddr2;
+               brdp->irq = conf.irq;
+               brdp->irqtype = conf.irqtype;
+               stl_brds[brdp->brdnr] = brdp;
+               if (stl_brdinit(brdp)) {
+                       stl_brds[brdp->brdnr] = NULL;
+                       kfree(brdp);
+               } else {
+                       for (j = 0; j < brdp->nrports; j++)
+                               tty_register_device(stl_serial,
+                                       brdp->brdnr * STL_MAXPORTS + j, NULL);
+                       stl_nrbrds = i + 1;
+               }
+       }
+
+       /* this has to be _after_ isa finding because of locking */
+       retval = pci_register_driver(&stl_pcidriver);
+       if (retval && stl_nrbrds == 0) {
+               printk(KERN_ERR "STALLION: can't register pci driver\n");
+               goto err_unrtty;
+       }
+
+/*
+ *     Set up a character driver for per board stuff. This is mainly used
+ *     to do stats ioctls on the ports.
+ */
+       if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem))
+               printk("STALLION: failed to register serial board device\n");
+
+       stallion_class = class_create(THIS_MODULE, "staliomem");
+       if (IS_ERR(stallion_class))
+               printk("STALLION: failed to create class\n");
+       for (i = 0; i < 4; i++)
+               device_create(stallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i),
+                             NULL, "staliomem%d", i);
+
+       return 0;
+err_unrtty:
+       tty_unregister_driver(stl_serial);
+err_frtty:
+       put_tty_driver(stl_serial);
+err:
+       return retval;
+}
+
+static void __exit stallion_module_exit(void)
+{
+       struct stlbrd *brdp;
+       unsigned int i, j;
+
+       pr_debug("cleanup_module()\n");
+
+       printk(KERN_INFO "Unloading %s: version %s\n", stl_drvtitle,
+               stl_drvversion);
+
+/*
+ *     Free up all allocated resources used by the ports. This includes
+ *     memory and interrupts. As part of this process we will also do
+ *     a hangup on every open port - to try to flush out any processes
+ *     hanging onto ports.
+ */
+       for (i = 0; i < stl_nrbrds; i++) {
+               if ((brdp = stl_brds[i]) == NULL || (brdp->state & STL_PROBED))
+                       continue;
+               for (j = 0; j < brdp->nrports; j++)
+                       tty_unregister_device(stl_serial,
+                               brdp->brdnr * STL_MAXPORTS + j);
+       }
+
+       for (i = 0; i < 4; i++)
+               device_destroy(stallion_class, MKDEV(STL_SIOMEMMAJOR, i));
+       unregister_chrdev(STL_SIOMEMMAJOR, "staliomem");
+       class_destroy(stallion_class);
+
+       pci_unregister_driver(&stl_pcidriver);
+
+       stl_free_isabrds();
+
+       tty_unregister_driver(stl_serial);
+       put_tty_driver(stl_serial);
+}
+
+module_init(stallion_module_init);
+module_exit(stallion_module_exit);
+
+MODULE_AUTHOR("Greg Ungerer");
+MODULE_DESCRIPTION("Stallion Multiport Serial Driver");
+MODULE_LICENSE("GPL");