summaryrefslogtreecommitdiff
path: root/drivers/staging
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2011-03-09 16:50:53 +1100
committerStephen Rothwell <sfr@canb.auug.org.au>2011-03-09 16:50:53 +1100
commitf41b9cea9280c1df1e7bb2ae06a67d2322a187b6 (patch)
treea4b029c9e08eda350c1650803877577272b8bbe8 /drivers/staging
parent3cf560ca9cf0b8c6967a19b06ec13da551124088 (diff)
parent550462378515a82279e07f12e2c105f617f112f8 (diff)
Merge remote-tracking branch 'tty/tty-next'
Conflicts: drivers/tty/serial/Kconfig drivers/tty/serial/Makefile
Diffstat (limited to 'drivers/staging')
-rw-r--r--drivers/staging/Kconfig4
-rw-r--r--drivers/staging/Makefile2
-rw-r--r--drivers/staging/generic_serial/Kconfig45
-rw-r--r--drivers/staging/generic_serial/Makefile6
-rw-r--r--drivers/staging/generic_serial/TODO6
-rw-r--r--drivers/staging/generic_serial/generic_serial.c844
-rw-r--r--drivers/staging/generic_serial/rio/Makefile12
-rw-r--r--drivers/staging/generic_serial/rio/board.h132
-rw-r--r--drivers/staging/generic_serial/rio/cirrus.h210
-rw-r--r--drivers/staging/generic_serial/rio/cmdblk.h53
-rw-r--r--drivers/staging/generic_serial/rio/cmdpkt.h177
-rw-r--r--drivers/staging/generic_serial/rio/daemon.h307
-rw-r--r--drivers/staging/generic_serial/rio/errors.h98
-rw-r--r--drivers/staging/generic_serial/rio/func.h143
-rw-r--r--drivers/staging/generic_serial/rio/host.h123
-rw-r--r--drivers/staging/generic_serial/rio/link.h96
-rw-r--r--drivers/staging/generic_serial/rio/linux_compat.h77
-rw-r--r--drivers/staging/generic_serial/rio/map.h98
-rw-r--r--drivers/staging/generic_serial/rio/param.h55
-rw-r--r--drivers/staging/generic_serial/rio/parmmap.h81
-rw-r--r--drivers/staging/generic_serial/rio/pci.h72
-rw-r--r--drivers/staging/generic_serial/rio/phb.h142
-rw-r--r--drivers/staging/generic_serial/rio/pkt.h77
-rw-r--r--drivers/staging/generic_serial/rio/port.h179
-rw-r--r--drivers/staging/generic_serial/rio/protsts.h110
-rw-r--r--drivers/staging/generic_serial/rio/rio.h208
-rw-r--r--drivers/staging/generic_serial/rio/rio_linux.c1204
-rw-r--r--drivers/staging/generic_serial/rio/rio_linux.h197
-rw-r--r--drivers/staging/generic_serial/rio/rioboard.h275
-rw-r--r--drivers/staging/generic_serial/rio/rioboot.c1113
-rw-r--r--drivers/staging/generic_serial/rio/riocmd.c939
-rw-r--r--drivers/staging/generic_serial/rio/rioctrl.c1504
-rw-r--r--drivers/staging/generic_serial/rio/riodrvr.h138
-rw-r--r--drivers/staging/generic_serial/rio/rioinfo.h92
-rw-r--r--drivers/staging/generic_serial/rio/rioinit.c421
-rw-r--r--drivers/staging/generic_serial/rio/riointr.c645
-rw-r--r--drivers/staging/generic_serial/rio/rioioctl.h57
-rw-r--r--drivers/staging/generic_serial/rio/rioparam.c663
-rw-r--r--drivers/staging/generic_serial/rio/rioroute.c1039
-rw-r--r--drivers/staging/generic_serial/rio/riospace.h154
-rw-r--r--drivers/staging/generic_serial/rio/riotable.c941
-rw-r--r--drivers/staging/generic_serial/rio/riotty.c654
-rw-r--r--drivers/staging/generic_serial/rio/route.h101
-rw-r--r--drivers/staging/generic_serial/rio/rup.h69
-rw-r--r--drivers/staging/generic_serial/rio/unixrup.h51
-rw-r--r--drivers/staging/generic_serial/ser_a2232.c831
-rw-r--r--drivers/staging/generic_serial/ser_a2232.h202
-rw-r--r--drivers/staging/generic_serial/ser_a2232fw.ax529
-rw-r--r--drivers/staging/generic_serial/ser_a2232fw.h306
-rw-r--r--drivers/staging/generic_serial/sx.c2894
-rw-r--r--drivers/staging/generic_serial/sx.h201
-rw-r--r--drivers/staging/generic_serial/sxboards.h206
-rw-r--r--drivers/staging/generic_serial/sxwindow.h393
-rw-r--r--drivers/staging/generic_serial/vme_scc.c1145
-rw-r--r--drivers/staging/quatech_usb2/quatech_usb2.c6
-rw-r--r--drivers/staging/serqt_usb2/serqt_usb2.c13
-rw-r--r--drivers/staging/tty/Kconfig87
-rw-r--r--drivers/staging/tty/Makefile7
-rw-r--r--drivers/staging/tty/TODO6
-rw-r--r--drivers/staging/tty/cd1865.h263
-rw-r--r--drivers/staging/tty/digi1.h100
-rw-r--r--drivers/staging/tty/digiFep1.h136
-rw-r--r--drivers/staging/tty/digiPCI.h42
-rw-r--r--drivers/staging/tty/epca.c2784
-rw-r--r--drivers/staging/tty/epca.h158
-rw-r--r--drivers/staging/tty/epcaconfig.h7
-rw-r--r--drivers/staging/tty/ip2/Makefile8
-rw-r--r--drivers/staging/tty/ip2/i2cmd.c210
-rw-r--r--drivers/staging/tty/ip2/i2cmd.h630
-rw-r--r--drivers/staging/tty/ip2/i2ellis.c1403
-rw-r--r--drivers/staging/tty/ip2/i2ellis.h566
-rw-r--r--drivers/staging/tty/ip2/i2hw.h652
-rw-r--r--drivers/staging/tty/ip2/i2lib.c2214
-rw-r--r--drivers/staging/tty/ip2/i2lib.h351
-rw-r--r--drivers/staging/tty/ip2/i2pack.h364
-rw-r--r--drivers/staging/tty/ip2/ip2.h107
-rw-r--r--drivers/staging/tty/ip2/ip2ioctl.h35
-rw-r--r--drivers/staging/tty/ip2/ip2main.c3234
-rw-r--r--drivers/staging/tty/ip2/ip2trace.h42
-rw-r--r--drivers/staging/tty/ip2/ip2types.h57
-rw-r--r--drivers/staging/tty/istallion.c4507
-rw-r--r--drivers/staging/tty/riscom8.c1560
-rw-r--r--drivers/staging/tty/riscom8.h91
-rw-r--r--drivers/staging/tty/riscom8_reg.h254
-rw-r--r--drivers/staging/tty/serial167.c2489
-rw-r--r--drivers/staging/tty/specialix.c2368
-rw-r--r--drivers/staging/tty/specialix_io8.h140
-rw-r--r--drivers/staging/tty/stallion.c4651
88 files changed, 49853 insertions, 10 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 74a8b272154f..1ae0c1befc77 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -41,6 +41,10 @@ config STAGING_EXCLUDE_BUILD
if !STAGING_EXCLUDE_BUILD
+source "drivers/staging/tty/Kconfig"
+
+source "drivers/staging/generic_serial/Kconfig"
+
source "drivers/staging/et131x/Kconfig"
source "drivers/staging/slicoss/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 9f50ec90f9d8..02691426703d 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -3,6 +3,8 @@
# fix for build system bug...
obj-$(CONFIG_STAGING) += staging.o
+obj-y += tty/
+obj-y += generic_serial/
obj-$(CONFIG_ET131X) += et131x/
obj-$(CONFIG_SLICOSS) += slicoss/
obj-$(CONFIG_VIDEO_GO7007) += go7007/
diff --git a/drivers/staging/generic_serial/Kconfig b/drivers/staging/generic_serial/Kconfig
new file mode 100644
index 000000000000..795daea37750
--- /dev/null
+++ b/drivers/staging/generic_serial/Kconfig
@@ -0,0 +1,45 @@
+config A2232
+ tristate "Commodore A2232 serial support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && ZORRO && BROKEN
+ ---help---
+ This option supports the 2232 7-port serial card shipped with the
+ Amiga 2000 and other Zorro-bus machines, dating from 1989. At
+ a max of 19,200 bps, the ports are served by a 6551 ACIA UART chip
+ each, plus a 8520 CIA, and a master 6502 CPU and buffer as well. The
+ ports were connected with 8 pin DIN connectors on the card bracket,
+ for which 8 pin to DB25 adapters were supplied. The card also had
+ jumpers internally to toggle various pinning configurations.
+
+ This driver can be built as a module; but then "generic_serial"
+ will also be built as a module. This has to be loaded before
+ "ser_a2232". If you want to do this, answer M here.
+
+config SX
+ tristate "Specialix SX (and SI) card support"
+ depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) && BROKEN
+ help
+ This is a driver for the SX and SI multiport serial cards.
+ Please read the file <file:Documentation/serial/sx.txt> for details.
+
+ This driver can only be built as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called sx. If you want to do that, say M here.
+
+config RIO
+ tristate "Specialix RIO system support"
+ depends on SERIAL_NONSTANDARD && BROKEN
+ help
+ This is a driver for the Specialix RIO, a smart serial card which
+ drives an outboard box that can support up to 128 ports. Product
+ information is at <http://www.perle.com/support/documentation.html#multiport>.
+ There are both ISA and PCI versions.
+
+config RIO_OLDPCI
+ bool "Support really old RIO/PCI cards"
+ depends on RIO
+ help
+ Older RIO PCI cards need some initialization-time configuration to
+ determine the IRQ and some control addresses. If you have a RIO and
+ this doesn't seem to work, try setting this to Y.
+
+
diff --git a/drivers/staging/generic_serial/Makefile b/drivers/staging/generic_serial/Makefile
new file mode 100644
index 000000000000..ffc90c8b013c
--- /dev/null
+++ b/drivers/staging/generic_serial/Makefile
@@ -0,0 +1,6 @@
+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_A2232) += ser_a2232.o generic_serial.o
+obj-$(CONFIG_SX) += sx.o generic_serial.o
+obj-$(CONFIG_RIO) += rio/ generic_serial.o
diff --git a/drivers/staging/generic_serial/TODO b/drivers/staging/generic_serial/TODO
new file mode 100644
index 000000000000..88756453ac6c
--- /dev/null
+++ b/drivers/staging/generic_serial/TODO
@@ -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/generic_serial/generic_serial.c b/drivers/staging/generic_serial/generic_serial.c
new file mode 100644
index 000000000000..466988dbc37d
--- /dev/null
+++ b/drivers/staging/generic_serial/generic_serial.c
@@ -0,0 +1,844 @@
+/*
+ * generic_serial.c
+ *
+ * Copyright (C) 1998/1999 R.E.Wolff@BitWizard.nl
+ *
+ * written for the SX serial driver.
+ * Contains the code that should be shared over all the serial drivers.
+ *
+ * Credit for the idea to do it this way might go to Alan Cox.
+ *
+ *
+ * Version 0.1 -- December, 1998. Initial version.
+ * Version 0.2 -- March, 1999. Some more routines. Bugfixes. Etc.
+ * Version 0.5 -- August, 1999. Some more fixes. Reformat for Linus.
+ *
+ * BitWizard is actively maintaining this file. We sometimes find
+ * that someone submitted changes to this file. We really appreciate
+ * your help, but please submit changes through us. We're doing our
+ * best to be responsive. -- REW
+ * */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/serial.h>
+#include <linux/mm.h>
+#include <linux/generic_serial.h>
+#include <linux/interrupt.h>
+#include <linux/tty_flip.h>
+#include <linux/delay.h>
+#include <linux/gfp.h>
+#include <asm/uaccess.h>
+
+#define DEBUG
+
+static int gs_debug;
+
+#ifdef DEBUG
+#define gs_dprintk(f, str...) if (gs_debug & f) printk (str)
+#else
+#define gs_dprintk(f, str...) /* nothing */
+#endif
+
+#define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter %s\n", __func__)
+#define func_exit() gs_dprintk (GS_DEBUG_FLOW, "gs: exit %s\n", __func__)
+
+#define RS_EVENT_WRITE_WAKEUP 1
+
+module_param(gs_debug, int, 0644);
+
+
+int gs_put_char(struct tty_struct * tty, unsigned char ch)
+{
+ struct gs_port *port;
+
+ func_enter ();
+
+ port = tty->driver_data;
+
+ if (!port) return 0;
+
+ if (! (port->port.flags & ASYNC_INITIALIZED)) return 0;
+
+ /* Take a lock on the serial tranmit buffer! */
+ mutex_lock(& port->port_write_mutex);
+
+ if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+ /* Sorry, buffer is full, drop character. Update statistics???? -- REW */
+ mutex_unlock(&port->port_write_mutex);
+ return 0;
+ }
+
+ port->xmit_buf[port->xmit_head++] = ch;
+ port->xmit_head &= SERIAL_XMIT_SIZE - 1;
+ port->xmit_cnt++; /* Characters in buffer */
+
+ mutex_unlock(&port->port_write_mutex);
+ func_exit ();
+ return 1;
+}
+
+
+/*
+> Problems to take into account are:
+> -1- Interrupts that empty part of the buffer.
+> -2- page faults on the access to userspace.
+> -3- Other processes that are also trying to do a "write".
+*/
+
+int gs_write(struct tty_struct * tty,
+ const unsigned char *buf, int count)
+{
+ struct gs_port *port;
+ int c, total = 0;
+ int t;
+
+ func_enter ();
+
+ port = tty->driver_data;
+
+ if (!port) return 0;
+
+ if (! (port->port.flags & ASYNC_INITIALIZED))
+ return 0;
+
+ /* get exclusive "write" access to this port (problem 3) */
+ /* This is not a spinlock because we can have a disk access (page
+ fault) in copy_from_user */
+ mutex_lock(& port->port_write_mutex);
+
+ while (1) {
+
+ c = count;
+
+ /* This is safe because we "OWN" the "head". Noone else can
+ change the "head": we own the port_write_mutex. */
+ /* Don't overrun the end of the buffer */
+ t = SERIAL_XMIT_SIZE - port->xmit_head;
+ if (t < c) c = t;
+
+ /* This is safe because the xmit_cnt can only decrease. This
+ would increase "t", so we might copy too little chars. */
+ /* Don't copy past the "head" of the buffer */
+ t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt;
+ if (t < c) c = t;
+
+ /* Can't copy more? break out! */
+ if (c <= 0) break;
+
+ memcpy (port->xmit_buf + port->xmit_head, buf, c);
+
+ port -> xmit_cnt += c;
+ port -> xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE -1);
+ buf += c;
+ count -= c;
+ total += c;
+ }
+ mutex_unlock(& port->port_write_mutex);
+
+ gs_dprintk (GS_DEBUG_WRITE, "write: interrupts are %s\n",
+ (port->port.flags & GS_TX_INTEN)?"enabled": "disabled");
+
+ if (port->xmit_cnt &&
+ !tty->stopped &&
+ !tty->hw_stopped &&
+ !(port->port.flags & GS_TX_INTEN)) {
+ port->port.flags |= GS_TX_INTEN;
+ port->rd->enable_tx_interrupts (port);
+ }
+ func_exit ();
+ return total;
+}
+
+
+
+int gs_write_room(struct tty_struct * tty)
+{
+ struct gs_port *port = tty->driver_data;
+ int ret;
+
+ func_enter ();
+ ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
+ if (ret < 0)
+ ret = 0;
+ func_exit ();
+ return ret;
+}
+
+
+int gs_chars_in_buffer(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ func_enter ();
+
+ func_exit ();
+ return port->xmit_cnt;
+}
+
+
+static int gs_real_chars_in_buffer(struct tty_struct *tty)
+{
+ struct gs_port *port;
+ func_enter ();
+
+ port = tty->driver_data;
+
+ if (!port->rd) return 0;
+ if (!port->rd->chars_in_buffer) return 0;
+
+ func_exit ();
+ return port->xmit_cnt + port->rd->chars_in_buffer (port);
+}
+
+
+static int gs_wait_tx_flushed (void * ptr, unsigned long timeout)
+{
+ struct gs_port *port = ptr;
+ unsigned long end_jiffies;
+ int jiffies_to_transmit, charsleft = 0, rv = 0;
+ int rcib;
+
+ func_enter();
+
+ gs_dprintk (GS_DEBUG_FLUSH, "port=%p.\n", port);
+ if (port) {
+ gs_dprintk (GS_DEBUG_FLUSH, "xmit_cnt=%x, xmit_buf=%p, tty=%p.\n",
+ port->xmit_cnt, port->xmit_buf, port->port.tty);
+ }
+
+ if (!port || port->xmit_cnt < 0 || !port->xmit_buf) {
+ gs_dprintk (GS_DEBUG_FLUSH, "ERROR: !port, !port->xmit_buf or prot->xmit_cnt < 0.\n");
+ func_exit();
+ return -EINVAL; /* This is an error which we don't know how to handle. */
+ }
+
+ rcib = gs_real_chars_in_buffer(port->port.tty);
+
+ if(rcib <= 0) {
+ gs_dprintk (GS_DEBUG_FLUSH, "nothing to wait for.\n");
+ func_exit();
+ return rv;
+ }
+ /* stop trying: now + twice the time it would normally take + seconds */
+ if (timeout == 0) timeout = MAX_SCHEDULE_TIMEOUT;
+ end_jiffies = jiffies;
+ if (timeout != MAX_SCHEDULE_TIMEOUT)
+ end_jiffies += port->baud?(2 * rcib * 10 * HZ / port->baud):0;
+ end_jiffies += timeout;
+
+ gs_dprintk (GS_DEBUG_FLUSH, "now=%lx, end=%lx (%ld).\n",
+ jiffies, end_jiffies, end_jiffies-jiffies);
+
+ /* the expression is actually jiffies < end_jiffies, but that won't
+ work around the wraparound. Tricky eh? */
+ while ((charsleft = gs_real_chars_in_buffer (port->port.tty)) &&
+ time_after (end_jiffies, jiffies)) {
+ /* Units check:
+ chars * (bits/char) * (jiffies /sec) / (bits/sec) = jiffies!
+ check! */
+
+ charsleft += 16; /* Allow 16 chars more to be transmitted ... */
+ jiffies_to_transmit = port->baud?(1 + charsleft * 10 * HZ / port->baud):0;
+ /* ^^^ Round up.... */
+ if (jiffies_to_transmit <= 0) jiffies_to_transmit = 1;
+
+ gs_dprintk (GS_DEBUG_FLUSH, "Expect to finish in %d jiffies "
+ "(%d chars).\n", jiffies_to_transmit, charsleft);
+
+ msleep_interruptible(jiffies_to_msecs(jiffies_to_transmit));
+ if (signal_pending (current)) {
+ gs_dprintk (GS_DEBUG_FLUSH, "Signal pending. Bombing out: ");
+ rv = -EINTR;
+ break;
+ }
+ }
+
+ gs_dprintk (GS_DEBUG_FLUSH, "charsleft = %d.\n", charsleft);
+ set_current_state (TASK_RUNNING);
+
+ func_exit();
+ return rv;
+}
+
+
+
+void gs_flush_buffer(struct tty_struct *tty)
+{
+ struct gs_port *port;
+ unsigned long flags;
+
+ func_enter ();
+
+ port = tty->driver_data;
+
+ if (!port) return;
+
+ /* XXX Would the write semaphore do? */
+ spin_lock_irqsave (&port->driver_lock, flags);
+ port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+ spin_unlock_irqrestore (&port->driver_lock, flags);
+
+ tty_wakeup(tty);
+ func_exit ();
+}
+
+
+void gs_flush_chars(struct tty_struct * tty)
+{
+ struct gs_port *port;
+
+ func_enter ();
+
+ port = tty->driver_data;
+
+ if (!port) return;
+
+ if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+ !port->xmit_buf) {
+ func_exit ();
+ return;
+ }
+
+ /* Beats me -- REW */
+ port->port.flags |= GS_TX_INTEN;
+ port->rd->enable_tx_interrupts (port);
+ func_exit ();
+}
+
+
+void gs_stop(struct tty_struct * tty)
+{
+ struct gs_port *port;
+
+ func_enter ();
+
+ port = tty->driver_data;
+
+ if (!port) return;
+
+ if (port->xmit_cnt &&
+ port->xmit_buf &&
+ (port->port.flags & GS_TX_INTEN) ) {
+ port->port.flags &= ~GS_TX_INTEN;
+ port->rd->disable_tx_interrupts (port);
+ }
+ func_exit ();
+}
+
+
+void gs_start(struct tty_struct * tty)
+{
+ struct gs_port *port;
+
+ port = tty->driver_data;
+
+ if (!port) return;
+
+ if (port->xmit_cnt &&
+ port->xmit_buf &&
+ !(port->port.flags & GS_TX_INTEN) ) {
+ port->port.flags |= GS_TX_INTEN;
+ port->rd->enable_tx_interrupts (port);
+ }
+ func_exit ();
+}
+
+
+static void gs_shutdown_port (struct gs_port *port)
+{
+ unsigned long flags;
+
+ func_enter();
+
+ if (!port) return;
+
+ if (!(port->port.flags & ASYNC_INITIALIZED))
+ return;
+
+ spin_lock_irqsave(&port->driver_lock, flags);
+
+ if (port->xmit_buf) {
+ free_page((unsigned long) port->xmit_buf);
+ port->xmit_buf = NULL;
+ }
+
+ if (port->port.tty)
+ set_bit(TTY_IO_ERROR, &port->port.tty->flags);
+
+ port->rd->shutdown_port (port);
+
+ port->port.flags &= ~ASYNC_INITIALIZED;
+ spin_unlock_irqrestore(&port->driver_lock, flags);
+
+ func_exit();
+}
+
+
+void gs_hangup(struct tty_struct *tty)
+{
+ struct gs_port *port;
+ unsigned long flags;
+
+ func_enter ();
+
+ port = tty->driver_data;
+ tty = port->port.tty;
+ if (!tty)
+ return;
+
+ gs_shutdown_port (port);
+ spin_lock_irqsave(&port->port.lock, flags);
+ port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|GS_ACTIVE);
+ port->port.tty = NULL;
+ port->port.count = 0;
+ spin_unlock_irqrestore(&port->port.lock, flags);
+
+ wake_up_interruptible(&port->port.open_wait);
+ func_exit ();
+}
+
+
+int gs_block_til_ready(void *port_, struct file * filp)
+{
+ struct gs_port *gp = port_;
+ struct tty_port *port = &gp->port;
+ DECLARE_WAITQUEUE(wait, current);
+ int retval;
+ int do_clocal = 0;
+ int CD;
+ struct tty_struct *tty;
+ unsigned long flags;
+
+ func_enter ();
+
+ if (!port) return 0;
+
+ tty = port->tty;
+
+ gs_dprintk (GS_DEBUG_BTR, "Entering gs_block_till_ready.\n");
+ /*
+ * 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->flags & ASYNC_CLOSING) {
+ interruptible_sleep_on(&port->close_wait);
+ if (port->flags & ASYNC_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+ }
+
+ gs_dprintk (GS_DEBUG_BTR, "after hung up\n");
+
+ /*
+ * 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->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ gs_dprintk (GS_DEBUG_BTR, "after nonblock\n");
+
+ 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, port->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->open_wait, &wait);
+
+ gs_dprintk (GS_DEBUG_BTR, "after add waitq.\n");
+ spin_lock_irqsave(&port->lock, flags);
+ if (!tty_hung_up_p(filp)) {
+ port->count--;
+ }
+ port->blocked_open++;
+ spin_unlock_irqrestore(&port->lock, flags);
+ while (1) {
+ CD = tty_port_carrier_raised(port);
+ gs_dprintk (GS_DEBUG_BTR, "CD is now %d.\n", CD);
+ set_current_state (TASK_INTERRUPTIBLE);
+ if (tty_hung_up_p(filp) ||
+ !(port->flags & ASYNC_INITIALIZED)) {
+ if (port->flags & ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+ break;
+ }
+ if (!(port->flags & ASYNC_CLOSING) &&
+ (do_clocal || CD))
+ break;
+ gs_dprintk (GS_DEBUG_BTR, "signal_pending is now: %d (%lx)\n",
+ (int)signal_pending (current), *(long*)(&current->blocked));
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+ gs_dprintk (GS_DEBUG_BTR, "Got out of the loop. (%d)\n",
+ port->blocked_open);
+ set_current_state (TASK_RUNNING);
+ remove_wait_queue(&port->open_wait, &wait);
+
+ spin_lock_irqsave(&port->lock, flags);
+ if (!tty_hung_up_p(filp)) {
+ port->count++;
+ }
+ port->blocked_open--;
+ if (retval == 0)
+ port->flags |= ASYNC_NORMAL_ACTIVE;
+ spin_unlock_irqrestore(&port->lock, flags);
+ func_exit ();
+ return retval;
+}
+
+
+void gs_close(struct tty_struct * tty, struct file * filp)
+{
+ unsigned long flags;
+ struct gs_port *port;
+
+ func_enter ();
+
+ port = tty->driver_data;
+
+ if (!port) return;
+
+ if (!port->port.tty) {
+ /* This seems to happen when this is called from vhangup. */
+ gs_dprintk (GS_DEBUG_CLOSE, "gs: Odd: port->port.tty is NULL\n");
+ port->port.tty = tty;
+ }
+
+ spin_lock_irqsave(&port->port.lock, flags);
+
+ if (tty_hung_up_p(filp)) {
+ spin_unlock_irqrestore(&port->port.lock, flags);
+ if (port->rd->hungup)
+ port->rd->hungup (port);
+ func_exit ();
+ return;
+ }
+
+ if ((tty->count == 1) && (port->port.count != 1)) {
+ printk(KERN_ERR "gs: gs_close port %p: bad port count;"
+ " tty->count is 1, port count is %d\n", port, port->port.count);
+ port->port.count = 1;
+ }
+ if (--port->port.count < 0) {
+ printk(KERN_ERR "gs: gs_close port %p: bad port count: %d\n", port, port->port.count);
+ port->port.count = 0;
+ }
+
+ if (port->port.count) {
+ gs_dprintk(GS_DEBUG_CLOSE, "gs_close port %p: count: %d\n", port, port->port.count);
+ spin_unlock_irqrestore(&port->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;
+ /* if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, 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.
+ */
+
+ spin_lock(&port->driver_lock);
+ port->rd->disable_rx_interrupts (port);
+ spin_unlock(&port->driver_lock);
+ spin_unlock_irqrestore(&port->port.lock, flags);
+
+ /* close has no way of returning "EINTR", so discard return value */
+ if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ gs_wait_tx_flushed (port, port->closing_wait);
+
+ port->port.flags &= ~GS_ACTIVE;
+
+ gs_flush_buffer(tty);
+
+ tty_ldisc_flush(tty);
+ tty->closing = 0;
+
+ spin_lock_irqsave(&port->driver_lock, flags);
+ port->event = 0;
+ port->rd->close (port);
+ port->rd->shutdown_port (port);
+ spin_unlock_irqrestore(&port->driver_lock, flags);
+
+ spin_lock_irqsave(&port->port.lock, flags);
+ port->port.tty = NULL;
+
+ if (port->port.blocked_open) {
+ if (port->close_delay) {
+ spin_unlock_irqrestore(&port->port.lock, flags);
+ msleep_interruptible(jiffies_to_msecs(port->close_delay));
+ spin_lock_irqsave(&port->port.lock, flags);
+ }
+ wake_up_interruptible(&port->port.open_wait);
+ }
+ port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING | ASYNC_INITIALIZED);
+ spin_unlock_irqrestore(&port->port.lock, flags);
+ wake_up_interruptible(&port->port.close_wait);
+
+ func_exit ();
+}
+
+
+void gs_set_termios (struct tty_struct * tty,
+ struct ktermios * old_termios)
+{
+ struct gs_port *port;
+ int baudrate, tmp, rv;
+ struct ktermios *tiosp;
+
+ func_enter();
+
+ port = tty->driver_data;
+
+ if (!port) return;
+ if (!port->port.tty) {
+ /* This seems to happen when this is called after gs_close. */
+ gs_dprintk (GS_DEBUG_TERMIOS, "gs: Odd: port->port.tty is NULL\n");
+ port->port.tty = tty;
+ }
+
+
+ tiosp = tty->termios;
+
+ if (gs_debug & GS_DEBUG_TERMIOS) {
+ gs_dprintk (GS_DEBUG_TERMIOS, "termios structure (%p):\n", tiosp);
+ }
+
+ if(old_termios && (gs_debug & GS_DEBUG_TERMIOS)) {
+ if(tiosp->c_iflag != old_termios->c_iflag) printk("c_iflag changed\n");
+ if(tiosp->c_oflag != old_termios->c_oflag) printk("c_oflag changed\n");
+ if(tiosp->c_cflag != old_termios->c_cflag) printk("c_cflag changed\n");
+ if(tiosp->c_lflag != old_termios->c_lflag) printk("c_lflag changed\n");
+ if(tiosp->c_line != old_termios->c_line) printk("c_line changed\n");
+ if(!memcmp(tiosp->c_cc, old_termios->c_cc, NCC)) printk("c_cc changed\n");
+ }
+
+ baudrate = tty_get_baud_rate(tty);
+
+ if ((tiosp->c_cflag & CBAUD) == B38400) {
+ if ( (port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ baudrate = 57600;
+ else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ baudrate = 115200;
+ else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ baudrate = 230400;
+ else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ baudrate = 460800;
+ else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+ baudrate = (port->baud_base / port->custom_divisor);
+ }
+
+ /* I recommend using THIS instead of the mess in termios (and
+ duplicating the above code). Next we should create a clean
+ interface towards this variable. If your card supports arbitrary
+ baud rates, (e.g. CD1400 or 16550 based cards) then everything
+ will be very easy..... */
+ port->baud = baudrate;
+
+ /* Two timer ticks seems enough to wakeup something like SLIP driver */
+ /* Baudrate/10 is cps. Divide by HZ to get chars per tick. */
+ tmp = (baudrate / 10 / HZ) * 2;
+
+ if (tmp < 0) tmp = 0;
+ if (tmp >= SERIAL_XMIT_SIZE) tmp = SERIAL_XMIT_SIZE-1;
+
+ port->wakeup_chars = tmp;
+
+ /* We should really wait for the characters to be all sent before
+ changing the settings. -- CAL */
+ rv = gs_wait_tx_flushed (port, MAX_SCHEDULE_TIMEOUT);
+ if (rv < 0) return /* rv */;
+
+ rv = port->rd->set_real_termios(port);
+ if (rv < 0) return /* rv */;
+
+ if ((!old_termios ||
+ (old_termios->c_cflag & CRTSCTS)) &&
+ !( tiosp->c_cflag & CRTSCTS)) {
+ tty->stopped = 0;
+ gs_start(tty);
+ }
+
+#ifdef tytso_patch_94Nov25_1726
+ /* This "makes sense", Why is it commented out? */
+
+ if (!(old_termios->c_cflag & CLOCAL) &&
+ (tty->termios->c_cflag & CLOCAL))
+ wake_up_interruptible(&port->gs.open_wait);
+#endif
+
+ func_exit();
+ return /* 0 */;
+}
+
+
+
+/* Must be called with interrupts enabled */
+int gs_init_port(struct gs_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);
+ spin_lock_irqsave (&port->driver_lock, flags);
+ if (port->xmit_buf)
+ free_page (tmp);
+ else
+ port->xmit_buf = (unsigned char *) tmp;
+ spin_unlock_irqrestore(&port->driver_lock, flags);
+ if (!port->xmit_buf) {
+ func_exit ();
+ return -ENOMEM;
+ }
+ }
+
+ spin_lock_irqsave (&port->driver_lock, flags);
+ if (port->port.tty)
+ clear_bit(TTY_IO_ERROR, &port->port.tty->flags);
+ mutex_init(&port->port_write_mutex);
+ port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+ spin_unlock_irqrestore(&port->driver_lock, flags);
+ gs_set_termios(port->port.tty, NULL);
+ spin_lock_irqsave (&port->driver_lock, flags);
+ port->port.flags |= ASYNC_INITIALIZED;
+ port->port.flags &= ~GS_TX_INTEN;
+
+ spin_unlock_irqrestore(&port->driver_lock, flags);
+ func_exit ();
+ return 0;
+}
+
+
+int gs_setserial(struct gs_port *port, struct serial_struct __user *sp)
+{
+ struct serial_struct sio;
+
+ if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
+ return(-EFAULT);
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ if ((sio.baud_base != port->baud_base) ||
+ (sio.close_delay != port->close_delay) ||
+ ((sio.flags & ~ASYNC_USR_MASK) !=
+ (port->port.flags & ~ASYNC_USR_MASK)))
+ return(-EPERM);
+ }
+
+ port->port.flags = (port->port.flags & ~ASYNC_USR_MASK) |
+ (sio.flags & ASYNC_USR_MASK);
+
+ port->baud_base = sio.baud_base;
+ port->close_delay = sio.close_delay;
+ port->closing_wait = sio.closing_wait;
+ port->custom_divisor = sio.custom_divisor;
+
+ gs_set_termios (port->port.tty, NULL);
+
+ return 0;
+}
+
+
+/*****************************************************************************/
+
+/*
+ * Generate the serial struct info.
+ */
+
+int gs_getserial(struct gs_port *port, struct serial_struct __user *sp)
+{
+ struct serial_struct sio;
+
+ memset(&sio, 0, sizeof(struct serial_struct));
+ sio.flags = port->port.flags;
+ sio.baud_base = port->baud_base;
+ sio.close_delay = port->close_delay;
+ sio.closing_wait = port->closing_wait;
+ sio.custom_divisor = port->custom_divisor;
+ sio.hub6 = 0;
+
+ /* If you want you can override these. */
+ sio.type = PORT_UNKNOWN;
+ sio.xmit_fifo_size = -1;
+ sio.line = -1;
+ sio.port = -1;
+ sio.irq = -1;
+
+ if (port->rd->getserial)
+ port->rd->getserial (port, &sio);
+
+ if (copy_to_user(sp, &sio, sizeof(struct serial_struct)))
+ return -EFAULT;
+ return 0;
+
+}
+
+
+void gs_got_break(struct gs_port *port)
+{
+ func_enter ();
+
+ tty_insert_flip_char(port->port.tty, 0, TTY_BREAK);
+ tty_schedule_flip(port->port.tty);
+ if (port->port.flags & ASYNC_SAK) {
+ do_SAK (port->port.tty);
+ }
+
+ func_exit ();
+}
+
+
+EXPORT_SYMBOL(gs_put_char);
+EXPORT_SYMBOL(gs_write);
+EXPORT_SYMBOL(gs_write_room);
+EXPORT_SYMBOL(gs_chars_in_buffer);
+EXPORT_SYMBOL(gs_flush_buffer);
+EXPORT_SYMBOL(gs_flush_chars);
+EXPORT_SYMBOL(gs_stop);
+EXPORT_SYMBOL(gs_start);
+EXPORT_SYMBOL(gs_hangup);
+EXPORT_SYMBOL(gs_block_til_ready);
+EXPORT_SYMBOL(gs_close);
+EXPORT_SYMBOL(gs_set_termios);
+EXPORT_SYMBOL(gs_init_port);
+EXPORT_SYMBOL(gs_setserial);
+EXPORT_SYMBOL(gs_getserial);
+EXPORT_SYMBOL(gs_got_break);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/generic_serial/rio/Makefile b/drivers/staging/generic_serial/rio/Makefile
new file mode 100644
index 000000000000..1661875883fb
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the linux rio-subsystem.
+#
+# (C) R.E.Wolff@BitWizard.nl
+#
+# This file is GPL. See other files for the full Blurb. I'm lazy today.
+#
+
+obj-$(CONFIG_RIO) += rio.o
+
+rio-y := rio_linux.o rioinit.o rioboot.o riocmd.o rioctrl.o riointr.o \
+ rioparam.o rioroute.o riotable.o riotty.o
diff --git a/drivers/staging/generic_serial/rio/board.h b/drivers/staging/generic_serial/rio/board.h
new file mode 100644
index 000000000000..bdea633a9076
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/board.h
@@ -0,0 +1,132 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : board.h
+** SID : 1.2
+** Last Modified : 11/6/98 11:34:07
+** Retrieved : 11/6/98 11:34:20
+**
+** ident @(#)board.h 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rio_board_h__
+#define __rio_board_h__
+
+/*
+** board.h contains the definitions for the *hardware* of the host cards.
+** It describes the memory overlay for the dual port RAM area.
+*/
+
+#define DP_SRAM1_SIZE 0x7C00
+#define DP_SRAM2_SIZE 0x0200
+#define DP_SRAM3_SIZE 0x7000
+#define DP_SCRATCH_SIZE 0x1000
+#define DP_PARMMAP_ADDR 0x01FE /* offset into SRAM2 */
+#define DP_STARTUP_ADDR 0x01F8 /* offset into SRAM2 */
+
+/*
+** The shape of the Host Control area, at offset 0x7C00, Write Only
+*/
+struct s_Ctrl {
+ u8 DpCtl; /* 7C00 */
+ u8 Dp_Unused2_[127];
+ u8 DpIntSet; /* 7C80 */
+ u8 Dp_Unused3_[127];
+ u8 DpTpuReset; /* 7D00 */
+ u8 Dp_Unused4_[127];
+ u8 DpIntReset; /* 7D80 */
+ u8 Dp_Unused5_[127];
+};
+
+/*
+** The PROM data area on the host (0x7C00), Read Only
+*/
+struct s_Prom {
+ u16 DpSlxCode[2];
+ u16 DpRev;
+ u16 Dp_Unused6_;
+ u16 DpUniq[4];
+ u16 DpJahre;
+ u16 DpWoche;
+ u16 DpHwFeature[5];
+ u16 DpOemId;
+ u16 DpSiggy[16];
+};
+
+/*
+** Union of the Ctrl and Prom areas
+*/
+union u_CtrlProm { /* This is the control/PROM area (0x7C00) */
+ struct s_Ctrl DpCtrl;
+ struct s_Prom DpProm;
+};
+
+/*
+** The top end of memory!
+*/
+struct s_ParmMapS { /* Area containing Parm Map Pointer */
+ u8 Dp_Unused8_[DP_PARMMAP_ADDR];
+ u16 DpParmMapAd;
+};
+
+struct s_StartUpS {
+ u8 Dp_Unused9_[DP_STARTUP_ADDR];
+ u8 Dp_LongJump[0x4];
+ u8 Dp_Unused10_[2];
+ u8 Dp_ShortJump[0x2];
+};
+
+union u_Sram2ParmMap { /* This is the top of memory (0x7E00-0x7FFF) */
+ u8 DpSramMem[DP_SRAM2_SIZE];
+ struct s_ParmMapS DpParmMapS;
+ struct s_StartUpS DpStartUpS;
+};
+
+/*
+** This is the DP RAM overlay.
+*/
+struct DpRam {
+ u8 DpSram1[DP_SRAM1_SIZE]; /* 0000 - 7BFF */
+ union u_CtrlProm DpCtrlProm; /* 7C00 - 7DFF */
+ union u_Sram2ParmMap DpSram2ParmMap; /* 7E00 - 7FFF */
+ u8 DpScratch[DP_SCRATCH_SIZE]; /* 8000 - 8FFF */
+ u8 DpSram3[DP_SRAM3_SIZE]; /* 9000 - FFFF */
+};
+
+#define DpControl DpCtrlProm.DpCtrl.DpCtl
+#define DpSetInt DpCtrlProm.DpCtrl.DpIntSet
+#define DpResetTpu DpCtrlProm.DpCtrl.DpTpuReset
+#define DpResetInt DpCtrlProm.DpCtrl.DpIntReset
+
+#define DpSlx DpCtrlProm.DpProm.DpSlxCode
+#define DpRevision DpCtrlProm.DpProm.DpRev
+#define DpUnique DpCtrlProm.DpProm.DpUniq
+#define DpYear DpCtrlProm.DpProm.DpJahre
+#define DpWeek DpCtrlProm.DpProm.DpWoche
+#define DpSignature DpCtrlProm.DpProm.DpSiggy
+
+#define DpParmMapR DpSram2ParmMap.DpParmMapS.DpParmMapAd
+#define DpSram2 DpSram2ParmMap.DpSramMem
+
+#endif
diff --git a/drivers/staging/generic_serial/rio/cirrus.h b/drivers/staging/generic_serial/rio/cirrus.h
new file mode 100644
index 000000000000..5ab51679caa2
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/cirrus.h
@@ -0,0 +1,210 @@
+/****************************************************************************
+ ******* *******
+ ******* CIRRUS.H *******
+ ******* *******
+ ****************************************************************************
+
+ Author : Jeremy Rolls
+ Date : 3 Aug 1990
+
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+
+ Version : 0.01
+
+
+ Mods
+ ----------------------------------------------------------------------------
+ Date By Description
+ ----------------------------------------------------------------------------
+
+ ***************************************************************************/
+
+#ifndef _cirrus_h
+#define _cirrus_h 1
+
+/* Bit fields for particular registers shared with driver */
+
+/* COR1 - driver and RTA */
+#define RIOC_COR1_ODD 0x80 /* Odd parity */
+#define RIOC_COR1_EVEN 0x00 /* Even parity */
+#define RIOC_COR1_NOP 0x00 /* No parity */
+#define RIOC_COR1_FORCE 0x20 /* Force parity */
+#define RIOC_COR1_NORMAL 0x40 /* With parity */
+#define RIOC_COR1_1STOP 0x00 /* 1 stop bit */
+#define RIOC_COR1_15STOP 0x04 /* 1.5 stop bits */
+#define RIOC_COR1_2STOP 0x08 /* 2 stop bits */
+#define RIOC_COR1_5BITS 0x00 /* 5 data bits */
+#define RIOC_COR1_6BITS 0x01 /* 6 data bits */
+#define RIOC_COR1_7BITS 0x02 /* 7 data bits */
+#define RIOC_COR1_8BITS 0x03 /* 8 data bits */
+
+#define RIOC_COR1_HOST 0xef /* Safe host bits */
+
+/* RTA only */
+#define RIOC_COR1_CINPCK 0x00 /* Check parity of received characters */
+#define RIOC_COR1_CNINPCK 0x10 /* Don't check parity */
+
+/* COR2 bits for both RTA and driver use */
+#define RIOC_COR2_IXANY 0x80 /* IXANY - any character is XON */
+#define RIOC_COR2_IXON 0x40 /* IXON - enable tx soft flowcontrol */
+#define RIOC_COR2_RTSFLOW 0x02 /* Enable tx hardware flow control */
+
+/* Additional driver bits */
+#define RIOC_COR2_HUPCL 0x20 /* Hang up on close */
+#define RIOC_COR2_CTSFLOW 0x04 /* Enable rx hardware flow control */
+#define RIOC_COR2_IXOFF 0x01 /* Enable rx software flow control */
+#define RIOC_COR2_DTRFLOW 0x08 /* Enable tx hardware flow control */
+
+/* RTA use only */
+#define RIOC_COR2_ETC 0x20 /* Embedded transmit options */
+#define RIOC_COR2_LOCAL 0x10 /* Local loopback mode */
+#define RIOC_COR2_REMOTE 0x08 /* Remote loopback mode */
+#define RIOC_COR2_HOST 0xc2 /* Safe host bits */
+
+/* COR3 - RTA use only */
+#define RIOC_COR3_SCDRNG 0x80 /* Enable special char detect for range */
+#define RIOC_COR3_SCD34 0x40 /* Special character detect for SCHR's 3 + 4 */
+#define RIOC_COR3_FCT 0x20 /* Flow control transparency */
+#define RIOC_COR3_SCD12 0x10 /* Special character detect for SCHR's 1 + 2 */
+#define RIOC_COR3_FIFO12 0x0c /* 12 chars for receive FIFO threshold */
+#define RIOC_COR3_FIFO10 0x0a /* 10 chars for receive FIFO threshold */
+#define RIOC_COR3_FIFO8 0x08 /* 8 chars for receive FIFO threshold */
+#define RIOC_COR3_FIFO6 0x06 /* 6 chars for receive FIFO threshold */
+
+#define RIOC_COR3_THRESHOLD RIOC_COR3_FIFO8 /* MUST BE LESS THAN MCOR_THRESHOLD */
+
+#define RIOC_COR3_DEFAULT (RIOC_COR3_FCT | RIOC_COR3_THRESHOLD)
+ /* Default bits for COR3 */
+
+/* COR4 driver and RTA use */
+#define RIOC_COR4_IGNCR 0x80 /* Throw away CR's on input */
+#define RIOC_COR4_ICRNL 0x40 /* Map CR -> NL on input */
+#define RIOC_COR4_INLCR 0x20 /* Map NL -> CR on input */
+#define RIOC_COR4_IGNBRK 0x10 /* Ignore Break */
+#define RIOC_COR4_NBRKINT 0x08 /* No interrupt on break (-BRKINT) */
+#define RIOC_COR4_RAISEMOD 0x01 /* Raise modem output lines on non-zero baud */
+
+
+/* COR4 driver only */
+#define RIOC_COR4_IGNPAR 0x04 /* IGNPAR (ignore characters with errors) */
+#define RIOC_COR4_PARMRK 0x02 /* PARMRK */
+
+#define RIOC_COR4_HOST 0xf8 /* Safe host bits */
+
+/* COR4 RTA only */
+#define RIOC_COR4_CIGNPAR 0x02 /* Thrown away bad characters */
+#define RIOC_COR4_CPARMRK 0x04 /* PARMRK characters */
+#define RIOC_COR4_CNPARMRK 0x03 /* Don't PARMRK */
+
+/* COR5 driver and RTA use */
+#define RIOC_COR5_ISTRIP 0x80 /* Strip input chars to 7 bits */
+#define RIOC_COR5_LNE 0x40 /* Enable LNEXT processing */
+#define RIOC_COR5_CMOE 0x20 /* Match good and errored characters */
+#define RIOC_COR5_ONLCR 0x02 /* NL -> CR NL on output */
+#define RIOC_COR5_OCRNL 0x01 /* CR -> NL on output */
+
+/*
+** Spare bits - these are not used in the CIRRUS registers, so we use
+** them to set various other features.
+*/
+/*
+** tstop and tbusy indication
+*/
+#define RIOC_COR5_TSTATE_ON 0x08 /* Turn on monitoring of tbusy and tstop */
+#define RIOC_COR5_TSTATE_OFF 0x04 /* Turn off monitoring of tbusy and tstop */
+/*
+** TAB3
+*/
+#define RIOC_COR5_TAB3 0x10 /* TAB3 mode */
+
+#define RIOC_COR5_HOST 0xc3 /* Safe host bits */
+
+/* CCSR */
+#define RIOC_CCSR_TXFLOFF 0x04 /* Tx is xoffed */
+
+/* MSVR1 */
+/* NB. DTR / CD swapped from Cirrus spec as the pins are also reversed on the
+ RTA. This is because otherwise DCD would get lost on the 1 parallel / 3
+ serial option.
+*/
+#define RIOC_MSVR1_CD 0x80 /* CD (DSR on Cirrus) */
+#define RIOC_MSVR1_RTS 0x40 /* RTS (CTS on Cirrus) */
+#define RIOC_MSVR1_RI 0x20 /* RI */
+#define RIOC_MSVR1_DTR 0x10 /* DTR (CD on Cirrus) */
+#define RIOC_MSVR1_CTS 0x01 /* CTS output pin (RTS on Cirrus) */
+/* Next two used to indicate state of tbusy and tstop to driver */
+#define RIOC_MSVR1_TSTOP 0x08 /* Set if port flow controlled */
+#define RIOC_MSVR1_TEMPTY 0x04 /* Set if port tx buffer empty */
+
+#define RIOC_MSVR1_HOST 0xf3 /* The bits the host wants */
+
+/* Defines for the subscripts of a CONFIG packet */
+#define RIOC_CONFIG_COR1 1 /* Option register 1 */
+#define RIOC_CONFIG_COR2 2 /* Option register 2 */
+#define RIOC_CONFIG_COR4 3 /* Option register 4 */
+#define RIOC_CONFIG_COR5 4 /* Option register 5 */
+#define RIOC_CONFIG_TXXON 5 /* Tx XON character */
+#define RIOC_CONFIG_TXXOFF 6 /* Tx XOFF character */
+#define RIOC_CONFIG_RXXON 7 /* Rx XON character */
+#define RIOC_CONFIG_RXXOFF 8 /* Rx XOFF character */
+#define RIOC_CONFIG_LNEXT 9 /* LNEXT character */
+#define RIOC_CONFIG_TXBAUD 10 /* Tx baud rate */
+#define RIOC_CONFIG_RXBAUD 11 /* Rx baud rate */
+
+#define RIOC_PRE_EMPTIVE 0x80 /* Pre-emptive bit in command field */
+
+/* Packet types going from Host to remote - with the exception of OPEN, MOPEN,
+ CONFIG, SBREAK and MEMDUMP the remaining bytes of the data array will not
+ be used
+*/
+#define RIOC_OPEN 0x00 /* Open a port */
+#define RIOC_CONFIG 0x01 /* Configure a port */
+#define RIOC_MOPEN 0x02 /* Modem open (block for DCD) */
+#define RIOC_CLOSE 0x03 /* Close a port */
+#define RIOC_WFLUSH (0x04 | RIOC_PRE_EMPTIVE) /* Write flush */
+#define RIOC_RFLUSH (0x05 | RIOC_PRE_EMPTIVE) /* Read flush */
+#define RIOC_RESUME (0x06 | RIOC_PRE_EMPTIVE) /* Resume if xoffed */
+#define RIOC_SBREAK 0x07 /* Start break */
+#define RIOC_EBREAK 0x08 /* End break */
+#define RIOC_SUSPEND (0x09 | RIOC_PRE_EMPTIVE) /* Susp op (behave as tho xoffed) */
+#define RIOC_FCLOSE (0x0a | RIOC_PRE_EMPTIVE) /* Force close */
+#define RIOC_XPRINT 0x0b /* Xprint packet */
+#define RIOC_MBIS (0x0c | RIOC_PRE_EMPTIVE) /* Set modem lines */
+#define RIOC_MBIC (0x0d | RIOC_PRE_EMPTIVE) /* Clear modem lines */
+#define RIOC_MSET (0x0e | RIOC_PRE_EMPTIVE) /* Set modem lines */
+#define RIOC_PCLOSE 0x0f /* Pseudo close - Leaves rx/tx enabled */
+#define RIOC_MGET (0x10 | RIOC_PRE_EMPTIVE) /* Force update of modem status */
+#define RIOC_MEMDUMP (0x11 | RIOC_PRE_EMPTIVE) /* Send back mem from addr supplied */
+#define RIOC_READ_REGISTER (0x12 | RIOC_PRE_EMPTIVE) /* Read CD1400 register (debug) */
+
+/* "Command" packets going from remote to host COMPLETE and MODEM_STATUS
+ use data[4] / data[3] to indicate current state and modem status respectively
+*/
+
+#define RIOC_COMPLETE (0x20 | RIOC_PRE_EMPTIVE)
+ /* Command complete */
+#define RIOC_BREAK_RECEIVED (0x21 | RIOC_PRE_EMPTIVE)
+ /* Break received */
+#define RIOC_MODEM_STATUS (0x22 | RIOC_PRE_EMPTIVE)
+ /* Change in modem status */
+
+/* "Command" packet that could go either way - handshake wake-up */
+#define RIOC_HANDSHAKE (0x23 | RIOC_PRE_EMPTIVE)
+ /* Wake-up to HOST / RTA */
+
+#endif
diff --git a/drivers/staging/generic_serial/rio/cmdblk.h b/drivers/staging/generic_serial/rio/cmdblk.h
new file mode 100644
index 000000000000..9ed4f861675a
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/cmdblk.h
@@ -0,0 +1,53 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : cmdblk.h
+** SID : 1.2
+** Last Modified : 11/6/98 11:34:09
+** Retrieved : 11/6/98 11:34:20
+**
+** ident @(#)cmdblk.h 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rio_cmdblk_h__
+#define __rio_cmdblk_h__
+
+/*
+** the structure of a command block, used to queue commands destined for
+** a rup.
+*/
+
+struct CmdBlk {
+ struct CmdBlk *NextP; /* Pointer to next command block */
+ struct PKT Packet; /* A packet, to copy to the rup */
+ /* The func to call to check if OK */
+ int (*PreFuncP) (unsigned long, struct CmdBlk *);
+ int PreArg; /* The arg for the func */
+ /* The func to call when completed */
+ int (*PostFuncP) (unsigned long, struct CmdBlk *);
+ int PostArg; /* The arg for the func */
+};
+
+#define NUM_RIO_CMD_BLKS (3 * (MAX_RUP * 4 + LINKS_PER_UNIT * 4))
+#endif
diff --git a/drivers/staging/generic_serial/rio/cmdpkt.h b/drivers/staging/generic_serial/rio/cmdpkt.h
new file mode 100644
index 000000000000..c1e7a2798070
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/cmdpkt.h
@@ -0,0 +1,177 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : cmdpkt.h
+** SID : 1.2
+** Last Modified : 11/6/98 11:34:09
+** Retrieved : 11/6/98 11:34:20
+**
+** ident @(#)cmdpkt.h 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+#ifndef __rio_cmdpkt_h__
+#define __rio_cmdpkt_h__
+
+/*
+** overlays for the data area of a packet. Used in both directions
+** (to build a packet to send, and to interpret a packet that arrives)
+** and is very inconvenient for MIPS, so they appear as two separate
+** structures - those used for modifying/reading packets on the card
+** and those for modifying/reading packets in real memory, which have an _M
+** suffix.
+*/
+
+#define RTA_BOOT_DATA_SIZE (PKT_MAX_DATA_LEN-2)
+
+/*
+** The boot information packet looks like this:
+** This structure overlays a PktCmd->CmdData structure, and so starts
+** at Data[2] in the actual pkt!
+*/
+struct BootSequence {
+ u16 NumPackets;
+ u16 LoadBase;
+ u16 CodeSize;
+};
+
+#define BOOT_SEQUENCE_LEN 8
+
+struct SamTop {
+ u8 Unit;
+ u8 Link;
+};
+
+struct CmdHdr {
+ u8 PcCommand;
+ union {
+ u8 PcPhbNum;
+ u8 PcLinkNum;
+ u8 PcIDNum;
+ } U0;
+};
+
+
+struct PktCmd {
+ union {
+ struct {
+ struct CmdHdr CmdHdr;
+ struct BootSequence PcBootSequence;
+ } S1;
+ struct {
+ u16 PcSequence;
+ u8 PcBootData[RTA_BOOT_DATA_SIZE];
+ } S2;
+ struct {
+ u16 __crud__;
+ u8 PcUniqNum[4]; /* this is really a uint. */
+ u8 PcModuleTypes; /* what modules are fitted */
+ } S3;
+ struct {
+ struct CmdHdr CmdHdr;
+ u8 __undefined__;
+ u8 PcModemStatus;
+ u8 PcPortStatus;
+ u8 PcSubCommand; /* commands like mem or register dump */
+ u16 PcSubAddr; /* Address for command */
+ u8 PcSubData[64]; /* Date area for command */
+ } S4;
+ struct {
+ struct CmdHdr CmdHdr;
+ u8 PcCommandText[1];
+ u8 __crud__[20];
+ u8 PcIDNum2; /* It had to go somewhere! */
+ } S5;
+ struct {
+ struct CmdHdr CmdHdr;
+ struct SamTop Topology[LINKS_PER_UNIT];
+ } S6;
+ } U1;
+};
+
+struct PktCmd_M {
+ union {
+ struct {
+ struct {
+ u8 PcCommand;
+ union {
+ u8 PcPhbNum;
+ u8 PcLinkNum;
+ u8 PcIDNum;
+ } U0;
+ } CmdHdr;
+ struct {
+ u16 NumPackets;
+ u16 LoadBase;
+ u16 CodeSize;
+ } PcBootSequence;
+ } S1;
+ struct {
+ u16 PcSequence;
+ u8 PcBootData[RTA_BOOT_DATA_SIZE];
+ } S2;
+ struct {
+ u16 __crud__;
+ u8 PcUniqNum[4]; /* this is really a uint. */
+ u8 PcModuleTypes; /* what modules are fitted */
+ } S3;
+ struct {
+ u16 __cmd_hdr__;
+ u8 __undefined__;
+ u8 PcModemStatus;
+ u8 PcPortStatus;
+ u8 PcSubCommand;
+ u16 PcSubAddr;
+ u8 PcSubData[64];
+ } S4;
+ struct {
+ u16 __cmd_hdr__;
+ u8 PcCommandText[1];
+ u8 __crud__[20];
+ u8 PcIDNum2; /* Tacked on end */
+ } S5;
+ struct {
+ u16 __cmd_hdr__;
+ struct Top Topology[LINKS_PER_UNIT];
+ } S6;
+ } U1;
+};
+
+#define Command U1.S1.CmdHdr.PcCommand
+#define PhbNum U1.S1.CmdHdr.U0.PcPhbNum
+#define IDNum U1.S1.CmdHdr.U0.PcIDNum
+#define IDNum2 U1.S5.PcIDNum2
+#define LinkNum U1.S1.CmdHdr.U0.PcLinkNum
+#define Sequence U1.S2.PcSequence
+#define BootData U1.S2.PcBootData
+#define BootSequence U1.S1.PcBootSequence
+#define UniqNum U1.S3.PcUniqNum
+#define ModemStatus U1.S4.PcModemStatus
+#define PortStatus U1.S4.PcPortStatus
+#define SubCommand U1.S4.PcSubCommand
+#define SubAddr U1.S4.PcSubAddr
+#define SubData U1.S4.PcSubData
+#define CommandText U1.S5.PcCommandText
+#define RouteTopology U1.S6.Topology
+#define ModuleTypes U1.S3.PcModuleTypes
+
+#endif
diff --git a/drivers/staging/generic_serial/rio/daemon.h b/drivers/staging/generic_serial/rio/daemon.h
new file mode 100644
index 000000000000..4af90323fd00
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/daemon.h
@@ -0,0 +1,307 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : daemon.h
+** SID : 1.3
+** Last Modified : 11/6/98 11:34:09
+** Retrieved : 11/6/98 11:34:21
+**
+** ident @(#)daemon.h 1.3
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rio_daemon_h__
+#define __rio_daemon_h__
+
+
+/*
+** structures used on /dev/rio
+*/
+
+struct Error {
+ unsigned int Error;
+ unsigned int Entry;
+ unsigned int Other;
+};
+
+struct DownLoad {
+ char __user *DataP;
+ unsigned int Count;
+ unsigned int ProductCode;
+};
+
+/*
+** A few constants....
+*/
+#ifndef MAX_VERSION_LEN
+#define MAX_VERSION_LEN 256
+#endif
+
+#ifndef MAX_XP_CTRL_LEN
+#define MAX_XP_CTRL_LEN 16 /* ALSO IN PORT.H */
+#endif
+
+struct PortSetup {
+ unsigned int From; /* Set/Clear XP & IXANY Control from this port.... */
+ unsigned int To; /* .... to this port */
+ unsigned int XpCps; /* at this speed */
+ char XpOn[MAX_XP_CTRL_LEN]; /* this is the start string */
+ char XpOff[MAX_XP_CTRL_LEN]; /* this is the stop string */
+ u8 IxAny; /* enable/disable IXANY */
+ u8 IxOn; /* enable/disable IXON */
+ u8 Lock; /* lock port params */
+ u8 Store; /* store params across closes */
+ u8 Drain; /* close only when drained */
+};
+
+struct LpbReq {
+ unsigned int Host;
+ unsigned int Link;
+ struct LPB __user *LpbP;
+};
+
+struct RupReq {
+ unsigned int HostNum;
+ unsigned int RupNum;
+ struct RUP __user *RupP;
+};
+
+struct PortReq {
+ unsigned int SysPort;
+ struct Port __user *PortP;
+};
+
+struct StreamInfo {
+ unsigned int SysPort;
+ int RQueue;
+ int WQueue;
+};
+
+struct HostReq {
+ unsigned int HostNum;
+ struct Host __user *HostP;
+};
+
+struct HostDpRam {
+ unsigned int HostNum;
+ struct DpRam __user *DpRamP;
+};
+
+struct DebugCtrl {
+ unsigned int SysPort;
+ unsigned int Debug;
+ unsigned int Wait;
+};
+
+struct MapInfo {
+ unsigned int FirstPort; /* 8 ports, starting from this (tty) number */
+ unsigned int RtaUnique; /* reside on this RTA (unique number) */
+};
+
+struct MapIn {
+ unsigned int NumEntries; /* How many port sets are we mapping? */
+ struct MapInfo *MapInfoP; /* Pointer to (user space) info */
+};
+
+struct SendPack {
+ unsigned int PortNum;
+ unsigned char Len;
+ unsigned char Data[PKT_MAX_DATA_LEN];
+};
+
+struct SpecialRupCmd {
+ struct PKT Packet;
+ unsigned short Host;
+ unsigned short RupNum;
+};
+
+struct IdentifyRta {
+ unsigned long RtaUnique;
+ u8 ID;
+};
+
+struct KillNeighbour {
+ unsigned long UniqueNum;
+ u8 Link;
+};
+
+struct rioVersion {
+ char version[MAX_VERSION_LEN];
+ char relid[MAX_VERSION_LEN];
+ int buildLevel;
+ char buildDate[MAX_VERSION_LEN];
+};
+
+
+/*
+** RIOC commands are for the daemon type operations
+**
+** 09.12.1998 ARG - ESIL 0776 part fix
+** Definition for 'RIOC' also appears in rioioctl.h, so we'd better do a
+** #ifndef here first.
+** rioioctl.h also now has #define 'RIO_QUICK_CHECK' as this ioctl is now
+** allowed to be used by customers.
+*/
+#ifndef RIOC
+#define RIOC ('R'<<8)|('i'<<16)|('o'<<24)
+#endif
+
+/*
+** Boot stuff
+*/
+#define RIO_GET_TABLE (RIOC | 100)
+#define RIO_PUT_TABLE (RIOC | 101)
+#define RIO_ASSIGN_RTA (RIOC | 102)
+#define RIO_DELETE_RTA (RIOC | 103)
+#define RIO_HOST_FOAD (RIOC | 104)
+#define RIO_QUICK_CHECK (RIOC | 105)
+#define RIO_SIGNALS_ON (RIOC | 106)
+#define RIO_SIGNALS_OFF (RIOC | 107)
+#define RIO_CHANGE_NAME (RIOC | 108)
+#define RIO_DOWNLOAD (RIOC | 109)
+#define RIO_GET_LOG (RIOC | 110)
+#define RIO_SETUP_PORTS (RIOC | 111)
+#define RIO_ALL_MODEM (RIOC | 112)
+
+/*
+** card state, debug stuff
+*/
+#define RIO_NUM_HOSTS (RIOC | 120)
+#define RIO_HOST_LPB (RIOC | 121)
+#define RIO_HOST_RUP (RIOC | 122)
+#define RIO_HOST_PORT (RIOC | 123)
+#define RIO_PARMS (RIOC | 124)
+#define RIO_HOST_REQ (RIOC | 125)
+#define RIO_READ_CONFIG (RIOC | 126)
+#define RIO_SET_CONFIG (RIOC | 127)
+#define RIO_VERSID (RIOC | 128)
+#define RIO_FLAGS (RIOC | 129)
+#define RIO_SETDEBUG (RIOC | 130)
+#define RIO_GETDEBUG (RIOC | 131)
+#define RIO_READ_LEVELS (RIOC | 132)
+#define RIO_SET_FAST_BUS (RIOC | 133)
+#define RIO_SET_SLOW_BUS (RIOC | 134)
+#define RIO_SET_BYTE_MODE (RIOC | 135)
+#define RIO_SET_WORD_MODE (RIOC | 136)
+#define RIO_STREAM_INFO (RIOC | 137)
+#define RIO_START_POLLER (RIOC | 138)
+#define RIO_STOP_POLLER (RIOC | 139)
+#define RIO_LAST_ERROR (RIOC | 140)
+#define RIO_TICK (RIOC | 141)
+#define RIO_TOCK (RIOC | 241) /* I did this on purpose, you know. */
+#define RIO_SEND_PACKET (RIOC | 142)
+#define RIO_SET_BUSY (RIOC | 143)
+#define SPECIAL_RUP_CMD (RIOC | 144)
+#define RIO_FOAD_RTA (RIOC | 145)
+#define RIO_ZOMBIE_RTA (RIOC | 146)
+#define RIO_IDENTIFY_RTA (RIOC | 147)
+#define RIO_KILL_NEIGHBOUR (RIOC | 148)
+#define RIO_DEBUG_MEM (RIOC | 149)
+/*
+** 150 - 167 used..... See below
+*/
+#define RIO_GET_PORT_SETUP (RIOC | 168)
+#define RIO_RESUME (RIOC | 169)
+#define RIO_MESG (RIOC | 170)
+#define RIO_NO_MESG (RIOC | 171)
+#define RIO_WHAT_MESG (RIOC | 172)
+#define RIO_HOST_DPRAM (RIOC | 173)
+#define RIO_MAP_B50_TO_50 (RIOC | 174)
+#define RIO_MAP_B50_TO_57600 (RIOC | 175)
+#define RIO_MAP_B110_TO_110 (RIOC | 176)
+#define RIO_MAP_B110_TO_115200 (RIOC | 177)
+#define RIO_GET_PORT_PARAMS (RIOC | 178)
+#define RIO_SET_PORT_PARAMS (RIOC | 179)
+#define RIO_GET_PORT_TTY (RIOC | 180)
+#define RIO_SET_PORT_TTY (RIOC | 181)
+#define RIO_SYSLOG_ONLY (RIOC | 182)
+#define RIO_SYSLOG_CONS (RIOC | 183)
+#define RIO_CONS_ONLY (RIOC | 184)
+#define RIO_BLOCK_OPENS (RIOC | 185)
+
+/*
+** 02.03.1999 ARG - ESIL 0820 fix :
+** RIOBootMode is no longer use by the driver, so these ioctls
+** are now obsolete :
+**
+#define RIO_GET_BOOT_MODE (RIOC | 186)
+#define RIO_SET_BOOT_MODE (RIOC | 187)
+**
+*/
+
+#define RIO_MEM_DUMP (RIOC | 189)
+#define RIO_READ_REGISTER (RIOC | 190)
+#define RIO_GET_MODTYPE (RIOC | 191)
+#define RIO_SET_TIMER (RIOC | 192)
+#define RIO_READ_CHECK (RIOC | 196)
+#define RIO_WAITING_FOR_RESTART (RIOC | 197)
+#define RIO_BIND_RTA (RIOC | 198)
+#define RIO_GET_BINDINGS (RIOC | 199)
+#define RIO_PUT_BINDINGS (RIOC | 200)
+
+#define RIO_MAKE_DEV (RIOC | 201)
+#define RIO_MINOR (RIOC | 202)
+
+#define RIO_IDENTIFY_DRIVER (RIOC | 203)
+#define RIO_DISPLAY_HOST_CFG (RIOC | 204)
+
+
+/*
+** MAKE_DEV / MINOR stuff
+*/
+#define RIO_DEV_DIRECT 0x0000
+#define RIO_DEV_MODEM 0x0200
+#define RIO_DEV_XPRINT 0x0400
+#define RIO_DEV_MASK 0x0600
+
+/*
+** port management, xprint stuff
+*/
+#define rIOCN(N) (RIOC|(N))
+#define rIOCR(N,T) (RIOC|(N))
+#define rIOCW(N,T) (RIOC|(N))
+
+#define RIO_GET_XP_ON rIOCR(150,char[16]) /* start xprint string */
+#define RIO_SET_XP_ON rIOCW(151,char[16])
+#define RIO_GET_XP_OFF rIOCR(152,char[16]) /* finish xprint string */
+#define RIO_SET_XP_OFF rIOCW(153,char[16])
+#define RIO_GET_XP_CPS rIOCR(154,int) /* xprint CPS */
+#define RIO_SET_XP_CPS rIOCW(155,int)
+#define RIO_GET_IXANY rIOCR(156,int) /* ixany allowed? */
+#define RIO_SET_IXANY rIOCW(157,int)
+#define RIO_SET_IXANY_ON rIOCN(158) /* allow ixany */
+#define RIO_SET_IXANY_OFF rIOCN(159) /* disallow ixany */
+#define RIO_GET_MODEM rIOCR(160,int) /* port is modem/direct line? */
+#define RIO_SET_MODEM rIOCW(161,int)
+#define RIO_SET_MODEM_ON rIOCN(162) /* port is a modem */
+#define RIO_SET_MODEM_OFF rIOCN(163) /* port is direct */
+#define RIO_GET_IXON rIOCR(164,int) /* ixon allowed? */
+#define RIO_SET_IXON rIOCW(165,int)
+#define RIO_SET_IXON_ON rIOCN(166) /* allow ixon */
+#define RIO_SET_IXON_OFF rIOCN(167) /* disallow ixon */
+
+#define RIO_GET_SIVIEW ((('s')<<8) | 106) /* backwards compatible with SI */
+
+#define RIO_IOCTL_UNKNOWN -2
+
+#endif
diff --git a/drivers/staging/generic_serial/rio/errors.h b/drivers/staging/generic_serial/rio/errors.h
new file mode 100644
index 000000000000..bdb05234090a
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/errors.h
@@ -0,0 +1,98 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : errors.h
+** SID : 1.2
+** Last Modified : 11/6/98 11:34:10
+** Retrieved : 11/6/98 11:34:21
+**
+** ident @(#)errors.h 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rio_errors_h__
+#define __rio_errors_h__
+
+/*
+** error codes
+*/
+
+#define NOTHING_WRONG_AT_ALL 0
+#define BAD_CHARACTER_IN_NAME 1
+#define TABLE_ENTRY_ISNT_PROPERLY_NULL 2
+#define UNKNOWN_HOST_NUMBER 3
+#define ZERO_RTA_ID 4
+#define BAD_RTA_ID 5
+#define DUPLICATED_RTA_ID 6
+#define DUPLICATE_UNIQUE_NUMBER 7
+#define BAD_TTY_NUMBER 8
+#define TTY_NUMBER_IN_USE 9
+#define NAME_USED_TWICE 10
+#define HOST_ID_NOT_ZERO 11
+#define BOOT_IN_PROGRESS 12
+#define COPYIN_FAILED 13
+#define HOST_FILE_TOO_LARGE 14
+#define COPYOUT_FAILED 15
+#define NOT_SUPER_USER 16
+#define RIO_ALREADY_POLLING 17
+
+#define ID_NUMBER_OUT_OF_RANGE 18
+#define PORT_NUMBER_OUT_OF_RANGE 19
+#define HOST_NUMBER_OUT_OF_RANGE 20
+#define RUP_NUMBER_OUT_OF_RANGE 21
+#define TTY_NUMBER_OUT_OF_RANGE 22
+#define LINK_NUMBER_OUT_OF_RANGE 23
+
+#define HOST_NOT_RUNNING 24
+#define IOCTL_COMMAND_UNKNOWN 25
+#define RIO_SYSTEM_HALTED 26
+#define WAIT_FOR_DRAIN_BROKEN 27
+#define PORT_NOT_MAPPED_INTO_SYSTEM 28
+#define EXCLUSIVE_USE_SET 29
+#define WAIT_FOR_NOT_CLOSING_BROKEN 30
+#define WAIT_FOR_PORT_TO_OPEN_BROKEN 31
+#define WAIT_FOR_CARRIER_BROKEN 32
+#define WAIT_FOR_NOT_IN_USE_BROKEN 33
+#define WAIT_FOR_CAN_ADD_COMMAND_BROKEN 34
+#define WAIT_FOR_ADD_COMMAND_BROKEN 35
+#define WAIT_FOR_NOT_PARAM_BROKEN 36
+#define WAIT_FOR_RETRY_BROKEN 37
+#define HOST_HAS_ALREADY_BEEN_BOOTED 38
+#define UNIT_IS_IN_USE 39
+#define COULDNT_FIND_ENTRY 40
+#define RTA_UNIQUE_NUMBER_ZERO 41
+#define CLOSE_COMMAND_FAILED 42
+#define WAIT_FOR_CLOSE_BROKEN 43
+#define CPS_VALUE_OUT_OF_RANGE 44
+#define ID_ALREADY_IN_USE 45
+#define SIGNALS_ALREADY_SET 46
+#define NOT_RECEIVING_PROCESS 47
+#define RTA_NUMBER_WRONG 48
+#define NO_SUCH_PRODUCT 49
+#define HOST_SYSPORT_BAD 50
+#define ID_NOT_TENTATIVE 51
+#define XPRINT_CPS_OUT_OF_RANGE 52
+#define NOT_ENOUGH_CORE_FOR_PCI_COPY 53
+
+
+#endif /* __rio_errors_h__ */
diff --git a/drivers/staging/generic_serial/rio/func.h b/drivers/staging/generic_serial/rio/func.h
new file mode 100644
index 000000000000..078d44f85e45
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/func.h
@@ -0,0 +1,143 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : func.h
+** SID : 1.3
+** Last Modified : 11/6/98 11:34:10
+** Retrieved : 11/6/98 11:34:21
+**
+** ident @(#)func.h 1.3
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __func_h_def
+#define __func_h_def
+
+#include <linux/kdev_t.h>
+
+/* rioboot.c */
+int RIOBootCodeRTA(struct rio_info *, struct DownLoad *);
+int RIOBootCodeHOST(struct rio_info *, struct DownLoad *);
+int RIOBootCodeUNKNOWN(struct rio_info *, struct DownLoad *);
+void msec_timeout(struct Host *);
+int RIOBootRup(struct rio_info *, unsigned int, struct Host *, struct PKT __iomem *);
+int RIOBootOk(struct rio_info *, struct Host *, unsigned long);
+int RIORtaBound(struct rio_info *, unsigned int);
+void rio_fill_host_slot(int, int, unsigned int, struct Host *);
+
+/* riocmd.c */
+int RIOFoadRta(struct Host *, struct Map *);
+int RIOZombieRta(struct Host *, struct Map *);
+int RIOCommandRta(struct rio_info *, unsigned long, int (*func) (struct Host *, struct Map *));
+int RIOIdentifyRta(struct rio_info *, void __user *);
+int RIOKillNeighbour(struct rio_info *, void __user *);
+int RIOSuspendBootRta(struct Host *, int, int);
+int RIOFoadWakeup(struct rio_info *);
+struct CmdBlk *RIOGetCmdBlk(void);
+void RIOFreeCmdBlk(struct CmdBlk *);
+int RIOQueueCmdBlk(struct Host *, unsigned int, struct CmdBlk *);
+void RIOPollHostCommands(struct rio_info *, struct Host *);
+int RIOWFlushMark(unsigned long, struct CmdBlk *);
+int RIORFlushEnable(unsigned long, struct CmdBlk *);
+int RIOUnUse(unsigned long, struct CmdBlk *);
+
+/* rioctrl.c */
+int riocontrol(struct rio_info *, dev_t, int, unsigned long, int);
+
+int RIOPreemptiveCmd(struct rio_info *, struct Port *, unsigned char);
+
+/* rioinit.c */
+void rioinit(struct rio_info *, struct RioHostInfo *);
+void RIOInitHosts(struct rio_info *, struct RioHostInfo *);
+void RIOISAinit(struct rio_info *, int);
+int RIODoAT(struct rio_info *, int, int);
+caddr_t RIOCheckForATCard(int);
+int RIOAssignAT(struct rio_info *, int, void __iomem *, int);
+int RIOBoardTest(unsigned long, void __iomem *, unsigned char, int);
+void RIOAllocDataStructs(struct rio_info *);
+void RIOSetupDataStructs(struct rio_info *);
+int RIODefaultName(struct rio_info *, struct Host *, unsigned int);
+struct rioVersion *RIOVersid(void);
+void RIOHostReset(unsigned int, struct DpRam __iomem *, unsigned int);
+
+/* riointr.c */
+void RIOTxEnable(char *);
+void RIOServiceHost(struct rio_info *, struct Host *);
+int riotproc(struct rio_info *, struct ttystatics *, int, int);
+
+/* rioparam.c */
+int RIOParam(struct Port *, int, int, int);
+int RIODelay(struct Port *PortP, int);
+int RIODelay_ni(struct Port *PortP, int);
+void ms_timeout(struct Port *);
+int can_add_transmit(struct PKT __iomem **, struct Port *);
+void add_transmit(struct Port *);
+void put_free_end(struct Host *, struct PKT __iomem *);
+int can_remove_receive(struct PKT __iomem **, struct Port *);
+void remove_receive(struct Port *);
+
+/* rioroute.c */
+int RIORouteRup(struct rio_info *, unsigned int, struct Host *, struct PKT __iomem *);
+void RIOFixPhbs(struct rio_info *, struct Host *, unsigned int);
+unsigned int GetUnitType(unsigned int);
+int RIOSetChange(struct rio_info *);
+int RIOFindFreeID(struct rio_info *, struct Host *, unsigned int *, unsigned int *);
+
+
+/* riotty.c */
+
+int riotopen(struct tty_struct *tty, struct file *filp);
+int riotclose(void *ptr);
+int riotioctl(struct rio_info *, struct tty_struct *, int, caddr_t);
+void ttyseth(struct Port *, struct ttystatics *, struct old_sgttyb *sg);
+
+/* riotable.c */
+int RIONewTable(struct rio_info *);
+int RIOApel(struct rio_info *);
+int RIODeleteRta(struct rio_info *, struct Map *);
+int RIOAssignRta(struct rio_info *, struct Map *);
+int RIOReMapPorts(struct rio_info *, struct Host *, struct Map *);
+int RIOChangeName(struct rio_info *, struct Map *);
+
+#if 0
+/* riodrvr.c */
+struct rio_info *rio_install(struct RioHostInfo *);
+int rio_uninstall(struct rio_info *);
+int rio_open(struct rio_info *, int, struct file *);
+int rio_close(struct rio_info *, struct file *);
+int rio_read(struct rio_info *, struct file *, char *, int);
+int rio_write(struct rio_info *, struct file *f, char *, int);
+int rio_ioctl(struct rio_info *, struct file *, int, char *);
+int rio_select(struct rio_info *, struct file *f, int, struct sel *);
+int rio_intr(char *);
+int rio_isr_thread(char *);
+struct rio_info *rio_info_store(int cmd, struct rio_info *p);
+#endif
+
+extern void rio_copy_to_card(void *from, void __iomem *to, int len);
+extern int rio_minor(struct tty_struct *tty);
+extern int rio_ismodem(struct tty_struct *tty);
+
+extern void rio_start_card_running(struct Host *HostP);
+
+#endif /* __func_h_def */
diff --git a/drivers/staging/generic_serial/rio/host.h b/drivers/staging/generic_serial/rio/host.h
new file mode 100644
index 000000000000..78f24540c224
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/host.h
@@ -0,0 +1,123 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : host.h
+** SID : 1.2
+** Last Modified : 11/6/98 11:34:10
+** Retrieved : 11/6/98 11:34:21
+**
+** ident @(#)host.h 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rio_host_h__
+#define __rio_host_h__
+
+/*
+** the host structure - one per host card in the system.
+*/
+
+#define MAX_EXTRA_UNITS 64
+
+/*
+** Host data structure. This is used for the software equiv. of
+** the host.
+*/
+struct Host {
+ struct pci_dev *pdev;
+ unsigned char Type; /* RIO_EISA, RIO_MCA, ... */
+ unsigned char Ivec; /* POLLED or ivec number */
+ unsigned char Mode; /* Control stuff */
+ unsigned char Slot; /* Slot */
+ void __iomem *Caddr; /* KV address of DPRAM */
+ struct DpRam __iomem *CardP; /* KV address of DPRAM, with overlay */
+ unsigned long PaddrP; /* Phys. address of DPRAM */
+ char Name[MAX_NAME_LEN]; /* The name of the host */
+ unsigned int UniqueNum; /* host unique number */
+ spinlock_t HostLock; /* Lock structure for MPX */
+ unsigned int WorkToBeDone; /* set to true each interrupt */
+ unsigned int InIntr; /* Being serviced? */
+ unsigned int IntSrvDone; /* host's interrupt has been serviced */
+ void (*Copy) (void *, void __iomem *, int); /* copy func */
+ struct timer_list timer;
+ /*
+ ** I M P O R T A N T !
+ **
+ ** The rest of this data structure is cleared to zero after
+ ** a RIO_HOST_FOAD command.
+ */
+
+ unsigned long Flags; /* Whats going down */
+#define RC_WAITING 0
+#define RC_STARTUP 1
+#define RC_RUNNING 2
+#define RC_STUFFED 3
+#define RC_READY 7
+#define RUN_STATE 7
+/*
+** Boot mode applies to the way in which hosts in this system will
+** boot RTAs
+*/
+#define RC_BOOT_ALL 0x8 /* Boot all RTAs attached */
+#define RC_BOOT_OWN 0x10 /* Only boot RTAs bound to this system */
+#define RC_BOOT_NONE 0x20 /* Don't boot any RTAs (slave mode) */
+
+ struct Top Topology[LINKS_PER_UNIT]; /* one per link */
+ struct Map Mapping[MAX_RUP]; /* Mappings for host */
+ struct PHB __iomem *PhbP; /* Pointer to the PHB array */
+ unsigned short __iomem *PhbNumP; /* Ptr to Number of PHB's */
+ struct LPB __iomem *LinkStrP; /* Link Structure Array */
+ struct RUP __iomem *RupP; /* Sixteen real rups here */
+ struct PARM_MAP __iomem *ParmMapP; /* points to the parmmap */
+ unsigned int ExtraUnits[MAX_EXTRA_UNITS]; /* unknown things */
+ unsigned int NumExtraBooted; /* how many of the above */
+ /*
+ ** Twenty logical rups.
+ ** The first sixteen are the real Rup entries (above), the last four
+ ** are the link RUPs.
+ */
+ struct UnixRup UnixRups[MAX_RUP + LINKS_PER_UNIT];
+ int timeout_id; /* For calling 100 ms delays */
+ int timeout_sem; /* For calling 100 ms delays */
+ unsigned long locks; /* long req'd for set_bit --RR */
+ char ____end_marker____;
+};
+#define Control CardP->DpControl
+#define SetInt CardP->DpSetInt
+#define ResetTpu CardP->DpResetTpu
+#define ResetInt CardP->DpResetInt
+#define Signature CardP->DpSignature
+#define Sram1 CardP->DpSram1
+#define Sram2 CardP->DpSram2
+#define Sram3 CardP->DpSram3
+#define Scratch CardP->DpScratch
+#define __ParmMapR CardP->DpParmMapR
+#define SLX CardP->DpSlx
+#define Revision CardP->DpRevision
+#define Unique CardP->DpUnique
+#define Year CardP->DpYear
+#define Week CardP->DpWeek
+
+#define RIO_DUMBPARM 0x0860 /* what not to expect */
+
+#endif
diff --git a/drivers/staging/generic_serial/rio/link.h b/drivers/staging/generic_serial/rio/link.h
new file mode 100644
index 000000000000..f3bf11a04d41
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/link.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+ ******* *******
+ ******* L I N K
+ ******* *******
+ ****************************************************************************
+
+ Author : Ian Nandhra / Jeremy Rolls
+ Date :
+
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+
+ Version : 0.01
+
+
+ Mods
+ ----------------------------------------------------------------------------
+ Date By Description
+ ----------------------------------------------------------------------------
+
+ ***************************************************************************/
+
+#ifndef _link_h
+#define _link_h 1
+
+/*************************************************
+ * Define the Link Status stuff
+ ************************************************/
+/* Boot request stuff */
+#define BOOT_REQUEST ((ushort) 0) /* Request for a boot */
+#define BOOT_ABORT ((ushort) 1) /* Abort a boot */
+#define BOOT_SEQUENCE ((ushort) 2) /* Packet with the number of packets
+ and load address */
+#define BOOT_COMPLETED ((ushort) 3) /* Boot completed */
+
+
+struct LPB {
+ u16 link_number; /* Link Number */
+ u16 in_ch; /* Link In Channel */
+ u16 out_ch; /* Link Out Channel */
+ u8 attached_serial[4]; /* Attached serial number */
+ u8 attached_host_serial[4];
+ /* Serial number of Host who
+ booted the other end */
+ u16 descheduled; /* Currently Descheduled */
+ u16 state; /* Current state */
+ u16 send_poll; /* Send a Poll Packet */
+ u16 ltt_p; /* Process Descriptor */
+ u16 lrt_p; /* Process Descriptor */
+ u16 lrt_status; /* Current lrt status */
+ u16 ltt_status; /* Current ltt status */
+ u16 timeout; /* Timeout value */
+ u16 topology; /* Topology bits */
+ u16 mon_ltt;
+ u16 mon_lrt;
+ u16 WaitNoBoot; /* Secs to hold off booting */
+ u16 add_packet_list; /* Add packets to here */
+ u16 remove_packet_list; /* Send packets from here */
+
+ u16 lrt_fail_chan; /* Lrt's failure channel */
+ u16 ltt_fail_chan; /* Ltt's failure channel */
+
+ /* RUP structure for HOST to driver communications */
+ struct RUP rup;
+ struct RUP link_rup; /* RUP for the link (POLL,
+ topology etc.) */
+ u16 attached_link; /* Number of attached link */
+ u16 csum_errors; /* csum errors */
+ u16 num_disconnects; /* number of disconnects */
+ u16 num_sync_rcvd; /* # sync's received */
+ u16 num_sync_rqst; /* # sync requests */
+ u16 num_tx; /* Num pkts sent */
+ u16 num_rx; /* Num pkts received */
+ u16 module_attached; /* Module tpyes of attached */
+ u16 led_timeout; /* LED timeout */
+ u16 first_port; /* First port to service */
+ u16 last_port; /* Last port to service */
+};
+
+#endif
+
+/*********** end of file ***********/
diff --git a/drivers/staging/generic_serial/rio/linux_compat.h b/drivers/staging/generic_serial/rio/linux_compat.h
new file mode 100644
index 000000000000..34c0d2899ef1
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/linux_compat.h
@@ -0,0 +1,77 @@
+/*
+ * (C) 2000 R.E.Wolff@BitWizard.nl
+ *
+ * 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/interrupt.h>
+
+
+#define DEBUG_ALL
+
+struct ttystatics {
+ struct termios tm;
+};
+
+extern int rio_debug;
+
+#define RIO_DEBUG_INIT 0x000001
+#define RIO_DEBUG_BOOT 0x000002
+#define RIO_DEBUG_CMD 0x000004
+#define RIO_DEBUG_CTRL 0x000008
+#define RIO_DEBUG_INTR 0x000010
+#define RIO_DEBUG_PARAM 0x000020
+#define RIO_DEBUG_ROUTE 0x000040
+#define RIO_DEBUG_TABLE 0x000080
+#define RIO_DEBUG_TTY 0x000100
+#define RIO_DEBUG_FLOW 0x000200
+#define RIO_DEBUG_MODEMSIGNALS 0x000400
+#define RIO_DEBUG_PROBE 0x000800
+#define RIO_DEBUG_CLEANUP 0x001000
+#define RIO_DEBUG_IFLOW 0x002000
+#define RIO_DEBUG_PFE 0x004000
+#define RIO_DEBUG_REC 0x008000
+#define RIO_DEBUG_SPINLOCK 0x010000
+#define RIO_DEBUG_DELAY 0x020000
+#define RIO_DEBUG_MOD_COUNT 0x040000
+
+
+/* Copied over from riowinif.h . This is ugly. The winif file declares
+also much other stuff which is incompatible with the headers from
+the older driver. The older driver includes "brates.h" which shadows
+the definitions from Linux, and is incompatible... */
+
+/* RxBaud and TxBaud definitions... */
+#define RIO_B0 0x00 /* RTS / DTR signals dropped */
+#define RIO_B50 0x01 /* 50 baud */
+#define RIO_B75 0x02 /* 75 baud */
+#define RIO_B110 0x03 /* 110 baud */
+#define RIO_B134 0x04 /* 134.5 baud */
+#define RIO_B150 0x05 /* 150 baud */
+#define RIO_B200 0x06 /* 200 baud */
+#define RIO_B300 0x07 /* 300 baud */
+#define RIO_B600 0x08 /* 600 baud */
+#define RIO_B1200 0x09 /* 1200 baud */
+#define RIO_B1800 0x0A /* 1800 baud */
+#define RIO_B2400 0x0B /* 2400 baud */
+#define RIO_B4800 0x0C /* 4800 baud */
+#define RIO_B9600 0x0D /* 9600 baud */
+#define RIO_B19200 0x0E /* 19200 baud */
+#define RIO_B38400 0x0F /* 38400 baud */
+#define RIO_B56000 0x10 /* 56000 baud */
+#define RIO_B57600 0x11 /* 57600 baud */
+#define RIO_B64000 0x12 /* 64000 baud */
+#define RIO_B115200 0x13 /* 115200 baud */
+#define RIO_B2000 0x14 /* 2000 baud */
diff --git a/drivers/staging/generic_serial/rio/map.h b/drivers/staging/generic_serial/rio/map.h
new file mode 100644
index 000000000000..8366978578c1
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/map.h
@@ -0,0 +1,98 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : map.h
+** SID : 1.2
+** Last Modified : 11/6/98 11:34:11
+** Retrieved : 11/6/98 11:34:21
+**
+** ident @(#)map.h 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rio_map_h__
+#define __rio_map_h__
+
+/*
+** mapping structure passed to and from the config.rio program to
+** determine the current topology of the world
+*/
+
+#define MAX_MAP_ENTRY 17
+#define TOTAL_MAP_ENTRIES (MAX_MAP_ENTRY*RIO_SLOTS)
+#define MAX_NAME_LEN 32
+
+struct Map {
+ unsigned int HostUniqueNum; /* Supporting hosts unique number */
+ unsigned int RtaUniqueNum; /* Unique number */
+ /*
+ ** The next two IDs must be swapped on big-endian architectures
+ ** when using a v2.04 /etc/rio/config with a v3.00 driver (when
+ ** upgrading for example).
+ */
+ unsigned short ID; /* ID used in the subnet */
+ unsigned short ID2; /* ID of 2nd block of 8 for 16 port */
+ unsigned long Flags; /* Booted, ID Given, Disconnected */
+ unsigned long SysPort; /* First tty mapped to this port */
+ struct Top Topology[LINKS_PER_UNIT]; /* ID connected to each link */
+ char Name[MAX_NAME_LEN]; /* Cute name by which RTA is known */
+};
+
+/*
+** Flag values:
+*/
+#define RTA_BOOTED 0x00000001
+#define RTA_NEWBOOT 0x00000010
+#define MSG_DONE 0x00000020
+#define RTA_INTERCONNECT 0x00000040
+#define RTA16_SECOND_SLOT 0x00000080
+#define BEEN_HERE 0x00000100
+#define SLOT_TENTATIVE 0x40000000
+#define SLOT_IN_USE 0x80000000
+
+/*
+** HostUniqueNum is the unique number from the host card that this RTA
+** is to be connected to.
+** RtaUniqueNum is the unique number of the RTA concerned. It will be ZERO
+** if the slot in the table is unused. If it is the same as the HostUniqueNum
+** then this slot represents a host card.
+** Flags contains current boot/route state info
+** SysPort is a value in the range 0-504, being the number of the first tty
+** on this RTA. Each RTA supports 8 ports. The SysPort value must be modulo 8.
+** SysPort 0-127 correspond to /dev/ttyr001 to /dev/ttyr128, with minor
+** numbers 0-127. SysPort 128-255 correspond to /dev/ttyr129 to /dev/ttyr256,
+** again with minor numbers 0-127, and so on for SysPorts 256-383 and 384-511
+** ID will be in the range 0-16 for a `known' RTA. ID will be 0xFFFF for an
+** unused slot/unknown ID etc.
+** The Topology array contains the ID of the unit connected to each of the
+** four links on this unit. The entry will be 0xFFFF if NOTHING is connected
+** to the link, or will be 0xFF00 if an UNKNOWN unit is connected to the link.
+** The Name field is a null-terminated string, upto 31 characters, containing
+** the 'cute' name that the sysadmin/users know the RTA by. It is permissible
+** for this string to contain any character in the range \040 to \176 inclusive.
+** In particular, ctrl sequences and DEL (0x7F, \177) are not allowed. The
+** special character '%' IS allowable, and needs no special action.
+**
+*/
+
+#endif
diff --git a/drivers/staging/generic_serial/rio/param.h b/drivers/staging/generic_serial/rio/param.h
new file mode 100644
index 000000000000..7e9b6283e8aa
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/param.h
@@ -0,0 +1,55 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : param.h
+** SID : 1.2
+** Last Modified : 11/6/98 11:34:12
+** Retrieved : 11/6/98 11:34:21
+**
+** ident @(#)param.h 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rio_param_h__
+#define __rio_param_h__
+
+/*
+** the param command block, as used in OPEN and PARAM calls.
+*/
+
+struct phb_param {
+ u8 Cmd; /* It is very important that these line up */
+ u8 Cor1; /* with what is expected at the other end. */
+ u8 Cor2; /* to confirm that you've got it right, */
+ u8 Cor4; /* check with cirrus/cirrus.h */
+ u8 Cor5;
+ u8 TxXon; /* Transmit X-On character */
+ u8 TxXoff; /* Transmit X-Off character */
+ u8 RxXon; /* Receive X-On character */
+ u8 RxXoff; /* Receive X-Off character */
+ u8 LNext; /* Literal-next character */
+ u8 TxBaud; /* Transmit baudrate */
+ u8 RxBaud; /* Receive baudrate */
+};
+
+#endif
diff --git a/drivers/staging/generic_serial/rio/parmmap.h b/drivers/staging/generic_serial/rio/parmmap.h
new file mode 100644
index 000000000000..acc8fa439df5
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/parmmap.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+ ******* *******
+ ******* H O S T M E M O R Y M A P
+ ******* *******
+ ****************************************************************************
+
+ Author : Ian Nandhra / Jeremy Rolls
+ Date :
+
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+
+ Version : 0.01
+
+
+ Mods
+ ----------------------------------------------------------------------------
+ Date By Description
+ ----------------------------------------------------------------------------
+6/4/1991 jonb Made changes to accommodate Mips R3230 bus
+ ***************************************************************************/
+
+#ifndef _parmap_h
+#define _parmap_h
+
+typedef struct PARM_MAP PARM_MAP;
+
+struct PARM_MAP {
+ u16 phb_ptr; /* Pointer to the PHB array */
+ u16 phb_num_ptr; /* Ptr to Number of PHB's */
+ u16 free_list; /* Free List pointer */
+ u16 free_list_end; /* Free List End pointer */
+ u16 q_free_list_ptr; /* Ptr to Q_BUF variable */
+ u16 unit_id_ptr; /* Unit Id */
+ u16 link_str_ptr; /* Link Structure Array */
+ u16 bootloader_1; /* 1st Stage Boot Loader */
+ u16 bootloader_2; /* 2nd Stage Boot Loader */
+ u16 port_route_map_ptr; /* Port Route Map */
+ u16 route_ptr; /* Unit Route Map */
+ u16 map_present; /* Route Map present */
+ s16 pkt_num; /* Total number of packets */
+ s16 q_num; /* Total number of Q packets */
+ u16 buffers_per_port; /* Number of buffers per port */
+ u16 heap_size; /* Initial size of heap */
+ u16 heap_left; /* Current Heap left */
+ u16 error; /* Error code */
+ u16 tx_max; /* Max number of tx pkts per phb */
+ u16 rx_max; /* Max number of rx pkts per phb */
+ u16 rx_limit; /* For high / low watermarks */
+ s16 links; /* Links to use */
+ s16 timer; /* Interrupts per second */
+ u16 rups; /* Pointer to the RUPs */
+ u16 max_phb; /* Mostly for debugging */
+ u16 living; /* Just increments!! */
+ u16 init_done; /* Initialisation over */
+ u16 booting_link;
+ u16 idle_count; /* Idle time counter */
+ u16 busy_count; /* Busy counter */
+ u16 idle_control; /* Control Idle Process */
+ u16 tx_intr; /* TX interrupt pending */
+ u16 rx_intr; /* RX interrupt pending */
+ u16 rup_intr; /* RUP interrupt pending */
+};
+
+#endif
+
+/*********** end of file ***********/
diff --git a/drivers/staging/generic_serial/rio/pci.h b/drivers/staging/generic_serial/rio/pci.h
new file mode 100644
index 000000000000..6032f9135956
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/pci.h
@@ -0,0 +1,72 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : pci.h
+** SID : 1.2
+** Last Modified : 11/6/98 11:34:12
+** Retrieved : 11/6/98 11:34:21
+**
+** ident @(#)pci.h 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rio_pci_h__
+#define __rio_pci_h__
+
+/*
+** PCI stuff
+*/
+
+#define PCITpFastClock 0x80
+#define PCITpSlowClock 0x00
+#define PCITpFastLinks 0x40
+#define PCITpSlowLinks 0x00
+#define PCITpIntEnable 0x04
+#define PCITpIntDisable 0x00
+#define PCITpBusEnable 0x02
+#define PCITpBusDisable 0x00
+#define PCITpBootFromRam 0x01
+#define PCITpBootFromLink 0x00
+
+#define RIO_PCI_VENDOR 0x11CB
+#define RIO_PCI_DEVICE 0x8000
+#define RIO_PCI_BASE_CLASS 0x02
+#define RIO_PCI_SUB_CLASS 0x80
+#define RIO_PCI_PROG_IFACE 0x00
+
+#define RIO_PCI_RID 0x0008
+#define RIO_PCI_BADR0 0x0010
+#define RIO_PCI_INTLN 0x003C
+#define RIO_PCI_INTPIN 0x003D
+
+#define RIO_PCI_MEM_SIZE 65536
+
+#define RIO_PCI_TURBO_TP 0x80
+#define RIO_PCI_FAST_LINKS 0x40
+#define RIO_PCI_INT_ENABLE 0x04
+#define RIO_PCI_TP_BUS_ENABLE 0x02
+#define RIO_PCI_BOOT_FROM_RAM 0x01
+
+#define RIO_PCI_DEFAULT_MODE 0x05
+
+#endif /* __rio_pci_h__ */
diff --git a/drivers/staging/generic_serial/rio/phb.h b/drivers/staging/generic_serial/rio/phb.h
new file mode 100644
index 000000000000..a4c48ae4e365
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/phb.h
@@ -0,0 +1,142 @@
+/****************************************************************************
+ ******* *******
+ ******* P H B H E A D E R *******
+ ******* *******
+ ****************************************************************************
+
+ Author : Ian Nandhra, Jeremy Rolls
+ Date :
+
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+
+ Version : 0.01
+
+
+ Mods
+ ----------------------------------------------------------------------------
+ Date By Description
+ ----------------------------------------------------------------------------
+
+ ***************************************************************************/
+
+#ifndef _phb_h
+#define _phb_h 1
+
+/*************************************************
+ * Handshake asserted. Deasserted by the LTT(s)
+ ************************************************/
+#define PHB_HANDSHAKE_SET ((ushort) 0x001) /* Set by LRT */
+
+#define PHB_HANDSHAKE_RESET ((ushort) 0x002) /* Set by ISR / driver */
+
+#define PHB_HANDSHAKE_FLAGS (PHB_HANDSHAKE_RESET | PHB_HANDSHAKE_SET)
+ /* Reset by ltt */
+
+
+/*************************************************
+ * Maximum number of PHB's
+ ************************************************/
+#define MAX_PHB ((ushort) 128) /* range 0-127 */
+
+/*************************************************
+ * Defines for the mode fields
+ ************************************************/
+#define TXPKT_INCOMPLETE 0x0001 /* Previous tx packet not completed */
+#define TXINTR_ENABLED 0x0002 /* Tx interrupt is enabled */
+#define TX_TAB3 0x0004 /* TAB3 mode */
+#define TX_OCRNL 0x0008 /* OCRNL mode */
+#define TX_ONLCR 0x0010 /* ONLCR mode */
+#define TX_SENDSPACES 0x0020 /* Send n spaces command needs
+ completing */
+#define TX_SENDNULL 0x0040 /* Escaping NULL needs completing */
+#define TX_SENDLF 0x0080 /* LF -> CR LF needs completing */
+#define TX_PARALLELBUG 0x0100 /* CD1400 LF -> CR LF bug on parallel
+ port */
+#define TX_HANGOVER (TX_SENDSPACES | TX_SENDLF | TX_SENDNULL)
+#define TX_DTRFLOW 0x0200 /* DTR tx flow control */
+#define TX_DTRFLOWED 0x0400 /* DTR is low - don't allow more data
+ into the FIFO */
+#define TX_DATAINFIFO 0x0800 /* There is data in the FIFO */
+#define TX_BUSY 0x1000 /* Data in FIFO, shift or holding regs */
+
+#define RX_SPARE 0x0001 /* SPARE */
+#define RXINTR_ENABLED 0x0002 /* Rx interrupt enabled */
+#define RX_ICRNL 0x0008 /* ICRNL mode */
+#define RX_INLCR 0x0010 /* INLCR mode */
+#define RX_IGNCR 0x0020 /* IGNCR mode */
+#define RX_CTSFLOW 0x0040 /* CTSFLOW enabled */
+#define RX_IXOFF 0x0080 /* IXOFF enabled */
+#define RX_CTSFLOWED 0x0100 /* CTSFLOW and CTS dropped */
+#define RX_IXOFFED 0x0200 /* IXOFF and xoff sent */
+#define RX_BUFFERED 0x0400 /* Try and pass on complete packets */
+
+#define PORT_ISOPEN 0x0001 /* Port open? */
+#define PORT_HUPCL 0x0002 /* Hangup on close? */
+#define PORT_MOPENPEND 0x0004 /* Modem open pending */
+#define PORT_ISPARALLEL 0x0008 /* Parallel port */
+#define PORT_BREAK 0x0010 /* Port on break */
+#define PORT_STATUSPEND 0x0020 /* Status packet pending */
+#define PORT_BREAKPEND 0x0040 /* Break packet pending */
+#define PORT_MODEMPEND 0x0080 /* Modem status packet pending */
+#define PORT_PARALLELBUG 0x0100 /* CD1400 LF -> CR LF bug on parallel
+ port */
+#define PORT_FULLMODEM 0x0200 /* Full modem signals */
+#define PORT_RJ45 0x0400 /* RJ45 connector - no RI signal */
+#define PORT_RESTRICTED 0x0600 /* Restricted connector - no RI / DTR */
+
+#define PORT_MODEMBITS 0x0600 /* Mask for modem fields */
+
+#define PORT_WCLOSE 0x0800 /* Waiting for close */
+#define PORT_HANDSHAKEFIX 0x1000 /* Port has H/W flow control fix */
+#define PORT_WASPCLOSED 0x2000 /* Port closed with PCLOSE */
+#define DUMPMODE 0x4000 /* Dump RTA mem */
+#define READ_REG 0x8000 /* Read CD1400 register */
+
+
+
+/**************************************************************************
+ * PHB Structure
+ * A few words.
+ *
+ * Normally Packets are added to the end of the list and removed from
+ * the start. The pointer tx_add points to a SPACE to put a Packet.
+ * The pointer tx_remove points to the next Packet to remove
+ *************************************************************************/
+
+struct PHB {
+ u8 source;
+ u8 handshake;
+ u8 status;
+ u16 timeout; /* Maximum of 1.9 seconds */
+ u8 link; /* Send down this link */
+ u8 destination;
+ u16 tx_start;
+ u16 tx_end;
+ u16 tx_add;
+ u16 tx_remove;
+
+ u16 rx_start;
+ u16 rx_end;
+ u16 rx_add;
+ u16 rx_remove;
+
+};
+
+#endif
+
+/*********** end of file ***********/
diff --git a/drivers/staging/generic_serial/rio/pkt.h b/drivers/staging/generic_serial/rio/pkt.h
new file mode 100644
index 000000000000..a9458164f02f
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/pkt.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+ ******* *******
+ ******* P A C K E T H E A D E R F I L E
+ ******* *******
+ ****************************************************************************
+
+ Author : Ian Nandhra / Jeremy Rolls
+ Date :
+
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+
+ Version : 0.01
+
+
+ Mods
+ ----------------------------------------------------------------------------
+ Date By Description
+ ----------------------------------------------------------------------------
+
+ ***************************************************************************/
+
+#ifndef _pkt_h
+#define _pkt_h 1
+
+#define PKT_CMD_BIT ((ushort) 0x080)
+#define PKT_CMD_DATA ((ushort) 0x080)
+
+#define PKT_ACK ((ushort) 0x040)
+
+#define PKT_TGL ((ushort) 0x020)
+
+#define PKT_LEN_MASK ((ushort) 0x07f)
+
+#define DATA_WNDW ((ushort) 0x10)
+#define PKT_TTL_MASK ((ushort) 0x0f)
+
+#define PKT_MAX_DATA_LEN 72
+
+#define PKT_LENGTH sizeof(struct PKT)
+#define SYNC_PKT_LENGTH (PKT_LENGTH + 4)
+
+#define CONTROL_PKT_LEN_MASK PKT_LEN_MASK
+#define CONTROL_PKT_CMD_BIT PKT_CMD_BIT
+#define CONTROL_PKT_ACK (PKT_ACK << 8)
+#define CONTROL_PKT_TGL (PKT_TGL << 8)
+#define CONTROL_PKT_TTL_MASK (PKT_TTL_MASK << 8)
+#define CONTROL_DATA_WNDW (DATA_WNDW << 8)
+
+struct PKT {
+ u8 dest_unit; /* Destination Unit Id */
+ u8 dest_port; /* Destination POrt */
+ u8 src_unit; /* Source Unit Id */
+ u8 src_port; /* Source POrt */
+ u8 len;
+ u8 control;
+ u8 data[PKT_MAX_DATA_LEN];
+ /* Actual data :-) */
+ u16 csum; /* C-SUM */
+};
+#endif
+
+/*********** end of file ***********/
diff --git a/drivers/staging/generic_serial/rio/port.h b/drivers/staging/generic_serial/rio/port.h
new file mode 100644
index 000000000000..49cf6d15ee54
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/port.h
@@ -0,0 +1,179 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : port.h
+** SID : 1.3
+** Last Modified : 11/6/98 11:34:12
+** Retrieved : 11/6/98 11:34:21
+**
+** ident @(#)port.h 1.3
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rio_port_h__
+#define __rio_port_h__
+
+/*
+** Port data structure
+*/
+struct Port {
+ struct gs_port gs;
+ int PortNum; /* RIO port no., 0-511 */
+ struct Host *HostP;
+ void __iomem *Caddr;
+ unsigned short HostPort; /* Port number on host card */
+ unsigned char RupNum; /* Number of RUP for port */
+ unsigned char ID2; /* Second ID of RTA for port */
+ unsigned long State; /* FLAGS for open & xopen */
+#define RIO_LOPEN 0x00001 /* Local open */
+#define RIO_MOPEN 0x00002 /* Modem open */
+#define RIO_WOPEN 0x00004 /* Waiting for open */
+#define RIO_CLOSING 0x00008 /* The port is being close */
+#define RIO_XPBUSY 0x00010 /* Transparent printer busy */
+#define RIO_BREAKING 0x00020 /* Break in progress */
+#define RIO_DIRECT 0x00040 /* Doing Direct output */
+#define RIO_EXCLUSIVE 0x00080 /* Stream open for exclusive use */
+#define RIO_NDELAY 0x00100 /* Stream is open FNDELAY */
+#define RIO_CARR_ON 0x00200 /* Stream has carrier present */
+#define RIO_XPWANTR 0x00400 /* Stream wanted by Xprint */
+#define RIO_RBLK 0x00800 /* Stream is read-blocked */
+#define RIO_BUSY 0x01000 /* Stream is BUSY for write */
+#define RIO_TIMEOUT 0x02000 /* Stream timeout in progress */
+#define RIO_TXSTOP 0x04000 /* Stream output is stopped */
+#define RIO_WAITFLUSH 0x08000 /* Stream waiting for flush */
+#define RIO_DYNOROD 0x10000 /* Drain failed */
+#define RIO_DELETED 0x20000 /* RTA has been deleted */
+#define RIO_ISSCANCODE 0x40000 /* This line is in scancode mode */
+#define RIO_USING_EUC 0x100000 /* Using extended Unix chars */
+#define RIO_CAN_COOK 0x200000 /* This line can do cooking */
+#define RIO_TRIAD_MODE 0x400000 /* Enable TRIAD special ops. */
+#define RIO_TRIAD_BLOCK 0x800000 /* Next read will block */
+#define RIO_TRIAD_FUNC 0x1000000 /* Seen a function key coming in */
+#define RIO_THROTTLE_RX 0x2000000 /* RX needs to be throttled. */
+
+ unsigned long Config; /* FLAGS for NOREAD.... */
+#define RIO_NOREAD 0x0001 /* Are not allowed to read port */
+#define RIO_NOWRITE 0x0002 /* Are not allowed to write port */
+#define RIO_NOXPRINT 0x0004 /* Are not allowed to xprint port */
+#define RIO_NOMASK 0x0007 /* All not allowed things */
+#define RIO_IXANY 0x0008 /* Port is allowed ixany */
+#define RIO_MODEM 0x0010 /* Stream is a modem device */
+#define RIO_IXON 0x0020 /* Port is allowed ixon */
+#define RIO_WAITDRAIN 0x0040 /* Wait for port to completely drain */
+#define RIO_MAP_50_TO_50 0x0080 /* Map 50 baud to 50 baud */
+#define RIO_MAP_110_TO_110 0x0100 /* Map 110 baud to 110 baud */
+
+/*
+** 15.10.1998 ARG - ESIL 0761 prt fix
+** As LynxOS does not appear to support Hardware Flow Control .....
+** Define our own flow control flags in 'Config'.
+*/
+#define RIO_CTSFLOW 0x0200 /* RIO's own CTSFLOW flag */
+#define RIO_RTSFLOW 0x0400 /* RIO's own RTSFLOW flag */
+
+
+ struct PHB __iomem *PhbP; /* pointer to PHB for port */
+ u16 __iomem *TxAdd; /* Add packets here */
+ u16 __iomem *TxStart; /* Start of add array */
+ u16 __iomem *TxEnd; /* End of add array */
+ u16 __iomem *RxRemove; /* Remove packets here */
+ u16 __iomem *RxStart; /* Start of remove array */
+ u16 __iomem *RxEnd; /* End of remove array */
+ unsigned int RtaUniqueNum; /* Unique number of RTA */
+ unsigned short PortState; /* status of port */
+ unsigned short ModemState; /* status of modem lines */
+ unsigned long ModemLines; /* Modem bits sent to RTA */
+ unsigned char CookMode; /* who expands CR/LF? */
+ unsigned char ParamSem; /* Prevent write during param */
+ unsigned char Mapped; /* if port mapped onto host */
+ unsigned char SecondBlock; /* if port belongs to 2nd block
+ of 16 port RTA */
+ unsigned char InUse; /* how many pre-emptive cmds */
+ unsigned char Lock; /* if params locked */
+ unsigned char Store; /* if params stored across closes */
+ unsigned char FirstOpen; /* TRUE if first time port opened */
+ unsigned char FlushCmdBodge; /* if doing a (non)flush */
+ unsigned char MagicFlags; /* require intr processing */
+#define MAGIC_FLUSH 0x01 /* mirror of WflushFlag */
+#define MAGIC_REBOOT 0x02 /* RTA re-booted, re-open ports */
+#define MORE_OUTPUT_EYGOR 0x04 /* riotproc failed to empty clists */
+ unsigned char WflushFlag; /* 1 How many WFLUSHs active */
+/*
+** Transparent print stuff
+*/
+ struct Xprint {
+#ifndef MAX_XP_CTRL_LEN
+#define MAX_XP_CTRL_LEN 16 /* ALSO IN DAEMON.H */
+#endif
+ unsigned int XpCps;
+ char XpOn[MAX_XP_CTRL_LEN];
+ char XpOff[MAX_XP_CTRL_LEN];
+ unsigned short XpLen; /* strlen(XpOn)+strlen(XpOff) */
+ unsigned char XpActive;
+ unsigned char XpLastTickOk; /* TRUE if we can process */
+#define XP_OPEN 00001
+#define XP_RUNABLE 00002
+ struct ttystatics *XttyP;
+ } Xprint;
+ unsigned char RxDataStart;
+ unsigned char Cor2Copy; /* copy of COR2 */
+ char *Name; /* points to the Rta's name */
+ char *TxRingBuffer;
+ unsigned short TxBufferIn; /* New data arrives here */
+ unsigned short TxBufferOut; /* Intr removes data here */
+ unsigned short OldTxBufferOut; /* Indicates if draining */
+ int TimeoutId; /* Timeout ID */
+ unsigned int Debug;
+ unsigned char WaitUntilBooted; /* True if open should block */
+ unsigned int statsGather; /* True if gathering stats */
+ unsigned long txchars; /* Chars transmitted */
+ unsigned long rxchars; /* Chars received */
+ unsigned long opens; /* port open count */
+ unsigned long closes; /* port close count */
+ unsigned long ioctls; /* ioctl count */
+ unsigned char LastRxTgl; /* Last state of rx toggle bit */
+ spinlock_t portSem; /* Lock using this sem */
+ int MonitorTstate; /* Monitoring ? */
+ int timeout_id; /* For calling 100 ms delays */
+ int timeout_sem; /* For calling 100 ms delays */
+ int firstOpen; /* First time open ? */
+ char *p; /* save the global struc here .. */
+};
+
+struct ModuleInfo {
+ char *Name;
+ unsigned int Flags[4]; /* one per port on a module */
+};
+
+/*
+** This struct is required because trying to grab an entire Port structure
+** runs into problems with differing struct sizes between driver and config.
+*/
+struct PortParams {
+ unsigned int Port;
+ unsigned long Config;
+ unsigned long State;
+ struct ttystatics *TtyP;
+};
+
+#endif
diff --git a/drivers/staging/generic_serial/rio/protsts.h b/drivers/staging/generic_serial/rio/protsts.h
new file mode 100644
index 000000000000..8ab79401d3ee
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/protsts.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+ ******* *******
+ ******* P R O T O C O L S T A T U S S T R U C T U R E *******
+ ******* *******
+ ****************************************************************************
+
+ Author : Ian Nandhra / Jeremy Rolls
+ Date :
+
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+
+ Version : 0.01
+
+
+ Mods
+ ----------------------------------------------------------------------------
+ Date By Description
+ ----------------------------------------------------------------------------
+
+ ***************************************************************************/
+
+#ifndef _protsts_h
+#define _protsts_h 1
+
+/*************************************************
+ * ACK bit. Last Packet received OK. Set by
+ * rxpkt to indicate that the Packet has been
+ * received OK and that the LTT must set the ACK
+ * bit in the next outward bound Packet
+ * and re-set by LTT's after xmit.
+ *
+ * Gets shoved into rx_status
+ ************************************************/
+#define PHB_RX_LAST_PKT_ACKED ((ushort) 0x080)
+
+/*******************************************************
+ * The Rx TOGGLE bit.
+ * Stuffed into rx_status by RXPKT
+ ******************************************************/
+#define PHB_RX_DATA_WNDW ((ushort) 0x040)
+
+/*******************************************************
+ * The Rx TOGGLE bit. Matches the setting in PKT.H
+ * Stuffed into rx_status
+ ******************************************************/
+#define PHB_RX_TGL ((ushort) 0x2000)
+
+
+/*************************************************
+ * This bit is set by the LRT to indicate that
+ * an ACK (packet) must be returned.
+ *
+ * Gets shoved into tx_status
+ ************************************************/
+#define PHB_TX_SEND_PKT_ACK ((ushort) 0x08)
+
+/*************************************************
+ * Set by LTT to indicate that an ACK is required
+ *************************************************/
+#define PHB_TX_ACK_RQRD ((ushort) 0x01)
+
+
+/*******************************************************
+ * The Tx TOGGLE bit.
+ * Stuffed into tx_status by RXPKT from the PKT WndW
+ * field. Looked by the LTT when the NEXT Packet
+ * is going to be sent.
+ ******************************************************/
+#define PHB_TX_DATA_WNDW ((ushort) 0x04)
+
+
+/*******************************************************
+ * The Tx TOGGLE bit. Matches the setting in PKT.H
+ * Stuffed into tx_status
+ ******************************************************/
+#define PHB_TX_TGL ((ushort) 0x02)
+
+/*******************************************************
+ * Request intr bit. Set when the queue has gone quiet
+ * and the PHB has requested an interrupt.
+ ******************************************************/
+#define PHB_TX_INTR ((ushort) 0x100)
+
+/*******************************************************
+ * SET if the PHB cannot send any more data down the
+ * Link
+ ******************************************************/
+#define PHB_TX_HANDSHAKE ((ushort) 0x010)
+
+
+#define RUP_SEND_WNDW ((ushort) 0x08) ;
+
+#endif
+
+/*********** end of file ***********/
diff --git a/drivers/staging/generic_serial/rio/rio.h b/drivers/staging/generic_serial/rio/rio.h
new file mode 100644
index 000000000000..1bf36223a4e8
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/rio.h
@@ -0,0 +1,208 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 1998 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : rio.h
+** SID : 1.3
+** Last Modified : 11/6/98 11:34:13
+** Retrieved : 11/6/98 11:34:22
+**
+** ident @(#)rio.h 1.3
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rio_rio_h__
+#define __rio_rio_h__
+
+/*
+** Maximum numbers of things
+*/
+#define RIO_SLOTS 4 /* number of configuration slots */
+#define RIO_HOSTS 4 /* number of hosts that can be found */
+#define PORTS_PER_HOST 128 /* number of ports per host */
+#define LINKS_PER_UNIT 4 /* number of links from a host */
+#define RIO_PORTS (PORTS_PER_HOST * RIO_HOSTS) /* max. no. of ports */
+#define RTAS_PER_HOST (MAX_RUP) /* number of RTAs per host */
+#define PORTS_PER_RTA (PORTS_PER_HOST/RTAS_PER_HOST) /* ports on a rta */
+#define PORTS_PER_MODULE 4 /* number of ports on a plug-in module */
+ /* number of modules on an RTA */
+#define MODULES_PER_RTA (PORTS_PER_RTA/PORTS_PER_MODULE)
+#define MAX_PRODUCT 16 /* numbr of different product codes */
+#define MAX_MODULE_TYPES 16 /* number of different types of module */
+
+#define RIO_CONTROL_DEV 128 /* minor number of host/control device */
+#define RIO_INVALID_MAJOR 0 /* test first host card's major no for validity */
+
+/*
+** number of RTAs that can be bound to a master
+*/
+#define MAX_RTA_BINDINGS (MAX_RUP * RIO_HOSTS)
+
+/*
+** Unit types
+*/
+#define PC_RTA16 0x90000000
+#define PC_RTA8 0xe0000000
+#define TYPE_HOST 0
+#define TYPE_RTA8 1
+#define TYPE_RTA16 2
+
+/*
+** Flag values returned by functions
+*/
+
+#define RIO_FAIL -1
+
+/*
+** SysPort value for something that hasn't any ports
+*/
+#define NO_PORT 0xFFFFFFFF
+
+/*
+** Unit ID Of all hosts
+*/
+#define HOST_ID 0
+
+/*
+** Break bytes into nybles
+*/
+#define LONYBLE(X) ((X) & 0xF)
+#define HINYBLE(X) (((X)>>4) & 0xF)
+
+/*
+** Flag values passed into some functions
+*/
+#define DONT_SLEEP 0
+#define OK_TO_SLEEP 1
+
+#define DONT_PRINT 1
+#define DO_PRINT 0
+
+#define PRINT_TO_LOG_CONS 0
+#define PRINT_TO_CONS 1
+#define PRINT_TO_LOG 2
+
+/*
+** Timeout has trouble with times of less than 3 ticks...
+*/
+#define MIN_TIMEOUT 3
+
+/*
+** Generally useful constants
+*/
+
+#define HUNDRED_MS ((HZ/10)?(HZ/10):1)
+#define ONE_MEG 0x100000
+#define SIXTY_FOUR_K 0x10000
+
+#define RIO_AT_MEM_SIZE SIXTY_FOUR_K
+#define RIO_EISA_MEM_SIZE SIXTY_FOUR_K
+#define RIO_MCA_MEM_SIZE SIXTY_FOUR_K
+
+#define COOK_WELL 0
+#define COOK_MEDIUM 1
+#define COOK_RAW 2
+
+/*
+** Pointer manipulation stuff
+** RIO_PTR takes hostp->Caddr and the offset into the DP RAM area
+** and produces a UNIX caddr_t (pointer) to the object
+** RIO_OBJ takes hostp->Caddr and a UNIX pointer to an object and
+** returns the offset into the DP RAM area.
+*/
+#define RIO_PTR(C,O) (((unsigned char __iomem *)(C))+(0xFFFF&(O)))
+#define RIO_OFF(C,O) ((unsigned char __iomem *)(O)-(unsigned char __iomem *)(C))
+
+/*
+** How to convert from various different device number formats:
+** DEV is a dev number, as passed to open, close etc - NOT a minor
+** number!
+**/
+
+#define RIO_MODEM_MASK 0x1FF
+#define RIO_MODEM_BIT 0x200
+#define RIO_UNMODEM(DEV) (MINOR(DEV) & RIO_MODEM_MASK)
+#define RIO_ISMODEM(DEV) (MINOR(DEV) & RIO_MODEM_BIT)
+#define RIO_PORT(DEV,FIRST_MAJ) ( (MAJOR(DEV) - FIRST_MAJ) * PORTS_PER_HOST) \
+ + MINOR(DEV)
+#define CSUM(pkt_ptr) (((u16 *)(pkt_ptr))[0] + ((u16 *)(pkt_ptr))[1] + \
+ ((u16 *)(pkt_ptr))[2] + ((u16 *)(pkt_ptr))[3] + \
+ ((u16 *)(pkt_ptr))[4] + ((u16 *)(pkt_ptr))[5] + \
+ ((u16 *)(pkt_ptr))[6] + ((u16 *)(pkt_ptr))[7] + \
+ ((u16 *)(pkt_ptr))[8] + ((u16 *)(pkt_ptr))[9] )
+
+#define RIO_LINK_ENABLE 0x80FF /* FF is a hack, mainly for Mips, to */
+ /* prevent a really stupid race condition. */
+
+#define NOT_INITIALISED 0
+#define INITIALISED 1
+
+#define NOT_POLLING 0
+#define POLLING 1
+
+#define NOT_CHANGED 0
+#define CHANGED 1
+
+#define NOT_INUSE 0
+
+#define DISCONNECT 0
+#define CONNECT 1
+
+/* ------ Control Codes ------ */
+
+#define CONTROL '^'
+#define IFOAD ( CONTROL + 1 )
+#define IDENTIFY ( CONTROL + 2 )
+#define ZOMBIE ( CONTROL + 3 )
+#define UFOAD ( CONTROL + 4 )
+#define IWAIT ( CONTROL + 5 )
+
+#define IFOAD_MAGIC 0xF0AD /* of course */
+#define ZOMBIE_MAGIC (~0xDEAD) /* not dead -> zombie */
+#define UFOAD_MAGIC 0xD1E /* kill-your-neighbour */
+#define IWAIT_MAGIC 0xB1DE /* Bide your time */
+
+/* ------ Error Codes ------ */
+
+#define E_NO_ERROR ((ushort) 0)
+
+/* ------ Free Lists ------ */
+
+struct rio_free_list {
+ u16 next;
+ u16 prev;
+};
+
+/* NULL for card side linked lists */
+#define TPNULL ((ushort)(0x8000))
+/* We can add another packet to a transmit queue if the packet pointer pointed
+ * to by the TxAdd pointer has PKT_IN_USE clear in its address. */
+#define PKT_IN_USE 0x1
+
+/* ------ Topology ------ */
+
+struct Top {
+ u8 Unit;
+ u8 Link;
+};
+
+#endif /* __rio_h__ */
diff --git a/drivers/staging/generic_serial/rio/rio_linux.c b/drivers/staging/generic_serial/rio/rio_linux.c
new file mode 100644
index 000000000000..5e33293d24e3
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/rio_linux.c
@@ -0,0 +1,1204 @@
+
+/* rio_linux.c -- Linux driver for the Specialix RIO series cards.
+ *
+ *
+ * (C) 1999 R.E.Wolff@BitWizard.nl
+ *
+ * Specialix pays for the development and support of this driver.
+ * Please DO contact support@specialix.co.uk if you require
+ * support. But please read the documentation (rio.txt) first.
+ *
+ *
+ *
+ * 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/kdev_t.h>
+#include <asm/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/slab.h>
+#include <linux/mutex.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+
+#include <linux/generic_serial.h>
+#include <asm/uaccess.h>
+
+#include "linux_compat.h"
+#include "pkt.h"
+#include "daemon.h"
+#include "rio.h"
+#include "riospace.h"
+#include "cmdpkt.h"
+#include "map.h"
+#include "rup.h"
+#include "port.h"
+#include "riodrvr.h"
+#include "rioinfo.h"
+#include "func.h"
+#include "errors.h"
+#include "pci.h"
+
+#include "parmmap.h"
+#include "unixrup.h"
+#include "board.h"
+#include "host.h"
+#include "phb.h"
+#include "link.h"
+#include "cmdblk.h"
+#include "route.h"
+#include "cirrus.h"
+#include "rioioctl.h"
+#include "param.h"
+#include "protsts.h"
+#include "rioboard.h"
+
+
+#include "rio_linux.h"
+
+/* I don't think that this driver can handle more than 512 ports on
+one machine. Specialix specifies max 4 boards in one machine. I don't
+know why. If you want to try anyway you'll have to increase the number
+of boards in rio.h. You'll have to allocate more majors if you need
+more than 512 ports.... */
+
+#ifndef RIO_NORMAL_MAJOR0
+/* This allows overriding on the compiler commandline, or in a "major.h"
+ include or something like that */
+#define RIO_NORMAL_MAJOR0 154
+#define RIO_NORMAL_MAJOR1 156
+#endif
+
+#ifndef PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8
+#define PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8 0x2000
+#endif
+
+#ifndef RIO_WINDOW_LEN
+#define RIO_WINDOW_LEN 0x10000
+#endif
+
+
+/* Configurable options:
+ (Don't be too sure that it'll work if you toggle them) */
+
+/* Am I paranoid or not ? ;-) */
+#undef RIO_PARANOIA_CHECK
+
+
+/* 20 -> 2000 per second. The card should rate-limit interrupts at 1000
+ Hz, but it is user configurable. I don't recommend going above 1000
+ Hz. The interrupt ratelimit might trigger if the interrupt is
+ shared with a very active other device.
+ undef this if you want to disable the check....
+*/
+#define IRQ_RATE_LIMIT 200
+
+
+/* These constants are derived from SCO Source */
+static DEFINE_MUTEX(rio_fw_mutex);
+static struct Conf
+ RIOConf = {
+ /* locator */ "RIO Config here",
+ /* startuptime */ HZ * 2,
+ /* how long to wait for card to run */
+ /* slowcook */ 0,
+ /* TRUE -> always use line disc. */
+ /* intrpolltime */ 1,
+ /* The frequency of OUR polls */
+ /* breakinterval */ 25,
+ /* x10 mS XXX: units seem to be 1ms not 10! -- REW */
+ /* timer */ 10,
+ /* mS */
+ /* RtaLoadBase */ 0x7000,
+ /* HostLoadBase */ 0x7C00,
+ /* XpHz */ 5,
+ /* number of Xprint hits per second */
+ /* XpCps */ 120,
+ /* Xprint characters per second */
+ /* XpOn */ "\033d#",
+ /* start Xprint for a wyse 60 */
+ /* XpOff */ "\024",
+ /* end Xprint for a wyse 60 */
+ /* MaxXpCps */ 2000,
+ /* highest Xprint speed */
+ /* MinXpCps */ 10,
+ /* slowest Xprint speed */
+ /* SpinCmds */ 1,
+ /* non-zero for mega fast boots */
+ /* First Addr */ 0x0A0000,
+ /* First address to look at */
+ /* Last Addr */ 0xFF0000,
+ /* Last address looked at */
+ /* BufferSize */ 1024,
+ /* Bytes per port of buffering */
+ /* LowWater */ 256,
+ /* how much data left before wakeup */
+ /* LineLength */ 80,
+ /* how wide is the console? */
+ /* CmdTimeout */ HZ,
+ /* how long a close command may take */
+};
+
+
+
+
+/* Function prototypes */
+
+static void rio_disable_tx_interrupts(void *ptr);
+static void rio_enable_tx_interrupts(void *ptr);
+static void rio_disable_rx_interrupts(void *ptr);
+static void rio_enable_rx_interrupts(void *ptr);
+static int rio_carrier_raised(struct tty_port *port);
+static void rio_shutdown_port(void *ptr);
+static int rio_set_real_termios(void *ptr);
+static void rio_hungup(void *ptr);
+static void rio_close(void *ptr);
+static int rio_chars_in_buffer(void *ptr);
+static long rio_fw_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+static int rio_init_drivers(void);
+
+static void my_hd(void *addr, int len);
+
+static struct tty_driver *rio_driver, *rio_driver2;
+
+/* The name "p" is a bit non-descript. But that's what the rio-lynxos
+sources use all over the place. */
+struct rio_info *p;
+
+int rio_debug;
+
+
+/* You can have the driver poll your card.
+ - Set rio_poll to 1 to poll every timer tick (10ms on Intel).
+ This is used when the card cannot use an interrupt for some reason.
+*/
+static int rio_poll = 1;
+
+
+/* These are the only open spaces in my computer. Yours may have more
+ or less.... */
+static int rio_probe_addrs[] = { 0xc0000, 0xd0000, 0xe0000 };
+
+#define NR_RIO_ADDRS ARRAY_SIZE(rio_probe_addrs)
+
+
+/* Set the mask to all-ones. This alas, only supports 32 interrupts.
+ Some architectures may need more. -- Changed to LONG to
+ support up to 64 bits on 64bit architectures. -- REW 20/06/99 */
+static long rio_irqmask = -1;
+
+MODULE_AUTHOR("Rogier Wolff <R.E.Wolff@bitwizard.nl>, Patrick van de Lageweg <patrick@bitwizard.nl>");
+MODULE_DESCRIPTION("RIO driver");
+MODULE_LICENSE("GPL");
+module_param(rio_poll, int, 0);
+module_param(rio_debug, int, 0644);
+module_param(rio_irqmask, long, 0);
+
+static struct real_driver rio_real_driver = {
+ rio_disable_tx_interrupts,
+ rio_enable_tx_interrupts,
+ rio_disable_rx_interrupts,
+ rio_enable_rx_interrupts,
+ rio_shutdown_port,
+ rio_set_real_termios,
+ rio_chars_in_buffer,
+ rio_close,
+ rio_hungup,
+ NULL
+};
+
+/*
+ * Firmware loader driver specific routines
+ *
+ */
+
+static const struct file_operations rio_fw_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = rio_fw_ioctl,
+ .llseek = noop_llseek,
+};
+
+static struct miscdevice rio_fw_device = {
+ RIOCTL_MISC_MINOR, "rioctl", &rio_fw_fops
+};
+
+
+
+
+
+#ifdef RIO_PARANOIA_CHECK
+
+/* This doesn't work. Who's paranoid around here? Not me! */
+
+static inline int rio_paranoia_check(struct rio_port const *port, char *name, const char *routine)
+{
+
+ static const char *badmagic = KERN_ERR "rio: Warning: bad rio port magic number for device %s in %s\n";
+ static const char *badinfo = KERN_ERR "rio: Warning: null rio port for device %s in %s\n";
+
+ if (!port) {
+ printk(badinfo, name, routine);
+ return 1;
+ }
+ if (port->magic != RIO_MAGIC) {
+ printk(badmagic, name, routine);
+ return 1;
+ }
+
+ return 0;
+}
+#else
+#define rio_paranoia_check(a,b,c) 0
+#endif
+
+
+#ifdef DEBUG
+static void my_hd(void *ad, int len)
+{
+ int i, j, ch;
+ unsigned char *addr = ad;
+
+ for (i = 0; i < len; i += 16) {
+ rio_dprintk(RIO_DEBUG_PARAM, "%08lx ", (unsigned long) addr + i);
+ for (j = 0; j < 16; j++) {
+ rio_dprintk(RIO_DEBUG_PARAM, "%02x %s", addr[j + i], (j == 7) ? " " : "");
+ }
+ for (j = 0; j < 16; j++) {
+ ch = addr[j + i];
+ rio_dprintk(RIO_DEBUG_PARAM, "%c", (ch < 0x20) ? '.' : ((ch > 0x7f) ? '.' : ch));
+ }
+ rio_dprintk(RIO_DEBUG_PARAM, "\n");
+ }
+}
+#else
+#define my_hd(ad,len) do{/* nothing*/ } while (0)
+#endif
+
+
+/* Delay a number of jiffies, allowing a signal to interrupt */
+int RIODelay(struct Port *PortP, int njiffies)
+{
+ func_enter();
+
+ rio_dprintk(RIO_DEBUG_DELAY, "delaying %d jiffies\n", njiffies);
+ msleep_interruptible(jiffies_to_msecs(njiffies));
+ func_exit();
+
+ if (signal_pending(current))
+ return RIO_FAIL;
+ else
+ return !RIO_FAIL;
+}
+
+
+/* Delay a number of jiffies, disallowing a signal to interrupt */
+int RIODelay_ni(struct Port *PortP, int njiffies)
+{
+ func_enter();
+
+ rio_dprintk(RIO_DEBUG_DELAY, "delaying %d jiffies (ni)\n", njiffies);
+ msleep(jiffies_to_msecs(njiffies));
+ func_exit();
+ return !RIO_FAIL;
+}
+
+void rio_copy_to_card(void *from, void __iomem *to, int len)
+{
+ rio_copy_toio(to, from, len);
+}
+
+int rio_minor(struct tty_struct *tty)
+{
+ return tty->index + ((tty->driver == rio_driver) ? 0 : 256);
+}
+
+static int rio_set_real_termios(void *ptr)
+{
+ return RIOParam((struct Port *) ptr, RIOC_CONFIG, 1, 1);
+}
+
+
+static void rio_reset_interrupt(struct Host *HostP)
+{
+ func_enter();
+
+ switch (HostP->Type) {
+ case RIO_AT:
+ case RIO_MCA:
+ case RIO_PCI:
+ writeb(0xFF, &HostP->ResetInt);
+ }
+
+ func_exit();
+}
+
+
+static irqreturn_t rio_interrupt(int irq, void *ptr)
+{
+ struct Host *HostP;
+ func_enter();
+
+ HostP = ptr; /* &p->RIOHosts[(long)ptr]; */
+ rio_dprintk(RIO_DEBUG_IFLOW, "rio: enter rio_interrupt (%d/%d)\n", irq, HostP->Ivec);
+
+ /* AAargh! The order in which to do these things is essential and
+ not trivial.
+
+ - hardware twiddling goes before "recursive". Otherwise when we
+ poll the card, and a recursive interrupt happens, we won't
+ ack the card, so it might keep on interrupting us. (especially
+ level sensitive interrupt systems like PCI).
+
+ - Rate limit goes before hardware twiddling. Otherwise we won't
+ catch a card that has gone bonkers.
+
+ - The "initialized" test goes after the hardware twiddling. Otherwise
+ the card will stick us in the interrupt routine again.
+
+ - The initialized test goes before recursive.
+ */
+
+ rio_dprintk(RIO_DEBUG_IFLOW, "rio: We've have noticed the interrupt\n");
+ if (HostP->Ivec == irq) {
+ /* Tell the card we've noticed the interrupt. */
+ rio_reset_interrupt(HostP);
+ }
+
+ if ((HostP->Flags & RUN_STATE) != RC_RUNNING)
+ return IRQ_HANDLED;
+
+ if (test_and_set_bit(RIO_BOARD_INTR_LOCK, &HostP->locks)) {
+ printk(KERN_ERR "Recursive interrupt! (host %p/irq%d)\n", ptr, HostP->Ivec);
+ return IRQ_HANDLED;
+ }
+
+ RIOServiceHost(p, HostP);
+
+ rio_dprintk(RIO_DEBUG_IFLOW, "riointr() doing host %p type %d\n", ptr, HostP->Type);
+
+ clear_bit(RIO_BOARD_INTR_LOCK, &HostP->locks);
+ rio_dprintk(RIO_DEBUG_IFLOW, "rio: exit rio_interrupt (%d/%d)\n", irq, HostP->Ivec);
+ func_exit();
+ return IRQ_HANDLED;
+}
+
+
+static void rio_pollfunc(unsigned long data)
+{
+ func_enter();
+
+ rio_interrupt(0, &p->RIOHosts[data]);
+ mod_timer(&p->RIOHosts[data].timer, jiffies + rio_poll);
+
+ func_exit();
+}
+
+
+/* ********************************************************************** *
+ * Here are the routines that actually *
+ * interface with the generic_serial driver *
+ * ********************************************************************** */
+
+/* Ehhm. I don't know how to fiddle with interrupts on the Specialix
+ cards. .... Hmm. Ok I figured it out. You don't. -- REW */
+
+static void rio_disable_tx_interrupts(void *ptr)
+{
+ func_enter();
+
+ /* port->gs.port.flags &= ~GS_TX_INTEN; */
+
+ func_exit();
+}
+
+
+static void rio_enable_tx_interrupts(void *ptr)
+{
+ struct Port *PortP = ptr;
+ /* int hn; */
+
+ func_enter();
+
+ /* hn = PortP->HostP - p->RIOHosts;
+
+ rio_dprintk (RIO_DEBUG_TTY, "Pushing host %d\n", hn);
+ rio_interrupt (-1,(void *) hn, NULL); */
+
+ RIOTxEnable((char *) PortP);
+
+ /*
+ * In general we cannot count on "tx empty" interrupts, although
+ * the interrupt routine seems to be able to tell the difference.
+ */
+ PortP->gs.port.flags &= ~GS_TX_INTEN;
+
+ func_exit();
+}
+
+
+static void rio_disable_rx_interrupts(void *ptr)
+{
+ func_enter();
+ func_exit();
+}
+
+static void rio_enable_rx_interrupts(void *ptr)
+{
+ /* struct rio_port *port = ptr; */
+ func_enter();
+ func_exit();
+}
+
+
+/* Jeez. Isn't this simple? */
+static int rio_carrier_raised(struct tty_port *port)
+{
+ struct Port *PortP = container_of(port, struct Port, gs.port);
+ int rv;
+
+ func_enter();
+ rv = (PortP->ModemState & RIOC_MSVR1_CD) != 0;
+
+ rio_dprintk(RIO_DEBUG_INIT, "Getting CD status: %d\n", rv);
+
+ func_exit();
+ return rv;
+}
+
+
+/* Jeez. Isn't this simple? Actually, we can sync with the actual port
+ by just pushing stuff into the queue going to the port... */
+static int rio_chars_in_buffer(void *ptr)
+{
+ func_enter();
+
+ func_exit();
+ return 0;
+}
+
+
+/* Nothing special here... */
+static void rio_shutdown_port(void *ptr)
+{
+ struct Port *PortP;
+
+ func_enter();
+
+ PortP = (struct Port *) ptr;
+ PortP->gs.port.tty = NULL;
+ func_exit();
+}
+
+
+/* I haven't the foggiest why the decrement use count has to happen
+ here. The whole linux serial drivers stuff needs to be redesigned.
+ My guess is that this is a hack to minimize the impact of a bug
+ elsewhere. Thinking about it some more. (try it sometime) Try
+ running minicom on a serial port that is driven by a modularized
+ driver. Have the modem hangup. Then remove the driver module. Then
+ exit minicom. I expect an "oops". -- REW */
+static void rio_hungup(void *ptr)
+{
+ struct Port *PortP;
+
+ func_enter();
+
+ PortP = (struct Port *) ptr;
+ PortP->gs.port.tty = NULL;
+
+ func_exit();
+}
+
+
+/* The standard serial_close would become shorter if you'd wrap it like
+ this.
+ rs_close (...){save_flags;cli;real_close();dec_use_count;restore_flags;}
+ */
+static void rio_close(void *ptr)
+{
+ struct Port *PortP;
+
+ func_enter();
+
+ PortP = (struct Port *) ptr;
+
+ riotclose(ptr);
+
+ if (PortP->gs.port.count) {
+ printk(KERN_ERR "WARNING port count:%d\n", PortP->gs.port.count);
+ PortP->gs.port.count = 0;
+ }
+
+ PortP->gs.port.tty = NULL;
+ func_exit();
+}
+
+
+
+static long rio_fw_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+ func_enter();
+
+ /* The "dev" argument isn't used. */
+ mutex_lock(&rio_fw_mutex);
+ rc = riocontrol(p, 0, cmd, arg, capable(CAP_SYS_ADMIN));
+ mutex_unlock(&rio_fw_mutex);
+
+ func_exit();
+ return rc;
+}
+
+extern int RIOShortCommand(struct rio_info *p, struct Port *PortP, int command, int len, int arg);
+
+static int rio_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int rc;
+ struct Port *PortP;
+ int ival;
+
+ func_enter();
+
+ PortP = (struct Port *) tty->driver_data;
+
+ rc = 0;
+ switch (cmd) {
+ case TIOCSSOFTCAR:
+ if ((rc = get_user(ival, (unsigned __user *) argp)) == 0) {
+ tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (ival ? CLOCAL : 0);
+ }
+ break;
+ case TIOCGSERIAL:
+ rc = -EFAULT;
+ if (access_ok(VERIFY_WRITE, argp, sizeof(struct serial_struct)))
+ rc = gs_getserial(&PortP->gs, argp);
+ break;
+ case TCSBRK:
+ if (PortP->State & RIO_DELETED) {
+ rio_dprintk(RIO_DEBUG_TTY, "BREAK on deleted RTA\n");
+ rc = -EIO;
+ } else {
+ if (RIOShortCommand(p, PortP, RIOC_SBREAK, 2, 250) ==
+ RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_INTR, "SBREAK RIOShortCommand failed\n");
+ rc = -EIO;
+ }
+ }
+ break;
+ case TCSBRKP:
+ if (PortP->State & RIO_DELETED) {
+ rio_dprintk(RIO_DEBUG_TTY, "BREAK on deleted RTA\n");
+ rc = -EIO;
+ } else {
+ int l;
+ l = arg ? arg * 100 : 250;
+ if (l > 255)
+ l = 255;
+ if (RIOShortCommand(p, PortP, RIOC_SBREAK, 2,
+ arg ? arg * 100 : 250) == RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_INTR, "SBREAK RIOShortCommand failed\n");
+ rc = -EIO;
+ }
+ }
+ break;
+ case TIOCSSERIAL:
+ rc = -EFAULT;
+ if (access_ok(VERIFY_READ, argp, sizeof(struct serial_struct)))
+ rc = gs_setserial(&PortP->gs, argp);
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+ func_exit();
+ return rc;
+}
+
+
+/* The throttle/unthrottle scheme for the Specialix card is different
+ * from other drivers and deserves some explanation.
+ * The Specialix hardware takes care of XON/XOFF
+ * and CTS/RTS flow control itself. This means that all we have to
+ * do when signalled by the upper tty layer to throttle/unthrottle is
+ * to make a note of it here. When we come to read characters from the
+ * rx buffers on the card (rio_receive_chars()) we look to see if the
+ * upper layer can accept more (as noted here in rio_rx_throt[]).
+ * If it can't we simply don't remove chars from the cards buffer.
+ * When the tty layer can accept chars, we again note that here and when
+ * rio_receive_chars() is called it will remove them from the cards buffer.
+ * The card will notice that a ports buffer has drained below some low
+ * water mark and will unflow control the line itself, using whatever
+ * flow control scheme is in use for that port. -- Simon Allen
+ */
+
+static void rio_throttle(struct tty_struct *tty)
+{
+ struct Port *port = (struct Port *) tty->driver_data;
+
+ func_enter();
+ /* If the port is using any type of input flow
+ * control then throttle the port.
+ */
+
+ if ((tty->termios->c_cflag & CRTSCTS) || (I_IXOFF(tty))) {
+ port->State |= RIO_THROTTLE_RX;
+ }
+
+ func_exit();
+}
+
+
+static void rio_unthrottle(struct tty_struct *tty)
+{
+ struct Port *port = (struct Port *) tty->driver_data;
+
+ func_enter();
+ /* Always unthrottle even if flow control is not enabled on
+ * this port in case we disabled flow control while the port
+ * was throttled
+ */
+
+ port->State &= ~RIO_THROTTLE_RX;
+
+ func_exit();
+ return;
+}
+
+
+
+
+
+/* ********************************************************************** *
+ * Here are the initialization routines. *
+ * ********************************************************************** */
+
+
+static struct vpd_prom *get_VPD_PROM(struct Host *hp)
+{
+ static struct vpd_prom vpdp;
+ char *p;
+ int i;
+
+ func_enter();
+ rio_dprintk(RIO_DEBUG_PROBE, "Going to verify vpd prom at %p.\n", hp->Caddr + RIO_VPD_ROM);
+
+ p = (char *) &vpdp;
+ for (i = 0; i < sizeof(struct vpd_prom); i++)
+ *p++ = readb(hp->Caddr + RIO_VPD_ROM + i * 2);
+ /* read_rio_byte (hp, RIO_VPD_ROM + i*2); */
+
+ /* Terminate the identifier string.
+ *** requires one extra byte in struct vpd_prom *** */
+ *p++ = 0;
+
+ if (rio_debug & RIO_DEBUG_PROBE)
+ my_hd((char *) &vpdp, 0x20);
+
+ func_exit();
+
+ return &vpdp;
+}
+
+static const struct tty_operations rio_ops = {
+ .open = riotopen,
+ .close = gs_close,
+ .write = gs_write,
+ .put_char = gs_put_char,
+ .flush_chars = gs_flush_chars,
+ .write_room = gs_write_room,
+ .chars_in_buffer = gs_chars_in_buffer,
+ .flush_buffer = gs_flush_buffer,
+ .ioctl = rio_ioctl,
+ .throttle = rio_throttle,
+ .unthrottle = rio_unthrottle,
+ .set_termios = gs_set_termios,
+ .stop = gs_stop,
+ .start = gs_start,
+ .hangup = gs_hangup,
+};
+
+static int rio_init_drivers(void)
+{
+ int error = -ENOMEM;
+
+ rio_driver = alloc_tty_driver(256);
+ if (!rio_driver)
+ goto out;
+ rio_driver2 = alloc_tty_driver(256);
+ if (!rio_driver2)
+ goto out1;
+
+ func_enter();
+
+ rio_driver->owner = THIS_MODULE;
+ rio_driver->driver_name = "specialix_rio";
+ rio_driver->name = "ttySR";
+ rio_driver->major = RIO_NORMAL_MAJOR0;
+ rio_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ rio_driver->subtype = SERIAL_TYPE_NORMAL;
+ rio_driver->init_termios = tty_std_termios;
+ rio_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ rio_driver->flags = TTY_DRIVER_REAL_RAW;
+ tty_set_operations(rio_driver, &rio_ops);
+
+ rio_driver2->owner = THIS_MODULE;
+ rio_driver2->driver_name = "specialix_rio";
+ rio_driver2->name = "ttySR";
+ rio_driver2->major = RIO_NORMAL_MAJOR1;
+ rio_driver2->type = TTY_DRIVER_TYPE_SERIAL;
+ rio_driver2->subtype = SERIAL_TYPE_NORMAL;
+ rio_driver2->init_termios = tty_std_termios;
+ rio_driver2->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ rio_driver2->flags = TTY_DRIVER_REAL_RAW;
+ tty_set_operations(rio_driver2, &rio_ops);
+
+ rio_dprintk(RIO_DEBUG_INIT, "set_termios = %p\n", gs_set_termios);
+
+ if ((error = tty_register_driver(rio_driver)))
+ goto out2;
+ if ((error = tty_register_driver(rio_driver2)))
+ goto out3;
+ func_exit();
+ return 0;
+ out3:
+ tty_unregister_driver(rio_driver);
+ out2:
+ put_tty_driver(rio_driver2);
+ out1:
+ put_tty_driver(rio_driver);
+ out:
+ printk(KERN_ERR "rio: Couldn't register a rio driver, error = %d\n", error);
+ return 1;
+}
+
+static const struct tty_port_operations rio_port_ops = {
+ .carrier_raised = rio_carrier_raised,
+};
+
+static int rio_init_datastructures(void)
+{
+ int i;
+ struct Port *port;
+ func_enter();
+
+ /* Many drivers statically allocate the maximum number of ports
+ There is no reason not to allocate them dynamically. Is there? -- REW */
+ /* However, the RIO driver allows users to configure their first
+ RTA as the ports numbered 504-511. We therefore need to allocate
+ the whole range. :-( -- REW */
+
+#define RI_SZ sizeof(struct rio_info)
+#define HOST_SZ sizeof(struct Host)
+#define PORT_SZ sizeof(struct Port *)
+#define TMIO_SZ sizeof(struct termios *)
+ rio_dprintk(RIO_DEBUG_INIT, "getting : %Zd %Zd %Zd %Zd %Zd bytes\n", RI_SZ, RIO_HOSTS * HOST_SZ, RIO_PORTS * PORT_SZ, RIO_PORTS * TMIO_SZ, RIO_PORTS * TMIO_SZ);
+
+ if (!(p = kzalloc(RI_SZ, GFP_KERNEL)))
+ goto free0;
+ if (!(p->RIOHosts = kzalloc(RIO_HOSTS * HOST_SZ, GFP_KERNEL)))
+ goto free1;
+ if (!(p->RIOPortp = kzalloc(RIO_PORTS * PORT_SZ, GFP_KERNEL)))
+ goto free2;
+ p->RIOConf = RIOConf;
+ rio_dprintk(RIO_DEBUG_INIT, "Got : %p %p %p\n", p, p->RIOHosts, p->RIOPortp);
+
+#if 1
+ for (i = 0; i < RIO_PORTS; i++) {
+ port = p->RIOPortp[i] = kzalloc(sizeof(struct Port), GFP_KERNEL);
+ if (!port) {
+ goto free6;
+ }
+ rio_dprintk(RIO_DEBUG_INIT, "initing port %d (%d)\n", i, port->Mapped);
+ tty_port_init(&port->gs.port);
+ port->gs.port.ops = &rio_port_ops;
+ port->PortNum = i;
+ port->gs.magic = RIO_MAGIC;
+ port->gs.close_delay = HZ / 2;
+ port->gs.closing_wait = 30 * HZ;
+ port->gs.rd = &rio_real_driver;
+ spin_lock_init(&port->portSem);
+ }
+#else
+ /* We could postpone initializing them to when they are configured. */
+#endif
+
+
+
+ if (rio_debug & RIO_DEBUG_INIT) {
+ my_hd(&rio_real_driver, sizeof(rio_real_driver));
+ }
+
+
+ func_exit();
+ return 0;
+
+ free6:for (i--; i >= 0; i--)
+ kfree(p->RIOPortp[i]);
+/*free5:
+ free4:
+ free3:*/ kfree(p->RIOPortp);
+ free2:kfree(p->RIOHosts);
+ free1:
+ rio_dprintk(RIO_DEBUG_INIT, "Not enough memory! %p %p %p\n", p, p->RIOHosts, p->RIOPortp);
+ kfree(p);
+ free0:
+ return -ENOMEM;
+}
+
+static void __exit rio_release_drivers(void)
+{
+ func_enter();
+ tty_unregister_driver(rio_driver2);
+ tty_unregister_driver(rio_driver);
+ put_tty_driver(rio_driver2);
+ put_tty_driver(rio_driver);
+ func_exit();
+}
+
+
+#ifdef CONFIG_PCI
+ /* This was written for SX, but applies to RIO too...
+ (including bugs....)
+
+ There is another bit besides Bit 17. Turning that bit off
+ (on boards shipped with the fix in the eeprom) results in a
+ hang on the next access to the card.
+ */
+
+ /********************************************************
+ * Setting bit 17 in the CNTRL register of the PLX 9050 *
+ * chip forces a retry on writes while a read is pending.*
+ * This is to prevent the card locking up on Intel Xeon *
+ * multiprocessor systems with the NX chipset. -- NV *
+ ********************************************************/
+
+/* Newer cards are produced with this bit set from the configuration
+ EEprom. As the bit is read/write for the CPU, we can fix it here,
+ if we detect that it isn't set correctly. -- REW */
+
+static void fix_rio_pci(struct pci_dev *pdev)
+{
+ unsigned long hwbase;
+ unsigned char __iomem *rebase;
+ unsigned int t;
+
+#define CNTRL_REG_OFFSET 0x50
+#define CNTRL_REG_GOODVALUE 0x18260000
+
+ hwbase = pci_resource_start(pdev, 0);
+ rebase = ioremap(hwbase, 0x80);
+ t = readl(rebase + CNTRL_REG_OFFSET);
+ if (t != CNTRL_REG_GOODVALUE) {
+ printk(KERN_DEBUG "rio: performing cntrl reg fix: %08x -> %08x\n", t, CNTRL_REG_GOODVALUE);
+ writel(CNTRL_REG_GOODVALUE, rebase + CNTRL_REG_OFFSET);
+ }
+ iounmap(rebase);
+}
+#endif
+
+
+static int __init rio_init(void)
+{
+ int found = 0;
+ int i;
+ struct Host *hp;
+ int retval;
+ struct vpd_prom *vpdp;
+ int okboard;
+
+#ifdef CONFIG_PCI
+ struct pci_dev *pdev = NULL;
+ unsigned short tshort;
+#endif
+
+ func_enter();
+ rio_dprintk(RIO_DEBUG_INIT, "Initing rio module... (rio_debug=%d)\n", rio_debug);
+
+ if (abs((long) (&rio_debug) - rio_debug) < 0x10000) {
+ printk(KERN_WARNING "rio: rio_debug is an address, instead of a value. " "Assuming -1. Was %x/%p.\n", rio_debug, &rio_debug);
+ rio_debug = -1;
+ }
+
+ if (misc_register(&rio_fw_device) < 0) {
+ printk(KERN_ERR "RIO: Unable to register firmware loader driver.\n");
+ return -EIO;
+ }
+
+ retval = rio_init_datastructures();
+ if (retval < 0) {
+ misc_deregister(&rio_fw_device);
+ return retval;
+ }
+#ifdef CONFIG_PCI
+ /* First look for the JET devices: */
+ while ((pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8, pdev))) {
+ u32 tint;
+
+ if (pci_enable_device(pdev))
+ continue;
+
+ /* Specialix has a whole bunch of cards with
+ 0x2000 as the device ID. They say its because
+ the standard requires it. Stupid standard. */
+ /* It seems that reading a word doesn't work reliably on 2.0.
+ Also, reading a non-aligned dword doesn't work. So we read the
+ whole dword at 0x2c and extract the word at 0x2e (SUBSYSTEM_ID)
+ ourselves */
+ pci_read_config_dword(pdev, 0x2c, &tint);
+ tshort = (tint >> 16) & 0xffff;
+ rio_dprintk(RIO_DEBUG_PROBE, "Got a specialix card: %x.\n", tint);
+ if (tshort != 0x0100) {
+ rio_dprintk(RIO_DEBUG_PROBE, "But it's not a RIO card (%d)...\n", tshort);
+ continue;
+ }
+ rio_dprintk(RIO_DEBUG_PROBE, "cp1\n");
+
+ hp = &p->RIOHosts[p->RIONumHosts];
+ hp->PaddrP = pci_resource_start(pdev, 2);
+ hp->Ivec = pdev->irq;
+ if (((1 << hp->Ivec) & rio_irqmask) == 0)
+ hp->Ivec = 0;
+ hp->Caddr = ioremap(p->RIOHosts[p->RIONumHosts].PaddrP, RIO_WINDOW_LEN);
+ hp->CardP = (struct DpRam __iomem *) hp->Caddr;
+ hp->Type = RIO_PCI;
+ hp->Copy = rio_copy_to_card;
+ hp->Mode = RIO_PCI_BOOT_FROM_RAM;
+ spin_lock_init(&hp->HostLock);
+ rio_reset_interrupt(hp);
+ rio_start_card_running(hp);
+
+ rio_dprintk(RIO_DEBUG_PROBE, "Going to test it (%p/%p).\n", (void *) p->RIOHosts[p->RIONumHosts].PaddrP, p->RIOHosts[p->RIONumHosts].Caddr);
+ if (RIOBoardTest(p->RIOHosts[p->RIONumHosts].PaddrP, p->RIOHosts[p->RIONumHosts].Caddr, RIO_PCI, 0) == 0) {
+ rio_dprintk(RIO_DEBUG_INIT, "Done RIOBoardTest\n");
+ writeb(0xFF, &p->RIOHosts[p->RIONumHosts].ResetInt);
+ p->RIOHosts[p->RIONumHosts].UniqueNum =
+ ((readb(&p->RIOHosts[p->RIONumHosts].Unique[0]) & 0xFF) << 0) |
+ ((readb(&p->RIOHosts[p->RIONumHosts].Unique[1]) & 0xFF) << 8) | ((readb(&p->RIOHosts[p->RIONumHosts].Unique[2]) & 0xFF) << 16) | ((readb(&p->RIOHosts[p->RIONumHosts].Unique[3]) & 0xFF) << 24);
+ rio_dprintk(RIO_DEBUG_PROBE, "Hmm Tested ok, uniqid = %x.\n", p->RIOHosts[p->RIONumHosts].UniqueNum);
+
+ fix_rio_pci(pdev);
+
+ p->RIOHosts[p->RIONumHosts].pdev = pdev;
+ pci_dev_get(pdev);
+
+ p->RIOLastPCISearch = 0;
+ p->RIONumHosts++;
+ found++;
+ } else {
+ iounmap(p->RIOHosts[p->RIONumHosts].Caddr);
+ p->RIOHosts[p->RIONumHosts].Caddr = NULL;
+ }
+ }
+
+ /* Then look for the older PCI card.... : */
+
+ /* These older PCI cards have problems (only byte-mode access is
+ supported), which makes them a bit awkward to support.
+ They also have problems sharing interrupts. Be careful.
+ (The driver now refuses to share interrupts for these
+ cards. This should be sufficient).
+ */
+
+ /* Then look for the older RIO/PCI devices: */
+ while ((pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_RIO, pdev))) {
+ if (pci_enable_device(pdev))
+ continue;
+
+#ifdef CONFIG_RIO_OLDPCI
+ hp = &p->RIOHosts[p->RIONumHosts];
+ hp->PaddrP = pci_resource_start(pdev, 0);
+ hp->Ivec = pdev->irq;
+ if (((1 << hp->Ivec) & rio_irqmask) == 0)
+ hp->Ivec = 0;
+ hp->Ivec |= 0x8000; /* Mark as non-sharable */
+ hp->Caddr = ioremap(p->RIOHosts[p->RIONumHosts].PaddrP, RIO_WINDOW_LEN);
+ hp->CardP = (struct DpRam __iomem *) hp->Caddr;
+ hp->Type = RIO_PCI;
+ hp->Copy = rio_copy_to_card;
+ hp->Mode = RIO_PCI_BOOT_FROM_RAM;
+ spin_lock_init(&hp->HostLock);
+
+ rio_dprintk(RIO_DEBUG_PROBE, "Ivec: %x\n", hp->Ivec);
+ rio_dprintk(RIO_DEBUG_PROBE, "Mode: %x\n", hp->Mode);
+
+ rio_reset_interrupt(hp);
+ rio_start_card_running(hp);
+ rio_dprintk(RIO_DEBUG_PROBE, "Going to test it (%p/%p).\n", (void *) p->RIOHosts[p->RIONumHosts].PaddrP, p->RIOHosts[p->RIONumHosts].Caddr);
+ if (RIOBoardTest(p->RIOHosts[p->RIONumHosts].PaddrP, p->RIOHosts[p->RIONumHosts].Caddr, RIO_PCI, 0) == 0) {
+ writeb(0xFF, &p->RIOHosts[p->RIONumHosts].ResetInt);
+ p->RIOHosts[p->RIONumHosts].UniqueNum =
+ ((readb(&p->RIOHosts[p->RIONumHosts].Unique[0]) & 0xFF) << 0) |
+ ((readb(&p->RIOHosts[p->RIONumHosts].Unique[1]) & 0xFF) << 8) | ((readb(&p->RIOHosts[p->RIONumHosts].Unique[2]) & 0xFF) << 16) | ((readb(&p->RIOHosts[p->RIONumHosts].Unique[3]) & 0xFF) << 24);
+ rio_dprintk(RIO_DEBUG_PROBE, "Hmm Tested ok, uniqid = %x.\n", p->RIOHosts[p->RIONumHosts].UniqueNum);
+
+ p->RIOHosts[p->RIONumHosts].pdev = pdev;
+ pci_dev_get(pdev);
+
+ p->RIOLastPCISearch = 0;
+ p->RIONumHosts++;
+ found++;
+ } else {
+ iounmap(p->RIOHosts[p->RIONumHosts].Caddr);
+ p->RIOHosts[p->RIONumHosts].Caddr = NULL;
+ }
+#else
+ printk(KERN_ERR "Found an older RIO PCI card, but the driver is not " "compiled to support it.\n");
+#endif
+ }
+#endif /* PCI */
+
+ /* Now probe for ISA cards... */
+ for (i = 0; i < NR_RIO_ADDRS; i++) {
+ hp = &p->RIOHosts[p->RIONumHosts];
+ hp->PaddrP = rio_probe_addrs[i];
+ /* There was something about the IRQs of these cards. 'Forget what.--REW */
+ hp->Ivec = 0;
+ hp->Caddr = ioremap(p->RIOHosts[p->RIONumHosts].PaddrP, RIO_WINDOW_LEN);
+ hp->CardP = (struct DpRam __iomem *) hp->Caddr;
+ hp->Type = RIO_AT;
+ hp->Copy = rio_copy_to_card; /* AT card PCI???? - PVDL
+ * -- YES! this is now a normal copy. Only the
+ * old PCI card uses the special PCI copy.
+ * Moreover, the ISA card will work with the
+ * special PCI copy anyway. -- REW */
+ hp->Mode = 0;
+ spin_lock_init(&hp->HostLock);
+
+ vpdp = get_VPD_PROM(hp);
+ rio_dprintk(RIO_DEBUG_PROBE, "Got VPD ROM\n");
+ okboard = 0;
+ if ((strncmp(vpdp->identifier, RIO_ISA_IDENT, 16) == 0) || (strncmp(vpdp->identifier, RIO_ISA2_IDENT, 16) == 0) || (strncmp(vpdp->identifier, RIO_ISA3_IDENT, 16) == 0)) {
+ /* Board is present... */
+ if (RIOBoardTest(hp->PaddrP, hp->Caddr, RIO_AT, 0) == 0) {
+ /* ... and feeling fine!!!! */
+ rio_dprintk(RIO_DEBUG_PROBE, "Hmm Tested ok, uniqid = %x.\n", p->RIOHosts[p->RIONumHosts].UniqueNum);
+ if (RIOAssignAT(p, hp->PaddrP, hp->Caddr, 0)) {
+ rio_dprintk(RIO_DEBUG_PROBE, "Hmm Tested ok, host%d uniqid = %x.\n", p->RIONumHosts, p->RIOHosts[p->RIONumHosts - 1].UniqueNum);
+ okboard++;
+ found++;
+ }
+ }
+
+ if (!okboard) {
+ iounmap(hp->Caddr);
+ hp->Caddr = NULL;
+ }
+ }
+ }
+
+
+ for (i = 0; i < p->RIONumHosts; i++) {
+ hp = &p->RIOHosts[i];
+ if (hp->Ivec) {
+ int mode = IRQF_SHARED;
+ if (hp->Ivec & 0x8000) {
+ mode = 0;
+ hp->Ivec &= 0x7fff;
+ }
+ rio_dprintk(RIO_DEBUG_INIT, "Requesting interrupt hp: %p rio_interrupt: %d Mode: %x\n", hp, hp->Ivec, hp->Mode);
+ retval = request_irq(hp->Ivec, rio_interrupt, mode, "rio", hp);
+ rio_dprintk(RIO_DEBUG_INIT, "Return value from request_irq: %d\n", retval);
+ if (retval) {
+ printk(KERN_ERR "rio: Cannot allocate irq %d.\n", hp->Ivec);
+ hp->Ivec = 0;
+ }
+ rio_dprintk(RIO_DEBUG_INIT, "Got irq %d.\n", hp->Ivec);
+ if (hp->Ivec != 0) {
+ rio_dprintk(RIO_DEBUG_INIT, "Enabling interrupts on rio card.\n");
+ hp->Mode |= RIO_PCI_INT_ENABLE;
+ } else
+ hp->Mode &= ~RIO_PCI_INT_ENABLE;
+ rio_dprintk(RIO_DEBUG_INIT, "New Mode: %x\n", hp->Mode);
+ rio_start_card_running(hp);
+ }
+ /* Init the timer "always" to make sure that it can safely be
+ deleted when we unload... */
+
+ setup_timer(&hp->timer, rio_pollfunc, i);
+ if (!hp->Ivec) {
+ rio_dprintk(RIO_DEBUG_INIT, "Starting polling at %dj intervals.\n", rio_poll);
+ mod_timer(&hp->timer, jiffies + rio_poll);
+ }
+ }
+
+ if (found) {
+ rio_dprintk(RIO_DEBUG_INIT, "rio: total of %d boards detected.\n", found);
+ rio_init_drivers();
+ } else {
+ /* deregister the misc device we created earlier */
+ misc_deregister(&rio_fw_device);
+ }
+
+ func_exit();
+ return found ? 0 : -EIO;
+}
+
+
+static void __exit rio_exit(void)
+{
+ int i;
+ struct Host *hp;
+
+ func_enter();
+
+ for (i = 0, hp = p->RIOHosts; i < p->RIONumHosts; i++, hp++) {
+ RIOHostReset(hp->Type, hp->CardP, hp->Slot);
+ if (hp->Ivec) {
+ free_irq(hp->Ivec, hp);
+ rio_dprintk(RIO_DEBUG_INIT, "freed irq %d.\n", hp->Ivec);
+ }
+ /* It is safe/allowed to del_timer a non-active timer */
+ del_timer_sync(&hp->timer);
+ if (hp->Caddr)
+ iounmap(hp->Caddr);
+ if (hp->Type == RIO_PCI)
+ pci_dev_put(hp->pdev);
+ }
+
+ if (misc_deregister(&rio_fw_device) < 0) {
+ printk(KERN_INFO "rio: couldn't deregister control-device\n");
+ }
+
+
+ rio_dprintk(RIO_DEBUG_CLEANUP, "Cleaning up drivers\n");
+
+ rio_release_drivers();
+
+ /* Release dynamically allocated memory */
+ kfree(p->RIOPortp);
+ kfree(p->RIOHosts);
+ kfree(p);
+
+ func_exit();
+}
+
+module_init(rio_init);
+module_exit(rio_exit);
diff --git a/drivers/staging/generic_serial/rio/rio_linux.h b/drivers/staging/generic_serial/rio/rio_linux.h
new file mode 100644
index 000000000000..7f26cd7c815e
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/rio_linux.h
@@ -0,0 +1,197 @@
+
+/*
+ * rio_linux.h
+ *
+ * Copyright (C) 1998,1999,2000 R.E.Wolff@BitWizard.nl
+ *
+ * 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.
+ *
+ * RIO serial driver.
+ *
+ * Version 1.0 -- July, 1999.
+ *
+ */
+
+#define RIO_NBOARDS 4
+#define RIO_PORTSPERBOARD 128
+#define RIO_NPORTS (RIO_NBOARDS * RIO_PORTSPERBOARD)
+
+#define MODEM_SUPPORT
+
+#ifdef __KERNEL__
+
+#define RIO_MAGIC 0x12345678
+
+
+struct vpd_prom {
+ unsigned short id;
+ char hwrev;
+ char hwass;
+ int uniqid;
+ char myear;
+ char mweek;
+ char hw_feature[5];
+ char oem_id;
+ char identifier[16];
+};
+
+
+#define RIO_DEBUG_ALL 0xffffffff
+
+#define O_OTHER(tty) \
+ ((O_OLCUC(tty)) ||\
+ (O_ONLCR(tty)) ||\
+ (O_OCRNL(tty)) ||\
+ (O_ONOCR(tty)) ||\
+ (O_ONLRET(tty)) ||\
+ (O_OFILL(tty)) ||\
+ (O_OFDEL(tty)) ||\
+ (O_NLDLY(tty)) ||\
+ (O_CRDLY(tty)) ||\
+ (O_TABDLY(tty)) ||\
+ (O_BSDLY(tty)) ||\
+ (O_VTDLY(tty)) ||\
+ (O_FFDLY(tty)))
+
+/* Same for input. */
+#define I_OTHER(tty) \
+ ((I_INLCR(tty)) ||\
+ (I_IGNCR(tty)) ||\
+ (I_ICRNL(tty)) ||\
+ (I_IUCLC(tty)) ||\
+ (L_ISIG(tty)))
+
+
+#endif /* __KERNEL__ */
+
+
+#define RIO_BOARD_INTR_LOCK 1
+
+
+#ifndef RIOCTL_MISC_MINOR
+/* Allow others to gather this into "major.h" or something like that */
+#define RIOCTL_MISC_MINOR 169
+#endif
+
+
+/* Allow us to debug "in the field" without requiring clients to
+ recompile.... */
+#if 1
+#define rio_spin_lock_irqsave(sem, flags) do { \
+ rio_dprintk (RIO_DEBUG_SPINLOCK, "spinlockirqsave: %p %s:%d\n", \
+ sem, __FILE__, __LINE__);\
+ spin_lock_irqsave(sem, flags);\
+ } while (0)
+
+#define rio_spin_unlock_irqrestore(sem, flags) do { \
+ rio_dprintk (RIO_DEBUG_SPINLOCK, "spinunlockirqrestore: %p %s:%d\n",\
+ sem, __FILE__, __LINE__);\
+ spin_unlock_irqrestore(sem, flags);\
+ } while (0)
+
+#define rio_spin_lock(sem) do { \
+ rio_dprintk (RIO_DEBUG_SPINLOCK, "spinlock: %p %s:%d\n",\
+ sem, __FILE__, __LINE__);\
+ spin_lock(sem);\
+ } while (0)
+
+#define rio_spin_unlock(sem) do { \
+ rio_dprintk (RIO_DEBUG_SPINLOCK, "spinunlock: %p %s:%d\n",\
+ sem, __FILE__, __LINE__);\
+ spin_unlock(sem);\
+ } while (0)
+#else
+#define rio_spin_lock_irqsave(sem, flags) \
+ spin_lock_irqsave(sem, flags)
+
+#define rio_spin_unlock_irqrestore(sem, flags) \
+ spin_unlock_irqrestore(sem, flags)
+
+#define rio_spin_lock(sem) \
+ spin_lock(sem)
+
+#define rio_spin_unlock(sem) \
+ spin_unlock(sem)
+
+#endif
+
+
+
+#ifdef CONFIG_RIO_OLDPCI
+static inline void __iomem *rio_memcpy_toio(void __iomem *dummy, void __iomem *dest, void *source, int n)
+{
+ char __iomem *dst = dest;
+ char *src = source;
+
+ while (n--) {
+ writeb(*src++, dst++);
+ (void) readb(dummy);
+ }
+
+ return dest;
+}
+
+static inline void __iomem *rio_copy_toio(void __iomem *dest, void *source, int n)
+{
+ char __iomem *dst = dest;
+ char *src = source;
+
+ while (n--)
+ writeb(*src++, dst++);
+
+ return dest;
+}
+
+
+static inline void *rio_memcpy_fromio(void *dest, void __iomem *source, int n)
+{
+ char *dst = dest;
+ char __iomem *src = source;
+
+ while (n--)
+ *dst++ = readb(src++);
+
+ return dest;
+}
+
+#else
+#define rio_memcpy_toio(dummy,dest,source,n) memcpy_toio(dest, source, n)
+#define rio_copy_toio memcpy_toio
+#define rio_memcpy_fromio memcpy_fromio
+#endif
+
+#define DEBUG 1
+
+
+/*
+ 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...
+*/
+
+#ifdef DEBUG
+#define rio_dprintk(f, str...) do { if (rio_debug & f) printk (str);} while (0)
+#define func_enter() rio_dprintk (RIO_DEBUG_FLOW, "rio: enter %s\n", __func__)
+#define func_exit() rio_dprintk (RIO_DEBUG_FLOW, "rio: exit %s\n", __func__)
+#define func_enter2() rio_dprintk (RIO_DEBUG_FLOW, "rio: enter %s (port %d)\n",__func__, port->line)
+#else
+#define rio_dprintk(f, str...) /* nothing */
+#define func_enter()
+#define func_exit()
+#define func_enter2()
+#endif
diff --git a/drivers/staging/generic_serial/rio/rioboard.h b/drivers/staging/generic_serial/rio/rioboard.h
new file mode 100644
index 000000000000..252230043c82
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/rioboard.h
@@ -0,0 +1,275 @@
+/************************************************************************/
+/* */
+/* Title : RIO Host Card Hardware Definitions */
+/* */
+/* Author : N.P.Vassallo */
+/* */
+/* Creation : 26th April 1999 */
+/* */
+/* Version : 1.0.0 */
+/* */
+/* Copyright : (c) Specialix International Ltd. 1999 *
+ *
+ * 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.
+ * */
+/* Description : Prototypes, structures and definitions */
+/* describing the RIO board hardware */
+/* */
+/************************************************************************/
+
+#ifndef _rioboard_h /* If RIOBOARD.H not already defined */
+#define _rioboard_h 1
+
+/*****************************************************************************
+*********************** ***********************
+*********************** Hardware Control Registers ***********************
+*********************** ***********************
+*****************************************************************************/
+
+/* Hardware Registers... */
+
+#define RIO_REG_BASE 0x7C00 /* Base of control registers */
+
+#define RIO_CONFIG RIO_REG_BASE + 0x0000 /* WRITE: Configuration Register */
+#define RIO_INTSET RIO_REG_BASE + 0x0080 /* WRITE: Interrupt Set */
+#define RIO_RESET RIO_REG_BASE + 0x0100 /* WRITE: Host Reset */
+#define RIO_INTRESET RIO_REG_BASE + 0x0180 /* WRITE: Interrupt Reset */
+
+#define RIO_VPD_ROM RIO_REG_BASE + 0x0000 /* READ: Vital Product Data ROM */
+#define RIO_INTSTAT RIO_REG_BASE + 0x0080 /* READ: Interrupt Status (Jet boards only) */
+#define RIO_RESETSTAT RIO_REG_BASE + 0x0100 /* READ: Reset Status (Jet boards only) */
+
+/* RIO_VPD_ROM definitions... */
+#define VPD_SLX_ID1 0x00 /* READ: Specialix Identifier #1 */
+#define VPD_SLX_ID2 0x01 /* READ: Specialix Identifier #2 */
+#define VPD_HW_REV 0x02 /* READ: Hardware Revision */
+#define VPD_HW_ASSEM 0x03 /* READ: Hardware Assembly Level */
+#define VPD_UNIQUEID4 0x04 /* READ: Unique Identifier #4 */
+#define VPD_UNIQUEID3 0x05 /* READ: Unique Identifier #3 */
+#define VPD_UNIQUEID2 0x06 /* READ: Unique Identifier #2 */
+#define VPD_UNIQUEID1 0x07 /* READ: Unique Identifier #1 */
+#define VPD_MANU_YEAR 0x08 /* READ: Year Of Manufacture (0 = 1970) */
+#define VPD_MANU_WEEK 0x09 /* READ: Week Of Manufacture (0 = week 1 Jan) */
+#define VPD_HWFEATURE1 0x0A /* READ: Hardware Feature Byte 1 */
+#define VPD_HWFEATURE2 0x0B /* READ: Hardware Feature Byte 2 */
+#define VPD_HWFEATURE3 0x0C /* READ: Hardware Feature Byte 3 */
+#define VPD_HWFEATURE4 0x0D /* READ: Hardware Feature Byte 4 */
+#define VPD_HWFEATURE5 0x0E /* READ: Hardware Feature Byte 5 */
+#define VPD_OEMID 0x0F /* READ: OEM Identifier */
+#define VPD_IDENT 0x10 /* READ: Identifier string (16 bytes) */
+#define VPD_IDENT_LEN 0x10
+
+/* VPD ROM Definitions... */
+#define SLX_ID1 0x4D
+#define SLX_ID2 0x98
+
+#define PRODUCT_ID(a) ((a>>4)&0xF) /* Use to obtain Product ID from VPD_UNIQUEID1 */
+
+#define ID_SX_ISA 0x2
+#define ID_RIO_EISA 0x3
+#define ID_SX_PCI 0x5
+#define ID_SX_EISA 0x7
+#define ID_RIO_RTA16 0x9
+#define ID_RIO_ISA 0xA
+#define ID_RIO_MCA 0xB
+#define ID_RIO_SBUS 0xC
+#define ID_RIO_PCI 0xD
+#define ID_RIO_RTA8 0xE
+
+/* Transputer bootstrap definitions... */
+
+#define BOOTLOADADDR (0x8000 - 6)
+#define BOOTINDICATE (0x8000 - 2)
+
+/* Firmware load position... */
+
+#define FIRMWARELOADADDR 0x7C00 /* Firmware is loaded _before_ this address */
+
+/*****************************************************************************
+***************************** *****************************
+***************************** RIO (Rev1) ISA *****************************
+***************************** *****************************
+*****************************************************************************/
+
+/* Control Register Definitions... */
+#define RIO_ISA_IDENT "JBJGPGGHINSMJPJR"
+
+#define RIO_ISA_CFG_BOOTRAM 0x01 /* Boot from RAM, else Link */
+#define RIO_ISA_CFG_BUSENABLE 0x02 /* Enable processor bus */
+#define RIO_ISA_CFG_IRQMASK 0x30 /* Interrupt mask */
+#define RIO_ISA_CFG_IRQ12 0x10 /* Interrupt Level 12 */
+#define RIO_ISA_CFG_IRQ11 0x20 /* Interrupt Level 11 */
+#define RIO_ISA_CFG_IRQ9 0x30 /* Interrupt Level 9 */
+#define RIO_ISA_CFG_LINK20 0x40 /* 20Mbps link, else 10Mbps */
+#define RIO_ISA_CFG_WAITSTATE0 0x80 /* 0 waitstates, else 1 */
+
+/*****************************************************************************
+***************************** *****************************
+***************************** RIO (Rev2) ISA *****************************
+***************************** *****************************
+*****************************************************************************/
+
+/* Control Register Definitions... */
+#define RIO_ISA2_IDENT "JBJGPGGHINSMJPJR"
+
+#define RIO_ISA2_CFG_BOOTRAM 0x01 /* Boot from RAM, else Link */
+#define RIO_ISA2_CFG_BUSENABLE 0x02 /* Enable processor bus */
+#define RIO_ISA2_CFG_INTENABLE 0x04 /* Interrupt enable, else disable */
+#define RIO_ISA2_CFG_16BIT 0x08 /* 16bit mode, else 8bit */
+#define RIO_ISA2_CFG_IRQMASK 0x30 /* Interrupt mask */
+#define RIO_ISA2_CFG_IRQ15 0x00 /* Interrupt Level 15 */
+#define RIO_ISA2_CFG_IRQ12 0x10 /* Interrupt Level 12 */
+#define RIO_ISA2_CFG_IRQ11 0x20 /* Interrupt Level 11 */
+#define RIO_ISA2_CFG_IRQ9 0x30 /* Interrupt Level 9 */
+#define RIO_ISA2_CFG_LINK20 0x40 /* 20Mbps link, else 10Mbps */
+#define RIO_ISA2_CFG_WAITSTATE0 0x80 /* 0 waitstates, else 1 */
+
+/*****************************************************************************
+***************************** ******************************
+***************************** RIO (Jet) ISA ******************************
+***************************** ******************************
+*****************************************************************************/
+
+/* Control Register Definitions... */
+#define RIO_ISA3_IDENT "JET HOST BY KEV#"
+
+#define RIO_ISA3_CFG_BUSENABLE 0x02 /* Enable processor bus */
+#define RIO_ISA3_CFG_INTENABLE 0x04 /* Interrupt enable, else disable */
+#define RIO_ISA32_CFG_IRQMASK 0xF30 /* Interrupt mask */
+#define RIO_ISA3_CFG_IRQ15 0xF0 /* Interrupt Level 15 */
+#define RIO_ISA3_CFG_IRQ12 0xC0 /* Interrupt Level 12 */
+#define RIO_ISA3_CFG_IRQ11 0xB0 /* Interrupt Level 11 */
+#define RIO_ISA3_CFG_IRQ10 0xA0 /* Interrupt Level 10 */
+#define RIO_ISA3_CFG_IRQ9 0x90 /* Interrupt Level 9 */
+
+/*****************************************************************************
+********************************* ********************************
+********************************* RIO MCA ********************************
+********************************* ********************************
+*****************************************************************************/
+
+/* Control Register Definitions... */
+#define RIO_MCA_IDENT "JBJGPGGHINSMJPJR"
+
+#define RIO_MCA_CFG_BOOTRAM 0x01 /* Boot from RAM, else Link */
+#define RIO_MCA_CFG_BUSENABLE 0x02 /* Enable processor bus */
+#define RIO_MCA_CFG_LINK20 0x40 /* 20Mbps link, else 10Mbps */
+
+/*****************************************************************************
+******************************** ********************************
+******************************** RIO EISA ********************************
+******************************** ********************************
+*****************************************************************************/
+
+/* EISA Configuration Space Definitions... */
+#define EISA_PRODUCT_ID1 0xC80
+#define EISA_PRODUCT_ID2 0xC81
+#define EISA_PRODUCT_NUMBER 0xC82
+#define EISA_REVISION_NUMBER 0xC83
+#define EISA_CARD_ENABLE 0xC84
+#define EISA_VPD_UNIQUEID4 0xC88 /* READ: Unique Identifier #4 */
+#define EISA_VPD_UNIQUEID3 0xC8A /* READ: Unique Identifier #3 */
+#define EISA_VPD_UNIQUEID2 0xC90 /* READ: Unique Identifier #2 */
+#define EISA_VPD_UNIQUEID1 0xC92 /* READ: Unique Identifier #1 */
+#define EISA_VPD_MANU_YEAR 0xC98 /* READ: Year Of Manufacture (0 = 1970) */
+#define EISA_VPD_MANU_WEEK 0xC9A /* READ: Week Of Manufacture (0 = week 1 Jan) */
+#define EISA_MEM_ADDR_23_16 0xC00
+#define EISA_MEM_ADDR_31_24 0xC01
+#define EISA_RIO_CONFIG 0xC02 /* WRITE: Configuration Register */
+#define EISA_RIO_INTSET 0xC03 /* WRITE: Interrupt Set */
+#define EISA_RIO_INTRESET 0xC03 /* READ: Interrupt Reset */
+
+/* Control Register Definitions... */
+#define RIO_EISA_CFG_BOOTRAM 0x01 /* Boot from RAM, else Link */
+#define RIO_EISA_CFG_LINK20 0x02 /* 20Mbps link, else 10Mbps */
+#define RIO_EISA_CFG_BUSENABLE 0x04 /* Enable processor bus */
+#define RIO_EISA_CFG_PROCRUN 0x08 /* Processor running, else reset */
+#define RIO_EISA_CFG_IRQMASK 0xF0 /* Interrupt mask */
+#define RIO_EISA_CFG_IRQ15 0xF0 /* Interrupt Level 15 */
+#define RIO_EISA_CFG_IRQ14 0xE0 /* Interrupt Level 14 */
+#define RIO_EISA_CFG_IRQ12 0xC0 /* Interrupt Level 12 */
+#define RIO_EISA_CFG_IRQ11 0xB0 /* Interrupt Level 11 */
+#define RIO_EISA_CFG_IRQ10 0xA0 /* Interrupt Level 10 */
+#define RIO_EISA_CFG_IRQ9 0x90 /* Interrupt Level 9 */
+#define RIO_EISA_CFG_IRQ7 0x70 /* Interrupt Level 7 */
+#define RIO_EISA_CFG_IRQ6 0x60 /* Interrupt Level 6 */
+#define RIO_EISA_CFG_IRQ5 0x50 /* Interrupt Level 5 */
+#define RIO_EISA_CFG_IRQ4 0x40 /* Interrupt Level 4 */
+#define RIO_EISA_CFG_IRQ3 0x30 /* Interrupt Level 3 */
+
+/*****************************************************************************
+******************************** ********************************
+******************************** RIO SBus ********************************
+******************************** ********************************
+*****************************************************************************/
+
+/* Control Register Definitions... */
+#define RIO_SBUS_IDENT "JBPGK#\0\0\0\0\0\0\0\0\0\0"
+
+#define RIO_SBUS_CFG_BOOTRAM 0x01 /* Boot from RAM, else Link */
+#define RIO_SBUS_CFG_BUSENABLE 0x02 /* Enable processor bus */
+#define RIO_SBUS_CFG_INTENABLE 0x04 /* Interrupt enable, else disable */
+#define RIO_SBUS_CFG_IRQMASK 0x38 /* Interrupt mask */
+#define RIO_SBUS_CFG_IRQNONE 0x00 /* No Interrupt */
+#define RIO_SBUS_CFG_IRQ7 0x38 /* Interrupt Level 7 */
+#define RIO_SBUS_CFG_IRQ6 0x30 /* Interrupt Level 6 */
+#define RIO_SBUS_CFG_IRQ5 0x28 /* Interrupt Level 5 */
+#define RIO_SBUS_CFG_IRQ4 0x20 /* Interrupt Level 4 */
+#define RIO_SBUS_CFG_IRQ3 0x18 /* Interrupt Level 3 */
+#define RIO_SBUS_CFG_IRQ2 0x10 /* Interrupt Level 2 */
+#define RIO_SBUS_CFG_IRQ1 0x08 /* Interrupt Level 1 */
+#define RIO_SBUS_CFG_LINK20 0x40 /* 20Mbps link, else 10Mbps */
+#define RIO_SBUS_CFG_PROC25 0x80 /* 25Mhz processor clock, else 20Mhz */
+
+/*****************************************************************************
+********************************* ********************************
+********************************* RIO PCI ********************************
+********************************* ********************************
+*****************************************************************************/
+
+/* Control Register Definitions... */
+#define RIO_PCI_IDENT "ECDDPGJGJHJRGSK#"
+
+#define RIO_PCI_CFG_BOOTRAM 0x01 /* Boot from RAM, else Link */
+#define RIO_PCI_CFG_BUSENABLE 0x02 /* Enable processor bus */
+#define RIO_PCI_CFG_INTENABLE 0x04 /* Interrupt enable, else disable */
+#define RIO_PCI_CFG_LINK20 0x40 /* 20Mbps link, else 10Mbps */
+#define RIO_PCI_CFG_PROC25 0x80 /* 25Mhz processor clock, else 20Mhz */
+
+/* PCI Definitions... */
+#define SPX_VENDOR_ID 0x11CB /* Assigned by the PCI SIG */
+#define SPX_DEVICE_ID 0x8000 /* RIO bridge boards */
+#define SPX_PLXDEVICE_ID 0x2000 /* PLX bridge boards */
+#define SPX_SUB_VENDOR_ID SPX_VENDOR_ID /* Same as vendor id */
+#define RIO_SUB_SYS_ID 0x0800 /* RIO PCI board */
+
+/*****************************************************************************
+***************************** ******************************
+***************************** RIO (Jet) PCI ******************************
+***************************** ******************************
+*****************************************************************************/
+
+/* Control Register Definitions... */
+#define RIO_PCI2_IDENT "JET HOST BY KEV#"
+
+#define RIO_PCI2_CFG_BUSENABLE 0x02 /* Enable processor bus */
+#define RIO_PCI2_CFG_INTENABLE 0x04 /* Interrupt enable, else disable */
+
+/* PCI Definitions... */
+#define RIO2_SUB_SYS_ID 0x0100 /* RIO (Jet) PCI board */
+
+#endif /*_rioboard_h */
+
+/* End of RIOBOARD.H */
diff --git a/drivers/staging/generic_serial/rio/rioboot.c b/drivers/staging/generic_serial/rio/rioboot.c
new file mode 100644
index 000000000000..d956dd316005
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/rioboot.c
@@ -0,0 +1,1113 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : rioboot.c
+** SID : 1.3
+** Last Modified : 11/6/98 10:33:36
+** Retrieved : 11/6/98 10:33:48
+**
+** ident @(#)rioboot.c 1.3
+**
+** -----------------------------------------------------------------------------
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/serial.h>
+#include <linux/vmalloc.h>
+#include <linux/generic_serial.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/string.h>
+#include <asm/uaccess.h>
+
+
+#include "linux_compat.h"
+#include "rio_linux.h"
+#include "pkt.h"
+#include "daemon.h"
+#include "rio.h"
+#include "riospace.h"
+#include "cmdpkt.h"
+#include "map.h"
+#include "rup.h"
+#include "port.h"
+#include "riodrvr.h"
+#include "rioinfo.h"
+#include "func.h"
+#include "errors.h"
+#include "pci.h"
+
+#include "parmmap.h"
+#include "unixrup.h"
+#include "board.h"
+#include "host.h"
+#include "phb.h"
+#include "link.h"
+#include "cmdblk.h"
+#include "route.h"
+
+static int RIOBootComplete(struct rio_info *p, struct Host *HostP, unsigned int Rup, struct PktCmd __iomem *PktCmdP);
+
+static const unsigned char RIOAtVec2Ctrl[] = {
+ /* 0 */ INTERRUPT_DISABLE,
+ /* 1 */ INTERRUPT_DISABLE,
+ /* 2 */ INTERRUPT_DISABLE,
+ /* 3 */ INTERRUPT_DISABLE,
+ /* 4 */ INTERRUPT_DISABLE,
+ /* 5 */ INTERRUPT_DISABLE,
+ /* 6 */ INTERRUPT_DISABLE,
+ /* 7 */ INTERRUPT_DISABLE,
+ /* 8 */ INTERRUPT_DISABLE,
+ /* 9 */ IRQ_9 | INTERRUPT_ENABLE,
+ /* 10 */ INTERRUPT_DISABLE,
+ /* 11 */ IRQ_11 | INTERRUPT_ENABLE,
+ /* 12 */ IRQ_12 | INTERRUPT_ENABLE,
+ /* 13 */ INTERRUPT_DISABLE,
+ /* 14 */ INTERRUPT_DISABLE,
+ /* 15 */ IRQ_15 | INTERRUPT_ENABLE
+};
+
+/**
+ * RIOBootCodeRTA - Load RTA boot code
+ * @p: RIO to load
+ * @rbp: Download descriptor
+ *
+ * Called when the user process initiates booting of the card firmware.
+ * Lads the firmware
+ */
+
+int RIOBootCodeRTA(struct rio_info *p, struct DownLoad * rbp)
+{
+ int offset;
+
+ func_enter();
+
+ rio_dprintk(RIO_DEBUG_BOOT, "Data at user address %p\n", rbp->DataP);
+
+ /*
+ ** Check that we have set asside enough memory for this
+ */
+ if (rbp->Count > SIXTY_FOUR_K) {
+ rio_dprintk(RIO_DEBUG_BOOT, "RTA Boot Code Too Large!\n");
+ p->RIOError.Error = HOST_FILE_TOO_LARGE;
+ func_exit();
+ return -ENOMEM;
+ }
+
+ if (p->RIOBooting) {
+ rio_dprintk(RIO_DEBUG_BOOT, "RTA Boot Code : BUSY BUSY BUSY!\n");
+ p->RIOError.Error = BOOT_IN_PROGRESS;
+ func_exit();
+ return -EBUSY;
+ }
+
+ /*
+ ** The data we load in must end on a (RTA_BOOT_DATA_SIZE) byte boundary,
+ ** so calculate how far we have to move the data up the buffer
+ ** to achieve this.
+ */
+ offset = (RTA_BOOT_DATA_SIZE - (rbp->Count % RTA_BOOT_DATA_SIZE)) % RTA_BOOT_DATA_SIZE;
+
+ /*
+ ** Be clean, and clear the 'unused' portion of the boot buffer,
+ ** because it will (eventually) be part of the Rta run time environment
+ ** and so should be zeroed.
+ */
+ memset(p->RIOBootPackets, 0, offset);
+
+ /*
+ ** Copy the data from user space into the array
+ */
+
+ if (copy_from_user(((u8 *)p->RIOBootPackets) + offset, rbp->DataP, rbp->Count)) {
+ rio_dprintk(RIO_DEBUG_BOOT, "Bad data copy from user space\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ func_exit();
+ return -EFAULT;
+ }
+
+ /*
+ ** Make sure that our copy of the size includes that offset we discussed
+ ** earlier.
+ */
+ p->RIONumBootPkts = (rbp->Count + offset) / RTA_BOOT_DATA_SIZE;
+ p->RIOBootCount = rbp->Count;
+
+ func_exit();
+ return 0;
+}
+
+/**
+ * rio_start_card_running - host card start
+ * @HostP: The RIO to kick off
+ *
+ * Start a RIO processor unit running. Encapsulates the knowledge
+ * of the card type.
+ */
+
+void rio_start_card_running(struct Host *HostP)
+{
+ switch (HostP->Type) {
+ case RIO_AT:
+ rio_dprintk(RIO_DEBUG_BOOT, "Start ISA card running\n");
+ writeb(BOOT_FROM_RAM | EXTERNAL_BUS_ON | HostP->Mode | RIOAtVec2Ctrl[HostP->Ivec & 0xF], &HostP->Control);
+ break;
+ case RIO_PCI:
+ /*
+ ** PCI is much the same as MCA. Everything is once again memory
+ ** mapped, so we are writing to memory registers instead of io
+ ** ports.
+ */
+ rio_dprintk(RIO_DEBUG_BOOT, "Start PCI card running\n");
+ writeb(PCITpBootFromRam | PCITpBusEnable | HostP->Mode, &HostP->Control);
+ break;
+ default:
+ rio_dprintk(RIO_DEBUG_BOOT, "Unknown host type %d\n", HostP->Type);
+ break;
+ }
+ return;
+}
+
+/*
+** Load in the host boot code - load it directly onto all halted hosts
+** of the correct type.
+**
+** Put your rubber pants on before messing with this code - even the magic
+** numbers have trouble understanding what they are doing here.
+*/
+
+int RIOBootCodeHOST(struct rio_info *p, struct DownLoad *rbp)
+{
+ struct Host *HostP;
+ u8 __iomem *Cad;
+ PARM_MAP __iomem *ParmMapP;
+ int RupN;
+ int PortN;
+ unsigned int host;
+ u8 __iomem *StartP;
+ u8 __iomem *DestP;
+ int wait_count;
+ u16 OldParmMap;
+ u16 offset; /* It is very important that this is a u16 */
+ u8 *DownCode = NULL;
+ unsigned long flags;
+
+ HostP = NULL; /* Assure the compiler we've initialized it */
+
+
+ /* Walk the hosts */
+ for (host = 0; host < p->RIONumHosts; host++) {
+ rio_dprintk(RIO_DEBUG_BOOT, "Attempt to boot host %d\n", host);
+ HostP = &p->RIOHosts[host];
+
+ rio_dprintk(RIO_DEBUG_BOOT, "Host Type = 0x%x, Mode = 0x%x, IVec = 0x%x\n", HostP->Type, HostP->Mode, HostP->Ivec);
+
+ /* Don't boot hosts already running */
+ if ((HostP->Flags & RUN_STATE) != RC_WAITING) {
+ rio_dprintk(RIO_DEBUG_BOOT, "%s %d already running\n", "Host", host);
+ continue;
+ }
+
+ /*
+ ** Grab a pointer to the card (ioremapped)
+ */
+ Cad = HostP->Caddr;
+
+ /*
+ ** We are going to (try) and load in rbp->Count bytes.
+ ** The last byte will reside at p->RIOConf.HostLoadBase-1;
+ ** Therefore, we need to start copying at address
+ ** (caddr+p->RIOConf.HostLoadBase-rbp->Count)
+ */
+ StartP = &Cad[p->RIOConf.HostLoadBase - rbp->Count];
+
+ rio_dprintk(RIO_DEBUG_BOOT, "kernel virtual address for host is %p\n", Cad);
+ rio_dprintk(RIO_DEBUG_BOOT, "kernel virtual address for download is %p\n", StartP);
+ rio_dprintk(RIO_DEBUG_BOOT, "host loadbase is 0x%x\n", p->RIOConf.HostLoadBase);
+ rio_dprintk(RIO_DEBUG_BOOT, "size of download is 0x%x\n", rbp->Count);
+
+ /* Make sure it fits */
+ if (p->RIOConf.HostLoadBase < rbp->Count) {
+ rio_dprintk(RIO_DEBUG_BOOT, "Bin too large\n");
+ p->RIOError.Error = HOST_FILE_TOO_LARGE;
+ func_exit();
+ return -EFBIG;
+ }
+ /*
+ ** Ensure that the host really is stopped.
+ ** Disable it's external bus & twang its reset line.
+ */
+ RIOHostReset(HostP->Type, HostP->CardP, HostP->Slot);
+
+ /*
+ ** Copy the data directly from user space to the SRAM.
+ ** This ain't going to be none too clever if the download
+ ** code is bigger than this segment.
+ */
+ rio_dprintk(RIO_DEBUG_BOOT, "Copy in code\n");
+
+ /* Buffer to local memory as we want to use I/O space and
+ some cards only do 8 or 16 bit I/O */
+
+ DownCode = vmalloc(rbp->Count);
+ if (!DownCode) {
+ p->RIOError.Error = NOT_ENOUGH_CORE_FOR_PCI_COPY;
+ func_exit();
+ return -ENOMEM;
+ }
+ if (copy_from_user(DownCode, rbp->DataP, rbp->Count)) {
+ kfree(DownCode);
+ p->RIOError.Error = COPYIN_FAILED;
+ func_exit();
+ return -EFAULT;
+ }
+ HostP->Copy(DownCode, StartP, rbp->Count);
+ vfree(DownCode);
+
+ rio_dprintk(RIO_DEBUG_BOOT, "Copy completed\n");
+
+ /*
+ ** S T O P !
+ **
+ ** Upto this point the code has been fairly rational, and possibly
+ ** even straight forward. What follows is a pile of crud that will
+ ** magically turn into six bytes of transputer assembler. Normally
+ ** you would expect an array or something, but, being me, I have
+ ** chosen [been told] to use a technique whereby the startup code
+ ** will be correct if we change the loadbase for the code. Which
+ ** brings us onto another issue - the loadbase is the *end* of the
+ ** code, not the start.
+ **
+ ** If I were you I wouldn't start from here.
+ */
+
+ /*
+ ** We now need to insert a short boot section into
+ ** the memory at the end of Sram2. This is normally (de)composed
+ ** of the last eight bytes of the download code. The
+ ** download has been assembled/compiled to expect to be
+ ** loaded from 0x7FFF downwards. We have loaded it
+ ** at some other address. The startup code goes into the small
+ ** ram window at Sram2, in the last 8 bytes, which are really
+ ** at addresses 0x7FF8-0x7FFF.
+ **
+ ** If the loadbase is, say, 0x7C00, then we need to branch to
+ ** address 0x7BFE to run the host.bin startup code. We assemble
+ ** this jump manually.
+ **
+ ** The two byte sequence 60 08 is loaded into memory at address
+ ** 0x7FFE,F. This is a local branch to location 0x7FF8 (60 is nfix 0,
+ ** which adds '0' to the .O register, complements .O, and then shifts
+ ** it left by 4 bit positions, 08 is a jump .O+8 instruction. This will
+ ** add 8 to .O (which was 0xFFF0), and will branch RELATIVE to the new
+ ** location. Now, the branch starts from the value of .PC (or .IP or
+ ** whatever the bloody register is called on this chip), and the .PC
+ ** will be pointing to the location AFTER the branch, in this case
+ ** .PC == 0x8000, so the branch will be to 0x8000+0xFFF8 = 0x7FF8.
+ **
+ ** A long branch is coded at 0x7FF8. This consists of loading a four
+ ** byte offset into .O using nfix (as above) and pfix operators. The
+ ** pfix operates in exactly the same way as the nfix operator, but
+ ** without the complement operation. The offset, of course, must be
+ ** relative to the address of the byte AFTER the branch instruction,
+ ** which will be (urm) 0x7FFC, so, our final destination of the branch
+ ** (loadbase-2), has to be reached from here. Imagine that the loadbase
+ ** is 0x7C00 (which it is), then we will need to branch to 0x7BFE (which
+ ** is the first byte of the initial two byte short local branch of the
+ ** download code).
+ **
+ ** To code a jump from 0x7FFC (which is where the branch will start
+ ** from) to 0x7BFE, we will need to branch 0xFC02 bytes (0x7FFC+0xFC02)=
+ ** 0x7BFE.
+ ** This will be coded as four bytes:
+ ** 60 2C 20 02
+ ** being nfix .O+0
+ ** pfix .O+C
+ ** pfix .O+0
+ ** jump .O+2
+ **
+ ** The nfix operator is used, so that the startup code will be
+ ** compatible with the whole Tp family. (lies, damn lies, it'll never
+ ** work in a month of Sundays).
+ **
+ ** The nfix nyble is the 1s complement of the nyble value you
+ ** want to load - in this case we wanted 'F' so we nfix loaded '0'.
+ */
+
+
+ /*
+ ** Dest points to the top 8 bytes of Sram2. The Tp jumps
+ ** to 0x7FFE at reset time, and starts executing. This is
+ ** a short branch to 0x7FF8, where a long branch is coded.
+ */
+
+ DestP = &Cad[0x7FF8]; /* <<<---- READ THE ABOVE COMMENTS */
+
+#define NFIX(N) (0x60 | (N)) /* .O = (~(.O + N))<<4 */
+#define PFIX(N) (0x20 | (N)) /* .O = (.O + N)<<4 */
+#define JUMP(N) (0x00 | (N)) /* .PC = .PC + .O */
+
+ /*
+ ** 0x7FFC is the address of the location following the last byte of
+ ** the four byte jump instruction.
+ ** READ THE ABOVE COMMENTS
+ **
+ ** offset is (TO-FROM) % MEMSIZE, but with compound buggering about.
+ ** Memsize is 64K for this range of Tp, so offset is a short (unsigned,
+ ** cos I don't understand 2's complement).
+ */
+ offset = (p->RIOConf.HostLoadBase - 2) - 0x7FFC;
+
+ writeb(NFIX(((unsigned short) (~offset) >> (unsigned short) 12) & 0xF), DestP);
+ writeb(PFIX((offset >> 8) & 0xF), DestP + 1);
+ writeb(PFIX((offset >> 4) & 0xF), DestP + 2);
+ writeb(JUMP(offset & 0xF), DestP + 3);
+
+ writeb(NFIX(0), DestP + 6);
+ writeb(JUMP(8), DestP + 7);
+
+ rio_dprintk(RIO_DEBUG_BOOT, "host loadbase is 0x%x\n", p->RIOConf.HostLoadBase);
+ rio_dprintk(RIO_DEBUG_BOOT, "startup offset is 0x%x\n", offset);
+
+ /*
+ ** Flag what is going on
+ */
+ HostP->Flags &= ~RUN_STATE;
+ HostP->Flags |= RC_STARTUP;
+
+ /*
+ ** Grab a copy of the current ParmMap pointer, so we
+ ** can tell when it has changed.
+ */
+ OldParmMap = readw(&HostP->__ParmMapR);
+
+ rio_dprintk(RIO_DEBUG_BOOT, "Original parmmap is 0x%x\n", OldParmMap);
+
+ /*
+ ** And start it running (I hope).
+ ** As there is nothing dodgy or obscure about the
+ ** above code, this is guaranteed to work every time.
+ */
+ rio_dprintk(RIO_DEBUG_BOOT, "Host Type = 0x%x, Mode = 0x%x, IVec = 0x%x\n", HostP->Type, HostP->Mode, HostP->Ivec);
+
+ rio_start_card_running(HostP);
+
+ rio_dprintk(RIO_DEBUG_BOOT, "Set control port\n");
+
+ /*
+ ** Now, wait for upto five seconds for the Tp to setup the parmmap
+ ** pointer:
+ */
+ for (wait_count = 0; (wait_count < p->RIOConf.StartupTime) && (readw(&HostP->__ParmMapR) == OldParmMap); wait_count++) {
+ rio_dprintk(RIO_DEBUG_BOOT, "Checkout %d, 0x%x\n", wait_count, readw(&HostP->__ParmMapR));
+ mdelay(100);
+
+ }
+
+ /*
+ ** If the parmmap pointer is unchanged, then the host code
+ ** has crashed & burned in a really spectacular way
+ */
+ if (readw(&HostP->__ParmMapR) == OldParmMap) {
+ rio_dprintk(RIO_DEBUG_BOOT, "parmmap 0x%x\n", readw(&HostP->__ParmMapR));
+ rio_dprintk(RIO_DEBUG_BOOT, "RIO Mesg Run Fail\n");
+ HostP->Flags &= ~RUN_STATE;
+ HostP->Flags |= RC_STUFFED;
+ RIOHostReset( HostP->Type, HostP->CardP, HostP->Slot );
+ continue;
+ }
+
+ rio_dprintk(RIO_DEBUG_BOOT, "Running 0x%x\n", readw(&HostP->__ParmMapR));
+
+ /*
+ ** Well, the board thought it was OK, and setup its parmmap
+ ** pointer. For the time being, we will pretend that this
+ ** board is running, and check out what the error flag says.
+ */
+
+ /*
+ ** Grab a 32 bit pointer to the parmmap structure
+ */
+ ParmMapP = (PARM_MAP __iomem *) RIO_PTR(Cad, readw(&HostP->__ParmMapR));
+ rio_dprintk(RIO_DEBUG_BOOT, "ParmMapP : %p\n", ParmMapP);
+ ParmMapP = (PARM_MAP __iomem *)(Cad + readw(&HostP->__ParmMapR));
+ rio_dprintk(RIO_DEBUG_BOOT, "ParmMapP : %p\n", ParmMapP);
+
+ /*
+ ** The links entry should be 0xFFFF; we set it up
+ ** with a mask to say how many PHBs to use, and
+ ** which links to use.
+ */
+ if (readw(&ParmMapP->links) != 0xFFFF) {
+ rio_dprintk(RIO_DEBUG_BOOT, "RIO Mesg Run Fail %s\n", HostP->Name);
+ rio_dprintk(RIO_DEBUG_BOOT, "Links = 0x%x\n", readw(&ParmMapP->links));
+ HostP->Flags &= ~RUN_STATE;
+ HostP->Flags |= RC_STUFFED;
+ RIOHostReset( HostP->Type, HostP->CardP, HostP->Slot );
+ continue;
+ }
+
+ writew(RIO_LINK_ENABLE, &ParmMapP->links);
+
+ /*
+ ** now wait for the card to set all the parmmap->XXX stuff
+ ** this is a wait of upto two seconds....
+ */
+ rio_dprintk(RIO_DEBUG_BOOT, "Looking for init_done - %d ticks\n", p->RIOConf.StartupTime);
+ HostP->timeout_id = 0;
+ for (wait_count = 0; (wait_count < p->RIOConf.StartupTime) && !readw(&ParmMapP->init_done); wait_count++) {
+ rio_dprintk(RIO_DEBUG_BOOT, "Waiting for init_done\n");
+ mdelay(100);
+ }
+ rio_dprintk(RIO_DEBUG_BOOT, "OK! init_done!\n");
+
+ if (readw(&ParmMapP->error) != E_NO_ERROR || !readw(&ParmMapP->init_done)) {
+ rio_dprintk(RIO_DEBUG_BOOT, "RIO Mesg Run Fail %s\n", HostP->Name);
+ rio_dprintk(RIO_DEBUG_BOOT, "Timedout waiting for init_done\n");
+ HostP->Flags &= ~RUN_STATE;
+ HostP->Flags |= RC_STUFFED;
+ RIOHostReset( HostP->Type, HostP->CardP, HostP->Slot );
+ continue;
+ }
+
+ rio_dprintk(RIO_DEBUG_BOOT, "Got init_done\n");
+
+ /*
+ ** It runs! It runs!
+ */
+ rio_dprintk(RIO_DEBUG_BOOT, "Host ID %x Running\n", HostP->UniqueNum);
+
+ /*
+ ** set the time period between interrupts.
+ */
+ writew(p->RIOConf.Timer, &ParmMapP->timer);
+
+ /*
+ ** Translate all the 16 bit pointers in the __ParmMapR into
+ ** 32 bit pointers for the driver in ioremap space.
+ */
+ HostP->ParmMapP = ParmMapP;
+ HostP->PhbP = (struct PHB __iomem *) RIO_PTR(Cad, readw(&ParmMapP->phb_ptr));
+ HostP->RupP = (struct RUP __iomem *) RIO_PTR(Cad, readw(&ParmMapP->rups));
+ HostP->PhbNumP = (unsigned short __iomem *) RIO_PTR(Cad, readw(&ParmMapP->phb_num_ptr));
+ HostP->LinkStrP = (struct LPB __iomem *) RIO_PTR(Cad, readw(&ParmMapP->link_str_ptr));
+
+ /*
+ ** point the UnixRups at the real Rups
+ */
+ for (RupN = 0; RupN < MAX_RUP; RupN++) {
+ HostP->UnixRups[RupN].RupP = &HostP->RupP[RupN];
+ HostP->UnixRups[RupN].Id = RupN + 1;
+ HostP->UnixRups[RupN].BaseSysPort = NO_PORT;
+ spin_lock_init(&HostP->UnixRups[RupN].RupLock);
+ }
+
+ for (RupN = 0; RupN < LINKS_PER_UNIT; RupN++) {
+ HostP->UnixRups[RupN + MAX_RUP].RupP = &HostP->LinkStrP[RupN].rup;
+ HostP->UnixRups[RupN + MAX_RUP].Id = 0;
+ HostP->UnixRups[RupN + MAX_RUP].BaseSysPort = NO_PORT;
+ spin_lock_init(&HostP->UnixRups[RupN + MAX_RUP].RupLock);
+ }
+
+ /*
+ ** point the PortP->Phbs at the real Phbs
+ */
+ for (PortN = p->RIOFirstPortsMapped; PortN < p->RIOLastPortsMapped + PORTS_PER_RTA; PortN++) {
+ if (p->RIOPortp[PortN]->HostP == HostP) {
+ struct Port *PortP = p->RIOPortp[PortN];
+ struct PHB __iomem *PhbP;
+ /* int oldspl; */
+
+ if (!PortP->Mapped)
+ continue;
+
+ PhbP = &HostP->PhbP[PortP->HostPort];
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+
+ PortP->PhbP = PhbP;
+
+ PortP->TxAdd = (u16 __iomem *) RIO_PTR(Cad, readw(&PhbP->tx_add));
+ PortP->TxStart = (u16 __iomem *) RIO_PTR(Cad, readw(&PhbP->tx_start));
+ PortP->TxEnd = (u16 __iomem *) RIO_PTR(Cad, readw(&PhbP->tx_end));
+ PortP->RxRemove = (u16 __iomem *) RIO_PTR(Cad, readw(&PhbP->rx_remove));
+ PortP->RxStart = (u16 __iomem *) RIO_PTR(Cad, readw(&PhbP->rx_start));
+ PortP->RxEnd = (u16 __iomem *) RIO_PTR(Cad, readw(&PhbP->rx_end));
+
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ /*
+ ** point the UnixRup at the base SysPort
+ */
+ if (!(PortN % PORTS_PER_RTA))
+ HostP->UnixRups[PortP->RupNum].BaseSysPort = PortN;
+ }
+ }
+
+ rio_dprintk(RIO_DEBUG_BOOT, "Set the card running... \n");
+ /*
+ ** last thing - show the world that everything is in place
+ */
+ HostP->Flags &= ~RUN_STATE;
+ HostP->Flags |= RC_RUNNING;
+ }
+ /*
+ ** MPX always uses a poller. This is actually patched into the system
+ ** configuration and called directly from each clock tick.
+ **
+ */
+ p->RIOPolling = 1;
+
+ p->RIOSystemUp++;
+
+ rio_dprintk(RIO_DEBUG_BOOT, "Done everything %x\n", HostP->Ivec);
+ func_exit();
+ return 0;
+}
+
+
+
+/**
+ * RIOBootRup - Boot an RTA
+ * @p: rio we are working with
+ * @Rup: Rup number
+ * @HostP: host object
+ * @PacketP: packet to use
+ *
+ * If we have successfully processed this boot, then
+ * return 1. If we havent, then return 0.
+ */
+
+int RIOBootRup(struct rio_info *p, unsigned int Rup, struct Host *HostP, struct PKT __iomem *PacketP)
+{
+ struct PktCmd __iomem *PktCmdP = (struct PktCmd __iomem *) PacketP->data;
+ struct PktCmd_M *PktReplyP;
+ struct CmdBlk *CmdBlkP;
+ unsigned int sequence;
+
+ /*
+ ** If we haven't been told what to boot, we can't boot it.
+ */
+ if (p->RIONumBootPkts == 0) {
+ rio_dprintk(RIO_DEBUG_BOOT, "No RTA code to download yet\n");
+ return 0;
+ }
+
+ /*
+ ** Special case of boot completed - if we get one of these then we
+ ** don't need a command block. For all other cases we do, so handle
+ ** this first and then get a command block, then handle every other
+ ** case, relinquishing the command block if disaster strikes!
+ */
+ if ((readb(&PacketP->len) & PKT_CMD_BIT) && (readb(&PktCmdP->Command) == BOOT_COMPLETED))
+ return RIOBootComplete(p, HostP, Rup, PktCmdP);
+
+ /*
+ ** Try to allocate a command block. This is in kernel space
+ */
+ if (!(CmdBlkP = RIOGetCmdBlk())) {
+ rio_dprintk(RIO_DEBUG_BOOT, "No command blocks to boot RTA! come back later.\n");
+ return 0;
+ }
+
+ /*
+ ** Fill in the default info on the command block
+ */
+ CmdBlkP->Packet.dest_unit = Rup < (unsigned short) MAX_RUP ? Rup : 0;
+ CmdBlkP->Packet.dest_port = BOOT_RUP;
+ CmdBlkP->Packet.src_unit = 0;
+ CmdBlkP->Packet.src_port = BOOT_RUP;
+
+ CmdBlkP->PreFuncP = CmdBlkP->PostFuncP = NULL;
+ PktReplyP = (struct PktCmd_M *) CmdBlkP->Packet.data;
+
+ /*
+ ** process COMMANDS on the boot rup!
+ */
+ if (readb(&PacketP->len) & PKT_CMD_BIT) {
+ /*
+ ** We only expect one type of command - a BOOT_REQUEST!
+ */
+ if (readb(&PktCmdP->Command) != BOOT_REQUEST) {
+ rio_dprintk(RIO_DEBUG_BOOT, "Unexpected command %d on BOOT RUP %d of host %Zd\n", readb(&PktCmdP->Command), Rup, HostP - p->RIOHosts);
+ RIOFreeCmdBlk(CmdBlkP);
+ return 1;
+ }
+
+ /*
+ ** Build a Boot Sequence command block
+ **
+ ** We no longer need to use "Boot Mode", we'll always allow
+ ** boot requests - the boot will not complete if the device
+ ** appears in the bindings table.
+ **
+ ** We'll just (always) set the command field in packet reply
+ ** to allow an attempted boot sequence :
+ */
+ PktReplyP->Command = BOOT_SEQUENCE;
+
+ PktReplyP->BootSequence.NumPackets = p->RIONumBootPkts;
+ PktReplyP->BootSequence.LoadBase = p->RIOConf.RtaLoadBase;
+ PktReplyP->BootSequence.CodeSize = p->RIOBootCount;
+
+ CmdBlkP->Packet.len = BOOT_SEQUENCE_LEN | PKT_CMD_BIT;
+
+ memcpy((void *) &CmdBlkP->Packet.data[BOOT_SEQUENCE_LEN], "BOOT", 4);
+
+ rio_dprintk(RIO_DEBUG_BOOT, "Boot RTA on Host %Zd Rup %d - %d (0x%x) packets to 0x%x\n", HostP - p->RIOHosts, Rup, p->RIONumBootPkts, p->RIONumBootPkts, p->RIOConf.RtaLoadBase);
+
+ /*
+ ** If this host is in slave mode, send the RTA an invalid boot
+ ** sequence command block to force it to kill the boot. We wait
+ ** for half a second before sending this packet to prevent the RTA
+ ** attempting to boot too often. The master host should then grab
+ ** the RTA and make it its own.
+ */
+ p->RIOBooting++;
+ RIOQueueCmdBlk(HostP, Rup, CmdBlkP);
+ return 1;
+ }
+
+ /*
+ ** It is a request for boot data.
+ */
+ sequence = readw(&PktCmdP->Sequence);
+
+ rio_dprintk(RIO_DEBUG_BOOT, "Boot block %d on Host %Zd Rup%d\n", sequence, HostP - p->RIOHosts, Rup);
+
+ if (sequence >= p->RIONumBootPkts) {
+ rio_dprintk(RIO_DEBUG_BOOT, "Got a request for packet %d, max is %d\n", sequence, p->RIONumBootPkts);
+ }
+
+ PktReplyP->Sequence = sequence;
+ memcpy(PktReplyP->BootData, p->RIOBootPackets[p->RIONumBootPkts - sequence - 1], RTA_BOOT_DATA_SIZE);
+ CmdBlkP->Packet.len = PKT_MAX_DATA_LEN;
+ RIOQueueCmdBlk(HostP, Rup, CmdBlkP);
+ return 1;
+}
+
+/**
+ * RIOBootComplete - RTA boot is done
+ * @p: RIO we are working with
+ * @HostP: Host structure
+ * @Rup: RUP being used
+ * @PktCmdP: Packet command that was used
+ *
+ * This function is called when an RTA been booted.
+ * If booted by a host, HostP->HostUniqueNum is the booting host.
+ * If booted by an RTA, HostP->Mapping[Rup].RtaUniqueNum is the booting RTA.
+ * RtaUniq is the booted RTA.
+ */
+
+static int RIOBootComplete(struct rio_info *p, struct Host *HostP, unsigned int Rup, struct PktCmd __iomem *PktCmdP)
+{
+ struct Map *MapP = NULL;
+ struct Map *MapP2 = NULL;
+ int Flag;
+ int found;
+ int host, rta;
+ int EmptySlot = -1;
+ int entry, entry2;
+ char *MyType, *MyName;
+ unsigned int MyLink;
+ unsigned short RtaType;
+ u32 RtaUniq = (readb(&PktCmdP->UniqNum[0])) + (readb(&PktCmdP->UniqNum[1]) << 8) + (readb(&PktCmdP->UniqNum[2]) << 16) + (readb(&PktCmdP->UniqNum[3]) << 24);
+
+ p->RIOBooting = 0;
+
+ rio_dprintk(RIO_DEBUG_BOOT, "RTA Boot completed - BootInProgress now %d\n", p->RIOBooting);
+
+ /*
+ ** Determine type of unit (16/8 port RTA).
+ */
+
+ RtaType = GetUnitType(RtaUniq);
+ if (Rup >= (unsigned short) MAX_RUP)
+ rio_dprintk(RIO_DEBUG_BOOT, "RIO: Host %s has booted an RTA(%d) on link %c\n", HostP->Name, 8 * RtaType, readb(&PktCmdP->LinkNum) + 'A');
+ else
+ rio_dprintk(RIO_DEBUG_BOOT, "RIO: RTA %s has booted an RTA(%d) on link %c\n", HostP->Mapping[Rup].Name, 8 * RtaType, readb(&PktCmdP->LinkNum) + 'A');
+
+ rio_dprintk(RIO_DEBUG_BOOT, "UniqNum is 0x%x\n", RtaUniq);
+
+ if (RtaUniq == 0x00000000 || RtaUniq == 0xffffffff) {
+ rio_dprintk(RIO_DEBUG_BOOT, "Illegal RTA Uniq Number\n");
+ return 1;
+ }
+
+ /*
+ ** If this RTA has just booted an RTA which doesn't belong to this
+ ** system, or the system is in slave mode, do not attempt to create
+ ** a new table entry for it.
+ */
+
+ if (!RIOBootOk(p, HostP, RtaUniq)) {
+ MyLink = readb(&PktCmdP->LinkNum);
+ if (Rup < (unsigned short) MAX_RUP) {
+ /*
+ ** RtaUniq was clone booted (by this RTA). Instruct this RTA
+ ** to hold off further attempts to boot on this link for 30
+ ** seconds.
+ */
+ if (RIOSuspendBootRta(HostP, HostP->Mapping[Rup].ID, MyLink)) {
+ rio_dprintk(RIO_DEBUG_BOOT, "RTA failed to suspend booting on link %c\n", 'A' + MyLink);
+ }
+ } else
+ /*
+ ** RtaUniq was booted by this host. Set the booting link
+ ** to hold off for 30 seconds to give another unit a
+ ** chance to boot it.
+ */
+ writew(30, &HostP->LinkStrP[MyLink].WaitNoBoot);
+ rio_dprintk(RIO_DEBUG_BOOT, "RTA %x not owned - suspend booting down link %c on unit %x\n", RtaUniq, 'A' + MyLink, HostP->Mapping[Rup].RtaUniqueNum);
+ return 1;
+ }
+
+ /*
+ ** Check for a SLOT_IN_USE entry for this RTA attached to the
+ ** current host card in the driver table.
+ **
+ ** If it exists, make a note that we have booted it. Other parts of
+ ** the driver are interested in this information at a later date,
+ ** in particular when the booting RTA asks for an ID for this unit,
+ ** we must have set the BOOTED flag, and the NEWBOOT flag is used
+ ** to force an open on any ports that where previously open on this
+ ** unit.
+ */
+ for (entry = 0; entry < MAX_RUP; entry++) {
+ unsigned int sysport;
+
+ if ((HostP->Mapping[entry].Flags & SLOT_IN_USE) && (HostP->Mapping[entry].RtaUniqueNum == RtaUniq)) {
+ HostP->Mapping[entry].Flags |= RTA_BOOTED | RTA_NEWBOOT;
+ if ((sysport = HostP->Mapping[entry].SysPort) != NO_PORT) {
+ if (sysport < p->RIOFirstPortsBooted)
+ p->RIOFirstPortsBooted = sysport;
+ if (sysport > p->RIOLastPortsBooted)
+ p->RIOLastPortsBooted = sysport;
+ /*
+ ** For a 16 port RTA, check the second bank of 8 ports
+ */
+ if (RtaType == TYPE_RTA16) {
+ entry2 = HostP->Mapping[entry].ID2 - 1;
+ HostP->Mapping[entry2].Flags |= RTA_BOOTED | RTA_NEWBOOT;
+ sysport = HostP->Mapping[entry2].SysPort;
+ if (sysport < p->RIOFirstPortsBooted)
+ p->RIOFirstPortsBooted = sysport;
+ if (sysport > p->RIOLastPortsBooted)
+ p->RIOLastPortsBooted = sysport;
+ }
+ }
+ if (RtaType == TYPE_RTA16)
+ rio_dprintk(RIO_DEBUG_BOOT, "RTA will be given IDs %d+%d\n", entry + 1, entry2 + 1);
+ else
+ rio_dprintk(RIO_DEBUG_BOOT, "RTA will be given ID %d\n", entry + 1);
+ return 1;
+ }
+ }
+
+ rio_dprintk(RIO_DEBUG_BOOT, "RTA not configured for this host\n");
+
+ if (Rup >= (unsigned short) MAX_RUP) {
+ /*
+ ** It was a host that did the booting
+ */
+ MyType = "Host";
+ MyName = HostP->Name;
+ } else {
+ /*
+ ** It was an RTA that did the booting
+ */
+ MyType = "RTA";
+ MyName = HostP->Mapping[Rup].Name;
+ }
+ MyLink = readb(&PktCmdP->LinkNum);
+
+ /*
+ ** There is no SLOT_IN_USE entry for this RTA attached to the current
+ ** host card in the driver table.
+ **
+ ** Check for a SLOT_TENTATIVE entry for this RTA attached to the
+ ** current host card in the driver table.
+ **
+ ** If we find one, then we re-use that slot.
+ */
+ for (entry = 0; entry < MAX_RUP; entry++) {
+ if ((HostP->Mapping[entry].Flags & SLOT_TENTATIVE) && (HostP->Mapping[entry].RtaUniqueNum == RtaUniq)) {
+ if (RtaType == TYPE_RTA16) {
+ entry2 = HostP->Mapping[entry].ID2 - 1;
+ if ((HostP->Mapping[entry2].Flags & SLOT_TENTATIVE) && (HostP->Mapping[entry2].RtaUniqueNum == RtaUniq))
+ rio_dprintk(RIO_DEBUG_BOOT, "Found previous tentative slots (%d+%d)\n", entry, entry2);
+ else
+ continue;
+ } else
+ rio_dprintk(RIO_DEBUG_BOOT, "Found previous tentative slot (%d)\n", entry);
+ if (!p->RIONoMessage)
+ printk("RTA connected to %s '%s' (%c) not configured.\n", MyType, MyName, MyLink + 'A');
+ return 1;
+ }
+ }
+
+ /*
+ ** There is no SLOT_IN_USE or SLOT_TENTATIVE entry for this RTA
+ ** attached to the current host card in the driver table.
+ **
+ ** Check if there is a SLOT_IN_USE or SLOT_TENTATIVE entry on another
+ ** host for this RTA in the driver table.
+ **
+ ** For a SLOT_IN_USE entry on another host, we need to delete the RTA
+ ** entry from the other host and add it to this host (using some of
+ ** the functions from table.c which do this).
+ ** For a SLOT_TENTATIVE entry on another host, we must cope with the
+ ** following scenario:
+ **
+ ** + Plug 8 port RTA into host A. (This creates SLOT_TENTATIVE entry
+ ** in table)
+ ** + Unplug RTA and plug into host B. (We now have 2 SLOT_TENTATIVE
+ ** entries)
+ ** + Configure RTA on host B. (This slot now becomes SLOT_IN_USE)
+ ** + Unplug RTA and plug back into host A.
+ ** + Configure RTA on host A. We now have the same RTA configured
+ ** with different ports on two different hosts.
+ */
+ rio_dprintk(RIO_DEBUG_BOOT, "Have we seen RTA %x before?\n", RtaUniq);
+ found = 0;
+ Flag = 0; /* Convince the compiler this variable is initialized */
+ for (host = 0; !found && (host < p->RIONumHosts); host++) {
+ for (rta = 0; rta < MAX_RUP; rta++) {
+ if ((p->RIOHosts[host].Mapping[rta].Flags & (SLOT_IN_USE | SLOT_TENTATIVE)) && (p->RIOHosts[host].Mapping[rta].RtaUniqueNum == RtaUniq)) {
+ Flag = p->RIOHosts[host].Mapping[rta].Flags;
+ MapP = &p->RIOHosts[host].Mapping[rta];
+ if (RtaType == TYPE_RTA16) {
+ MapP2 = &p->RIOHosts[host].Mapping[MapP->ID2 - 1];
+ rio_dprintk(RIO_DEBUG_BOOT, "This RTA is units %d+%d from host %s\n", rta + 1, MapP->ID2, p->RIOHosts[host].Name);
+ } else
+ rio_dprintk(RIO_DEBUG_BOOT, "This RTA is unit %d from host %s\n", rta + 1, p->RIOHosts[host].Name);
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ /*
+ ** There is no SLOT_IN_USE or SLOT_TENTATIVE entry for this RTA
+ ** attached to the current host card in the driver table.
+ **
+ ** If we have not found a SLOT_IN_USE or SLOT_TENTATIVE entry on
+ ** another host for this RTA in the driver table...
+ **
+ ** Check for a SLOT_IN_USE entry for this RTA in the config table.
+ */
+ if (!MapP) {
+ rio_dprintk(RIO_DEBUG_BOOT, "Look for RTA %x in RIOSavedTable\n", RtaUniq);
+ for (rta = 0; rta < TOTAL_MAP_ENTRIES; rta++) {
+ rio_dprintk(RIO_DEBUG_BOOT, "Check table entry %d (%x)", rta, p->RIOSavedTable[rta].RtaUniqueNum);
+
+ if ((p->RIOSavedTable[rta].Flags & SLOT_IN_USE) && (p->RIOSavedTable[rta].RtaUniqueNum == RtaUniq)) {
+ MapP = &p->RIOSavedTable[rta];
+ Flag = p->RIOSavedTable[rta].Flags;
+ if (RtaType == TYPE_RTA16) {
+ for (entry2 = rta + 1; entry2 < TOTAL_MAP_ENTRIES; entry2++) {
+ if (p->RIOSavedTable[entry2].RtaUniqueNum == RtaUniq)
+ break;
+ }
+ MapP2 = &p->RIOSavedTable[entry2];
+ rio_dprintk(RIO_DEBUG_BOOT, "This RTA is from table entries %d+%d\n", rta, entry2);
+ } else
+ rio_dprintk(RIO_DEBUG_BOOT, "This RTA is from table entry %d\n", rta);
+ break;
+ }
+ }
+ }
+
+ /*
+ ** There is no SLOT_IN_USE or SLOT_TENTATIVE entry for this RTA
+ ** attached to the current host card in the driver table.
+ **
+ ** We may have found a SLOT_IN_USE entry on another host for this
+ ** RTA in the config table, or a SLOT_IN_USE or SLOT_TENTATIVE entry
+ ** on another host for this RTA in the driver table.
+ **
+ ** Check the driver table for room to fit this newly discovered RTA.
+ ** RIOFindFreeID() first looks for free slots and if it does not
+ ** find any free slots it will then attempt to oust any
+ ** tentative entry in the table.
+ */
+ EmptySlot = 1;
+ if (RtaType == TYPE_RTA16) {
+ if (RIOFindFreeID(p, HostP, &entry, &entry2) == 0) {
+ RIODefaultName(p, HostP, entry);
+ rio_fill_host_slot(entry, entry2, RtaUniq, HostP);
+ EmptySlot = 0;
+ }
+ } else {
+ if (RIOFindFreeID(p, HostP, &entry, NULL) == 0) {
+ RIODefaultName(p, HostP, entry);
+ rio_fill_host_slot(entry, 0, RtaUniq, HostP);
+ EmptySlot = 0;
+ }
+ }
+
+ /*
+ ** There is no SLOT_IN_USE or SLOT_TENTATIVE entry for this RTA
+ ** attached to the current host card in the driver table.
+ **
+ ** If we found a SLOT_IN_USE entry on another host for this
+ ** RTA in the config or driver table, and there are enough free
+ ** slots in the driver table, then we need to move it over and
+ ** delete it from the other host.
+ ** If we found a SLOT_TENTATIVE entry on another host for this
+ ** RTA in the driver table, just delete the other host entry.
+ */
+ if (EmptySlot == 0) {
+ if (MapP) {
+ if (Flag & SLOT_IN_USE) {
+ rio_dprintk(RIO_DEBUG_BOOT, "This RTA configured on another host - move entry to current host (1)\n");
+ HostP->Mapping[entry].SysPort = MapP->SysPort;
+ memcpy(HostP->Mapping[entry].Name, MapP->Name, MAX_NAME_LEN);
+ HostP->Mapping[entry].Flags = SLOT_IN_USE | RTA_BOOTED | RTA_NEWBOOT;
+ RIOReMapPorts(p, HostP, &HostP->Mapping[entry]);
+ if (HostP->Mapping[entry].SysPort < p->RIOFirstPortsBooted)
+ p->RIOFirstPortsBooted = HostP->Mapping[entry].SysPort;
+ if (HostP->Mapping[entry].SysPort > p->RIOLastPortsBooted)
+ p->RIOLastPortsBooted = HostP->Mapping[entry].SysPort;
+ rio_dprintk(RIO_DEBUG_BOOT, "SysPort %d, Name %s\n", (int) MapP->SysPort, MapP->Name);
+ } else {
+ rio_dprintk(RIO_DEBUG_BOOT, "This RTA has a tentative entry on another host - delete that entry (1)\n");
+ HostP->Mapping[entry].Flags = SLOT_TENTATIVE | RTA_BOOTED | RTA_NEWBOOT;
+ }
+ if (RtaType == TYPE_RTA16) {
+ if (Flag & SLOT_IN_USE) {
+ HostP->Mapping[entry2].Flags = SLOT_IN_USE | RTA_BOOTED | RTA_NEWBOOT | RTA16_SECOND_SLOT;
+ HostP->Mapping[entry2].SysPort = MapP2->SysPort;
+ /*
+ ** Map second block of ttys for 16 port RTA
+ */
+ RIOReMapPorts(p, HostP, &HostP->Mapping[entry2]);
+ if (HostP->Mapping[entry2].SysPort < p->RIOFirstPortsBooted)
+ p->RIOFirstPortsBooted = HostP->Mapping[entry2].SysPort;
+ if (HostP->Mapping[entry2].SysPort > p->RIOLastPortsBooted)
+ p->RIOLastPortsBooted = HostP->Mapping[entry2].SysPort;
+ rio_dprintk(RIO_DEBUG_BOOT, "SysPort %d, Name %s\n", (int) HostP->Mapping[entry2].SysPort, HostP->Mapping[entry].Name);
+ } else
+ HostP->Mapping[entry2].Flags = SLOT_TENTATIVE | RTA_BOOTED | RTA_NEWBOOT | RTA16_SECOND_SLOT;
+ memset(MapP2, 0, sizeof(struct Map));
+ }
+ memset(MapP, 0, sizeof(struct Map));
+ if (!p->RIONoMessage)
+ printk("An orphaned RTA has been adopted by %s '%s' (%c).\n", MyType, MyName, MyLink + 'A');
+ } else if (!p->RIONoMessage)
+ printk("RTA connected to %s '%s' (%c) not configured.\n", MyType, MyName, MyLink + 'A');
+ RIOSetChange(p);
+ return 1;
+ }
+
+ /*
+ ** There is no room in the driver table to make an entry for the
+ ** booted RTA. Keep a note of its Uniq Num in the overflow table,
+ ** so we can ignore it's ID requests.
+ */
+ if (!p->RIONoMessage)
+ printk("The RTA connected to %s '%s' (%c) cannot be configured. You cannot configure more than 128 ports to one host card.\n", MyType, MyName, MyLink + 'A');
+ for (entry = 0; entry < HostP->NumExtraBooted; entry++) {
+ if (HostP->ExtraUnits[entry] == RtaUniq) {
+ /*
+ ** already got it!
+ */
+ return 1;
+ }
+ }
+ /*
+ ** If there is room, add the unit to the list of extras
+ */
+ if (HostP->NumExtraBooted < MAX_EXTRA_UNITS)
+ HostP->ExtraUnits[HostP->NumExtraBooted++] = RtaUniq;
+ return 1;
+}
+
+
+/*
+** If the RTA or its host appears in the RIOBindTab[] structure then
+** we mustn't boot the RTA and should return 0.
+** This operation is slightly different from the other drivers for RIO
+** in that this is designed to work with the new utilities
+** not config.rio and is FAR SIMPLER.
+** We no longer support the RIOBootMode variable. It is all done from the
+** "boot/noboot" field in the rio.cf file.
+*/
+int RIOBootOk(struct rio_info *p, struct Host *HostP, unsigned long RtaUniq)
+{
+ int Entry;
+ unsigned int HostUniq = HostP->UniqueNum;
+
+ /*
+ ** Search bindings table for RTA or its parent.
+ ** If it exists, return 0, else 1.
+ */
+ for (Entry = 0; (Entry < MAX_RTA_BINDINGS) && (p->RIOBindTab[Entry] != 0); Entry++) {
+ if ((p->RIOBindTab[Entry] == HostUniq) || (p->RIOBindTab[Entry] == RtaUniq))
+ return 0;
+ }
+ return 1;
+}
+
+/*
+** Make an empty slot tentative. If this is a 16 port RTA, make both
+** slots tentative, and the second one RTA_SECOND_SLOT as well.
+*/
+
+void rio_fill_host_slot(int entry, int entry2, unsigned int rta_uniq, struct Host *host)
+{
+ int link;
+
+ rio_dprintk(RIO_DEBUG_BOOT, "rio_fill_host_slot(%d, %d, 0x%x...)\n", entry, entry2, rta_uniq);
+
+ host->Mapping[entry].Flags = (RTA_BOOTED | RTA_NEWBOOT | SLOT_TENTATIVE);
+ host->Mapping[entry].SysPort = NO_PORT;
+ host->Mapping[entry].RtaUniqueNum = rta_uniq;
+ host->Mapping[entry].HostUniqueNum = host->UniqueNum;
+ host->Mapping[entry].ID = entry + 1;
+ host->Mapping[entry].ID2 = 0;
+ if (entry2) {
+ host->Mapping[entry2].Flags = (RTA_BOOTED | RTA_NEWBOOT | SLOT_TENTATIVE | RTA16_SECOND_SLOT);
+ host->Mapping[entry2].SysPort = NO_PORT;
+ host->Mapping[entry2].RtaUniqueNum = rta_uniq;
+ host->Mapping[entry2].HostUniqueNum = host->UniqueNum;
+ host->Mapping[entry2].Name[0] = '\0';
+ host->Mapping[entry2].ID = entry2 + 1;
+ host->Mapping[entry2].ID2 = entry + 1;
+ host->Mapping[entry].ID2 = entry2 + 1;
+ }
+ /*
+ ** Must set these up, so that utilities show
+ ** topology of 16 port RTAs correctly
+ */
+ for (link = 0; link < LINKS_PER_UNIT; link++) {
+ host->Mapping[entry].Topology[link].Unit = ROUTE_DISCONNECT;
+ host->Mapping[entry].Topology[link].Link = NO_LINK;
+ if (entry2) {
+ host->Mapping[entry2].Topology[link].Unit = ROUTE_DISCONNECT;
+ host->Mapping[entry2].Topology[link].Link = NO_LINK;
+ }
+ }
+}
diff --git a/drivers/staging/generic_serial/rio/riocmd.c b/drivers/staging/generic_serial/rio/riocmd.c
new file mode 100644
index 000000000000..f121357e5af0
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/riocmd.c
@@ -0,0 +1,939 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** ported from the existing SCO driver source
+**
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : riocmd.c
+** SID : 1.2
+** Last Modified : 11/6/98 10:33:41
+** Retrieved : 11/6/98 10:33:49
+**
+** ident @(#)riocmd.c 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/termios.h>
+#include <linux/serial.h>
+
+#include <linux/generic_serial.h>
+
+#include "linux_compat.h"
+#include "rio_linux.h"
+#include "pkt.h"
+#include "daemon.h"
+#include "rio.h"
+#include "riospace.h"
+#include "cmdpkt.h"
+#include "map.h"
+#include "rup.h"
+#include "port.h"
+#include "riodrvr.h"
+#include "rioinfo.h"
+#include "func.h"
+#include "errors.h"
+#include "pci.h"
+
+#include "parmmap.h"
+#include "unixrup.h"
+#include "board.h"
+#include "host.h"
+#include "phb.h"
+#include "link.h"
+#include "cmdblk.h"
+#include "route.h"
+#include "cirrus.h"
+
+
+static struct IdentifyRta IdRta;
+static struct KillNeighbour KillUnit;
+
+int RIOFoadRta(struct Host *HostP, struct Map *MapP)
+{
+ struct CmdBlk *CmdBlkP;
+
+ rio_dprintk(RIO_DEBUG_CMD, "FOAD RTA\n");
+
+ CmdBlkP = RIOGetCmdBlk();
+
+ if (!CmdBlkP) {
+ rio_dprintk(RIO_DEBUG_CMD, "FOAD RTA: GetCmdBlk failed\n");
+ return -ENXIO;
+ }
+
+ CmdBlkP->Packet.dest_unit = MapP->ID;
+ CmdBlkP->Packet.dest_port = BOOT_RUP;
+ CmdBlkP->Packet.src_unit = 0;
+ CmdBlkP->Packet.src_port = BOOT_RUP;
+ CmdBlkP->Packet.len = 0x84;
+ CmdBlkP->Packet.data[0] = IFOAD;
+ CmdBlkP->Packet.data[1] = 0;
+ CmdBlkP->Packet.data[2] = IFOAD_MAGIC & 0xFF;
+ CmdBlkP->Packet.data[3] = (IFOAD_MAGIC >> 8) & 0xFF;
+
+ if (RIOQueueCmdBlk(HostP, MapP->ID - 1, CmdBlkP) == RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_CMD, "FOAD RTA: Failed to queue foad command\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+int RIOZombieRta(struct Host *HostP, struct Map *MapP)
+{
+ struct CmdBlk *CmdBlkP;
+
+ rio_dprintk(RIO_DEBUG_CMD, "ZOMBIE RTA\n");
+
+ CmdBlkP = RIOGetCmdBlk();
+
+ if (!CmdBlkP) {
+ rio_dprintk(RIO_DEBUG_CMD, "ZOMBIE RTA: GetCmdBlk failed\n");
+ return -ENXIO;
+ }
+
+ CmdBlkP->Packet.dest_unit = MapP->ID;
+ CmdBlkP->Packet.dest_port = BOOT_RUP;
+ CmdBlkP->Packet.src_unit = 0;
+ CmdBlkP->Packet.src_port = BOOT_RUP;
+ CmdBlkP->Packet.len = 0x84;
+ CmdBlkP->Packet.data[0] = ZOMBIE;
+ CmdBlkP->Packet.data[1] = 0;
+ CmdBlkP->Packet.data[2] = ZOMBIE_MAGIC & 0xFF;
+ CmdBlkP->Packet.data[3] = (ZOMBIE_MAGIC >> 8) & 0xFF;
+
+ if (RIOQueueCmdBlk(HostP, MapP->ID - 1, CmdBlkP) == RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_CMD, "ZOMBIE RTA: Failed to queue zombie command\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+int RIOCommandRta(struct rio_info *p, unsigned long RtaUnique, int (*func) (struct Host * HostP, struct Map * MapP))
+{
+ unsigned int Host;
+
+ rio_dprintk(RIO_DEBUG_CMD, "Command RTA 0x%lx func %p\n", RtaUnique, func);
+
+ if (!RtaUnique)
+ return (0);
+
+ for (Host = 0; Host < p->RIONumHosts; Host++) {
+ unsigned int Rta;
+ struct Host *HostP = &p->RIOHosts[Host];
+
+ for (Rta = 0; Rta < RTAS_PER_HOST; Rta++) {
+ struct Map *MapP = &HostP->Mapping[Rta];
+
+ if (MapP->RtaUniqueNum == RtaUnique) {
+ uint Link;
+
+ /*
+ ** now, lets just check we have a route to it...
+ ** IF the routing stuff is working, then one of the
+ ** topology entries for this unit will have a legit
+ ** route *somewhere*. We care not where - if its got
+ ** any connections, we can get to it.
+ */
+ for (Link = 0; Link < LINKS_PER_UNIT; Link++) {
+ if (MapP->Topology[Link].Unit <= (u8) MAX_RUP) {
+ /*
+ ** Its worth trying the operation...
+ */
+ return (*func) (HostP, MapP);
+ }
+ }
+ }
+ }
+ }
+ return -ENXIO;
+}
+
+
+int RIOIdentifyRta(struct rio_info *p, void __user * arg)
+{
+ unsigned int Host;
+
+ if (copy_from_user(&IdRta, arg, sizeof(IdRta))) {
+ rio_dprintk(RIO_DEBUG_CMD, "RIO_IDENTIFY_RTA copy failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+
+ for (Host = 0; Host < p->RIONumHosts; Host++) {
+ unsigned int Rta;
+ struct Host *HostP = &p->RIOHosts[Host];
+
+ for (Rta = 0; Rta < RTAS_PER_HOST; Rta++) {
+ struct Map *MapP = &HostP->Mapping[Rta];
+
+ if (MapP->RtaUniqueNum == IdRta.RtaUnique) {
+ uint Link;
+ /*
+ ** now, lets just check we have a route to it...
+ ** IF the routing stuff is working, then one of the
+ ** topology entries for this unit will have a legit
+ ** route *somewhere*. We care not where - if its got
+ ** any connections, we can get to it.
+ */
+ for (Link = 0; Link < LINKS_PER_UNIT; Link++) {
+ if (MapP->Topology[Link].Unit <= (u8) MAX_RUP) {
+ /*
+ ** Its worth trying the operation...
+ */
+ struct CmdBlk *CmdBlkP;
+
+ rio_dprintk(RIO_DEBUG_CMD, "IDENTIFY RTA\n");
+
+ CmdBlkP = RIOGetCmdBlk();
+
+ if (!CmdBlkP) {
+ rio_dprintk(RIO_DEBUG_CMD, "IDENTIFY RTA: GetCmdBlk failed\n");
+ return -ENXIO;
+ }
+
+ CmdBlkP->Packet.dest_unit = MapP->ID;
+ CmdBlkP->Packet.dest_port = BOOT_RUP;
+ CmdBlkP->Packet.src_unit = 0;
+ CmdBlkP->Packet.src_port = BOOT_RUP;
+ CmdBlkP->Packet.len = 0x84;
+ CmdBlkP->Packet.data[0] = IDENTIFY;
+ CmdBlkP->Packet.data[1] = 0;
+ CmdBlkP->Packet.data[2] = IdRta.ID;
+
+ if (RIOQueueCmdBlk(HostP, MapP->ID - 1, CmdBlkP) == RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_CMD, "IDENTIFY RTA: Failed to queue command\n");
+ return -EIO;
+ }
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ return -ENOENT;
+}
+
+
+int RIOKillNeighbour(struct rio_info *p, void __user * arg)
+{
+ uint Host;
+ uint ID;
+ struct Host *HostP;
+ struct CmdBlk *CmdBlkP;
+
+ rio_dprintk(RIO_DEBUG_CMD, "KILL HOST NEIGHBOUR\n");
+
+ if (copy_from_user(&KillUnit, arg, sizeof(KillUnit))) {
+ rio_dprintk(RIO_DEBUG_CMD, "RIO_KILL_NEIGHBOUR copy failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+
+ if (KillUnit.Link > 3)
+ return -ENXIO;
+
+ CmdBlkP = RIOGetCmdBlk();
+
+ if (!CmdBlkP) {
+ rio_dprintk(RIO_DEBUG_CMD, "UFOAD: GetCmdBlk failed\n");
+ return -ENXIO;
+ }
+
+ CmdBlkP->Packet.dest_unit = 0;
+ CmdBlkP->Packet.src_unit = 0;
+ CmdBlkP->Packet.dest_port = BOOT_RUP;
+ CmdBlkP->Packet.src_port = BOOT_RUP;
+ CmdBlkP->Packet.len = 0x84;
+ CmdBlkP->Packet.data[0] = UFOAD;
+ CmdBlkP->Packet.data[1] = KillUnit.Link;
+ CmdBlkP->Packet.data[2] = UFOAD_MAGIC & 0xFF;
+ CmdBlkP->Packet.data[3] = (UFOAD_MAGIC >> 8) & 0xFF;
+
+ for (Host = 0; Host < p->RIONumHosts; Host++) {
+ ID = 0;
+ HostP = &p->RIOHosts[Host];
+
+ if (HostP->UniqueNum == KillUnit.UniqueNum) {
+ if (RIOQueueCmdBlk(HostP, RTAS_PER_HOST + KillUnit.Link, CmdBlkP) == RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_CMD, "UFOAD: Failed queue command\n");
+ return -EIO;
+ }
+ return 0;
+ }
+
+ for (ID = 0; ID < RTAS_PER_HOST; ID++) {
+ if (HostP->Mapping[ID].RtaUniqueNum == KillUnit.UniqueNum) {
+ CmdBlkP->Packet.dest_unit = ID + 1;
+ if (RIOQueueCmdBlk(HostP, ID, CmdBlkP) == RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_CMD, "UFOAD: Failed queue command\n");
+ return -EIO;
+ }
+ return 0;
+ }
+ }
+ }
+ RIOFreeCmdBlk(CmdBlkP);
+ return -ENXIO;
+}
+
+int RIOSuspendBootRta(struct Host *HostP, int ID, int Link)
+{
+ struct CmdBlk *CmdBlkP;
+
+ rio_dprintk(RIO_DEBUG_CMD, "SUSPEND BOOT ON RTA ID %d, link %c\n", ID, 'A' + Link);
+
+ CmdBlkP = RIOGetCmdBlk();
+
+ if (!CmdBlkP) {
+ rio_dprintk(RIO_DEBUG_CMD, "SUSPEND BOOT ON RTA: GetCmdBlk failed\n");
+ return -ENXIO;
+ }
+
+ CmdBlkP->Packet.dest_unit = ID;
+ CmdBlkP->Packet.dest_port = BOOT_RUP;
+ CmdBlkP->Packet.src_unit = 0;
+ CmdBlkP->Packet.src_port = BOOT_RUP;
+ CmdBlkP->Packet.len = 0x84;
+ CmdBlkP->Packet.data[0] = IWAIT;
+ CmdBlkP->Packet.data[1] = Link;
+ CmdBlkP->Packet.data[2] = IWAIT_MAGIC & 0xFF;
+ CmdBlkP->Packet.data[3] = (IWAIT_MAGIC >> 8) & 0xFF;
+
+ if (RIOQueueCmdBlk(HostP, ID - 1, CmdBlkP) == RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_CMD, "SUSPEND BOOT ON RTA: Failed to queue iwait command\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+int RIOFoadWakeup(struct rio_info *p)
+{
+ int port;
+ struct Port *PortP;
+ unsigned long flags;
+
+ for (port = 0; port < RIO_PORTS; port++) {
+ PortP = p->RIOPortp[port];
+
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ PortP->Config = 0;
+ PortP->State = 0;
+ PortP->InUse = NOT_INUSE;
+ PortP->PortState = 0;
+ PortP->FlushCmdBodge = 0;
+ PortP->ModemLines = 0;
+ PortP->ModemState = 0;
+ PortP->CookMode = 0;
+ PortP->ParamSem = 0;
+ PortP->Mapped = 0;
+ PortP->WflushFlag = 0;
+ PortP->MagicFlags = 0;
+ PortP->RxDataStart = 0;
+ PortP->TxBufferIn = 0;
+ PortP->TxBufferOut = 0;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ }
+ return (0);
+}
+
+/*
+** Incoming command on the COMMAND_RUP to be processed.
+*/
+static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struct PKT __iomem *PacketP)
+{
+ struct PktCmd __iomem *PktCmdP = (struct PktCmd __iomem *)PacketP->data;
+ struct Port *PortP;
+ struct UnixRup *UnixRupP;
+ unsigned short SysPort;
+ unsigned short ReportedModemStatus;
+ unsigned short rup;
+ unsigned short subCommand;
+ unsigned long flags;
+
+ func_enter();
+
+ /*
+ ** 16 port RTA note:
+ ** Command rup packets coming from the RTA will have pkt->data[1] (which
+ ** translates to PktCmdP->PhbNum) set to the host port number for the
+ ** particular unit. To access the correct BaseSysPort for a 16 port RTA,
+ ** we can use PhbNum to get the rup number for the appropriate 8 port
+ ** block (for the first block, this should be equal to 'Rup').
+ */
+ rup = readb(&PktCmdP->PhbNum) / (unsigned short) PORTS_PER_RTA;
+ UnixRupP = &HostP->UnixRups[rup];
+ SysPort = UnixRupP->BaseSysPort + (readb(&PktCmdP->PhbNum) % (unsigned short) PORTS_PER_RTA);
+ rio_dprintk(RIO_DEBUG_CMD, "Command on rup %d, port %d\n", rup, SysPort);
+
+ if (UnixRupP->BaseSysPort == NO_PORT) {
+ rio_dprintk(RIO_DEBUG_CMD, "OBSCURE ERROR!\n");
+ rio_dprintk(RIO_DEBUG_CMD, "Diagnostics follow. Please WRITE THESE DOWN and report them to Specialix Technical Support\n");
+ rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: Host number %Zd, name ``%s''\n", HostP - p->RIOHosts, HostP->Name);
+ rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: Rup number 0x%x\n", rup);
+
+ if (Rup < (unsigned short) MAX_RUP) {
+ rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: This is the RUP for RTA ``%s''\n", HostP->Mapping[Rup].Name);
+ } else
+ rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: This is the RUP for link ``%c'' of host ``%s''\n", ('A' + Rup - MAX_RUP), HostP->Name);
+
+ rio_dprintk(RIO_DEBUG_CMD, "PACKET information: Destination 0x%x:0x%x\n", readb(&PacketP->dest_unit), readb(&PacketP->dest_port));
+ rio_dprintk(RIO_DEBUG_CMD, "PACKET information: Source 0x%x:0x%x\n", readb(&PacketP->src_unit), readb(&PacketP->src_port));
+ rio_dprintk(RIO_DEBUG_CMD, "PACKET information: Length 0x%x (%d)\n", readb(&PacketP->len), readb(&PacketP->len));
+ rio_dprintk(RIO_DEBUG_CMD, "PACKET information: Control 0x%x (%d)\n", readb(&PacketP->control), readb(&PacketP->control));
+ rio_dprintk(RIO_DEBUG_CMD, "PACKET information: Check 0x%x (%d)\n", readw(&PacketP->csum), readw(&PacketP->csum));
+ rio_dprintk(RIO_DEBUG_CMD, "COMMAND information: Host Port Number 0x%x, " "Command Code 0x%x\n", readb(&PktCmdP->PhbNum), readb(&PktCmdP->Command));
+ return 1;
+ }
+ PortP = p->RIOPortp[SysPort];
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ switch (readb(&PktCmdP->Command)) {
+ case RIOC_BREAK_RECEIVED:
+ rio_dprintk(RIO_DEBUG_CMD, "Received a break!\n");
+ /* If the current line disc. is not multi-threading and
+ the current processor is not the default, reset rup_intr
+ and return 0 to ensure that the command packet is
+ not freed. */
+ /* Call tmgr HANGUP HERE */
+ /* Fix this later when every thing works !!!! RAMRAJ */
+ gs_got_break(&PortP->gs);
+ break;
+
+ case RIOC_COMPLETE:
+ rio_dprintk(RIO_DEBUG_CMD, "Command complete on phb %d host %Zd\n", readb(&PktCmdP->PhbNum), HostP - p->RIOHosts);
+ subCommand = 1;
+ switch (readb(&PktCmdP->SubCommand)) {
+ case RIOC_MEMDUMP:
+ rio_dprintk(RIO_DEBUG_CMD, "Memory dump cmd (0x%x) from addr 0x%x\n", readb(&PktCmdP->SubCommand), readw(&PktCmdP->SubAddr));
+ break;
+ case RIOC_READ_REGISTER:
+ rio_dprintk(RIO_DEBUG_CMD, "Read register (0x%x)\n", readw(&PktCmdP->SubAddr));
+ p->CdRegister = (readb(&PktCmdP->ModemStatus) & RIOC_MSVR1_HOST);
+ break;
+ default:
+ subCommand = 0;
+ break;
+ }
+ if (subCommand)
+ break;
+ rio_dprintk(RIO_DEBUG_CMD, "New status is 0x%x was 0x%x\n", readb(&PktCmdP->PortStatus), PortP->PortState);
+ if (PortP->PortState != readb(&PktCmdP->PortStatus)) {
+ rio_dprintk(RIO_DEBUG_CMD, "Mark status & wakeup\n");
+ PortP->PortState = readb(&PktCmdP->PortStatus);
+ /* What should we do here ...
+ wakeup( &PortP->PortState );
+ */
+ } else
+ rio_dprintk(RIO_DEBUG_CMD, "No change\n");
+
+ /* FALLTHROUGH */
+ case RIOC_MODEM_STATUS:
+ /*
+ ** Knock out the tbusy and tstop bits, as these are not relevant
+ ** to the check for modem status change (they're just there because
+ ** it's a convenient place to put them!).
+ */
+ ReportedModemStatus = readb(&PktCmdP->ModemStatus);
+ if ((PortP->ModemState & RIOC_MSVR1_HOST) ==
+ (ReportedModemStatus & RIOC_MSVR1_HOST)) {
+ rio_dprintk(RIO_DEBUG_CMD, "Modem status unchanged 0x%x\n", PortP->ModemState);
+ /*
+ ** Update ModemState just in case tbusy or tstop states have
+ ** changed.
+ */
+ PortP->ModemState = ReportedModemStatus;
+ } else {
+ rio_dprintk(RIO_DEBUG_CMD, "Modem status change from 0x%x to 0x%x\n", PortP->ModemState, ReportedModemStatus);
+ PortP->ModemState = ReportedModemStatus;
+#ifdef MODEM_SUPPORT
+ if (PortP->Mapped) {
+ /***********************************************************\
+ *************************************************************
+ *** ***
+ *** M O D E M S T A T E C H A N G E ***
+ *** ***
+ *************************************************************
+ \***********************************************************/
+ /*
+ ** If the device is a modem, then check the modem
+ ** carrier.
+ */
+ if (PortP->gs.port.tty == NULL)
+ break;
+ if (PortP->gs.port.tty->termios == NULL)
+ break;
+
+ if (!(PortP->gs.port.tty->termios->c_cflag & CLOCAL) && ((PortP->State & (RIO_MOPEN | RIO_WOPEN)))) {
+
+ rio_dprintk(RIO_DEBUG_CMD, "Is there a Carrier?\n");
+ /*
+ ** Is there a carrier?
+ */
+ if (PortP->ModemState & RIOC_MSVR1_CD) {
+ /*
+ ** Has carrier just appeared?
+ */
+ if (!(PortP->State & RIO_CARR_ON)) {
+ rio_dprintk(RIO_DEBUG_CMD, "Carrier just came up.\n");
+ PortP->State |= RIO_CARR_ON;
+ /*
+ ** wakeup anyone in WOPEN
+ */
+ if (PortP->State & (PORT_ISOPEN | RIO_WOPEN))
+ wake_up_interruptible(&PortP->gs.port.open_wait);
+ }
+ } else {
+ /*
+ ** Has carrier just dropped?
+ */
+ if (PortP->State & RIO_CARR_ON) {
+ if (PortP->State & (PORT_ISOPEN | RIO_WOPEN | RIO_MOPEN))
+ tty_hangup(PortP->gs.port.tty);
+ PortP->State &= ~RIO_CARR_ON;
+ rio_dprintk(RIO_DEBUG_CMD, "Carrirer just went down\n");
+ }
+ }
+ }
+ }
+#endif
+ }
+ break;
+
+ default:
+ rio_dprintk(RIO_DEBUG_CMD, "Unknown command %d on CMD_RUP of host %Zd\n", readb(&PktCmdP->Command), HostP - p->RIOHosts);
+ break;
+ }
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+
+ func_exit();
+
+ return 1;
+}
+
+/*
+** The command mechanism:
+** Each rup has a chain of commands associated with it.
+** This chain is maintained by routines in this file.
+** Periodically we are called and we run a quick check of all the
+** active chains to determine if there is a command to be executed,
+** and if the rup is ready to accept it.
+**
+*/
+
+/*
+** Allocate an empty command block.
+*/
+struct CmdBlk *RIOGetCmdBlk(void)
+{
+ struct CmdBlk *CmdBlkP;
+
+ CmdBlkP = kzalloc(sizeof(struct CmdBlk), GFP_ATOMIC);
+ return CmdBlkP;
+}
+
+/*
+** Return a block to the head of the free list.
+*/
+void RIOFreeCmdBlk(struct CmdBlk *CmdBlkP)
+{
+ kfree(CmdBlkP);
+}
+
+/*
+** attach a command block to the list of commands to be performed for
+** a given rup.
+*/
+int RIOQueueCmdBlk(struct Host *HostP, uint Rup, struct CmdBlk *CmdBlkP)
+{
+ struct CmdBlk **Base;
+ struct UnixRup *UnixRupP;
+ unsigned long flags;
+
+ if (Rup >= (unsigned short) (MAX_RUP + LINKS_PER_UNIT)) {
+ rio_dprintk(RIO_DEBUG_CMD, "Illegal rup number %d in RIOQueueCmdBlk\n", Rup);
+ RIOFreeCmdBlk(CmdBlkP);
+ return RIO_FAIL;
+ }
+
+ UnixRupP = &HostP->UnixRups[Rup];
+
+ rio_spin_lock_irqsave(&UnixRupP->RupLock, flags);
+
+ /*
+ ** If the RUP is currently inactive, then put the request
+ ** straight on the RUP....
+ */
+ if ((UnixRupP->CmdsWaitingP == NULL) && (UnixRupP->CmdPendingP == NULL) && (readw(&UnixRupP->RupP->txcontrol) == TX_RUP_INACTIVE) && (CmdBlkP->PreFuncP ? (*CmdBlkP->PreFuncP) (CmdBlkP->PreArg, CmdBlkP)
+ : 1)) {
+ rio_dprintk(RIO_DEBUG_CMD, "RUP inactive-placing command straight on. Cmd byte is 0x%x\n", CmdBlkP->Packet.data[0]);
+
+ /*
+ ** Whammy! blat that pack!
+ */
+ HostP->Copy(&CmdBlkP->Packet, RIO_PTR(HostP->Caddr, readw(&UnixRupP->RupP->txpkt)), sizeof(struct PKT));
+
+ /*
+ ** place command packet on the pending position.
+ */
+ UnixRupP->CmdPendingP = CmdBlkP;
+
+ /*
+ ** set the command register
+ */
+ writew(TX_PACKET_READY, &UnixRupP->RupP->txcontrol);
+
+ rio_spin_unlock_irqrestore(&UnixRupP->RupLock, flags);
+
+ return 0;
+ }
+ rio_dprintk(RIO_DEBUG_CMD, "RUP active - en-queing\n");
+
+ if (UnixRupP->CmdsWaitingP != NULL)
+ rio_dprintk(RIO_DEBUG_CMD, "Rup active - command waiting\n");
+ if (UnixRupP->CmdPendingP != NULL)
+ rio_dprintk(RIO_DEBUG_CMD, "Rup active - command pending\n");
+ if (readw(&UnixRupP->RupP->txcontrol) != TX_RUP_INACTIVE)
+ rio_dprintk(RIO_DEBUG_CMD, "Rup active - command rup not ready\n");
+
+ Base = &UnixRupP->CmdsWaitingP;
+
+ rio_dprintk(RIO_DEBUG_CMD, "First try to queue cmdblk %p at %p\n", CmdBlkP, Base);
+
+ while (*Base) {
+ rio_dprintk(RIO_DEBUG_CMD, "Command cmdblk %p here\n", *Base);
+ Base = &((*Base)->NextP);
+ rio_dprintk(RIO_DEBUG_CMD, "Now try to queue cmd cmdblk %p at %p\n", CmdBlkP, Base);
+ }
+
+ rio_dprintk(RIO_DEBUG_CMD, "Will queue cmdblk %p at %p\n", CmdBlkP, Base);
+
+ *Base = CmdBlkP;
+
+ CmdBlkP->NextP = NULL;
+
+ rio_spin_unlock_irqrestore(&UnixRupP->RupLock, flags);
+
+ return 0;
+}
+
+/*
+** Here we go - if there is an empty rup, fill it!
+** must be called at splrio() or higher.
+*/
+void RIOPollHostCommands(struct rio_info *p, struct Host *HostP)
+{
+ struct CmdBlk *CmdBlkP;
+ struct UnixRup *UnixRupP;
+ struct PKT __iomem *PacketP;
+ unsigned short Rup;
+ unsigned long flags;
+
+
+ Rup = MAX_RUP + LINKS_PER_UNIT;
+
+ do { /* do this loop for each RUP */
+ /*
+ ** locate the rup we are processing & lock it
+ */
+ UnixRupP = &HostP->UnixRups[--Rup];
+
+ spin_lock_irqsave(&UnixRupP->RupLock, flags);
+
+ /*
+ ** First check for incoming commands:
+ */
+ if (readw(&UnixRupP->RupP->rxcontrol) != RX_RUP_INACTIVE) {
+ int FreeMe;
+
+ PacketP = (struct PKT __iomem *) RIO_PTR(HostP->Caddr, readw(&UnixRupP->RupP->rxpkt));
+
+ switch (readb(&PacketP->dest_port)) {
+ case BOOT_RUP:
+ rio_dprintk(RIO_DEBUG_CMD, "Incoming Boot %s packet '%x'\n", readb(&PacketP->len) & 0x80 ? "Command" : "Data", readb(&PacketP->data[0]));
+ rio_spin_unlock_irqrestore(&UnixRupP->RupLock, flags);
+ FreeMe = RIOBootRup(p, Rup, HostP, PacketP);
+ rio_spin_lock_irqsave(&UnixRupP->RupLock, flags);
+ break;
+
+ case COMMAND_RUP:
+ /*
+ ** Free the RUP lock as loss of carrier causes a
+ ** ttyflush which will (eventually) call another
+ ** routine that uses the RUP lock.
+ */
+ rio_spin_unlock_irqrestore(&UnixRupP->RupLock, flags);
+ FreeMe = RIOCommandRup(p, Rup, HostP, PacketP);
+ if (readb(&PacketP->data[5]) == RIOC_MEMDUMP) {
+ rio_dprintk(RIO_DEBUG_CMD, "Memdump from 0x%x complete\n", readw(&(PacketP->data[6])));
+ rio_memcpy_fromio(p->RIOMemDump, &(PacketP->data[8]), 32);
+ }
+ rio_spin_lock_irqsave(&UnixRupP->RupLock, flags);
+ break;
+
+ case ROUTE_RUP:
+ rio_spin_unlock_irqrestore(&UnixRupP->RupLock, flags);
+ FreeMe = RIORouteRup(p, Rup, HostP, PacketP);
+ rio_spin_lock_irqsave(&UnixRupP->RupLock, flags);
+ break;
+
+ default:
+ rio_dprintk(RIO_DEBUG_CMD, "Unknown RUP %d\n", readb(&PacketP->dest_port));
+ FreeMe = 1;
+ break;
+ }
+
+ if (FreeMe) {
+ rio_dprintk(RIO_DEBUG_CMD, "Free processed incoming command packet\n");
+ put_free_end(HostP, PacketP);
+
+ writew(RX_RUP_INACTIVE, &UnixRupP->RupP->rxcontrol);
+
+ if (readw(&UnixRupP->RupP->handshake) == PHB_HANDSHAKE_SET) {
+ rio_dprintk(RIO_DEBUG_CMD, "Handshake rup %d\n", Rup);
+ writew(PHB_HANDSHAKE_SET | PHB_HANDSHAKE_RESET, &UnixRupP->RupP->handshake);
+ }
+ }
+ }
+
+ /*
+ ** IF a command was running on the port,
+ ** and it has completed, then tidy it up.
+ */
+ if ((CmdBlkP = UnixRupP->CmdPendingP) && /* ASSIGN! */
+ (readw(&UnixRupP->RupP->txcontrol) == TX_RUP_INACTIVE)) {
+ /*
+ ** we are idle.
+ ** there is a command in pending.
+ ** Therefore, this command has finished.
+ ** So, wakeup whoever is waiting for it (and tell them
+ ** what happened).
+ */
+ if (CmdBlkP->Packet.dest_port == BOOT_RUP)
+ rio_dprintk(RIO_DEBUG_CMD, "Free Boot %s Command Block '%x'\n", CmdBlkP->Packet.len & 0x80 ? "Command" : "Data", CmdBlkP->Packet.data[0]);
+
+ rio_dprintk(RIO_DEBUG_CMD, "Command %p completed\n", CmdBlkP);
+
+ /*
+ ** Clear the Rup lock to prevent mutual exclusion.
+ */
+ if (CmdBlkP->PostFuncP) {
+ rio_spin_unlock_irqrestore(&UnixRupP->RupLock, flags);
+ (*CmdBlkP->PostFuncP) (CmdBlkP->PostArg, CmdBlkP);
+ rio_spin_lock_irqsave(&UnixRupP->RupLock, flags);
+ }
+
+ /*
+ ** ....clear the pending flag....
+ */
+ UnixRupP->CmdPendingP = NULL;
+
+ /*
+ ** ....and return the command block to the freelist.
+ */
+ RIOFreeCmdBlk(CmdBlkP);
+ }
+
+ /*
+ ** If there is a command for this rup, and the rup
+ ** is idle, then process the command
+ */
+ if ((CmdBlkP = UnixRupP->CmdsWaitingP) && /* ASSIGN! */
+ (UnixRupP->CmdPendingP == NULL) && (readw(&UnixRupP->RupP->txcontrol) == TX_RUP_INACTIVE)) {
+ /*
+ ** if the pre-function is non-zero, call it.
+ ** If it returns RIO_FAIL then don't
+ ** send this command yet!
+ */
+ if (!(CmdBlkP->PreFuncP ? (*CmdBlkP->PreFuncP) (CmdBlkP->PreArg, CmdBlkP) : 1)) {
+ rio_dprintk(RIO_DEBUG_CMD, "Not ready to start command %p\n", CmdBlkP);
+ } else {
+ rio_dprintk(RIO_DEBUG_CMD, "Start new command %p Cmd byte is 0x%x\n", CmdBlkP, CmdBlkP->Packet.data[0]);
+ /*
+ ** Whammy! blat that pack!
+ */
+ HostP->Copy(&CmdBlkP->Packet, RIO_PTR(HostP->Caddr, readw(&UnixRupP->RupP->txpkt)), sizeof(struct PKT));
+
+ /*
+ ** remove the command from the rup command queue...
+ */
+ UnixRupP->CmdsWaitingP = CmdBlkP->NextP;
+
+ /*
+ ** ...and place it on the pending position.
+ */
+ UnixRupP->CmdPendingP = CmdBlkP;
+
+ /*
+ ** set the command register
+ */
+ writew(TX_PACKET_READY, &UnixRupP->RupP->txcontrol);
+
+ /*
+ ** the command block will be freed
+ ** when the command has been processed.
+ */
+ }
+ }
+ spin_unlock_irqrestore(&UnixRupP->RupLock, flags);
+ } while (Rup);
+}
+
+int RIOWFlushMark(unsigned long iPortP, struct CmdBlk *CmdBlkP)
+{
+ struct Port *PortP = (struct Port *) iPortP;
+ unsigned long flags;
+
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ PortP->WflushFlag++;
+ PortP->MagicFlags |= MAGIC_FLUSH;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return RIOUnUse(iPortP, CmdBlkP);
+}
+
+int RIORFlushEnable(unsigned long iPortP, struct CmdBlk *CmdBlkP)
+{
+ struct Port *PortP = (struct Port *) iPortP;
+ struct PKT __iomem *PacketP;
+ unsigned long flags;
+
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+
+ while (can_remove_receive(&PacketP, PortP)) {
+ remove_receive(PortP);
+ put_free_end(PortP->HostP, PacketP);
+ }
+
+ if (readw(&PortP->PhbP->handshake) == PHB_HANDSHAKE_SET) {
+ /*
+ ** MAGIC! (Basically, handshake the RX buffer, so that
+ ** the RTAs upstream can be re-enabled.)
+ */
+ rio_dprintk(RIO_DEBUG_CMD, "Util: Set RX handshake bit\n");
+ writew(PHB_HANDSHAKE_SET | PHB_HANDSHAKE_RESET, &PortP->PhbP->handshake);
+ }
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return RIOUnUse(iPortP, CmdBlkP);
+}
+
+int RIOUnUse(unsigned long iPortP, struct CmdBlk *CmdBlkP)
+{
+ struct Port *PortP = (struct Port *) iPortP;
+ unsigned long flags;
+
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+
+ rio_dprintk(RIO_DEBUG_CMD, "Decrement in use count for port\n");
+
+ if (PortP->InUse) {
+ if (--PortP->InUse != NOT_INUSE) {
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return 0;
+ }
+ }
+ /*
+ ** While PortP->InUse is set (i.e. a preemptive command has been sent to
+ ** the RTA and is awaiting completion), any transmit data is prevented from
+ ** being transferred from the write queue into the transmit packets
+ ** (add_transmit) and no furthur transmit interrupt will be sent for that
+ ** data. The next interrupt will occur up to 500ms later (RIOIntr is called
+ ** twice a second as a saftey measure). This was the case when kermit was
+ ** used to send data into a RIO port. After each packet was sent, TCFLSH
+ ** was called to flush the read queue preemptively. PortP->InUse was
+ ** incremented, thereby blocking the 6 byte acknowledgement packet
+ ** transmitted back. This acknowledgment hung around for 500ms before
+ ** being sent, thus reducing input performance substantially!.
+ ** When PortP->InUse becomes NOT_INUSE, we must ensure that any data
+ ** hanging around in the transmit buffer is sent immediately.
+ */
+ writew(1, &PortP->HostP->ParmMapP->tx_intr);
+ /* What to do here ..
+ wakeup( (caddr_t)&(PortP->InUse) );
+ */
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return 0;
+}
+
+/*
+**
+** How to use this file:
+**
+** To send a command down a rup, you need to allocate a command block, fill
+** in the packet information, fill in the command number, fill in the pre-
+** and post- functions and arguments, and then add the command block to the
+** queue of command blocks for the port in question. When the port is idle,
+** then the pre-function will be called. If this returns RIO_FAIL then the
+** command will be re-queued and tried again at a later date (probably in one
+** clock tick). If the pre-function returns NOT RIO_FAIL, then the command
+** packet will be queued on the RUP, and the txcontrol field set to the
+** command number. When the txcontrol field has changed from being the
+** command number, then the post-function will be called, with the argument
+** specified earlier, a pointer to the command block, and the value of
+** txcontrol.
+**
+** To allocate a command block, call RIOGetCmdBlk(). This returns a pointer
+** to the command block structure allocated, or NULL if there aren't any.
+** The block will have been zeroed for you.
+**
+** The structure has the following fields:
+**
+** struct CmdBlk
+** {
+** struct CmdBlk *NextP; ** Pointer to next command block **
+** struct PKT Packet; ** A packet, to copy to the rup **
+** int (*PreFuncP)(); ** The func to call to check if OK **
+** int PreArg; ** The arg for the func **
+** int (*PostFuncP)(); ** The func to call when completed **
+** int PostArg; ** The arg for the func **
+** };
+**
+** You need to fill in ALL fields EXCEPT NextP, which is used to link the
+** blocks together either on the free list or on the Rup list.
+**
+** Packet is an actual packet structure to be filled in with the packet
+** information associated with the command. You need to fill in everything,
+** as the command processor doesn't process the command packet in any way.
+**
+** The PreFuncP is called before the packet is enqueued on the host rup.
+** PreFuncP is called as (*PreFuncP)(PreArg, CmdBlkP);. PreFuncP must
+** return !RIO_FAIL to have the packet queued on the rup, and RIO_FAIL
+** if the packet is NOT to be queued.
+**
+** The PostFuncP is called when the command has completed. It is called
+** as (*PostFuncP)(PostArg, CmdBlkP, txcontrol);. PostFuncP is not expected
+** to return a value. PostFuncP does NOT need to free the command block,
+** as this happens automatically after PostFuncP returns.
+**
+** Once the command block has been filled in, it is attached to the correct
+** queue by calling RIOQueueCmdBlk( HostP, Rup, CmdBlkP ) where HostP is
+** a pointer to the struct Host, Rup is the NUMBER of the rup (NOT a pointer
+** to it!), and CmdBlkP is the pointer to the command block allocated using
+** RIOGetCmdBlk().
+**
+*/
diff --git a/drivers/staging/generic_serial/rio/rioctrl.c b/drivers/staging/generic_serial/rio/rioctrl.c
new file mode 100644
index 000000000000..780506326a73
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/rioctrl.c
@@ -0,0 +1,1504 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : rioctrl.c
+** SID : 1.3
+** Last Modified : 11/6/98 10:33:42
+** Retrieved : 11/6/98 10:33:49
+**
+** ident @(#)rioctrl.c 1.3
+**
+** -----------------------------------------------------------------------------
+*/
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/termios.h>
+#include <linux/serial.h>
+
+#include <linux/generic_serial.h>
+
+
+#include "linux_compat.h"
+#include "rio_linux.h"
+#include "pkt.h"
+#include "daemon.h"
+#include "rio.h"
+#include "riospace.h"
+#include "cmdpkt.h"
+#include "map.h"
+#include "rup.h"
+#include "port.h"
+#include "riodrvr.h"
+#include "rioinfo.h"
+#include "func.h"
+#include "errors.h"
+#include "pci.h"
+
+#include "parmmap.h"
+#include "unixrup.h"
+#include "board.h"
+#include "host.h"
+#include "phb.h"
+#include "link.h"
+#include "cmdblk.h"
+#include "route.h"
+#include "cirrus.h"
+#include "rioioctl.h"
+
+
+static struct LpbReq LpbReq;
+static struct RupReq RupReq;
+static struct PortReq PortReq;
+static struct HostReq HostReq; /* oh really? global? and no locking? */
+static struct HostDpRam HostDpRam;
+static struct DebugCtrl DebugCtrl;
+static struct Map MapEnt;
+static struct PortSetup PortSetup;
+static struct DownLoad DownLoad;
+static struct SendPack SendPack;
+/* static struct StreamInfo StreamInfo; */
+/* static char modemtable[RIO_PORTS]; */
+static struct SpecialRupCmd SpecialRupCmd;
+static struct PortParams PortParams;
+static struct portStats portStats;
+
+static struct SubCmdStruct {
+ ushort Host;
+ ushort Rup;
+ ushort Port;
+ ushort Addr;
+} SubCmd;
+
+struct PortTty {
+ uint port;
+ struct ttystatics Tty;
+};
+
+static struct PortTty PortTty;
+typedef struct ttystatics TERMIO;
+
+/*
+** This table is used when the config.rio downloads bin code to the
+** driver. We index the table using the product code, 0-F, and call
+** the function pointed to by the entry, passing the information
+** about the boot.
+** The RIOBootCodeUNKNOWN entry is there to politely tell the calling
+** process to bog off.
+*/
+static int
+ (*RIOBootTable[MAX_PRODUCT]) (struct rio_info *, struct DownLoad *) = {
+ /* 0 */ RIOBootCodeHOST,
+ /* Host Card */
+ /* 1 */ RIOBootCodeRTA,
+ /* RTA */
+};
+
+#define drv_makedev(maj, min) ((((uint) maj & 0xff) << 8) | ((uint) min & 0xff))
+
+static int copy_from_io(void __user *to, void __iomem *from, size_t size)
+{
+ void *buf = kmalloc(size, GFP_KERNEL);
+ int res = -ENOMEM;
+ if (buf) {
+ rio_memcpy_fromio(buf, from, size);
+ res = copy_to_user(to, buf, size);
+ kfree(buf);
+ }
+ return res;
+}
+
+int riocontrol(struct rio_info *p, dev_t dev, int cmd, unsigned long arg, int su)
+{
+ uint Host; /* leave me unsigned! */
+ uint port; /* and me! */
+ struct Host *HostP;
+ ushort loop;
+ int Entry;
+ struct Port *PortP;
+ struct PKT __iomem *PacketP;
+ int retval = 0;
+ unsigned long flags;
+ void __user *argp = (void __user *)arg;
+
+ func_enter();
+
+ /* Confuse the compiler to think that we've initialized these */
+ Host = 0;
+ PortP = NULL;
+
+ rio_dprintk(RIO_DEBUG_CTRL, "control ioctl cmd: 0x%x arg: %p\n", cmd, argp);
+
+ switch (cmd) {
+ /*
+ ** RIO_SET_TIMER
+ **
+ ** Change the value of the host card interrupt timer.
+ ** If the host card number is -1 then all host cards are changed
+ ** otherwise just the specified host card will be changed.
+ */
+ case RIO_SET_TIMER:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_SET_TIMER to %ldms\n", arg);
+ {
+ int host, value;
+ host = (arg >> 16) & 0x0000FFFF;
+ value = arg & 0x0000ffff;
+ if (host == -1) {
+ for (host = 0; host < p->RIONumHosts; host++) {
+ if (p->RIOHosts[host].Flags == RC_RUNNING) {
+ writew(value, &p->RIOHosts[host].ParmMapP->timer);
+ }
+ }
+ } else if (host >= p->RIONumHosts) {
+ return -EINVAL;
+ } else {
+ if (p->RIOHosts[host].Flags == RC_RUNNING) {
+ writew(value, &p->RIOHosts[host].ParmMapP->timer);
+ }
+ }
+ }
+ return 0;
+
+ case RIO_FOAD_RTA:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_FOAD_RTA\n");
+ return RIOCommandRta(p, arg, RIOFoadRta);
+
+ case RIO_ZOMBIE_RTA:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_ZOMBIE_RTA\n");
+ return RIOCommandRta(p, arg, RIOZombieRta);
+
+ case RIO_IDENTIFY_RTA:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_IDENTIFY_RTA\n");
+ return RIOIdentifyRta(p, argp);
+
+ case RIO_KILL_NEIGHBOUR:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_KILL_NEIGHBOUR\n");
+ return RIOKillNeighbour(p, argp);
+
+ case SPECIAL_RUP_CMD:
+ {
+ struct CmdBlk *CmdBlkP;
+
+ rio_dprintk(RIO_DEBUG_CTRL, "SPECIAL_RUP_CMD\n");
+ if (copy_from_user(&SpecialRupCmd, argp, sizeof(SpecialRupCmd))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "SPECIAL_RUP_CMD copy failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ CmdBlkP = RIOGetCmdBlk();
+ if (!CmdBlkP) {
+ rio_dprintk(RIO_DEBUG_CTRL, "SPECIAL_RUP_CMD GetCmdBlk failed\n");
+ return -ENXIO;
+ }
+ CmdBlkP->Packet = SpecialRupCmd.Packet;
+ if (SpecialRupCmd.Host >= p->RIONumHosts)
+ SpecialRupCmd.Host = 0;
+ rio_dprintk(RIO_DEBUG_CTRL, "Queue special rup command for host %d rup %d\n", SpecialRupCmd.Host, SpecialRupCmd.RupNum);
+ if (RIOQueueCmdBlk(&p->RIOHosts[SpecialRupCmd.Host], SpecialRupCmd.RupNum, CmdBlkP) == RIO_FAIL) {
+ printk(KERN_WARNING "rio: FAILED TO QUEUE SPECIAL RUP COMMAND\n");
+ }
+ return 0;
+ }
+
+ case RIO_DEBUG_MEM:
+ return -EPERM;
+
+ case RIO_ALL_MODEM:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_ALL_MODEM\n");
+ p->RIOError.Error = IOCTL_COMMAND_UNKNOWN;
+ return -EINVAL;
+
+ case RIO_GET_TABLE:
+ /*
+ ** Read the routing table from the device driver to user space
+ */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_TABLE\n");
+
+ if ((retval = RIOApel(p)) != 0)
+ return retval;
+
+ if (copy_to_user(argp, p->RIOConnectTable, TOTAL_MAP_ENTRIES * sizeof(struct Map))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_TABLE copy failed\n");
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+
+ {
+ int entry;
+ rio_dprintk(RIO_DEBUG_CTRL, "*****\nMAP ENTRIES\n");
+ for (entry = 0; entry < TOTAL_MAP_ENTRIES; entry++) {
+ if ((p->RIOConnectTable[entry].ID == 0) && (p->RIOConnectTable[entry].HostUniqueNum == 0) && (p->RIOConnectTable[entry].RtaUniqueNum == 0))
+ continue;
+
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.HostUniqueNum = 0x%x\n", entry, p->RIOConnectTable[entry].HostUniqueNum);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.RtaUniqueNum = 0x%x\n", entry, p->RIOConnectTable[entry].RtaUniqueNum);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.ID = 0x%x\n", entry, p->RIOConnectTable[entry].ID);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.ID2 = 0x%x\n", entry, p->RIOConnectTable[entry].ID2);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Flags = 0x%x\n", entry, (int) p->RIOConnectTable[entry].Flags);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.SysPort = 0x%x\n", entry, (int) p->RIOConnectTable[entry].SysPort);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[0].Unit = %x\n", entry, p->RIOConnectTable[entry].Topology[0].Unit);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[0].Link = %x\n", entry, p->RIOConnectTable[entry].Topology[0].Link);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[1].Unit = %x\n", entry, p->RIOConnectTable[entry].Topology[1].Unit);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[1].Link = %x\n", entry, p->RIOConnectTable[entry].Topology[1].Link);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[2].Unit = %x\n", entry, p->RIOConnectTable[entry].Topology[2].Unit);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[2].Link = %x\n", entry, p->RIOConnectTable[entry].Topology[2].Link);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[3].Unit = %x\n", entry, p->RIOConnectTable[entry].Topology[3].Unit);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Top[4].Link = %x\n", entry, p->RIOConnectTable[entry].Topology[3].Link);
+ rio_dprintk(RIO_DEBUG_CTRL, "Map entry %d.Name = %s\n", entry, p->RIOConnectTable[entry].Name);
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "*****\nEND MAP ENTRIES\n");
+ }
+ p->RIOQuickCheck = NOT_CHANGED; /* a table has been gotten */
+ return 0;
+
+ case RIO_PUT_TABLE:
+ /*
+ ** Write the routing table to the device driver from user space
+ */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_PUT_TABLE\n");
+
+ if (!su) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_PUT_TABLE !Root\n");
+ p->RIOError.Error = NOT_SUPER_USER;
+ return -EPERM;
+ }
+ if (copy_from_user(&p->RIOConnectTable[0], argp, TOTAL_MAP_ENTRIES * sizeof(struct Map))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_PUT_TABLE copy failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+/*
+***********************************
+ {
+ int entry;
+ rio_dprint(RIO_DEBUG_CTRL, ("*****\nMAP ENTRIES\n") );
+ for ( entry=0; entry<TOTAL_MAP_ENTRIES; entry++ )
+ {
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.HostUniqueNum = 0x%x\n", entry, p->RIOConnectTable[entry].HostUniqueNum ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.RtaUniqueNum = 0x%x\n", entry, p->RIOConnectTable[entry].RtaUniqueNum ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.ID = 0x%x\n", entry, p->RIOConnectTable[entry].ID ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.ID2 = 0x%x\n", entry, p->RIOConnectTable[entry].ID2 ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Flags = 0x%x\n", entry, p->RIOConnectTable[entry].Flags ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.SysPort = 0x%x\n", entry, p->RIOConnectTable[entry].SysPort ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[0].Unit = %b\n", entry, p->RIOConnectTable[entry].Topology[0].Unit ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[0].Link = %b\n", entry, p->RIOConnectTable[entry].Topology[0].Link ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[1].Unit = %b\n", entry, p->RIOConnectTable[entry].Topology[1].Unit ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[1].Link = %b\n", entry, p->RIOConnectTable[entry].Topology[1].Link ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[2].Unit = %b\n", entry, p->RIOConnectTable[entry].Topology[2].Unit ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[2].Link = %b\n", entry, p->RIOConnectTable[entry].Topology[2].Link ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[3].Unit = %b\n", entry, p->RIOConnectTable[entry].Topology[3].Unit ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Top[4].Link = %b\n", entry, p->RIOConnectTable[entry].Topology[3].Link ) );
+ rio_dprint(RIO_DEBUG_CTRL, ("Map entry %d.Name = %s\n", entry, p->RIOConnectTable[entry].Name ) );
+ }
+ rio_dprint(RIO_DEBUG_CTRL, ("*****\nEND MAP ENTRIES\n") );
+ }
+***********************************
+*/
+ return RIONewTable(p);
+
+ case RIO_GET_BINDINGS:
+ /*
+ ** Send bindings table, containing unique numbers of RTAs owned
+ ** by this system to user space
+ */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_BINDINGS\n");
+
+ if (!su) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_BINDINGS !Root\n");
+ p->RIOError.Error = NOT_SUPER_USER;
+ return -EPERM;
+ }
+ if (copy_to_user(argp, p->RIOBindTab, (sizeof(ulong) * MAX_RTA_BINDINGS))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_BINDINGS copy failed\n");
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return 0;
+
+ case RIO_PUT_BINDINGS:
+ /*
+ ** Receive a bindings table, containing unique numbers of RTAs owned
+ ** by this system
+ */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_PUT_BINDINGS\n");
+
+ if (!su) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_PUT_BINDINGS !Root\n");
+ p->RIOError.Error = NOT_SUPER_USER;
+ return -EPERM;
+ }
+ if (copy_from_user(&p->RIOBindTab[0], argp, (sizeof(ulong) * MAX_RTA_BINDINGS))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_PUT_BINDINGS copy failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ return 0;
+
+ case RIO_BIND_RTA:
+ {
+ int EmptySlot = -1;
+ /*
+ ** Bind this RTA to host, so that it will be booted by
+ ** host in 'boot owned RTAs' mode.
+ */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_BIND_RTA\n");
+
+ if (!su) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_BIND_RTA !Root\n");
+ p->RIOError.Error = NOT_SUPER_USER;
+ return -EPERM;
+ }
+ for (Entry = 0; Entry < MAX_RTA_BINDINGS; Entry++) {
+ if ((EmptySlot == -1) && (p->RIOBindTab[Entry] == 0L))
+ EmptySlot = Entry;
+ else if (p->RIOBindTab[Entry] == arg) {
+ /*
+ ** Already exists - delete
+ */
+ p->RIOBindTab[Entry] = 0L;
+ rio_dprintk(RIO_DEBUG_CTRL, "Removing Rta %ld from p->RIOBindTab\n", arg);
+ return 0;
+ }
+ }
+ /*
+ ** Dosen't exist - add
+ */
+ if (EmptySlot != -1) {
+ p->RIOBindTab[EmptySlot] = arg;
+ rio_dprintk(RIO_DEBUG_CTRL, "Adding Rta %lx to p->RIOBindTab\n", arg);
+ } else {
+ rio_dprintk(RIO_DEBUG_CTRL, "p->RIOBindTab full! - Rta %lx not added\n", arg);
+ return -ENOMEM;
+ }
+ return 0;
+ }
+
+ case RIO_RESUME:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESUME\n");
+ port = arg;
+ if ((port < 0) || (port > 511)) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESUME: Bad port number %d\n", port);
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+ PortP = p->RIOPortp[port];
+ if (!PortP->Mapped) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESUME: Port %d not mapped\n", port);
+ p->RIOError.Error = PORT_NOT_MAPPED_INTO_SYSTEM;
+ return -EINVAL;
+ }
+ if (!(PortP->State & (RIO_LOPEN | RIO_MOPEN))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESUME: Port %d not open\n", port);
+ return -EINVAL;
+ }
+
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ if (RIOPreemptiveCmd(p, (p->RIOPortp[port]), RIOC_RESUME) ==
+ RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESUME failed\n");
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return -EBUSY;
+ } else {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESUME: Port %d resumed\n", port);
+ PortP->State |= RIO_BUSY;
+ }
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return retval;
+
+ case RIO_ASSIGN_RTA:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_ASSIGN_RTA\n");
+ if (!su) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_ASSIGN_RTA !Root\n");
+ p->RIOError.Error = NOT_SUPER_USER;
+ return -EPERM;
+ }
+ if (copy_from_user(&MapEnt, argp, sizeof(MapEnt))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "Copy from user space failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ return RIOAssignRta(p, &MapEnt);
+
+ case RIO_CHANGE_NAME:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_CHANGE_NAME\n");
+ if (!su) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_CHANGE_NAME !Root\n");
+ p->RIOError.Error = NOT_SUPER_USER;
+ return -EPERM;
+ }
+ if (copy_from_user(&MapEnt, argp, sizeof(MapEnt))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "Copy from user space failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ return RIOChangeName(p, &MapEnt);
+
+ case RIO_DELETE_RTA:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_DELETE_RTA\n");
+ if (!su) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_DELETE_RTA !Root\n");
+ p->RIOError.Error = NOT_SUPER_USER;
+ return -EPERM;
+ }
+ if (copy_from_user(&MapEnt, argp, sizeof(MapEnt))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "Copy from data space failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ return RIODeleteRta(p, &MapEnt);
+
+ case RIO_QUICK_CHECK:
+ if (copy_to_user(argp, &p->RIORtaDisCons, sizeof(unsigned int))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return 0;
+
+ case RIO_LAST_ERROR:
+ if (copy_to_user(argp, &p->RIOError, sizeof(struct Error)))
+ return -EFAULT;
+ return 0;
+
+ case RIO_GET_LOG:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_LOG\n");
+ return -EINVAL;
+
+ case RIO_GET_MODTYPE:
+ if (copy_from_user(&port, argp, sizeof(unsigned int))) {
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "Get module type for port %d\n", port);
+ if (port < 0 || port > 511) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_MODTYPE: Bad port number %d\n", port);
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+ PortP = (p->RIOPortp[port]);
+ if (!PortP->Mapped) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_MODTYPE: Port %d not mapped\n", port);
+ p->RIOError.Error = PORT_NOT_MAPPED_INTO_SYSTEM;
+ return -EINVAL;
+ }
+ /*
+ ** Return module type of port
+ */
+ port = PortP->HostP->UnixRups[PortP->RupNum].ModTypes;
+ if (copy_to_user(argp, &port, sizeof(unsigned int))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return (0);
+ case RIO_BLOCK_OPENS:
+ rio_dprintk(RIO_DEBUG_CTRL, "Opens block until booted\n");
+ for (Entry = 0; Entry < RIO_PORTS; Entry++) {
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ p->RIOPortp[Entry]->WaitUntilBooted = 1;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ }
+ return 0;
+
+ case RIO_SETUP_PORTS:
+ rio_dprintk(RIO_DEBUG_CTRL, "Setup ports\n");
+ if (copy_from_user(&PortSetup, argp, sizeof(PortSetup))) {
+ p->RIOError.Error = COPYIN_FAILED;
+ rio_dprintk(RIO_DEBUG_CTRL, "EFAULT");
+ return -EFAULT;
+ }
+ if (PortSetup.From > PortSetup.To || PortSetup.To >= RIO_PORTS) {
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ rio_dprintk(RIO_DEBUG_CTRL, "ENXIO");
+ return -ENXIO;
+ }
+ if (PortSetup.XpCps > p->RIOConf.MaxXpCps || PortSetup.XpCps < p->RIOConf.MinXpCps) {
+ p->RIOError.Error = XPRINT_CPS_OUT_OF_RANGE;
+ rio_dprintk(RIO_DEBUG_CTRL, "EINVAL");
+ return -EINVAL;
+ }
+ if (!p->RIOPortp) {
+ printk(KERN_ERR "rio: No p->RIOPortp array!\n");
+ rio_dprintk(RIO_DEBUG_CTRL, "No p->RIOPortp array!\n");
+ return -EIO;
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "entering loop (%d %d)!\n", PortSetup.From, PortSetup.To);
+ for (loop = PortSetup.From; loop <= PortSetup.To; loop++) {
+ rio_dprintk(RIO_DEBUG_CTRL, "in loop (%d)!\n", loop);
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "after loop (%d)!\n", loop);
+ rio_dprintk(RIO_DEBUG_CTRL, "Retval:%x\n", retval);
+ return retval;
+
+ case RIO_GET_PORT_SETUP:
+ rio_dprintk(RIO_DEBUG_CTRL, "Get port setup\n");
+ if (copy_from_user(&PortSetup, argp, sizeof(PortSetup))) {
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ if (PortSetup.From >= RIO_PORTS) {
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -ENXIO;
+ }
+
+ port = PortSetup.To = PortSetup.From;
+ PortSetup.IxAny = (p->RIOPortp[port]->Config & RIO_IXANY) ? 1 : 0;
+ PortSetup.IxOn = (p->RIOPortp[port]->Config & RIO_IXON) ? 1 : 0;
+ PortSetup.Drain = (p->RIOPortp[port]->Config & RIO_WAITDRAIN) ? 1 : 0;
+ PortSetup.Store = p->RIOPortp[port]->Store;
+ PortSetup.Lock = p->RIOPortp[port]->Lock;
+ PortSetup.XpCps = p->RIOPortp[port]->Xprint.XpCps;
+ memcpy(PortSetup.XpOn, p->RIOPortp[port]->Xprint.XpOn, MAX_XP_CTRL_LEN);
+ memcpy(PortSetup.XpOff, p->RIOPortp[port]->Xprint.XpOff, MAX_XP_CTRL_LEN);
+ PortSetup.XpOn[MAX_XP_CTRL_LEN - 1] = '\0';
+ PortSetup.XpOff[MAX_XP_CTRL_LEN - 1] = '\0';
+
+ if (copy_to_user(argp, &PortSetup, sizeof(PortSetup))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return retval;
+
+ case RIO_GET_PORT_PARAMS:
+ rio_dprintk(RIO_DEBUG_CTRL, "Get port params\n");
+ if (copy_from_user(&PortParams, argp, sizeof(struct PortParams))) {
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ if (PortParams.Port >= RIO_PORTS) {
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -ENXIO;
+ }
+ PortP = (p->RIOPortp[PortParams.Port]);
+ PortParams.Config = PortP->Config;
+ PortParams.State = PortP->State;
+ rio_dprintk(RIO_DEBUG_CTRL, "Port %d\n", PortParams.Port);
+
+ if (copy_to_user(argp, &PortParams, sizeof(struct PortParams))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return retval;
+
+ case RIO_GET_PORT_TTY:
+ rio_dprintk(RIO_DEBUG_CTRL, "Get port tty\n");
+ if (copy_from_user(&PortTty, argp, sizeof(struct PortTty))) {
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ if (PortTty.port >= RIO_PORTS) {
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -ENXIO;
+ }
+
+ rio_dprintk(RIO_DEBUG_CTRL, "Port %d\n", PortTty.port);
+ PortP = (p->RIOPortp[PortTty.port]);
+ if (copy_to_user(argp, &PortTty, sizeof(struct PortTty))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return retval;
+
+ case RIO_SET_PORT_TTY:
+ if (copy_from_user(&PortTty, argp, sizeof(struct PortTty))) {
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "Set port %d tty\n", PortTty.port);
+ if (PortTty.port >= (ushort) RIO_PORTS) {
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -ENXIO;
+ }
+ PortP = (p->RIOPortp[PortTty.port]);
+ RIOParam(PortP, RIOC_CONFIG, PortP->State & RIO_MODEM,
+ OK_TO_SLEEP);
+ return retval;
+
+ case RIO_SET_PORT_PARAMS:
+ rio_dprintk(RIO_DEBUG_CTRL, "Set port params\n");
+ if (copy_from_user(&PortParams, argp, sizeof(PortParams))) {
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ if (PortParams.Port >= (ushort) RIO_PORTS) {
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -ENXIO;
+ }
+ PortP = (p->RIOPortp[PortParams.Port]);
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ PortP->Config = PortParams.Config;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return retval;
+
+ case RIO_GET_PORT_STATS:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_GET_PORT_STATS\n");
+ if (copy_from_user(&portStats, argp, sizeof(struct portStats))) {
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ if (portStats.port < 0 || portStats.port >= RIO_PORTS) {
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -ENXIO;
+ }
+ PortP = (p->RIOPortp[portStats.port]);
+ portStats.gather = PortP->statsGather;
+ portStats.txchars = PortP->txchars;
+ portStats.rxchars = PortP->rxchars;
+ portStats.opens = PortP->opens;
+ portStats.closes = PortP->closes;
+ portStats.ioctls = PortP->ioctls;
+ if (copy_to_user(argp, &portStats, sizeof(struct portStats))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return retval;
+
+ case RIO_RESET_PORT_STATS:
+ port = arg;
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_RESET_PORT_STATS\n");
+ if (port >= RIO_PORTS) {
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -ENXIO;
+ }
+ PortP = (p->RIOPortp[port]);
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ PortP->txchars = 0;
+ PortP->rxchars = 0;
+ PortP->opens = 0;
+ PortP->closes = 0;
+ PortP->ioctls = 0;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return retval;
+
+ case RIO_GATHER_PORT_STATS:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_GATHER_PORT_STATS\n");
+ if (copy_from_user(&portStats, argp, sizeof(struct portStats))) {
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ if (portStats.port < 0 || portStats.port >= RIO_PORTS) {
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -ENXIO;
+ }
+ PortP = (p->RIOPortp[portStats.port]);
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ PortP->statsGather = portStats.gather;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return retval;
+
+ case RIO_READ_CONFIG:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_READ_CONFIG\n");
+ if (copy_to_user(argp, &p->RIOConf, sizeof(struct Conf))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return retval;
+
+ case RIO_SET_CONFIG:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_SET_CONFIG\n");
+ if (!su) {
+ p->RIOError.Error = NOT_SUPER_USER;
+ return -EPERM;
+ }
+ if (copy_from_user(&p->RIOConf, argp, sizeof(struct Conf))) {
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ /*
+ ** move a few value around
+ */
+ for (Host = 0; Host < p->RIONumHosts; Host++)
+ if ((p->RIOHosts[Host].Flags & RUN_STATE) == RC_RUNNING)
+ writew(p->RIOConf.Timer, &p->RIOHosts[Host].ParmMapP->timer);
+ return retval;
+
+ case RIO_START_POLLER:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_START_POLLER\n");
+ return -EINVAL;
+
+ case RIO_STOP_POLLER:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_STOP_POLLER\n");
+ if (!su) {
+ p->RIOError.Error = NOT_SUPER_USER;
+ return -EPERM;
+ }
+ p->RIOPolling = NOT_POLLING;
+ return retval;
+
+ case RIO_SETDEBUG:
+ case RIO_GETDEBUG:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_SETDEBUG/RIO_GETDEBUG\n");
+ if (copy_from_user(&DebugCtrl, argp, sizeof(DebugCtrl))) {
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ if (DebugCtrl.SysPort == NO_PORT) {
+ if (cmd == RIO_SETDEBUG) {
+ if (!su) {
+ p->RIOError.Error = NOT_SUPER_USER;
+ return -EPERM;
+ }
+ p->rio_debug = DebugCtrl.Debug;
+ p->RIODebugWait = DebugCtrl.Wait;
+ rio_dprintk(RIO_DEBUG_CTRL, "Set global debug to 0x%x set wait to 0x%x\n", p->rio_debug, p->RIODebugWait);
+ } else {
+ rio_dprintk(RIO_DEBUG_CTRL, "Get global debug 0x%x wait 0x%x\n", p->rio_debug, p->RIODebugWait);
+ DebugCtrl.Debug = p->rio_debug;
+ DebugCtrl.Wait = p->RIODebugWait;
+ if (copy_to_user(argp, &DebugCtrl, sizeof(DebugCtrl))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_SET/GET DEBUG: bad port number %d\n", DebugCtrl.SysPort);
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ }
+ } else if (DebugCtrl.SysPort >= RIO_PORTS && DebugCtrl.SysPort != NO_PORT) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_SET/GET DEBUG: bad port number %d\n", DebugCtrl.SysPort);
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -ENXIO;
+ } else if (cmd == RIO_SETDEBUG) {
+ if (!su) {
+ p->RIOError.Error = NOT_SUPER_USER;
+ return -EPERM;
+ }
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ p->RIOPortp[DebugCtrl.SysPort]->Debug = DebugCtrl.Debug;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_SETDEBUG 0x%x\n", p->RIOPortp[DebugCtrl.SysPort]->Debug);
+ } else {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_GETDEBUG 0x%x\n", p->RIOPortp[DebugCtrl.SysPort]->Debug);
+ DebugCtrl.Debug = p->RIOPortp[DebugCtrl.SysPort]->Debug;
+ if (copy_to_user(argp, &DebugCtrl, sizeof(DebugCtrl))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_GETDEBUG: Bad copy to user space\n");
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ }
+ return retval;
+
+ case RIO_VERSID:
+ /*
+ ** Enquire about the release and version.
+ ** We return MAX_VERSION_LEN bytes, being a
+ ** textual null terminated string.
+ */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_VERSID\n");
+ if (copy_to_user(argp, RIOVersid(), sizeof(struct rioVersion))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_VERSID: Bad copy to user space (host=%d)\n", Host);
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return retval;
+
+ case RIO_NUM_HOSTS:
+ /*
+ ** Enquire as to the number of hosts located
+ ** at init time.
+ */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_NUM_HOSTS\n");
+ if (copy_to_user(argp, &p->RIONumHosts, sizeof(p->RIONumHosts))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_NUM_HOSTS: Bad copy to user space\n");
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return retval;
+
+ case RIO_HOST_FOAD:
+ /*
+ ** Kill host. This may not be in the final version...
+ */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_FOAD %ld\n", arg);
+ if (!su) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_FOAD: Not super user\n");
+ p->RIOError.Error = NOT_SUPER_USER;
+ return -EPERM;
+ }
+ p->RIOHalted = 1;
+ p->RIOSystemUp = 0;
+
+ for (Host = 0; Host < p->RIONumHosts; Host++) {
+ (void) RIOBoardTest(p->RIOHosts[Host].PaddrP, p->RIOHosts[Host].Caddr, p->RIOHosts[Host].Type, p->RIOHosts[Host].Slot);
+ memset(&p->RIOHosts[Host].Flags, 0, ((char *) &p->RIOHosts[Host].____end_marker____) - ((char *) &p->RIOHosts[Host].Flags));
+ p->RIOHosts[Host].Flags = RC_WAITING;
+ }
+ RIOFoadWakeup(p);
+ p->RIONumBootPkts = 0;
+ p->RIOBooting = 0;
+ printk("HEEEEELP!\n");
+
+ for (loop = 0; loop < RIO_PORTS; loop++) {
+ spin_lock_init(&p->RIOPortp[loop]->portSem);
+ p->RIOPortp[loop]->InUse = NOT_INUSE;
+ }
+
+ p->RIOSystemUp = 0;
+ return retval;
+
+ case RIO_DOWNLOAD:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_DOWNLOAD\n");
+ if (!su) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_DOWNLOAD: Not super user\n");
+ p->RIOError.Error = NOT_SUPER_USER;
+ return -EPERM;
+ }
+ if (copy_from_user(&DownLoad, argp, sizeof(DownLoad))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_DOWNLOAD: Copy in from user space failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "Copied in download code for product code 0x%x\n", DownLoad.ProductCode);
+
+ /*
+ ** It is important that the product code is an unsigned object!
+ */
+ if (DownLoad.ProductCode >= MAX_PRODUCT) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_DOWNLOAD: Bad product code %d passed\n", DownLoad.ProductCode);
+ p->RIOError.Error = NO_SUCH_PRODUCT;
+ return -ENXIO;
+ }
+ /*
+ ** do something!
+ */
+ retval = (*(RIOBootTable[DownLoad.ProductCode])) (p, &DownLoad);
+ /* <-- Panic */
+ p->RIOHalted = 0;
+ /*
+ ** and go back, content with a job well completed.
+ */
+ return retval;
+
+ case RIO_PARMS:
+ {
+ unsigned int host;
+
+ if (copy_from_user(&host, argp, sizeof(host))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_REQ: Copy in from user space failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ /*
+ ** Fetch the parmmap
+ */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_PARMS\n");
+ if (copy_from_io(argp, p->RIOHosts[host].ParmMapP, sizeof(PARM_MAP))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_PARMS: Copy out to user space failed\n");
+ return -EFAULT;
+ }
+ }
+ return retval;
+
+ case RIO_HOST_REQ:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_REQ\n");
+ if (copy_from_user(&HostReq, argp, sizeof(HostReq))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_REQ: Copy in from user space failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ if (HostReq.HostNum >= p->RIONumHosts) {
+ p->RIOError.Error = HOST_NUMBER_OUT_OF_RANGE;
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_REQ: Illegal host number %d\n", HostReq.HostNum);
+ return -ENXIO;
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "Request for host %d\n", HostReq.HostNum);
+
+ if (copy_to_user(HostReq.HostP, &p->RIOHosts[HostReq.HostNum], sizeof(struct Host))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_REQ: Bad copy to user space\n");
+ return -EFAULT;
+ }
+ return retval;
+
+ case RIO_HOST_DPRAM:
+ rio_dprintk(RIO_DEBUG_CTRL, "Request for DPRAM\n");
+ if (copy_from_user(&HostDpRam, argp, sizeof(HostDpRam))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_DPRAM: Copy in from user space failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ if (HostDpRam.HostNum >= p->RIONumHosts) {
+ p->RIOError.Error = HOST_NUMBER_OUT_OF_RANGE;
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_DPRAM: Illegal host number %d\n", HostDpRam.HostNum);
+ return -ENXIO;
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "Request for host %d\n", HostDpRam.HostNum);
+
+ if (p->RIOHosts[HostDpRam.HostNum].Type == RIO_PCI) {
+ int off;
+ /* It's hardware like this that really gets on my tits. */
+ static unsigned char copy[sizeof(struct DpRam)];
+ for (off = 0; off < sizeof(struct DpRam); off++)
+ copy[off] = readb(p->RIOHosts[HostDpRam.HostNum].Caddr + off);
+ if (copy_to_user(HostDpRam.DpRamP, copy, sizeof(struct DpRam))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_DPRAM: Bad copy to user space\n");
+ return -EFAULT;
+ }
+ } else if (copy_from_io(HostDpRam.DpRamP, p->RIOHosts[HostDpRam.HostNum].Caddr, sizeof(struct DpRam))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_DPRAM: Bad copy to user space\n");
+ return -EFAULT;
+ }
+ return retval;
+
+ case RIO_SET_BUSY:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_SET_BUSY\n");
+ if (arg > 511) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_SET_BUSY: Bad port number %ld\n", arg);
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ p->RIOPortp[arg]->State |= RIO_BUSY;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return retval;
+
+ case RIO_HOST_PORT:
+ /*
+ ** The daemon want port information
+ ** (probably for debug reasons)
+ */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_PORT\n");
+ if (copy_from_user(&PortReq, argp, sizeof(PortReq))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_PORT: Copy in from user space failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+
+ if (PortReq.SysPort >= RIO_PORTS) { /* SysPort is unsigned */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_PORT: Illegal port number %d\n", PortReq.SysPort);
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -ENXIO;
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "Request for port %d\n", PortReq.SysPort);
+ if (copy_to_user(PortReq.PortP, p->RIOPortp[PortReq.SysPort], sizeof(struct Port))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_PORT: Bad copy to user space\n");
+ return -EFAULT;
+ }
+ return retval;
+
+ case RIO_HOST_RUP:
+ /*
+ ** The daemon want rup information
+ ** (probably for debug reasons)
+ */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_RUP\n");
+ if (copy_from_user(&RupReq, argp, sizeof(RupReq))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_RUP: Copy in from user space failed\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ if (RupReq.HostNum >= p->RIONumHosts) { /* host is unsigned */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_RUP: Illegal host number %d\n", RupReq.HostNum);
+ p->RIOError.Error = HOST_NUMBER_OUT_OF_RANGE;
+ return -ENXIO;
+ }
+ if (RupReq.RupNum >= MAX_RUP + LINKS_PER_UNIT) { /* eek! */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_RUP: Illegal rup number %d\n", RupReq.RupNum);
+ p->RIOError.Error = RUP_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+ HostP = &p->RIOHosts[RupReq.HostNum];
+
+ if ((HostP->Flags & RUN_STATE) != RC_RUNNING) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_RUP: Host %d not running\n", RupReq.HostNum);
+ p->RIOError.Error = HOST_NOT_RUNNING;
+ return -EIO;
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "Request for rup %d from host %d\n", RupReq.RupNum, RupReq.HostNum);
+
+ if (copy_from_io(RupReq.RupP, HostP->UnixRups[RupReq.RupNum].RupP, sizeof(struct RUP))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_RUP: Bad copy to user space\n");
+ return -EFAULT;
+ }
+ return retval;
+
+ case RIO_HOST_LPB:
+ /*
+ ** The daemon want lpb information
+ ** (probably for debug reasons)
+ */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_LPB\n");
+ if (copy_from_user(&LpbReq, argp, sizeof(LpbReq))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_LPB: Bad copy from user space\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ if (LpbReq.Host >= p->RIONumHosts) { /* host is unsigned */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_LPB: Illegal host number %d\n", LpbReq.Host);
+ p->RIOError.Error = HOST_NUMBER_OUT_OF_RANGE;
+ return -ENXIO;
+ }
+ if (LpbReq.Link >= LINKS_PER_UNIT) { /* eek! */
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_LPB: Illegal link number %d\n", LpbReq.Link);
+ p->RIOError.Error = LINK_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+ HostP = &p->RIOHosts[LpbReq.Host];
+
+ if ((HostP->Flags & RUN_STATE) != RC_RUNNING) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_LPB: Host %d not running\n", LpbReq.Host);
+ p->RIOError.Error = HOST_NOT_RUNNING;
+ return -EIO;
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "Request for lpb %d from host %d\n", LpbReq.Link, LpbReq.Host);
+
+ if (copy_from_io(LpbReq.LpbP, &HostP->LinkStrP[LpbReq.Link], sizeof(struct LPB))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_HOST_LPB: Bad copy to user space\n");
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return retval;
+
+ /*
+ ** Here 3 IOCTL's that allow us to change the way in which
+ ** rio logs errors. send them just to syslog or send them
+ ** to both syslog and console or send them to just the console.
+ **
+ ** See RioStrBuf() in util.c for the other half.
+ */
+ case RIO_SYSLOG_ONLY:
+ p->RIOPrintLogState = PRINT_TO_LOG; /* Just syslog */
+ return 0;
+
+ case RIO_SYSLOG_CONS:
+ p->RIOPrintLogState = PRINT_TO_LOG_CONS; /* syslog and console */
+ return 0;
+
+ case RIO_CONS_ONLY:
+ p->RIOPrintLogState = PRINT_TO_CONS; /* Just console */
+ return 0;
+
+ case RIO_SIGNALS_ON:
+ if (p->RIOSignalProcess) {
+ p->RIOError.Error = SIGNALS_ALREADY_SET;
+ return -EBUSY;
+ }
+ /* FIXME: PID tracking */
+ p->RIOSignalProcess = current->pid;
+ p->RIOPrintDisabled = DONT_PRINT;
+ return retval;
+
+ case RIO_SIGNALS_OFF:
+ if (p->RIOSignalProcess != current->pid) {
+ p->RIOError.Error = NOT_RECEIVING_PROCESS;
+ return -EPERM;
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "Clear signal process to zero\n");
+ p->RIOSignalProcess = 0;
+ return retval;
+
+ case RIO_SET_BYTE_MODE:
+ for (Host = 0; Host < p->RIONumHosts; Host++)
+ if (p->RIOHosts[Host].Type == RIO_AT)
+ p->RIOHosts[Host].Mode &= ~WORD_OPERATION;
+ return retval;
+
+ case RIO_SET_WORD_MODE:
+ for (Host = 0; Host < p->RIONumHosts; Host++)
+ if (p->RIOHosts[Host].Type == RIO_AT)
+ p->RIOHosts[Host].Mode |= WORD_OPERATION;
+ return retval;
+
+ case RIO_SET_FAST_BUS:
+ for (Host = 0; Host < p->RIONumHosts; Host++)
+ if (p->RIOHosts[Host].Type == RIO_AT)
+ p->RIOHosts[Host].Mode |= FAST_AT_BUS;
+ return retval;
+
+ case RIO_SET_SLOW_BUS:
+ for (Host = 0; Host < p->RIONumHosts; Host++)
+ if (p->RIOHosts[Host].Type == RIO_AT)
+ p->RIOHosts[Host].Mode &= ~FAST_AT_BUS;
+ return retval;
+
+ case RIO_MAP_B50_TO_50:
+ case RIO_MAP_B50_TO_57600:
+ case RIO_MAP_B110_TO_110:
+ case RIO_MAP_B110_TO_115200:
+ rio_dprintk(RIO_DEBUG_CTRL, "Baud rate mapping\n");
+ port = arg;
+ if (port < 0 || port > 511) {
+ rio_dprintk(RIO_DEBUG_CTRL, "Baud rate mapping: Bad port number %d\n", port);
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ switch (cmd) {
+ case RIO_MAP_B50_TO_50:
+ p->RIOPortp[port]->Config |= RIO_MAP_50_TO_50;
+ break;
+ case RIO_MAP_B50_TO_57600:
+ p->RIOPortp[port]->Config &= ~RIO_MAP_50_TO_50;
+ break;
+ case RIO_MAP_B110_TO_110:
+ p->RIOPortp[port]->Config |= RIO_MAP_110_TO_110;
+ break;
+ case RIO_MAP_B110_TO_115200:
+ p->RIOPortp[port]->Config &= ~RIO_MAP_110_TO_110;
+ break;
+ }
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return retval;
+
+ case RIO_STREAM_INFO:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_STREAM_INFO\n");
+ return -EINVAL;
+
+ case RIO_SEND_PACKET:
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_SEND_PACKET\n");
+ if (copy_from_user(&SendPack, argp, sizeof(SendPack))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_SEND_PACKET: Bad copy from user space\n");
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ if (SendPack.PortNum >= 128) {
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -ENXIO;
+ }
+
+ PortP = p->RIOPortp[SendPack.PortNum];
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+
+ if (!can_add_transmit(&PacketP, PortP)) {
+ p->RIOError.Error = UNIT_IS_IN_USE;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return -ENOSPC;
+ }
+
+ for (loop = 0; loop < (ushort) (SendPack.Len & 127); loop++)
+ writeb(SendPack.Data[loop], &PacketP->data[loop]);
+
+ writeb(SendPack.Len, &PacketP->len);
+
+ add_transmit(PortP);
+ /*
+ ** Count characters transmitted for port statistics reporting
+ */
+ if (PortP->statsGather)
+ PortP->txchars += (SendPack.Len & 127);
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return retval;
+
+ case RIO_NO_MESG:
+ if (su)
+ p->RIONoMessage = 1;
+ return su ? 0 : -EPERM;
+
+ case RIO_MESG:
+ if (su)
+ p->RIONoMessage = 0;
+ return su ? 0 : -EPERM;
+
+ case RIO_WHAT_MESG:
+ if (copy_to_user(argp, &p->RIONoMessage, sizeof(p->RIONoMessage))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_WHAT_MESG: Bad copy to user space\n");
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return 0;
+
+ case RIO_MEM_DUMP:
+ if (copy_from_user(&SubCmd, argp, sizeof(struct SubCmdStruct))) {
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_MEM_DUMP host %d rup %d addr %x\n", SubCmd.Host, SubCmd.Rup, SubCmd.Addr);
+
+ if (SubCmd.Rup >= MAX_RUP + LINKS_PER_UNIT) {
+ p->RIOError.Error = RUP_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ if (SubCmd.Host >= p->RIONumHosts) {
+ p->RIOError.Error = HOST_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ port = p->RIOHosts[SubCmd.Host].UnixRups[SubCmd.Rup].BaseSysPort;
+
+ PortP = p->RIOPortp[port];
+
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+
+ if (RIOPreemptiveCmd(p, PortP, RIOC_MEMDUMP) == RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_MEM_DUMP failed\n");
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return -EBUSY;
+ } else
+ PortP->State |= RIO_BUSY;
+
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ if (copy_to_user(argp, p->RIOMemDump, MEMDUMP_SIZE)) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_MEM_DUMP copy failed\n");
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return 0;
+
+ case RIO_TICK:
+ if (arg >= p->RIONumHosts)
+ return -EINVAL;
+ rio_dprintk(RIO_DEBUG_CTRL, "Set interrupt for host %ld\n", arg);
+ writeb(0xFF, &p->RIOHosts[arg].SetInt);
+ return 0;
+
+ case RIO_TOCK:
+ if (arg >= p->RIONumHosts)
+ return -EINVAL;
+ rio_dprintk(RIO_DEBUG_CTRL, "Clear interrupt for host %ld\n", arg);
+ writeb(0xFF, &p->RIOHosts[arg].ResetInt);
+ return 0;
+
+ case RIO_READ_CHECK:
+ /* Check reads for pkts with data[0] the same */
+ p->RIOReadCheck = !p->RIOReadCheck;
+ if (copy_to_user(argp, &p->RIOReadCheck, sizeof(unsigned int))) {
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return 0;
+
+ case RIO_READ_REGISTER:
+ if (copy_from_user(&SubCmd, argp, sizeof(struct SubCmdStruct))) {
+ p->RIOError.Error = COPYIN_FAILED;
+ return -EFAULT;
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_READ_REGISTER host %d rup %d port %d reg %x\n", SubCmd.Host, SubCmd.Rup, SubCmd.Port, SubCmd.Addr);
+
+ if (SubCmd.Port > 511) {
+ rio_dprintk(RIO_DEBUG_CTRL, "Baud rate mapping: Bad port number %d\n", SubCmd.Port);
+ p->RIOError.Error = PORT_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ if (SubCmd.Rup >= MAX_RUP + LINKS_PER_UNIT) {
+ p->RIOError.Error = RUP_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ if (SubCmd.Host >= p->RIONumHosts) {
+ p->RIOError.Error = HOST_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ port = p->RIOHosts[SubCmd.Host].UnixRups[SubCmd.Rup].BaseSysPort + SubCmd.Port;
+ PortP = p->RIOPortp[port];
+
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+
+ if (RIOPreemptiveCmd(p, PortP, RIOC_READ_REGISTER) ==
+ RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_READ_REGISTER failed\n");
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return -EBUSY;
+ } else
+ PortP->State |= RIO_BUSY;
+
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ if (copy_to_user(argp, &p->CdRegister, sizeof(unsigned int))) {
+ rio_dprintk(RIO_DEBUG_CTRL, "RIO_READ_REGISTER copy failed\n");
+ p->RIOError.Error = COPYOUT_FAILED;
+ return -EFAULT;
+ }
+ return 0;
+ /*
+ ** rio_make_dev: given port number (0-511) ORed with port type
+ ** (RIO_DEV_DIRECT, RIO_DEV_MODEM, RIO_DEV_XPRINT) return dev_t
+ ** value to pass to mknod to create the correct device node.
+ */
+ case RIO_MAKE_DEV:
+ {
+ unsigned int port = arg & RIO_MODEM_MASK;
+ unsigned int ret;
+
+ switch (arg & RIO_DEV_MASK) {
+ case RIO_DEV_DIRECT:
+ ret = drv_makedev(MAJOR(dev), port);
+ rio_dprintk(RIO_DEBUG_CTRL, "Makedev direct 0x%x is 0x%x\n", port, ret);
+ return ret;
+ case RIO_DEV_MODEM:
+ ret = drv_makedev(MAJOR(dev), (port | RIO_MODEM_BIT));
+ rio_dprintk(RIO_DEBUG_CTRL, "Makedev modem 0x%x is 0x%x\n", port, ret);
+ return ret;
+ case RIO_DEV_XPRINT:
+ ret = drv_makedev(MAJOR(dev), port);
+ rio_dprintk(RIO_DEBUG_CTRL, "Makedev printer 0x%x is 0x%x\n", port, ret);
+ return ret;
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "MAKE Device is called\n");
+ return -EINVAL;
+ }
+ /*
+ ** rio_minor: given a dev_t from a stat() call, return
+ ** the port number (0-511) ORed with the port type
+ ** ( RIO_DEV_DIRECT, RIO_DEV_MODEM, RIO_DEV_XPRINT )
+ */
+ case RIO_MINOR:
+ {
+ dev_t dv;
+ int mino;
+ unsigned long ret;
+
+ dv = (dev_t) (arg);
+ mino = RIO_UNMODEM(dv);
+
+ if (RIO_ISMODEM(dv)) {
+ rio_dprintk(RIO_DEBUG_CTRL, "Minor for device 0x%x: modem %d\n", dv, mino);
+ ret = mino | RIO_DEV_MODEM;
+ } else {
+ rio_dprintk(RIO_DEBUG_CTRL, "Minor for device 0x%x: direct %d\n", dv, mino);
+ ret = mino | RIO_DEV_DIRECT;
+ }
+ return ret;
+ }
+ }
+ rio_dprintk(RIO_DEBUG_CTRL, "INVALID DAEMON IOCTL 0x%x\n", cmd);
+ p->RIOError.Error = IOCTL_COMMAND_UNKNOWN;
+
+ func_exit();
+ return -EINVAL;
+}
+
+/*
+** Pre-emptive commands go on RUPs and are only one byte long.
+*/
+int RIOPreemptiveCmd(struct rio_info *p, struct Port *PortP, u8 Cmd)
+{
+ struct CmdBlk *CmdBlkP;
+ struct PktCmd_M *PktCmdP;
+ int Ret;
+ ushort rup;
+ int port;
+
+ if (PortP->State & RIO_DELETED) {
+ rio_dprintk(RIO_DEBUG_CTRL, "Preemptive command to deleted RTA ignored\n");
+ return RIO_FAIL;
+ }
+
+ if ((PortP->InUse == (typeof(PortP->InUse))-1) ||
+ !(CmdBlkP = RIOGetCmdBlk())) {
+ rio_dprintk(RIO_DEBUG_CTRL, "Cannot allocate command block "
+ "for command %d on port %d\n", Cmd, PortP->PortNum);
+ return RIO_FAIL;
+ }
+
+ rio_dprintk(RIO_DEBUG_CTRL, "Command blk %p - InUse now %d\n",
+ CmdBlkP, PortP->InUse);
+
+ PktCmdP = (struct PktCmd_M *)&CmdBlkP->Packet.data[0];
+
+ CmdBlkP->Packet.src_unit = 0;
+ if (PortP->SecondBlock)
+ rup = PortP->ID2;
+ else
+ rup = PortP->RupNum;
+ CmdBlkP->Packet.dest_unit = rup;
+ CmdBlkP->Packet.src_port = COMMAND_RUP;
+ CmdBlkP->Packet.dest_port = COMMAND_RUP;
+ CmdBlkP->Packet.len = PKT_CMD_BIT | 2;
+ CmdBlkP->PostFuncP = RIOUnUse;
+ CmdBlkP->PostArg = (unsigned long) PortP;
+ PktCmdP->Command = Cmd;
+ port = PortP->HostPort % (ushort) PORTS_PER_RTA;
+ /*
+ ** Index ports 8-15 for 2nd block of 16 port RTA.
+ */
+ if (PortP->SecondBlock)
+ port += (ushort) PORTS_PER_RTA;
+ PktCmdP->PhbNum = port;
+
+ switch (Cmd) {
+ case RIOC_MEMDUMP:
+ rio_dprintk(RIO_DEBUG_CTRL, "Queue MEMDUMP command blk %p "
+ "(addr 0x%x)\n", CmdBlkP, (int) SubCmd.Addr);
+ PktCmdP->SubCommand = RIOC_MEMDUMP;
+ PktCmdP->SubAddr = SubCmd.Addr;
+ break;
+ case RIOC_FCLOSE:
+ rio_dprintk(RIO_DEBUG_CTRL, "Queue FCLOSE command blk %p\n",
+ CmdBlkP);
+ break;
+ case RIOC_READ_REGISTER:
+ rio_dprintk(RIO_DEBUG_CTRL, "Queue READ_REGISTER (0x%x) "
+ "command blk %p\n", (int) SubCmd.Addr, CmdBlkP);
+ PktCmdP->SubCommand = RIOC_READ_REGISTER;
+ PktCmdP->SubAddr = SubCmd.Addr;
+ break;
+ case RIOC_RESUME:
+ rio_dprintk(RIO_DEBUG_CTRL, "Queue RESUME command blk %p\n",
+ CmdBlkP);
+ break;
+ case RIOC_RFLUSH:
+ rio_dprintk(RIO_DEBUG_CTRL, "Queue RFLUSH command blk %p\n",
+ CmdBlkP);
+ CmdBlkP->PostFuncP = RIORFlushEnable;
+ break;
+ case RIOC_SUSPEND:
+ rio_dprintk(RIO_DEBUG_CTRL, "Queue SUSPEND command blk %p\n",
+ CmdBlkP);
+ break;
+
+ case RIOC_MGET:
+ rio_dprintk(RIO_DEBUG_CTRL, "Queue MGET command blk %p\n",
+ CmdBlkP);
+ break;
+
+ case RIOC_MSET:
+ case RIOC_MBIC:
+ case RIOC_MBIS:
+ CmdBlkP->Packet.data[4] = (char) PortP->ModemLines;
+ rio_dprintk(RIO_DEBUG_CTRL, "Queue MSET/MBIC/MBIS command "
+ "blk %p\n", CmdBlkP);
+ break;
+
+ case RIOC_WFLUSH:
+ /*
+ ** If we have queued up the maximum number of Write flushes
+ ** allowed then we should not bother sending any more to the
+ ** RTA.
+ */
+ if (PortP->WflushFlag == (typeof(PortP->WflushFlag))-1) {
+ rio_dprintk(RIO_DEBUG_CTRL, "Trashed WFLUSH, "
+ "WflushFlag about to wrap!");
+ RIOFreeCmdBlk(CmdBlkP);
+ return (RIO_FAIL);
+ } else {
+ rio_dprintk(RIO_DEBUG_CTRL, "Queue WFLUSH command "
+ "blk %p\n", CmdBlkP);
+ CmdBlkP->PostFuncP = RIOWFlushMark;
+ }
+ break;
+ }
+
+ PortP->InUse++;
+
+ Ret = RIOQueueCmdBlk(PortP->HostP, rup, CmdBlkP);
+
+ return Ret;
+}
diff --git a/drivers/staging/generic_serial/rio/riodrvr.h b/drivers/staging/generic_serial/rio/riodrvr.h
new file mode 100644
index 000000000000..0907e711b355
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/riodrvr.h
@@ -0,0 +1,138 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : riodrvr.h
+** SID : 1.3
+** Last Modified : 11/6/98 09:22:46
+** Retrieved : 11/6/98 09:22:46
+**
+** ident @(#)riodrvr.h 1.3
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __riodrvr_h
+#define __riodrvr_h
+
+#include <asm/param.h> /* for HZ */
+
+#define MEMDUMP_SIZE 32
+#define MOD_DISABLE (RIO_NOREAD|RIO_NOWRITE|RIO_NOXPRINT)
+
+
+struct rio_info {
+ int mode; /* Intr or polled, word/byte */
+ spinlock_t RIOIntrSem; /* Interrupt thread sem */
+ int current_chan; /* current channel */
+ int RIOFailed; /* Not initialised ? */
+ int RIOInstallAttempts; /* no. of rio-install() calls */
+ int RIOLastPCISearch; /* status of last search */
+ int RIONumHosts; /* Number of RIO Hosts */
+ struct Host *RIOHosts; /* RIO Host values */
+ struct Port **RIOPortp; /* RIO port values */
+/*
+** 02.03.1999 ARG - ESIL 0820 fix
+** We no longer use RIOBootMode
+**
+ int RIOBootMode; * RIO boot mode *
+**
+*/
+ int RIOPrintDisabled; /* RIO printing disabled ? */
+ int RIOPrintLogState; /* RIO printing state ? */
+ int RIOPolling; /* Polling ? */
+/*
+** 09.12.1998 ARG - ESIL 0776 part fix
+** The 'RIO_QUICK_CHECK' ioctl was using RIOHalted.
+** The fix for this ESIL introduces another member (RIORtaDisCons) here to be
+** updated in RIOConCon() - to keep track of RTA connections/disconnections.
+** 'RIO_QUICK_CHECK' now returns the value of RIORtaDisCons.
+*/
+ int RIOHalted; /* halted ? */
+ int RIORtaDisCons; /* RTA connections/disconnections */
+ unsigned int RIOReadCheck; /* Rio read check */
+ unsigned int RIONoMessage; /* To display message or not */
+ unsigned int RIONumBootPkts; /* how many packets for an RTA */
+ unsigned int RIOBootCount; /* size of RTA code */
+ unsigned int RIOBooting; /* count of outstanding boots */
+ unsigned int RIOSystemUp; /* Booted ?? */
+ unsigned int RIOCounting; /* for counting interrupts */
+ unsigned int RIOIntCount; /* # of intr since last check */
+ unsigned int RIOTxCount; /* number of xmit intrs */
+ unsigned int RIORxCount; /* number of rx intrs */
+ unsigned int RIORupCount; /* number of rup intrs */
+ int RIXTimer;
+ int RIOBufferSize; /* Buffersize */
+ int RIOBufferMask; /* Buffersize */
+
+ int RIOFirstMajor; /* First host card's major no */
+
+ unsigned int RIOLastPortsMapped; /* highest port number known */
+ unsigned int RIOFirstPortsMapped; /* lowest port number known */
+
+ unsigned int RIOLastPortsBooted; /* highest port number running */
+ unsigned int RIOFirstPortsBooted; /* lowest port number running */
+
+ unsigned int RIOLastPortsOpened; /* highest port number running */
+ unsigned int RIOFirstPortsOpened; /* lowest port number running */
+
+ /* Flag to say that the topology information has been changed. */
+ unsigned int RIOQuickCheck;
+ unsigned int CdRegister; /* ??? */
+ int RIOSignalProcess; /* Signalling process */
+ int rio_debug; /* To debug ... */
+ int RIODebugWait; /* For what ??? */
+ int tpri; /* Thread prio */
+ int tid; /* Thread id */
+ unsigned int _RIO_Polled; /* Counter for polling */
+ unsigned int _RIO_Interrupted; /* Counter for interrupt */
+ int intr_tid; /* iointset return value */
+ int TxEnSem; /* TxEnable Semaphore */
+
+
+ struct Error RIOError; /* to Identify what went wrong */
+ struct Conf RIOConf; /* Configuration ??? */
+ struct ttystatics channel[RIO_PORTS]; /* channel information */
+ char RIOBootPackets[1 + (SIXTY_FOUR_K / RTA_BOOT_DATA_SIZE)]
+ [RTA_BOOT_DATA_SIZE];
+ struct Map RIOConnectTable[TOTAL_MAP_ENTRIES];
+ struct Map RIOSavedTable[TOTAL_MAP_ENTRIES];
+
+ /* RTA to host binding table for master/slave operation */
+ unsigned long RIOBindTab[MAX_RTA_BINDINGS];
+ /* RTA memory dump variable */
+ unsigned char RIOMemDump[MEMDUMP_SIZE];
+ struct ModuleInfo RIOModuleTypes[MAX_MODULE_TYPES];
+
+};
+
+
+#ifdef linux
+#define debug(x) printk x
+#else
+#define debug(x) kkprintf x
+#endif
+
+
+
+#define RIO_RESET_INT 0x7d80
+
+#endif /* __riodrvr.h */
diff --git a/drivers/staging/generic_serial/rio/rioinfo.h b/drivers/staging/generic_serial/rio/rioinfo.h
new file mode 100644
index 000000000000..42ff1e79d96f
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/rioinfo.h
@@ -0,0 +1,92 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : rioinfo.h
+** SID : 1.2
+** Last Modified : 11/6/98 14:07:49
+** Retrieved : 11/6/98 14:07:50
+**
+** ident @(#)rioinfo.h 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rioinfo_h
+#define __rioinfo_h
+
+/*
+** Host card data structure
+*/
+struct RioHostInfo {
+ long location; /* RIO Card Base I/O address */
+ long vector; /* RIO Card IRQ vector */
+ int bus; /* ISA/EISA/MCA/PCI */
+ int mode; /* pointer to host mode - INTERRUPT / POLLED */
+ struct old_sgttyb
+ *Sg; /* pointer to default term characteristics */
+};
+
+
+/* Mode in rio device info */
+#define INTERRUPTED_MODE 0x01 /* Interrupt is generated */
+#define POLLED_MODE 0x02 /* No interrupt */
+#define AUTO_MODE 0x03 /* Auto mode */
+
+#define WORD_ACCESS_MODE 0x10 /* Word Access Mode */
+#define BYTE_ACCESS_MODE 0x20 /* Byte Access Mode */
+
+
+/* Bus type that RIO supports */
+#define ISA_BUS 0x01 /* The card is ISA */
+#define EISA_BUS 0x02 /* The card is EISA */
+#define MCA_BUS 0x04 /* The card is MCA */
+#define PCI_BUS 0x08 /* The card is PCI */
+
+/*
+** 11.11.1998 ARG - ESIL ???? part fix
+** Moved definition for 'CHAN' here from rioinfo.c (it is now
+** called 'DEF_TERM_CHARACTERISTICS').
+*/
+
+#define DEF_TERM_CHARACTERISTICS \
+{ \
+ B19200, B19200, /* input and output speed */ \
+ 'H' - '@', /* erase char */ \
+ -1, /* 2nd erase char */ \
+ 'U' - '@', /* kill char */ \
+ ECHO | CRMOD, /* mode */ \
+ 'C' - '@', /* interrupt character */ \
+ '\\' - '@', /* quit char */ \
+ 'Q' - '@', /* start char */ \
+ 'S' - '@', /* stop char */ \
+ 'D' - '@', /* EOF */ \
+ -1, /* brk */ \
+ (LCRTBS | LCRTERA | LCRTKIL | LCTLECH), /* local mode word */ \
+ 'Z' - '@', /* process stop */ \
+ 'Y' - '@', /* delayed stop */ \
+ 'R' - '@', /* reprint line */ \
+ 'O' - '@', /* flush output */ \
+ 'W' - '@', /* word erase */ \
+ 'V' - '@' /* literal next char */ \
+}
+
+#endif /* __rioinfo_h */
diff --git a/drivers/staging/generic_serial/rio/rioinit.c b/drivers/staging/generic_serial/rio/rioinit.c
new file mode 100644
index 000000000000..24a282bb89d4
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/rioinit.c
@@ -0,0 +1,421 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : rioinit.c
+** SID : 1.3
+** Last Modified : 11/6/98 10:33:43
+** Retrieved : 11/6/98 10:33:49
+**
+** ident @(#)rioinit.c 1.3
+**
+** -----------------------------------------------------------------------------
+*/
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/termios.h>
+#include <linux/serial.h>
+
+#include <linux/generic_serial.h>
+
+
+#include "linux_compat.h"
+#include "pkt.h"
+#include "daemon.h"
+#include "rio.h"
+#include "riospace.h"
+#include "cmdpkt.h"
+#include "map.h"
+#include "rup.h"
+#include "port.h"
+#include "riodrvr.h"
+#include "rioinfo.h"
+#include "func.h"
+#include "errors.h"
+#include "pci.h"
+
+#include "parmmap.h"
+#include "unixrup.h"
+#include "board.h"
+#include "host.h"
+#include "phb.h"
+#include "link.h"
+#include "cmdblk.h"
+#include "route.h"
+#include "cirrus.h"
+#include "rioioctl.h"
+#include "rio_linux.h"
+
+int RIOPCIinit(struct rio_info *p, int Mode);
+
+static int RIOScrub(int, u8 __iomem *, int);
+
+
+/**
+** RIOAssignAT :
+**
+** Fill out the fields in the p->RIOHosts structure now we know we know
+** we have a board present.
+**
+** bits < 0 indicates 8 bit operation requested,
+** bits > 0 indicates 16 bit operation.
+*/
+
+int RIOAssignAT(struct rio_info *p, int Base, void __iomem *virtAddr, int mode)
+{
+ int bits;
+ struct DpRam __iomem *cardp = (struct DpRam __iomem *)virtAddr;
+
+ if ((Base < ONE_MEG) || (mode & BYTE_ACCESS_MODE))
+ bits = BYTE_OPERATION;
+ else
+ bits = WORD_OPERATION;
+
+ /*
+ ** Board has passed its scrub test. Fill in all the
+ ** transient stuff.
+ */
+ p->RIOHosts[p->RIONumHosts].Caddr = virtAddr;
+ p->RIOHosts[p->RIONumHosts].CardP = virtAddr;
+
+ /*
+ ** Revision 01 AT host cards don't support WORD operations,
+ */
+ if (readb(&cardp->DpRevision) == 01)
+ bits = BYTE_OPERATION;
+
+ p->RIOHosts[p->RIONumHosts].Type = RIO_AT;
+ p->RIOHosts[p->RIONumHosts].Copy = rio_copy_to_card;
+ /* set this later */
+ p->RIOHosts[p->RIONumHosts].Slot = -1;
+ p->RIOHosts[p->RIONumHosts].Mode = SLOW_LINKS | SLOW_AT_BUS | bits;
+ writeb(BOOT_FROM_RAM | EXTERNAL_BUS_OFF | p->RIOHosts[p->RIONumHosts].Mode | INTERRUPT_DISABLE ,
+ &p->RIOHosts[p->RIONumHosts].Control);
+ writeb(0xFF, &p->RIOHosts[p->RIONumHosts].ResetInt);
+ writeb(BOOT_FROM_RAM | EXTERNAL_BUS_OFF | p->RIOHosts[p->RIONumHosts].Mode | INTERRUPT_DISABLE,
+ &p->RIOHosts[p->RIONumHosts].Control);
+ writeb(0xFF, &p->RIOHosts[p->RIONumHosts].ResetInt);
+ p->RIOHosts[p->RIONumHosts].UniqueNum =
+ ((readb(&p->RIOHosts[p->RIONumHosts].Unique[0])&0xFF)<<0)|
+ ((readb(&p->RIOHosts[p->RIONumHosts].Unique[1])&0xFF)<<8)|
+ ((readb(&p->RIOHosts[p->RIONumHosts].Unique[2])&0xFF)<<16)|
+ ((readb(&p->RIOHosts[p->RIONumHosts].Unique[3])&0xFF)<<24);
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Uniquenum 0x%x\n",p->RIOHosts[p->RIONumHosts].UniqueNum);
+
+ p->RIONumHosts++;
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Tests Passed at 0x%x\n", Base);
+ return(1);
+}
+
+static u8 val[] = {
+#ifdef VERY_LONG_TEST
+ 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+ 0xa5, 0xff, 0x5a, 0x00, 0xff, 0xc9, 0x36,
+#endif
+ 0xff, 0x00, 0x00 };
+
+#define TEST_END sizeof(val)
+
+/*
+** RAM test a board.
+** Nothing too complicated, just enough to check it out.
+*/
+int RIOBoardTest(unsigned long paddr, void __iomem *caddr, unsigned char type, int slot)
+{
+ struct DpRam __iomem *DpRam = caddr;
+ void __iomem *ram[4];
+ int size[4];
+ int op, bank;
+ int nbanks;
+
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Reset host type=%d, DpRam=%p, slot=%d\n",
+ type, DpRam, slot);
+
+ RIOHostReset(type, DpRam, slot);
+
+ /*
+ ** Scrub the memory. This comes in several banks:
+ ** DPsram1 - 7000h bytes
+ ** DPsram2 - 200h bytes
+ ** DPsram3 - 7000h bytes
+ ** scratch - 1000h bytes
+ */
+
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Setup ram/size arrays\n");
+
+ size[0] = DP_SRAM1_SIZE;
+ size[1] = DP_SRAM2_SIZE;
+ size[2] = DP_SRAM3_SIZE;
+ size[3] = DP_SCRATCH_SIZE;
+
+ ram[0] = DpRam->DpSram1;
+ ram[1] = DpRam->DpSram2;
+ ram[2] = DpRam->DpSram3;
+ nbanks = (type == RIO_PCI) ? 3 : 4;
+ if (nbanks == 4)
+ ram[3] = DpRam->DpScratch;
+
+
+ if (nbanks == 3) {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Memory: %p(0x%x), %p(0x%x), %p(0x%x)\n",
+ ram[0], size[0], ram[1], size[1], ram[2], size[2]);
+ } else {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: %p(0x%x), %p(0x%x), %p(0x%x), %p(0x%x)\n",
+ ram[0], size[0], ram[1], size[1], ram[2], size[2], ram[3], size[3]);
+ }
+
+ /*
+ ** This scrub operation will test for crosstalk between
+ ** banks. TEST_END is a magic number, and relates to the offset
+ ** within the 'val' array used by Scrub.
+ */
+ for (op=0; op<TEST_END; op++) {
+ for (bank=0; bank<nbanks; bank++) {
+ if (RIOScrub(op, ram[bank], size[bank]) == RIO_FAIL) {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: RIOScrub band %d, op %d failed\n",
+ bank, op);
+ return RIO_FAIL;
+ }
+ }
+ }
+
+ rio_dprintk (RIO_DEBUG_INIT, "Test completed\n");
+ return 0;
+}
+
+
+/*
+** Scrub an area of RAM.
+** Define PRETEST and POSTTEST for a more thorough checking of the
+** state of the memory.
+** Call with op set to an index into the above 'val' array to determine
+** which value will be written into memory.
+** Call with op set to zero means that the RAM will not be read and checked
+** before it is written.
+** Call with op not zero and the RAM will be read and compared with val[op-1]
+** to check that the data from the previous phase was retained.
+*/
+
+static int RIOScrub(int op, u8 __iomem *ram, int size)
+{
+ int off;
+ unsigned char oldbyte;
+ unsigned char newbyte;
+ unsigned char invbyte;
+ unsigned short oldword;
+ unsigned short newword;
+ unsigned short invword;
+ unsigned short swapword;
+
+ if (op) {
+ oldbyte = val[op-1];
+ oldword = oldbyte | (oldbyte<<8);
+ } else
+ oldbyte = oldword = 0; /* Tell the compiler we've initilalized them. */
+ newbyte = val[op];
+ newword = newbyte | (newbyte<<8);
+ invbyte = ~newbyte;
+ invword = invbyte | (invbyte<<8);
+
+ /*
+ ** Check that the RAM contains the value that should have been left there
+ ** by the previous test (not applicable for pass zero)
+ */
+ if (op) {
+ for (off=0; off<size; off++) {
+ if (readb(ram + off) != oldbyte) {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Byte Pre Check 1: BYTE at offset 0x%x should have been=%x, was=%x\n", off, oldbyte, readb(ram + off));
+ return RIO_FAIL;
+ }
+ }
+ for (off=0; off<size; off+=2) {
+ if (readw(ram + off) != oldword) {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Word Pre Check: WORD at offset 0x%x should have been=%x, was=%x\n",off,oldword, readw(ram + off));
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Word Pre Check: BYTE at offset 0x%x is %x BYTE at offset 0x%x is %x\n", off, readb(ram + off), off+1, readb(ram+off+1));
+ return RIO_FAIL;
+ }
+ }
+ }
+
+ /*
+ ** Now write the INVERSE of the test data into every location, using
+ ** BYTE write operations, first checking before each byte is written
+ ** that the location contains the old value still, and checking after
+ ** the write that the location contains the data specified - this is
+ ** the BYTE read/write test.
+ */
+ for (off=0; off<size; off++) {
+ if (op && (readb(ram + off) != oldbyte)) {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Byte Pre Check 2: BYTE at offset 0x%x should have been=%x, was=%x\n", off, oldbyte, readb(ram + off));
+ return RIO_FAIL;
+ }
+ writeb(invbyte, ram + off);
+ if (readb(ram + off) != invbyte) {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Byte Inv Check: BYTE at offset 0x%x should have been=%x, was=%x\n", off, invbyte, readb(ram + off));
+ return RIO_FAIL;
+ }
+ }
+
+ /*
+ ** now, use WORD operations to write the test value into every location,
+ ** check as before that the location contains the previous test value
+ ** before overwriting, and that it contains the data value written
+ ** afterwards.
+ ** This is the WORD operation test.
+ */
+ for (off=0; off<size; off+=2) {
+ if (readw(ram + off) != invword) {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Word Inv Check: WORD at offset 0x%x should have been=%x, was=%x\n", off, invword, readw(ram + off));
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Word Inv Check: BYTE at offset 0x%x is %x BYTE at offset 0x%x is %x\n", off, readb(ram + off), off+1, readb(ram+off+1));
+ return RIO_FAIL;
+ }
+
+ writew(newword, ram + off);
+ if ( readw(ram + off) != newword ) {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Post Word Check 1: WORD at offset 0x%x should have been=%x, was=%x\n", off, newword, readw(ram + off));
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Post Word Check 1: BYTE at offset 0x%x is %x BYTE at offset 0x%x is %x\n", off, readb(ram + off), off+1, readb(ram + off + 1));
+ return RIO_FAIL;
+ }
+ }
+
+ /*
+ ** now run through the block of memory again, first in byte mode
+ ** then in word mode, and check that all the locations contain the
+ ** required test data.
+ */
+ for (off=0; off<size; off++) {
+ if (readb(ram + off) != newbyte) {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Post Byte Check: BYTE at offset 0x%x should have been=%x, was=%x\n", off, newbyte, readb(ram + off));
+ return RIO_FAIL;
+ }
+ }
+
+ for (off=0; off<size; off+=2) {
+ if (readw(ram + off) != newword ) {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Post Word Check 2: WORD at offset 0x%x should have been=%x, was=%x\n", off, newword, readw(ram + off));
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: Post Word Check 2: BYTE at offset 0x%x is %x BYTE at offset 0x%x is %x\n", off, readb(ram + off), off+1, readb(ram + off + 1));
+ return RIO_FAIL;
+ }
+ }
+
+ /*
+ ** time to check out byte swapping errors
+ */
+ swapword = invbyte | (newbyte << 8);
+
+ for (off=0; off<size; off+=2) {
+ writeb(invbyte, &ram[off]);
+ writeb(newbyte, &ram[off+1]);
+ }
+
+ for ( off=0; off<size; off+=2 ) {
+ if (readw(ram + off) != swapword) {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: SwapWord Check 1: WORD at offset 0x%x should have been=%x, was=%x\n", off, swapword, readw(ram + off));
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: SwapWord Check 1: BYTE at offset 0x%x is %x BYTE at offset 0x%x is %x\n", off, readb(ram + off), off+1, readb(ram + off + 1));
+ return RIO_FAIL;
+ }
+ writew(~swapword, ram + off);
+ }
+
+ for (off=0; off<size; off+=2) {
+ if (readb(ram + off) != newbyte) {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: SwapWord Check 2: BYTE at offset 0x%x should have been=%x, was=%x\n", off, newbyte, readb(ram + off));
+ return RIO_FAIL;
+ }
+ if (readb(ram + off + 1) != invbyte) {
+ rio_dprintk (RIO_DEBUG_INIT, "RIO-init: SwapWord Check 2: BYTE at offset 0x%x should have been=%x, was=%x\n", off+1, invbyte, readb(ram + off + 1));
+ return RIO_FAIL;
+ }
+ writew(newword, ram + off);
+ }
+ return 0;
+}
+
+
+int RIODefaultName(struct rio_info *p, struct Host *HostP, unsigned int UnitId)
+{
+ memcpy(HostP->Mapping[UnitId].Name, "UNKNOWN RTA X-XX", 17);
+ HostP->Mapping[UnitId].Name[12]='1'+(HostP-p->RIOHosts);
+ if ((UnitId+1) > 9) {
+ HostP->Mapping[UnitId].Name[14]='0'+((UnitId+1)/10);
+ HostP->Mapping[UnitId].Name[15]='0'+((UnitId+1)%10);
+ }
+ else {
+ HostP->Mapping[UnitId].Name[14]='1'+UnitId;
+ HostP->Mapping[UnitId].Name[15]=0;
+ }
+ return 0;
+}
+
+#define RIO_RELEASE "Linux"
+#define RELEASE_ID "1.0"
+
+static struct rioVersion stVersion;
+
+struct rioVersion *RIOVersid(void)
+{
+ strlcpy(stVersion.version, "RIO driver for linux V1.0",
+ sizeof(stVersion.version));
+ strlcpy(stVersion.buildDate, __DATE__,
+ sizeof(stVersion.buildDate));
+
+ return &stVersion;
+}
+
+void RIOHostReset(unsigned int Type, struct DpRam __iomem *DpRamP, unsigned int Slot)
+{
+ /*
+ ** Reset the Tpu
+ */
+ rio_dprintk (RIO_DEBUG_INIT, "RIOHostReset: type 0x%x", Type);
+ switch ( Type ) {
+ case RIO_AT:
+ rio_dprintk (RIO_DEBUG_INIT, " (RIO_AT)\n");
+ writeb(BOOT_FROM_RAM | EXTERNAL_BUS_OFF | INTERRUPT_DISABLE | BYTE_OPERATION |
+ SLOW_LINKS | SLOW_AT_BUS, &DpRamP->DpControl);
+ writeb(0xFF, &DpRamP->DpResetTpu);
+ udelay(3);
+ rio_dprintk (RIO_DEBUG_INIT, "RIOHostReset: Don't know if it worked. Try reset again\n");
+ writeb(BOOT_FROM_RAM | EXTERNAL_BUS_OFF | INTERRUPT_DISABLE |
+ BYTE_OPERATION | SLOW_LINKS | SLOW_AT_BUS, &DpRamP->DpControl);
+ writeb(0xFF, &DpRamP->DpResetTpu);
+ udelay(3);
+ break;
+ case RIO_PCI:
+ rio_dprintk (RIO_DEBUG_INIT, " (RIO_PCI)\n");
+ writeb(RIO_PCI_BOOT_FROM_RAM, &DpRamP->DpControl);
+ writeb(0xFF, &DpRamP->DpResetInt);
+ writeb(0xFF, &DpRamP->DpResetTpu);
+ udelay(100);
+ break;
+ default:
+ rio_dprintk (RIO_DEBUG_INIT, " (UNKNOWN)\n");
+ break;
+ }
+ return;
+}
diff --git a/drivers/staging/generic_serial/rio/riointr.c b/drivers/staging/generic_serial/rio/riointr.c
new file mode 100644
index 000000000000..2e71aecae206
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/riointr.c
@@ -0,0 +1,645 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : riointr.c
+** SID : 1.2
+** Last Modified : 11/6/98 10:33:44
+** Retrieved : 11/6/98 10:33:49
+**
+** ident @(#)riointr.c 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/termios.h>
+#include <linux/serial.h>
+
+#include <linux/generic_serial.h>
+
+#include <linux/delay.h>
+
+#include "linux_compat.h"
+#include "rio_linux.h"
+#include "pkt.h"
+#include "daemon.h"
+#include "rio.h"
+#include "riospace.h"
+#include "cmdpkt.h"
+#include "map.h"
+#include "rup.h"
+#include "port.h"
+#include "riodrvr.h"
+#include "rioinfo.h"
+#include "func.h"
+#include "errors.h"
+#include "pci.h"
+
+#include "parmmap.h"
+#include "unixrup.h"
+#include "board.h"
+#include "host.h"
+#include "phb.h"
+#include "link.h"
+#include "cmdblk.h"
+#include "route.h"
+#include "cirrus.h"
+#include "rioioctl.h"
+
+
+static void RIOReceive(struct rio_info *, struct Port *);
+
+
+static char *firstchars(char *p, int nch)
+{
+ static char buf[2][128];
+ static int t = 0;
+ t = !t;
+ memcpy(buf[t], p, nch);
+ buf[t][nch] = 0;
+ return buf[t];
+}
+
+
+#define INCR( P, I ) ((P) = (((P)+(I)) & p->RIOBufferMask))
+/* Enable and start the transmission of packets */
+void RIOTxEnable(char *en)
+{
+ struct Port *PortP;
+ struct rio_info *p;
+ struct tty_struct *tty;
+ int c;
+ struct PKT __iomem *PacketP;
+ unsigned long flags;
+
+ PortP = (struct Port *) en;
+ p = (struct rio_info *) PortP->p;
+ tty = PortP->gs.port.tty;
+
+
+ rio_dprintk(RIO_DEBUG_INTR, "tx port %d: %d chars queued.\n", PortP->PortNum, PortP->gs.xmit_cnt);
+
+ if (!PortP->gs.xmit_cnt)
+ return;
+
+
+ /* This routine is an order of magnitude simpler than the specialix
+ version. One of the disadvantages is that this version will send
+ an incomplete packet (usually 64 bytes instead of 72) once for
+ every 4k worth of data. Let's just say that this won't influence
+ performance significantly..... */
+
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+
+ while (can_add_transmit(&PacketP, PortP)) {
+ c = PortP->gs.xmit_cnt;
+ if (c > PKT_MAX_DATA_LEN)
+ c = PKT_MAX_DATA_LEN;
+
+ /* Don't copy past the end of the source buffer */
+ if (c > SERIAL_XMIT_SIZE - PortP->gs.xmit_tail)
+ c = SERIAL_XMIT_SIZE - PortP->gs.xmit_tail;
+
+ {
+ int t;
+ t = (c > 10) ? 10 : c;
+
+ rio_dprintk(RIO_DEBUG_INTR, "rio: tx port %d: copying %d chars: %s - %s\n", PortP->PortNum, c, firstchars(PortP->gs.xmit_buf + PortP->gs.xmit_tail, t), firstchars(PortP->gs.xmit_buf + PortP->gs.xmit_tail + c - t, t));
+ }
+ /* If for one reason or another, we can't copy more data,
+ we're done! */
+ if (c == 0)
+ break;
+
+ rio_memcpy_toio(PortP->HostP->Caddr, PacketP->data, PortP->gs.xmit_buf + PortP->gs.xmit_tail, c);
+ /* udelay (1); */
+
+ writeb(c, &(PacketP->len));
+ if (!(PortP->State & RIO_DELETED)) {
+ add_transmit(PortP);
+ /*
+ ** Count chars tx'd for port statistics reporting
+ */
+ if (PortP->statsGather)
+ PortP->txchars += c;
+ }
+ PortP->gs.xmit_tail = (PortP->gs.xmit_tail + c) & (SERIAL_XMIT_SIZE - 1);
+ PortP->gs.xmit_cnt -= c;
+ }
+
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+
+ if (PortP->gs.xmit_cnt <= (PortP->gs.wakeup_chars + 2 * PKT_MAX_DATA_LEN))
+ tty_wakeup(PortP->gs.port.tty);
+
+}
+
+
+/*
+** RIO Host Service routine. Does all the work traditionally associated with an
+** interrupt.
+*/
+static int RupIntr;
+static int RxIntr;
+static int TxIntr;
+
+void RIOServiceHost(struct rio_info *p, struct Host *HostP)
+{
+ rio_spin_lock(&HostP->HostLock);
+ if ((HostP->Flags & RUN_STATE) != RC_RUNNING) {
+ static int t = 0;
+ rio_spin_unlock(&HostP->HostLock);
+ if ((t++ % 200) == 0)
+ rio_dprintk(RIO_DEBUG_INTR, "Interrupt but host not running. flags=%x.\n", (int) HostP->Flags);
+ return;
+ }
+ rio_spin_unlock(&HostP->HostLock);
+
+ if (readw(&HostP->ParmMapP->rup_intr)) {
+ writew(0, &HostP->ParmMapP->rup_intr);
+ p->RIORupCount++;
+ RupIntr++;
+ rio_dprintk(RIO_DEBUG_INTR, "rio: RUP interrupt on host %Zd\n", HostP - p->RIOHosts);
+ RIOPollHostCommands(p, HostP);
+ }
+
+ if (readw(&HostP->ParmMapP->rx_intr)) {
+ int port;
+
+ writew(0, &HostP->ParmMapP->rx_intr);
+ p->RIORxCount++;
+ RxIntr++;
+
+ rio_dprintk(RIO_DEBUG_INTR, "rio: RX interrupt on host %Zd\n", HostP - p->RIOHosts);
+ /*
+ ** Loop through every port. If the port is mapped into
+ ** the system ( i.e. has /dev/ttyXXXX associated ) then it is
+ ** worth checking. If the port isn't open, grab any packets
+ ** hanging on its receive queue and stuff them on the free
+ ** list; check for commands on the way.
+ */
+ for (port = p->RIOFirstPortsBooted; port < p->RIOLastPortsBooted + PORTS_PER_RTA; port++) {
+ struct Port *PortP = p->RIOPortp[port];
+ struct tty_struct *ttyP;
+ struct PKT __iomem *PacketP;
+
+ /*
+ ** not mapped in - most of the RIOPortp[] information
+ ** has not been set up!
+ ** Optimise: ports come in bundles of eight.
+ */
+ if (!PortP->Mapped) {
+ port += 7;
+ continue; /* with the next port */
+ }
+
+ /*
+ ** If the host board isn't THIS host board, check the next one.
+ ** optimise: ports come in bundles of eight.
+ */
+ if (PortP->HostP != HostP) {
+ port += 7;
+ continue;
+ }
+
+ /*
+ ** Let us see - is the port open? If not, then don't service it.
+ */
+ if (!(PortP->PortState & PORT_ISOPEN)) {
+ continue;
+ }
+
+ /*
+ ** find corresponding tty structure. The process of mapping
+ ** the ports puts these here.
+ */
+ ttyP = PortP->gs.port.tty;
+
+ /*
+ ** Lock the port before we begin working on it.
+ */
+ rio_spin_lock(&PortP->portSem);
+
+ /*
+ ** Process received data if there is any.
+ */
+ if (can_remove_receive(&PacketP, PortP))
+ RIOReceive(p, PortP);
+
+ /*
+ ** If there is no data left to be read from the port, and
+ ** it's handshake bit is set, then we must clear the handshake,
+ ** so that that downstream RTA is re-enabled.
+ */
+ if (!can_remove_receive(&PacketP, PortP) && (readw(&PortP->PhbP->handshake) == PHB_HANDSHAKE_SET)) {
+ /*
+ ** MAGIC! ( Basically, handshake the RX buffer, so that
+ ** the RTAs upstream can be re-enabled. )
+ */
+ rio_dprintk(RIO_DEBUG_INTR, "Set RX handshake bit\n");
+ writew(PHB_HANDSHAKE_SET | PHB_HANDSHAKE_RESET, &PortP->PhbP->handshake);
+ }
+ rio_spin_unlock(&PortP->portSem);
+ }
+ }
+
+ if (readw(&HostP->ParmMapP->tx_intr)) {
+ int port;
+
+ writew(0, &HostP->ParmMapP->tx_intr);
+
+ p->RIOTxCount++;
+ TxIntr++;
+ rio_dprintk(RIO_DEBUG_INTR, "rio: TX interrupt on host %Zd\n", HostP - p->RIOHosts);
+
+ /*
+ ** Loop through every port.
+ ** If the port is mapped into the system ( i.e. has /dev/ttyXXXX
+ ** associated ) then it is worth checking.
+ */
+ for (port = p->RIOFirstPortsBooted; port < p->RIOLastPortsBooted + PORTS_PER_RTA; port++) {
+ struct Port *PortP = p->RIOPortp[port];
+ struct tty_struct *ttyP;
+ struct PKT __iomem *PacketP;
+
+ /*
+ ** not mapped in - most of the RIOPortp[] information
+ ** has not been set up!
+ */
+ if (!PortP->Mapped) {
+ port += 7;
+ continue; /* with the next port */
+ }
+
+ /*
+ ** If the host board isn't running, then its data structures
+ ** are no use to us - continue quietly.
+ */
+ if (PortP->HostP != HostP) {
+ port += 7;
+ continue; /* with the next port */
+ }
+
+ /*
+ ** Let us see - is the port open? If not, then don't service it.
+ */
+ if (!(PortP->PortState & PORT_ISOPEN)) {
+ continue;
+ }
+
+ rio_dprintk(RIO_DEBUG_INTR, "rio: Looking into port %d.\n", port);
+ /*
+ ** Lock the port before we begin working on it.
+ */
+ rio_spin_lock(&PortP->portSem);
+
+ /*
+ ** If we can't add anything to the transmit queue, then
+ ** we need do none of this processing.
+ */
+ if (!can_add_transmit(&PacketP, PortP)) {
+ rio_dprintk(RIO_DEBUG_INTR, "Can't add to port, so skipping.\n");
+ rio_spin_unlock(&PortP->portSem);
+ continue;
+ }
+
+ /*
+ ** find corresponding tty structure. The process of mapping
+ ** the ports puts these here.
+ */
+ ttyP = PortP->gs.port.tty;
+ /* If ttyP is NULL, the port is getting closed. Forget about it. */
+ if (!ttyP) {
+ rio_dprintk(RIO_DEBUG_INTR, "no tty, so skipping.\n");
+ rio_spin_unlock(&PortP->portSem);
+ continue;
+ }
+ /*
+ ** If there is more room available we start up the transmit
+ ** data process again. This can be direct I/O, if the cookmode
+ ** is set to COOK_RAW or COOK_MEDIUM, or will be a call to the
+ ** riotproc( T_OUTPUT ) if we are in COOK_WELL mode, to fetch
+ ** characters via the line discipline. We must always call
+ ** the line discipline,
+ ** so that user input characters can be echoed correctly.
+ **
+ ** ++++ Update +++++
+ ** With the advent of double buffering, we now see if
+ ** TxBufferOut-In is non-zero. If so, then we copy a packet
+ ** to the output place, and set it going. If this empties
+ ** the buffer, then we must issue a wakeup( ) on OUT.
+ ** If it frees space in the buffer then we must issue
+ ** a wakeup( ) on IN.
+ **
+ ** ++++ Extra! Extra! If PortP->WflushFlag is set, then we
+ ** have to send a WFLUSH command down the PHB, to mark the
+ ** end point of a WFLUSH. We also need to clear out any
+ ** data from the double buffer! ( note that WflushFlag is a
+ ** *count* of the number of WFLUSH commands outstanding! )
+ **
+ ** ++++ And there's more!
+ ** If an RTA is powered off, then on again, and rebooted,
+ ** whilst it has ports open, then we need to re-open the ports.
+ ** ( reasonable enough ). We can't do this when we spot the
+ ** re-boot, in interrupt time, because the queue is probably
+ ** full. So, when we come in here, we need to test if any
+ ** ports are in this condition, and re-open the port before
+ ** we try to send any more data to it. Now, the re-booted
+ ** RTA will be discarding packets from the PHB until it
+ ** receives this open packet, but don't worry tooo much
+ ** about that. The one thing that is interesting is the
+ ** combination of this effect and the WFLUSH effect!
+ */
+ /* For now don't handle RTA reboots. -- REW.
+ Reenabled. Otherwise RTA reboots didn't work. Duh. -- REW */
+ if (PortP->MagicFlags) {
+ if (PortP->MagicFlags & MAGIC_REBOOT) {
+ /*
+ ** well, the RTA has been rebooted, and there is room
+ ** on its queue to add the open packet that is required.
+ **
+ ** The messy part of this line is trying to decide if
+ ** we need to call the Param function as a tty or as
+ ** a modem.
+ ** DONT USE CLOCAL AS A TEST FOR THIS!
+ **
+ ** If we can't param the port, then move on to the
+ ** next port.
+ */
+ PortP->InUse = NOT_INUSE;
+
+ rio_spin_unlock(&PortP->portSem);
+ if (RIOParam(PortP, RIOC_OPEN, ((PortP->Cor2Copy & (RIOC_COR2_RTSFLOW | RIOC_COR2_CTSFLOW)) == (RIOC_COR2_RTSFLOW | RIOC_COR2_CTSFLOW)) ? 1 : 0, DONT_SLEEP) == RIO_FAIL)
+ continue; /* with next port */
+ rio_spin_lock(&PortP->portSem);
+ PortP->MagicFlags &= ~MAGIC_REBOOT;
+ }
+
+ /*
+ ** As mentioned above, this is a tacky hack to cope
+ ** with WFLUSH
+ */
+ if (PortP->WflushFlag) {
+ rio_dprintk(RIO_DEBUG_INTR, "Want to WFLUSH mark this port\n");
+
+ if (PortP->InUse)
+ rio_dprintk(RIO_DEBUG_INTR, "FAILS - PORT IS IN USE\n");
+ }
+
+ while (PortP->WflushFlag && can_add_transmit(&PacketP, PortP) && (PortP->InUse == NOT_INUSE)) {
+ int p;
+ struct PktCmd __iomem *PktCmdP;
+
+ rio_dprintk(RIO_DEBUG_INTR, "Add WFLUSH marker to data queue\n");
+ /*
+ ** make it look just like a WFLUSH command
+ */
+ PktCmdP = (struct PktCmd __iomem *) &PacketP->data[0];
+
+ writeb(RIOC_WFLUSH, &PktCmdP->Command);
+
+ p = PortP->HostPort % (u16) PORTS_PER_RTA;
+
+ /*
+ ** If second block of ports for 16 port RTA, add 8
+ ** to index 8-15.
+ */
+ if (PortP->SecondBlock)
+ p += PORTS_PER_RTA;
+
+ writeb(p, &PktCmdP->PhbNum);
+
+ /*
+ ** to make debuggery easier
+ */
+ writeb('W', &PacketP->data[2]);
+ writeb('F', &PacketP->data[3]);
+ writeb('L', &PacketP->data[4]);
+ writeb('U', &PacketP->data[5]);
+ writeb('S', &PacketP->data[6]);
+ writeb('H', &PacketP->data[7]);
+ writeb(' ', &PacketP->data[8]);
+ writeb('0' + PortP->WflushFlag, &PacketP->data[9]);
+ writeb(' ', &PacketP->data[10]);
+ writeb(' ', &PacketP->data[11]);
+ writeb('\0', &PacketP->data[12]);
+
+ /*
+ ** its two bytes long!
+ */
+ writeb(PKT_CMD_BIT | 2, &PacketP->len);
+
+ /*
+ ** queue it!
+ */
+ if (!(PortP->State & RIO_DELETED)) {
+ add_transmit(PortP);
+ /*
+ ** Count chars tx'd for port statistics reporting
+ */
+ if (PortP->statsGather)
+ PortP->txchars += 2;
+ }
+
+ if (--(PortP->WflushFlag) == 0) {
+ PortP->MagicFlags &= ~MAGIC_FLUSH;
+ }
+
+ rio_dprintk(RIO_DEBUG_INTR, "Wflush count now stands at %d\n", PortP->WflushFlag);
+ }
+ if (PortP->MagicFlags & MORE_OUTPUT_EYGOR) {
+ if (PortP->MagicFlags & MAGIC_FLUSH) {
+ PortP->MagicFlags |= MORE_OUTPUT_EYGOR;
+ } else {
+ if (!can_add_transmit(&PacketP, PortP)) {
+ rio_spin_unlock(&PortP->portSem);
+ continue;
+ }
+ rio_spin_unlock(&PortP->portSem);
+ RIOTxEnable((char *) PortP);
+ rio_spin_lock(&PortP->portSem);
+ PortP->MagicFlags &= ~MORE_OUTPUT_EYGOR;
+ }
+ }
+ }
+
+
+ /*
+ ** If we can't add anything to the transmit queue, then
+ ** we need do none of the remaining processing.
+ */
+ if (!can_add_transmit(&PacketP, PortP)) {
+ rio_spin_unlock(&PortP->portSem);
+ continue;
+ }
+
+ rio_spin_unlock(&PortP->portSem);
+ RIOTxEnable((char *) PortP);
+ }
+ }
+}
+
+/*
+** Routine for handling received data for tty drivers
+*/
+static void RIOReceive(struct rio_info *p, struct Port *PortP)
+{
+ struct tty_struct *TtyP;
+ unsigned short transCount;
+ struct PKT __iomem *PacketP;
+ register unsigned int DataCnt;
+ unsigned char __iomem *ptr;
+ unsigned char *buf;
+ int copied = 0;
+
+ static int intCount, RxIntCnt;
+
+ /*
+ ** The receive data process is to remove packets from the
+ ** PHB until there aren't any more or the current cblock
+ ** is full. When this occurs, there will be some left over
+ ** data in the packet, that we must do something with.
+ ** As we haven't unhooked the packet from the read list
+ ** yet, we can just leave the packet there, having first
+ ** made a note of how far we got. This means that we need
+ ** a pointer per port saying where we start taking the
+ ** data from - this will normally be zero, but when we
+ ** run out of space it will be set to the offset of the
+ ** next byte to copy from the packet data area. The packet
+ ** length field is decremented by the number of bytes that
+ ** we successfully removed from the packet. When this reaches
+ ** zero, we reset the offset pointer to be zero, and free
+ ** the packet from the front of the queue.
+ */
+
+ intCount++;
+
+ TtyP = PortP->gs.port.tty;
+ if (!TtyP) {
+ rio_dprintk(RIO_DEBUG_INTR, "RIOReceive: tty is null. \n");
+ return;
+ }
+
+ if (PortP->State & RIO_THROTTLE_RX) {
+ rio_dprintk(RIO_DEBUG_INTR, "RIOReceive: Throttled. Can't handle more input.\n");
+ return;
+ }
+
+ if (PortP->State & RIO_DELETED) {
+ while (can_remove_receive(&PacketP, PortP)) {
+ remove_receive(PortP);
+ put_free_end(PortP->HostP, PacketP);
+ }
+ } else {
+ /*
+ ** loop, just so long as:
+ ** i ) there's some data ( i.e. can_remove_receive )
+ ** ii ) we haven't been blocked
+ ** iii ) there's somewhere to put the data
+ ** iv ) we haven't outstayed our welcome
+ */
+ transCount = 1;
+ while (can_remove_receive(&PacketP, PortP)
+ && transCount) {
+ RxIntCnt++;
+
+ /*
+ ** check that it is not a command!
+ */
+ if (readb(&PacketP->len) & PKT_CMD_BIT) {
+ rio_dprintk(RIO_DEBUG_INTR, "RIO: unexpected command packet received on PHB\n");
+ /* rio_dprint(RIO_DEBUG_INTR, (" sysport = %d\n", p->RIOPortp->PortNum)); */
+ rio_dprintk(RIO_DEBUG_INTR, " dest_unit = %d\n", readb(&PacketP->dest_unit));
+ rio_dprintk(RIO_DEBUG_INTR, " dest_port = %d\n", readb(&PacketP->dest_port));
+ rio_dprintk(RIO_DEBUG_INTR, " src_unit = %d\n", readb(&PacketP->src_unit));
+ rio_dprintk(RIO_DEBUG_INTR, " src_port = %d\n", readb(&PacketP->src_port));
+ rio_dprintk(RIO_DEBUG_INTR, " len = %d\n", readb(&PacketP->len));
+ rio_dprintk(RIO_DEBUG_INTR, " control = %d\n", readb(&PacketP->control));
+ rio_dprintk(RIO_DEBUG_INTR, " csum = %d\n", readw(&PacketP->csum));
+ rio_dprintk(RIO_DEBUG_INTR, " data bytes: ");
+ for (DataCnt = 0; DataCnt < PKT_MAX_DATA_LEN; DataCnt++)
+ rio_dprintk(RIO_DEBUG_INTR, "%d\n", readb(&PacketP->data[DataCnt]));
+ remove_receive(PortP);
+ put_free_end(PortP->HostP, PacketP);
+ continue; /* with next packet */
+ }
+
+ /*
+ ** How many characters can we move 'upstream' ?
+ **
+ ** Determine the minimum of the amount of data
+ ** available and the amount of space in which to
+ ** put it.
+ **
+ ** 1. Get the packet length by masking 'len'
+ ** for only the length bits.
+ ** 2. Available space is [buffer size] - [space used]
+ **
+ ** Transfer count is the minimum of packet length
+ ** and available space.
+ */
+
+ transCount = tty_buffer_request_room(TtyP, readb(&PacketP->len) & PKT_LEN_MASK);
+ rio_dprintk(RIO_DEBUG_REC, "port %d: Copy %d bytes\n", PortP->PortNum, transCount);
+ /*
+ ** To use the following 'kkprintfs' for debugging - change the '#undef'
+ ** to '#define', (this is the only place ___DEBUG_IT___ occurs in the
+ ** driver).
+ */
+ ptr = (unsigned char __iomem *) PacketP->data + PortP->RxDataStart;
+
+ tty_prepare_flip_string(TtyP, &buf, transCount);
+ rio_memcpy_fromio(buf, ptr, transCount);
+ PortP->RxDataStart += transCount;
+ writeb(readb(&PacketP->len)-transCount, &PacketP->len);
+ copied += transCount;
+
+
+
+ if (readb(&PacketP->len) == 0) {
+ /*
+ ** If we have emptied the packet, then we can
+ ** free it, and reset the start pointer for
+ ** the next packet.
+ */
+ remove_receive(PortP);
+ put_free_end(PortP->HostP, PacketP);
+ PortP->RxDataStart = 0;
+ }
+ }
+ }
+ if (copied) {
+ rio_dprintk(RIO_DEBUG_REC, "port %d: pushing tty flip buffer: %d total bytes copied.\n", PortP->PortNum, copied);
+ tty_flip_buffer_push(TtyP);
+ }
+
+ return;
+}
+
diff --git a/drivers/staging/generic_serial/rio/rioioctl.h b/drivers/staging/generic_serial/rio/rioioctl.h
new file mode 100644
index 000000000000..e8af5b30519e
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/rioioctl.h
@@ -0,0 +1,57 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : rioioctl.h
+** SID : 1.2
+** Last Modified : 11/6/98 11:34:13
+** Retrieved : 11/6/98 11:34:22
+**
+** ident @(#)rioioctl.h 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rioioctl_h__
+#define __rioioctl_h__
+
+/*
+** RIO device driver - user ioctls and associated structures.
+*/
+
+struct portStats {
+ int port;
+ int gather;
+ unsigned long txchars;
+ unsigned long rxchars;
+ unsigned long opens;
+ unsigned long closes;
+ unsigned long ioctls;
+};
+
+#define RIOC ('R'<<8)|('i'<<16)|('o'<<24)
+
+#define RIO_QUICK_CHECK (RIOC | 105)
+#define RIO_GATHER_PORT_STATS (RIOC | 193)
+#define RIO_RESET_PORT_STATS (RIOC | 194)
+#define RIO_GET_PORT_STATS (RIOC | 195)
+
+#endif /* __rioioctl_h__ */
diff --git a/drivers/staging/generic_serial/rio/rioparam.c b/drivers/staging/generic_serial/rio/rioparam.c
new file mode 100644
index 000000000000..6415f3f32a72
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/rioparam.c
@@ -0,0 +1,663 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : rioparam.c
+** SID : 1.3
+** Last Modified : 11/6/98 10:33:45
+** Retrieved : 11/6/98 10:33:50
+**
+** ident @(#)rioparam.c 1.3
+**
+** -----------------------------------------------------------------------------
+*/
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/termios.h>
+#include <linux/serial.h>
+
+#include <linux/generic_serial.h>
+
+
+#include "linux_compat.h"
+#include "rio_linux.h"
+#include "pkt.h"
+#include "daemon.h"
+#include "rio.h"
+#include "riospace.h"
+#include "cmdpkt.h"
+#include "map.h"
+#include "rup.h"
+#include "port.h"
+#include "riodrvr.h"
+#include "rioinfo.h"
+#include "func.h"
+#include "errors.h"
+#include "pci.h"
+
+#include "parmmap.h"
+#include "unixrup.h"
+#include "board.h"
+#include "host.h"
+#include "phb.h"
+#include "link.h"
+#include "cmdblk.h"
+#include "route.h"
+#include "cirrus.h"
+#include "rioioctl.h"
+#include "param.h"
+
+
+
+/*
+** The Scam, based on email from jeremyr@bugs.specialix.co.uk....
+**
+** To send a command on a particular port, you put a packet with the
+** command bit set onto the port. The command bit is in the len field,
+** and gets ORed in with the actual byte count.
+**
+** When you send a packet with the command bit set the first
+** data byte (data[0]) is interpreted as the command to execute.
+** It also governs what data structure overlay should accompany the packet.
+** Commands are defined in cirrus/cirrus.h
+**
+** If you want the command to pre-emt data already on the queue for the
+** port, set the pre-emptive bit in conjunction with the command bit.
+** It is not defined what will happen if you set the preemptive bit
+** on a packet that is NOT a command.
+**
+** Pre-emptive commands should be queued at the head of the queue using
+** add_start(), whereas normal commands and data are enqueued using
+** add_end().
+**
+** Most commands do not use the remaining bytes in the data array. The
+** exceptions are OPEN MOPEN and CONFIG. (NB. As with the SI CONFIG and
+** OPEN are currently analogous). With these three commands the following
+** 11 data bytes are all used to pass config information such as baud rate etc.
+** The fields are also defined in cirrus.h. Some contain straightforward
+** information such as the transmit XON character. Two contain the transmit and
+** receive baud rates respectively. For most baud rates there is a direct
+** mapping between the rates defined in <sys/termio.h> and the byte in the
+** packet. There are additional (non UNIX-standard) rates defined in
+** /u/dos/rio/cirrus/h/brates.h.
+**
+** The rest of the data fields contain approximations to the Cirrus registers
+** that are used to program number of bits etc. Each registers bit fields is
+** defined in cirrus.h.
+**
+** NB. Only use those bits that are defined as being driver specific
+** or common to the RTA and the driver.
+**
+** All commands going from RTA->Host will be dealt with by the Host code - you
+** will never see them. As with the SI there will be three fields to look out
+** for in each phb (not yet defined - needs defining a.s.a.p).
+**
+** modem_status - current state of handshake pins.
+**
+** port_status - current port status - equivalent to hi_stat for SI, indicates
+** if port is IDLE_OPEN, IDLE_CLOSED etc.
+**
+** break_status - bit X set if break has been received.
+**
+** Happy hacking.
+**
+*/
+
+/*
+** RIOParam is used to open or configure a port. You pass it a PortP,
+** which will have a tty struct attached to it. You also pass a command,
+** either OPEN or CONFIG. The port's setup is taken from the t_ fields
+** of the tty struct inside the PortP, and the port is either opened
+** or re-configured. You must also tell RIOParam if the device is a modem
+** device or not (i.e. top bit of minor number set or clear - take special
+** care when deciding on this!).
+** RIOParam neither flushes nor waits for drain, and is NOT preemptive.
+**
+** RIOParam assumes it will be called at splrio(), and also assumes
+** that CookMode is set correctly in the port structure.
+**
+** NB. for MPX
+** tty lock must NOT have been previously acquired.
+*/
+int RIOParam(struct Port *PortP, int cmd, int Modem, int SleepFlag)
+{
+ struct tty_struct *TtyP;
+ int retval;
+ struct phb_param __iomem *phb_param_ptr;
+ struct PKT __iomem *PacketP;
+ int res;
+ u8 Cor1 = 0, Cor2 = 0, Cor4 = 0, Cor5 = 0;
+ u8 TxXon = 0, TxXoff = 0, RxXon = 0, RxXoff = 0;
+ u8 LNext = 0, TxBaud = 0, RxBaud = 0;
+ int retries = 0xff;
+ unsigned long flags;
+
+ func_enter();
+
+ TtyP = PortP->gs.port.tty;
+
+ rio_dprintk(RIO_DEBUG_PARAM, "RIOParam: Port:%d cmd:%d Modem:%d SleepFlag:%d Mapped: %d, tty=%p\n", PortP->PortNum, cmd, Modem, SleepFlag, PortP->Mapped, TtyP);
+
+ if (!TtyP) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Can't call rioparam with null tty.\n");
+
+ func_exit();
+
+ return RIO_FAIL;
+ }
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+
+ if (cmd == RIOC_OPEN) {
+ /*
+ ** If the port is set to store or lock the parameters, and it is
+ ** paramed with OPEN, we want to restore the saved port termio, but
+ ** only if StoredTermio has been saved, i.e. NOT 1st open after reboot.
+ */
+ }
+
+ /*
+ ** wait for space
+ */
+ while (!(res = can_add_transmit(&PacketP, PortP)) || (PortP->InUse != NOT_INUSE)) {
+ if (retries-- <= 0) {
+ break;
+ }
+ if (PortP->InUse != NOT_INUSE) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Port IN_USE for pre-emptive command\n");
+ }
+
+ if (!res) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Port has no space on transmit queue\n");
+ }
+
+ if (SleepFlag != OK_TO_SLEEP) {
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ func_exit();
+
+ return RIO_FAIL;
+ }
+
+ rio_dprintk(RIO_DEBUG_PARAM, "wait for can_add_transmit\n");
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ retval = RIODelay(PortP, HUNDRED_MS);
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ if (retval == RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_PARAM, "wait for can_add_transmit broken by signal\n");
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ func_exit();
+ return -EINTR;
+ }
+ if (PortP->State & RIO_DELETED) {
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ func_exit();
+ return 0;
+ }
+ }
+
+ if (!res) {
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ func_exit();
+
+ return RIO_FAIL;
+ }
+
+ rio_dprintk(RIO_DEBUG_PARAM, "can_add_transmit() returns %x\n", res);
+ rio_dprintk(RIO_DEBUG_PARAM, "Packet is %p\n", PacketP);
+
+ phb_param_ptr = (struct phb_param __iomem *) PacketP->data;
+
+
+ switch (TtyP->termios->c_cflag & CSIZE) {
+ case CS5:
+ {
+ rio_dprintk(RIO_DEBUG_PARAM, "5 bit data\n");
+ Cor1 |= RIOC_COR1_5BITS;
+ break;
+ }
+ case CS6:
+ {
+ rio_dprintk(RIO_DEBUG_PARAM, "6 bit data\n");
+ Cor1 |= RIOC_COR1_6BITS;
+ break;
+ }
+ case CS7:
+ {
+ rio_dprintk(RIO_DEBUG_PARAM, "7 bit data\n");
+ Cor1 |= RIOC_COR1_7BITS;
+ break;
+ }
+ case CS8:
+ {
+ rio_dprintk(RIO_DEBUG_PARAM, "8 bit data\n");
+ Cor1 |= RIOC_COR1_8BITS;
+ break;
+ }
+ }
+
+ if (TtyP->termios->c_cflag & CSTOPB) {
+ rio_dprintk(RIO_DEBUG_PARAM, "2 stop bits\n");
+ Cor1 |= RIOC_COR1_2STOP;
+ } else {
+ rio_dprintk(RIO_DEBUG_PARAM, "1 stop bit\n");
+ Cor1 |= RIOC_COR1_1STOP;
+ }
+
+ if (TtyP->termios->c_cflag & PARENB) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Enable parity\n");
+ Cor1 |= RIOC_COR1_NORMAL;
+ } else {
+ rio_dprintk(RIO_DEBUG_PARAM, "Disable parity\n");
+ Cor1 |= RIOC_COR1_NOP;
+ }
+ if (TtyP->termios->c_cflag & PARODD) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Odd parity\n");
+ Cor1 |= RIOC_COR1_ODD;
+ } else {
+ rio_dprintk(RIO_DEBUG_PARAM, "Even parity\n");
+ Cor1 |= RIOC_COR1_EVEN;
+ }
+
+ /*
+ ** COR 2
+ */
+ if (TtyP->termios->c_iflag & IXON) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Enable start/stop output control\n");
+ Cor2 |= RIOC_COR2_IXON;
+ } else {
+ if (PortP->Config & RIO_IXON) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Force enable start/stop output control\n");
+ Cor2 |= RIOC_COR2_IXON;
+ } else
+ rio_dprintk(RIO_DEBUG_PARAM, "IXON has been disabled.\n");
+ }
+
+ if (TtyP->termios->c_iflag & IXANY) {
+ if (PortP->Config & RIO_IXANY) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Enable any key to restart output\n");
+ Cor2 |= RIOC_COR2_IXANY;
+ } else
+ rio_dprintk(RIO_DEBUG_PARAM, "IXANY has been disabled due to sanity reasons.\n");
+ }
+
+ if (TtyP->termios->c_iflag & IXOFF) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Enable start/stop input control 2\n");
+ Cor2 |= RIOC_COR2_IXOFF;
+ }
+
+ if (TtyP->termios->c_cflag & HUPCL) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Hangup on last close\n");
+ Cor2 |= RIOC_COR2_HUPCL;
+ }
+
+ if (C_CRTSCTS(TtyP)) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Rx hardware flow control enabled\n");
+ Cor2 |= RIOC_COR2_CTSFLOW;
+ Cor2 |= RIOC_COR2_RTSFLOW;
+ } else {
+ rio_dprintk(RIO_DEBUG_PARAM, "Rx hardware flow control disabled\n");
+ Cor2 &= ~RIOC_COR2_CTSFLOW;
+ Cor2 &= ~RIOC_COR2_RTSFLOW;
+ }
+
+
+ if (TtyP->termios->c_cflag & CLOCAL) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Local line\n");
+ } else {
+ rio_dprintk(RIO_DEBUG_PARAM, "Possible Modem line\n");
+ }
+
+ /*
+ ** COR 4 (there is no COR 3)
+ */
+ if (TtyP->termios->c_iflag & IGNBRK) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Ignore break condition\n");
+ Cor4 |= RIOC_COR4_IGNBRK;
+ }
+ if (!(TtyP->termios->c_iflag & BRKINT)) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Break generates NULL condition\n");
+ Cor4 |= RIOC_COR4_NBRKINT;
+ } else {
+ rio_dprintk(RIO_DEBUG_PARAM, "Interrupt on break condition\n");
+ }
+
+ if (TtyP->termios->c_iflag & INLCR) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Map newline to carriage return on input\n");
+ Cor4 |= RIOC_COR4_INLCR;
+ }
+
+ if (TtyP->termios->c_iflag & IGNCR) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Ignore carriage return on input\n");
+ Cor4 |= RIOC_COR4_IGNCR;
+ }
+
+ if (TtyP->termios->c_iflag & ICRNL) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Map carriage return to newline on input\n");
+ Cor4 |= RIOC_COR4_ICRNL;
+ }
+ if (TtyP->termios->c_iflag & IGNPAR) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Ignore characters with parity errors\n");
+ Cor4 |= RIOC_COR4_IGNPAR;
+ }
+ if (TtyP->termios->c_iflag & PARMRK) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Mark parity errors\n");
+ Cor4 |= RIOC_COR4_PARMRK;
+ }
+
+ /*
+ ** Set the RAISEMOD flag to ensure that the modem lines are raised
+ ** on reception of a config packet.
+ ** The download code handles the zero baud condition.
+ */
+ Cor4 |= RIOC_COR4_RAISEMOD;
+
+ /*
+ ** COR 5
+ */
+
+ Cor5 = RIOC_COR5_CMOE;
+
+ /*
+ ** Set to monitor tbusy/tstop (or not).
+ */
+
+ if (PortP->MonitorTstate)
+ Cor5 |= RIOC_COR5_TSTATE_ON;
+ else
+ Cor5 |= RIOC_COR5_TSTATE_OFF;
+
+ /*
+ ** Could set LNE here if you wanted LNext processing. SVR4 will use it.
+ */
+ if (TtyP->termios->c_iflag & ISTRIP) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Strip input characters\n");
+ if (!(PortP->State & RIO_TRIAD_MODE)) {
+ Cor5 |= RIOC_COR5_ISTRIP;
+ }
+ }
+
+ if (TtyP->termios->c_oflag & ONLCR) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Map newline to carriage-return, newline on output\n");
+ if (PortP->CookMode == COOK_MEDIUM)
+ Cor5 |= RIOC_COR5_ONLCR;
+ }
+ if (TtyP->termios->c_oflag & OCRNL) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Map carriage return to newline on output\n");
+ if (PortP->CookMode == COOK_MEDIUM)
+ Cor5 |= RIOC_COR5_OCRNL;
+ }
+ if ((TtyP->termios->c_oflag & TABDLY) == TAB3) {
+ rio_dprintk(RIO_DEBUG_PARAM, "Tab delay 3 set\n");
+ if (PortP->CookMode == COOK_MEDIUM)
+ Cor5 |= RIOC_COR5_TAB3;
+ }
+
+ /*
+ ** Flow control bytes.
+ */
+ TxXon = TtyP->termios->c_cc[VSTART];
+ TxXoff = TtyP->termios->c_cc[VSTOP];
+ RxXon = TtyP->termios->c_cc[VSTART];
+ RxXoff = TtyP->termios->c_cc[VSTOP];
+ /*
+ ** LNEXT byte
+ */
+ LNext = 0;
+
+ /*
+ ** Baud rate bytes
+ */
+ rio_dprintk(RIO_DEBUG_PARAM, "Mapping of rx/tx baud %x (%x)\n", TtyP->termios->c_cflag, CBAUD);
+
+ switch (TtyP->termios->c_cflag & CBAUD) {
+#define e(b) case B ## b : RxBaud = TxBaud = RIO_B ## b ;break
+ e(50);
+ e(75);
+ e(110);
+ e(134);
+ e(150);
+ e(200);
+ e(300);
+ e(600);
+ e(1200);
+ e(1800);
+ e(2400);
+ e(4800);
+ e(9600);
+ e(19200);
+ e(38400);
+ e(57600);
+ e(115200); /* e(230400);e(460800); e(921600); */
+ }
+
+ rio_dprintk(RIO_DEBUG_PARAM, "tx baud 0x%x, rx baud 0x%x\n", TxBaud, RxBaud);
+
+
+ /*
+ ** Leftovers
+ */
+ if (TtyP->termios->c_cflag & CREAD)
+ rio_dprintk(RIO_DEBUG_PARAM, "Enable receiver\n");
+#ifdef RCV1EN
+ if (TtyP->termios->c_cflag & RCV1EN)
+ rio_dprintk(RIO_DEBUG_PARAM, "RCV1EN (?)\n");
+#endif
+#ifdef XMT1EN
+ if (TtyP->termios->c_cflag & XMT1EN)
+ rio_dprintk(RIO_DEBUG_PARAM, "XMT1EN (?)\n");
+#endif
+ if (TtyP->termios->c_lflag & ISIG)
+ rio_dprintk(RIO_DEBUG_PARAM, "Input character signal generating enabled\n");
+ if (TtyP->termios->c_lflag & ICANON)
+ rio_dprintk(RIO_DEBUG_PARAM, "Canonical input: erase and kill enabled\n");
+ if (TtyP->termios->c_lflag & XCASE)
+ rio_dprintk(RIO_DEBUG_PARAM, "Canonical upper/lower presentation\n");
+ if (TtyP->termios->c_lflag & ECHO)
+ rio_dprintk(RIO_DEBUG_PARAM, "Enable input echo\n");
+ if (TtyP->termios->c_lflag & ECHOE)
+ rio_dprintk(RIO_DEBUG_PARAM, "Enable echo erase\n");
+ if (TtyP->termios->c_lflag & ECHOK)
+ rio_dprintk(RIO_DEBUG_PARAM, "Enable echo kill\n");
+ if (TtyP->termios->c_lflag & ECHONL)
+ rio_dprintk(RIO_DEBUG_PARAM, "Enable echo newline\n");
+ if (TtyP->termios->c_lflag & NOFLSH)
+ rio_dprintk(RIO_DEBUG_PARAM, "Disable flush after interrupt or quit\n");
+#ifdef TOSTOP
+ if (TtyP->termios->c_lflag & TOSTOP)
+ rio_dprintk(RIO_DEBUG_PARAM, "Send SIGTTOU for background output\n");
+#endif
+#ifdef XCLUDE
+ if (TtyP->termios->c_lflag & XCLUDE)
+ rio_dprintk(RIO_DEBUG_PARAM, "Exclusive use of this line\n");
+#endif
+ if (TtyP->termios->c_iflag & IUCLC)
+ rio_dprintk(RIO_DEBUG_PARAM, "Map uppercase to lowercase on input\n");
+ if (TtyP->termios->c_oflag & OPOST)
+ rio_dprintk(RIO_DEBUG_PARAM, "Enable output post-processing\n");
+ if (TtyP->termios->c_oflag & OLCUC)
+ rio_dprintk(RIO_DEBUG_PARAM, "Map lowercase to uppercase on output\n");
+ if (TtyP->termios->c_oflag & ONOCR)
+ rio_dprintk(RIO_DEBUG_PARAM, "No carriage return output at column 0\n");
+ if (TtyP->termios->c_oflag & ONLRET)
+ rio_dprintk(RIO_DEBUG_PARAM, "Newline performs carriage return function\n");
+ if (TtyP->termios->c_oflag & OFILL)
+ rio_dprintk(RIO_DEBUG_PARAM, "Use fill characters for delay\n");
+ if (TtyP->termios->c_oflag & OFDEL)
+ rio_dprintk(RIO_DEBUG_PARAM, "Fill character is DEL\n");
+ if (TtyP->termios->c_oflag & NLDLY)
+ rio_dprintk(RIO_DEBUG_PARAM, "Newline delay set\n");
+ if (TtyP->termios->c_oflag & CRDLY)
+ rio_dprintk(RIO_DEBUG_PARAM, "Carriage return delay set\n");
+ if (TtyP->termios->c_oflag & TABDLY)
+ rio_dprintk(RIO_DEBUG_PARAM, "Tab delay set\n");
+ /*
+ ** These things are kind of useful in a later life!
+ */
+ PortP->Cor2Copy = Cor2;
+
+ if (PortP->State & RIO_DELETED) {
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ func_exit();
+
+ return RIO_FAIL;
+ }
+
+ /*
+ ** Actually write the info into the packet to be sent
+ */
+ writeb(cmd, &phb_param_ptr->Cmd);
+ writeb(Cor1, &phb_param_ptr->Cor1);
+ writeb(Cor2, &phb_param_ptr->Cor2);
+ writeb(Cor4, &phb_param_ptr->Cor4);
+ writeb(Cor5, &phb_param_ptr->Cor5);
+ writeb(TxXon, &phb_param_ptr->TxXon);
+ writeb(RxXon, &phb_param_ptr->RxXon);
+ writeb(TxXoff, &phb_param_ptr->TxXoff);
+ writeb(RxXoff, &phb_param_ptr->RxXoff);
+ writeb(LNext, &phb_param_ptr->LNext);
+ writeb(TxBaud, &phb_param_ptr->TxBaud);
+ writeb(RxBaud, &phb_param_ptr->RxBaud);
+
+ /*
+ ** Set the length/command field
+ */
+ writeb(12 | PKT_CMD_BIT, &PacketP->len);
+
+ /*
+ ** The packet is formed - now, whack it off
+ ** to its final destination:
+ */
+ add_transmit(PortP);
+ /*
+ ** Count characters transmitted for port statistics reporting
+ */
+ if (PortP->statsGather)
+ PortP->txchars += 12;
+
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+
+ rio_dprintk(RIO_DEBUG_PARAM, "add_transmit returned.\n");
+ /*
+ ** job done.
+ */
+ func_exit();
+
+ return 0;
+}
+
+
+/*
+** We can add another packet to a transmit queue if the packet pointer pointed
+** to by the TxAdd pointer has PKT_IN_USE clear in its address.
+*/
+int can_add_transmit(struct PKT __iomem **PktP, struct Port *PortP)
+{
+ struct PKT __iomem *tp;
+
+ *PktP = tp = (struct PKT __iomem *) RIO_PTR(PortP->Caddr, readw(PortP->TxAdd));
+
+ return !((unsigned long) tp & PKT_IN_USE);
+}
+
+/*
+** To add a packet to the queue, you set the PKT_IN_USE bit in the address,
+** and then move the TxAdd pointer along one position to point to the next
+** packet pointer. You must wrap the pointer from the end back to the start.
+*/
+void add_transmit(struct Port *PortP)
+{
+ if (readw(PortP->TxAdd) & PKT_IN_USE) {
+ rio_dprintk(RIO_DEBUG_PARAM, "add_transmit: Packet has been stolen!");
+ }
+ writew(readw(PortP->TxAdd) | PKT_IN_USE, PortP->TxAdd);
+ PortP->TxAdd = (PortP->TxAdd == PortP->TxEnd) ? PortP->TxStart : PortP->TxAdd + 1;
+ writew(RIO_OFF(PortP->Caddr, PortP->TxAdd), &PortP->PhbP->tx_add);
+}
+
+/****************************************
+ * Put a packet onto the end of the
+ * free list
+ ****************************************/
+void put_free_end(struct Host *HostP, struct PKT __iomem *PktP)
+{
+ struct rio_free_list __iomem *tmp_pointer;
+ unsigned short old_end, new_end;
+ unsigned long flags;
+
+ rio_spin_lock_irqsave(&HostP->HostLock, flags);
+
+ /*************************************************
+ * Put a packet back onto the back of the free list
+ *
+ ************************************************/
+
+ rio_dprintk(RIO_DEBUG_PFE, "put_free_end(PktP=%p)\n", PktP);
+
+ if ((old_end = readw(&HostP->ParmMapP->free_list_end)) != TPNULL) {
+ new_end = RIO_OFF(HostP->Caddr, PktP);
+ tmp_pointer = (struct rio_free_list __iomem *) RIO_PTR(HostP->Caddr, old_end);
+ writew(new_end, &tmp_pointer->next);
+ writew(old_end, &((struct rio_free_list __iomem *) PktP)->prev);
+ writew(TPNULL, &((struct rio_free_list __iomem *) PktP)->next);
+ writew(new_end, &HostP->ParmMapP->free_list_end);
+ } else { /* First packet on the free list this should never happen! */
+ rio_dprintk(RIO_DEBUG_PFE, "put_free_end(): This should never happen\n");
+ writew(RIO_OFF(HostP->Caddr, PktP), &HostP->ParmMapP->free_list_end);
+ tmp_pointer = (struct rio_free_list __iomem *) PktP;
+ writew(TPNULL, &tmp_pointer->prev);
+ writew(TPNULL, &tmp_pointer->next);
+ }
+ rio_dprintk(RIO_DEBUG_CMD, "Before unlock: %p\n", &HostP->HostLock);
+ rio_spin_unlock_irqrestore(&HostP->HostLock, flags);
+}
+
+/*
+** can_remove_receive(PktP,P) returns non-zero if PKT_IN_USE is set
+** for the next packet on the queue. It will also set PktP to point to the
+** relevant packet, [having cleared the PKT_IN_USE bit]. If PKT_IN_USE is clear,
+** then can_remove_receive() returns 0.
+*/
+int can_remove_receive(struct PKT __iomem **PktP, struct Port *PortP)
+{
+ if (readw(PortP->RxRemove) & PKT_IN_USE) {
+ *PktP = (struct PKT __iomem *) RIO_PTR(PortP->Caddr, readw(PortP->RxRemove) & ~PKT_IN_USE);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+** To remove a packet from the receive queue you clear its PKT_IN_USE bit,
+** and then bump the pointers. Once the pointers get to the end, they must
+** be wrapped back to the start.
+*/
+void remove_receive(struct Port *PortP)
+{
+ writew(readw(PortP->RxRemove) & ~PKT_IN_USE, PortP->RxRemove);
+ PortP->RxRemove = (PortP->RxRemove == PortP->RxEnd) ? PortP->RxStart : PortP->RxRemove + 1;
+ writew(RIO_OFF(PortP->Caddr, PortP->RxRemove), &PortP->PhbP->rx_remove);
+}
diff --git a/drivers/staging/generic_serial/rio/rioroute.c b/drivers/staging/generic_serial/rio/rioroute.c
new file mode 100644
index 000000000000..f9b936ac3394
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/rioroute.c
@@ -0,0 +1,1039 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : rioroute.c
+** SID : 1.3
+** Last Modified : 11/6/98 10:33:46
+** Retrieved : 11/6/98 10:33:50
+**
+** ident @(#)rioroute.c 1.3
+**
+** -----------------------------------------------------------------------------
+*/
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/termios.h>
+#include <linux/serial.h>
+
+#include <linux/generic_serial.h>
+
+
+#include "linux_compat.h"
+#include "rio_linux.h"
+#include "pkt.h"
+#include "daemon.h"
+#include "rio.h"
+#include "riospace.h"
+#include "cmdpkt.h"
+#include "map.h"
+#include "rup.h"
+#include "port.h"
+#include "riodrvr.h"
+#include "rioinfo.h"
+#include "func.h"
+#include "errors.h"
+#include "pci.h"
+
+#include "parmmap.h"
+#include "unixrup.h"
+#include "board.h"
+#include "host.h"
+#include "phb.h"
+#include "link.h"
+#include "cmdblk.h"
+#include "route.h"
+#include "cirrus.h"
+#include "rioioctl.h"
+#include "param.h"
+
+static int RIOCheckIsolated(struct rio_info *, struct Host *, unsigned int);
+static int RIOIsolate(struct rio_info *, struct Host *, unsigned int);
+static int RIOCheck(struct Host *, unsigned int);
+static void RIOConCon(struct rio_info *, struct Host *, unsigned int, unsigned int, unsigned int, unsigned int, int);
+
+
+/*
+** Incoming on the ROUTE_RUP
+** I wrote this while I was tired. Forgive me.
+*/
+int RIORouteRup(struct rio_info *p, unsigned int Rup, struct Host *HostP, struct PKT __iomem * PacketP)
+{
+ struct PktCmd __iomem *PktCmdP = (struct PktCmd __iomem *) PacketP->data;
+ struct PktCmd_M *PktReplyP;
+ struct CmdBlk *CmdBlkP;
+ struct Port *PortP;
+ struct Map *MapP;
+ struct Top *TopP;
+ int ThisLink, ThisLinkMin, ThisLinkMax;
+ int port;
+ int Mod, Mod1, Mod2;
+ unsigned short RtaType;
+ unsigned int RtaUniq;
+ unsigned int ThisUnit, ThisUnit2; /* 2 ids to accommodate 16 port RTA */
+ unsigned int OldUnit, NewUnit, OldLink, NewLink;
+ char *MyType, *MyName;
+ int Lies;
+ unsigned long flags;
+
+ /*
+ ** Is this unit telling us it's current link topology?
+ */
+ if (readb(&PktCmdP->Command) == ROUTE_TOPOLOGY) {
+ MapP = HostP->Mapping;
+
+ /*
+ ** The packet can be sent either by the host or by an RTA.
+ ** If it comes from the host, then we need to fill in the
+ ** Topology array in the host structure. If it came in
+ ** from an RTA then we need to fill in the Mapping structure's
+ ** Topology array for the unit.
+ */
+ if (Rup >= (unsigned short) MAX_RUP) {
+ ThisUnit = HOST_ID;
+ TopP = HostP->Topology;
+ MyType = "Host";
+ MyName = HostP->Name;
+ ThisLinkMin = ThisLinkMax = Rup - MAX_RUP;
+ } else {
+ ThisUnit = Rup + 1;
+ TopP = HostP->Mapping[Rup].Topology;
+ MyType = "RTA";
+ MyName = HostP->Mapping[Rup].Name;
+ ThisLinkMin = 0;
+ ThisLinkMax = LINKS_PER_UNIT - 1;
+ }
+
+ /*
+ ** Lies will not be tolerated.
+ ** If any pair of links claim to be connected to the same
+ ** place, then ignore this packet completely.
+ */
+ Lies = 0;
+ for (ThisLink = ThisLinkMin + 1; ThisLink <= ThisLinkMax; ThisLink++) {
+ /*
+ ** it won't lie about network interconnect, total disconnects
+ ** and no-IDs. (or at least, it doesn't *matter* if it does)
+ */
+ if (readb(&PktCmdP->RouteTopology[ThisLink].Unit) > (unsigned short) MAX_RUP)
+ continue;
+
+ for (NewLink = ThisLinkMin; NewLink < ThisLink; NewLink++) {
+ if ((readb(&PktCmdP->RouteTopology[ThisLink].Unit) == readb(&PktCmdP->RouteTopology[NewLink].Unit)) && (readb(&PktCmdP->RouteTopology[ThisLink].Link) == readb(&PktCmdP->RouteTopology[NewLink].Link))) {
+ Lies++;
+ }
+ }
+ }
+
+ if (Lies) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "LIES! DAMN LIES! %d LIES!\n", Lies);
+ rio_dprintk(RIO_DEBUG_ROUTE, "%d:%c %d:%c %d:%c %d:%c\n",
+ readb(&PktCmdP->RouteTopology[0].Unit),
+ 'A' + readb(&PktCmdP->RouteTopology[0].Link),
+ readb(&PktCmdP->RouteTopology[1].Unit),
+ 'A' + readb(&PktCmdP->RouteTopology[1].Link), readb(&PktCmdP->RouteTopology[2].Unit), 'A' + readb(&PktCmdP->RouteTopology[2].Link), readb(&PktCmdP->RouteTopology[3].Unit), 'A' + readb(&PktCmdP->RouteTopology[3].Link));
+ return 1;
+ }
+
+ /*
+ ** now, process each link.
+ */
+ for (ThisLink = ThisLinkMin; ThisLink <= ThisLinkMax; ThisLink++) {
+ /*
+ ** this is what it was connected to
+ */
+ OldUnit = TopP[ThisLink].Unit;
+ OldLink = TopP[ThisLink].Link;
+
+ /*
+ ** this is what it is now connected to
+ */
+ NewUnit = readb(&PktCmdP->RouteTopology[ThisLink].Unit);
+ NewLink = readb(&PktCmdP->RouteTopology[ThisLink].Link);
+
+ if (OldUnit != NewUnit || OldLink != NewLink) {
+ /*
+ ** something has changed!
+ */
+
+ if (NewUnit > MAX_RUP && NewUnit != ROUTE_DISCONNECT && NewUnit != ROUTE_NO_ID && NewUnit != ROUTE_INTERCONNECT) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "I have a link from %s %s to unit %d:%d - I don't like it.\n", MyType, MyName, NewUnit, NewLink);
+ } else {
+ /*
+ ** put the new values in
+ */
+ TopP[ThisLink].Unit = NewUnit;
+ TopP[ThisLink].Link = NewLink;
+
+ RIOSetChange(p);
+
+ if (OldUnit <= MAX_RUP) {
+ /*
+ ** If something has become bust, then re-enable them messages
+ */
+ if (!p->RIONoMessage)
+ RIOConCon(p, HostP, ThisUnit, ThisLink, OldUnit, OldLink, DISCONNECT);
+ }
+
+ if ((NewUnit <= MAX_RUP) && !p->RIONoMessage)
+ RIOConCon(p, HostP, ThisUnit, ThisLink, NewUnit, NewLink, CONNECT);
+
+ if (NewUnit == ROUTE_NO_ID)
+ rio_dprintk(RIO_DEBUG_ROUTE, "%s %s (%c) is connected to an unconfigured unit.\n", MyType, MyName, 'A' + ThisLink);
+
+ if (NewUnit == ROUTE_INTERCONNECT) {
+ if (!p->RIONoMessage)
+ printk(KERN_DEBUG "rio: %s '%s' (%c) is connected to another network.\n", MyType, MyName, 'A' + ThisLink);
+ }
+
+ /*
+ ** perform an update for 'the other end', so that these messages
+ ** only appears once. Only disconnect the other end if it is pointing
+ ** at us!
+ */
+ if (OldUnit == HOST_ID) {
+ if (HostP->Topology[OldLink].Unit == ThisUnit && HostP->Topology[OldLink].Link == ThisLink) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "SETTING HOST (%c) TO DISCONNECTED!\n", OldLink + 'A');
+ HostP->Topology[OldLink].Unit = ROUTE_DISCONNECT;
+ HostP->Topology[OldLink].Link = NO_LINK;
+ } else {
+ rio_dprintk(RIO_DEBUG_ROUTE, "HOST(%c) WAS NOT CONNECTED TO %s (%c)!\n", OldLink + 'A', HostP->Mapping[ThisUnit - 1].Name, ThisLink + 'A');
+ }
+ } else if (OldUnit <= MAX_RUP) {
+ if (HostP->Mapping[OldUnit - 1].Topology[OldLink].Unit == ThisUnit && HostP->Mapping[OldUnit - 1].Topology[OldLink].Link == ThisLink) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "SETTING RTA %s (%c) TO DISCONNECTED!\n", HostP->Mapping[OldUnit - 1].Name, OldLink + 'A');
+ HostP->Mapping[OldUnit - 1].Topology[OldLink].Unit = ROUTE_DISCONNECT;
+ HostP->Mapping[OldUnit - 1].Topology[OldLink].Link = NO_LINK;
+ } else {
+ rio_dprintk(RIO_DEBUG_ROUTE, "RTA %s (%c) WAS NOT CONNECTED TO %s (%c)\n", HostP->Mapping[OldUnit - 1].Name, OldLink + 'A', HostP->Mapping[ThisUnit - 1].Name, ThisLink + 'A');
+ }
+ }
+ if (NewUnit == HOST_ID) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "MARKING HOST (%c) CONNECTED TO %s (%c)\n", NewLink + 'A', MyName, ThisLink + 'A');
+ HostP->Topology[NewLink].Unit = ThisUnit;
+ HostP->Topology[NewLink].Link = ThisLink;
+ } else if (NewUnit <= MAX_RUP) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "MARKING RTA %s (%c) CONNECTED TO %s (%c)\n", HostP->Mapping[NewUnit - 1].Name, NewLink + 'A', MyName, ThisLink + 'A');
+ HostP->Mapping[NewUnit - 1].Topology[NewLink].Unit = ThisUnit;
+ HostP->Mapping[NewUnit - 1].Topology[NewLink].Link = ThisLink;
+ }
+ }
+ RIOSetChange(p);
+ RIOCheckIsolated(p, HostP, OldUnit);
+ }
+ }
+ return 1;
+ }
+
+ /*
+ ** The only other command we recognise is a route_request command
+ */
+ if (readb(&PktCmdP->Command) != ROUTE_REQUEST) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "Unknown command %d received on rup %d host %p ROUTE_RUP\n", readb(&PktCmdP->Command), Rup, HostP);
+ return 1;
+ }
+
+ RtaUniq = (readb(&PktCmdP->UniqNum[0])) + (readb(&PktCmdP->UniqNum[1]) << 8) + (readb(&PktCmdP->UniqNum[2]) << 16) + (readb(&PktCmdP->UniqNum[3]) << 24);
+
+ /*
+ ** Determine if 8 or 16 port RTA
+ */
+ RtaType = GetUnitType(RtaUniq);
+
+ rio_dprintk(RIO_DEBUG_ROUTE, "Received a request for an ID for serial number %x\n", RtaUniq);
+
+ Mod = readb(&PktCmdP->ModuleTypes);
+ Mod1 = LONYBLE(Mod);
+ if (RtaType == TYPE_RTA16) {
+ /*
+ ** Only one ident is set for a 16 port RTA. To make compatible
+ ** with 8 port, set 2nd ident in Mod2 to the same as Mod1.
+ */
+ Mod2 = Mod1;
+ rio_dprintk(RIO_DEBUG_ROUTE, "Backplane type is %s (all ports)\n", p->RIOModuleTypes[Mod1].Name);
+ } else {
+ Mod2 = HINYBLE(Mod);
+ rio_dprintk(RIO_DEBUG_ROUTE, "Module types are %s (ports 0-3) and %s (ports 4-7)\n", p->RIOModuleTypes[Mod1].Name, p->RIOModuleTypes[Mod2].Name);
+ }
+
+ /*
+ ** try to unhook a command block from the command free list.
+ */
+ if (!(CmdBlkP = RIOGetCmdBlk())) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "No command blocks to route RTA! come back later.\n");
+ return 0;
+ }
+
+ /*
+ ** Fill in the default info on the command block
+ */
+ CmdBlkP->Packet.dest_unit = Rup;
+ CmdBlkP->Packet.dest_port = ROUTE_RUP;
+ CmdBlkP->Packet.src_unit = HOST_ID;
+ CmdBlkP->Packet.src_port = ROUTE_RUP;
+ CmdBlkP->Packet.len = PKT_CMD_BIT | 1;
+ CmdBlkP->PreFuncP = CmdBlkP->PostFuncP = NULL;
+ PktReplyP = (struct PktCmd_M *) CmdBlkP->Packet.data;
+
+ if (!RIOBootOk(p, HostP, RtaUniq)) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "RTA %x tried to get an ID, but does not belong - FOAD it!\n", RtaUniq);
+ PktReplyP->Command = ROUTE_FOAD;
+ memcpy(PktReplyP->CommandText, "RT_FOAD", 7);
+ RIOQueueCmdBlk(HostP, Rup, CmdBlkP);
+ return 1;
+ }
+
+ /*
+ ** Check to see if the RTA is configured for this host
+ */
+ for (ThisUnit = 0; ThisUnit < MAX_RUP; ThisUnit++) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "Entry %d Flags=%s %s UniqueNum=0x%x\n",
+ ThisUnit, HostP->Mapping[ThisUnit].Flags & SLOT_IN_USE ? "Slot-In-Use" : "Not In Use", HostP->Mapping[ThisUnit].Flags & SLOT_TENTATIVE ? "Slot-Tentative" : "Not Tentative", HostP->Mapping[ThisUnit].RtaUniqueNum);
+
+ /*
+ ** We have an entry for it.
+ */
+ if ((HostP->Mapping[ThisUnit].Flags & (SLOT_IN_USE | SLOT_TENTATIVE)) && (HostP->Mapping[ThisUnit].RtaUniqueNum == RtaUniq)) {
+ if (RtaType == TYPE_RTA16) {
+ ThisUnit2 = HostP->Mapping[ThisUnit].ID2 - 1;
+ rio_dprintk(RIO_DEBUG_ROUTE, "Found unit 0x%x at slots %d+%d\n", RtaUniq, ThisUnit, ThisUnit2);
+ } else
+ rio_dprintk(RIO_DEBUG_ROUTE, "Found unit 0x%x at slot %d\n", RtaUniq, ThisUnit);
+ /*
+ ** If we have no knowledge of booting it, then the host has
+ ** been re-booted, and so we must kill the RTA, so that it
+ ** will be booted again (potentially with new bins)
+ ** and it will then re-ask for an ID, which we will service.
+ */
+ if ((HostP->Mapping[ThisUnit].Flags & SLOT_IN_USE) && !(HostP->Mapping[ThisUnit].Flags & RTA_BOOTED)) {
+ if (!(HostP->Mapping[ThisUnit].Flags & MSG_DONE)) {
+ if (!p->RIONoMessage)
+ printk(KERN_DEBUG "rio: RTA '%s' is being updated.\n", HostP->Mapping[ThisUnit].Name);
+ HostP->Mapping[ThisUnit].Flags |= MSG_DONE;
+ }
+ PktReplyP->Command = ROUTE_FOAD;
+ memcpy(PktReplyP->CommandText, "RT_FOAD", 7);
+ RIOQueueCmdBlk(HostP, Rup, CmdBlkP);
+ return 1;
+ }
+
+ /*
+ ** Send the ID (entry) to this RTA. The ID number is implicit as
+ ** the offset into the table. It is worth noting at this stage
+ ** that offset zero in the table contains the entries for the
+ ** RTA with ID 1!!!!
+ */
+ PktReplyP->Command = ROUTE_ALLOCATE;
+ PktReplyP->IDNum = ThisUnit + 1;
+ if (RtaType == TYPE_RTA16) {
+ if (HostP->Mapping[ThisUnit].Flags & SLOT_IN_USE)
+ /*
+ ** Adjust the phb and tx pkt dest_units for 2nd block of 8
+ ** only if the RTA has ports associated (SLOT_IN_USE)
+ */
+ RIOFixPhbs(p, HostP, ThisUnit2);
+ PktReplyP->IDNum2 = ThisUnit2 + 1;
+ rio_dprintk(RIO_DEBUG_ROUTE, "RTA '%s' has been allocated IDs %d+%d\n", HostP->Mapping[ThisUnit].Name, PktReplyP->IDNum, PktReplyP->IDNum2);
+ } else {
+ PktReplyP->IDNum2 = ROUTE_NO_ID;
+ rio_dprintk(RIO_DEBUG_ROUTE, "RTA '%s' has been allocated ID %d\n", HostP->Mapping[ThisUnit].Name, PktReplyP->IDNum);
+ }
+ memcpy(PktReplyP->CommandText, "RT_ALLOCAT", 10);
+
+ RIOQueueCmdBlk(HostP, Rup, CmdBlkP);
+
+ /*
+ ** If this is a freshly booted RTA, then we need to re-open
+ ** the ports, if any where open, so that data may once more
+ ** flow around the system!
+ */
+ if ((HostP->Mapping[ThisUnit].Flags & RTA_NEWBOOT) && (HostP->Mapping[ThisUnit].SysPort != NO_PORT)) {
+ /*
+ ** look at the ports associated with this beast and
+ ** see if any where open. If they was, then re-open
+ ** them, using the info from the tty flags.
+ */
+ for (port = 0; port < PORTS_PER_RTA; port++) {
+ PortP = p->RIOPortp[port + HostP->Mapping[ThisUnit].SysPort];
+ if (PortP->State & (RIO_MOPEN | RIO_LOPEN)) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "Re-opened this port\n");
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ PortP->MagicFlags |= MAGIC_REBOOT;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ }
+ }
+ if (RtaType == TYPE_RTA16) {
+ for (port = 0; port < PORTS_PER_RTA; port++) {
+ PortP = p->RIOPortp[port + HostP->Mapping[ThisUnit2].SysPort];
+ if (PortP->State & (RIO_MOPEN | RIO_LOPEN)) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "Re-opened this port\n");
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ PortP->MagicFlags |= MAGIC_REBOOT;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ }
+ }
+ }
+ }
+
+ /*
+ ** keep a copy of the module types!
+ */
+ HostP->UnixRups[ThisUnit].ModTypes = Mod;
+ if (RtaType == TYPE_RTA16)
+ HostP->UnixRups[ThisUnit2].ModTypes = Mod;
+
+ /*
+ ** If either of the modules on this unit is read-only or write-only
+ ** or none-xprint, then we need to transfer that info over to the
+ ** relevant ports.
+ */
+ if (HostP->Mapping[ThisUnit].SysPort != NO_PORT) {
+ for (port = 0; port < PORTS_PER_MODULE; port++) {
+ p->RIOPortp[port + HostP->Mapping[ThisUnit].SysPort]->Config &= ~RIO_NOMASK;
+ p->RIOPortp[port + HostP->Mapping[ThisUnit].SysPort]->Config |= p->RIOModuleTypes[Mod1].Flags[port];
+ p->RIOPortp[port + PORTS_PER_MODULE + HostP->Mapping[ThisUnit].SysPort]->Config &= ~RIO_NOMASK;
+ p->RIOPortp[port + PORTS_PER_MODULE + HostP->Mapping[ThisUnit].SysPort]->Config |= p->RIOModuleTypes[Mod2].Flags[port];
+ }
+ if (RtaType == TYPE_RTA16) {
+ for (port = 0; port < PORTS_PER_MODULE; port++) {
+ p->RIOPortp[port + HostP->Mapping[ThisUnit2].SysPort]->Config &= ~RIO_NOMASK;
+ p->RIOPortp[port + HostP->Mapping[ThisUnit2].SysPort]->Config |= p->RIOModuleTypes[Mod1].Flags[port];
+ p->RIOPortp[port + PORTS_PER_MODULE + HostP->Mapping[ThisUnit2].SysPort]->Config &= ~RIO_NOMASK;
+ p->RIOPortp[port + PORTS_PER_MODULE + HostP->Mapping[ThisUnit2].SysPort]->Config |= p->RIOModuleTypes[Mod2].Flags[port];
+ }
+ }
+ }
+
+ /*
+ ** Job done, get on with the interrupts!
+ */
+ return 1;
+ }
+ }
+ /*
+ ** There is no table entry for this RTA at all.
+ **
+ ** Lets check to see if we actually booted this unit - if not,
+ ** then we reset it and it will go round the loop of being booted
+ ** we can then worry about trying to fit it into the table.
+ */
+ for (ThisUnit = 0; ThisUnit < HostP->NumExtraBooted; ThisUnit++)
+ if (HostP->ExtraUnits[ThisUnit] == RtaUniq)
+ break;
+ if (ThisUnit == HostP->NumExtraBooted && ThisUnit != MAX_EXTRA_UNITS) {
+ /*
+ ** if the unit wasn't in the table, and the table wasn't full, then
+ ** we reset the unit, because we didn't boot it.
+ ** However, if the table is full, it could be that we did boot
+ ** this unit, and so we won't reboot it, because it isn't really
+ ** all that disasterous to keep the old bins in most cases. This
+ ** is a rather tacky feature, but we are on the edge of reallity
+ ** here, because the implication is that someone has connected
+ ** 16+MAX_EXTRA_UNITS onto one host.
+ */
+ static int UnknownMesgDone = 0;
+
+ if (!UnknownMesgDone) {
+ if (!p->RIONoMessage)
+ printk(KERN_DEBUG "rio: One or more unknown RTAs are being updated.\n");
+ UnknownMesgDone = 1;
+ }
+
+ PktReplyP->Command = ROUTE_FOAD;
+ memcpy(PktReplyP->CommandText, "RT_FOAD", 7);
+ } else {
+ /*
+ ** we did boot it (as an extra), and there may now be a table
+ ** slot free (because of a delete), so we will try to make
+ ** a tentative entry for it, so that the configurator can see it
+ ** and fill in the details for us.
+ */
+ if (RtaType == TYPE_RTA16) {
+ if (RIOFindFreeID(p, HostP, &ThisUnit, &ThisUnit2) == 0) {
+ RIODefaultName(p, HostP, ThisUnit);
+ rio_fill_host_slot(ThisUnit, ThisUnit2, RtaUniq, HostP);
+ }
+ } else {
+ if (RIOFindFreeID(p, HostP, &ThisUnit, NULL) == 0) {
+ RIODefaultName(p, HostP, ThisUnit);
+ rio_fill_host_slot(ThisUnit, 0, RtaUniq, HostP);
+ }
+ }
+ PktReplyP->Command = ROUTE_USED;
+ memcpy(PktReplyP->CommandText, "RT_USED", 7);
+ }
+ RIOQueueCmdBlk(HostP, Rup, CmdBlkP);
+ return 1;
+}
+
+
+void RIOFixPhbs(struct rio_info *p, struct Host *HostP, unsigned int unit)
+{
+ unsigned short link, port;
+ struct Port *PortP;
+ unsigned long flags;
+ int PortN = HostP->Mapping[unit].SysPort;
+
+ rio_dprintk(RIO_DEBUG_ROUTE, "RIOFixPhbs unit %d sysport %d\n", unit, PortN);
+
+ if (PortN != -1) {
+ unsigned short dest_unit = HostP->Mapping[unit].ID2;
+
+ /*
+ ** Get the link number used for the 1st 8 phbs on this unit.
+ */
+ PortP = p->RIOPortp[HostP->Mapping[dest_unit - 1].SysPort];
+
+ link = readw(&PortP->PhbP->link);
+
+ for (port = 0; port < PORTS_PER_RTA; port++, PortN++) {
+ unsigned short dest_port = port + 8;
+ u16 __iomem *TxPktP;
+ struct PKT __iomem *Pkt;
+
+ PortP = p->RIOPortp[PortN];
+
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ /*
+ ** If RTA is not powered on, the tx packets will be
+ ** unset, so go no further.
+ */
+ if (!PortP->TxStart) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "Tx pkts not set up yet\n");
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ break;
+ }
+
+ /*
+ ** For the second slot of a 16 port RTA, the driver needs to
+ ** sort out the phb to port mappings. The dest_unit for this
+ ** group of 8 phbs is set to the dest_unit of the accompanying
+ ** 8 port block. The dest_port of the second unit is set to
+ ** be in the range 8-15 (i.e. 8 is added). Thus, for a 16 port
+ ** RTA with IDs 5 and 6, traffic bound for port 6 of unit 6
+ ** (being the second map ID) will be sent to dest_unit 5, port
+ ** 14. When this RTA is deleted, dest_unit for ID 6 will be
+ ** restored, and the dest_port will be reduced by 8.
+ ** Transmit packets also have a destination field which needs
+ ** adjusting in the same manner.
+ ** Note that the unit/port bytes in 'dest' are swapped.
+ ** We also need to adjust the phb and rup link numbers for the
+ ** second block of 8 ttys.
+ */
+ for (TxPktP = PortP->TxStart; TxPktP <= PortP->TxEnd; TxPktP++) {
+ /*
+ ** *TxPktP is the pointer to the transmit packet on the host
+ ** card. This needs to be translated into a 32 bit pointer
+ ** so it can be accessed from the driver.
+ */
+ Pkt = (struct PKT __iomem *) RIO_PTR(HostP->Caddr, readw(TxPktP));
+
+ /*
+ ** If the packet is used, reset it.
+ */
+ Pkt = (struct PKT __iomem *) ((unsigned long) Pkt & ~PKT_IN_USE);
+ writeb(dest_unit, &Pkt->dest_unit);
+ writeb(dest_port, &Pkt->dest_port);
+ }
+ rio_dprintk(RIO_DEBUG_ROUTE, "phb dest: Old %x:%x New %x:%x\n", readw(&PortP->PhbP->destination) & 0xff, (readw(&PortP->PhbP->destination) >> 8) & 0xff, dest_unit, dest_port);
+ writew(dest_unit + (dest_port << 8), &PortP->PhbP->destination);
+ writew(link, &PortP->PhbP->link);
+
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ }
+ /*
+ ** Now make sure the range of ports to be serviced includes
+ ** the 2nd 8 on this 16 port RTA.
+ */
+ if (link > 3)
+ return;
+ if (((unit * 8) + 7) > readw(&HostP->LinkStrP[link].last_port)) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "last port on host link %d: %d\n", link, (unit * 8) + 7);
+ writew((unit * 8) + 7, &HostP->LinkStrP[link].last_port);
+ }
+ }
+}
+
+/*
+** Check to see if the new disconnection has isolated this unit.
+** If it has, then invalidate all its link information, and tell
+** the world about it. This is done to ensure that the configurator
+** only gets up-to-date information about what is going on.
+*/
+static int RIOCheckIsolated(struct rio_info *p, struct Host *HostP, unsigned int UnitId)
+{
+ unsigned long flags;
+ rio_spin_lock_irqsave(&HostP->HostLock, flags);
+
+ if (RIOCheck(HostP, UnitId)) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "Unit %d is NOT isolated\n", UnitId);
+ rio_spin_unlock_irqrestore(&HostP->HostLock, flags);
+ return (0);
+ }
+
+ RIOIsolate(p, HostP, UnitId);
+ RIOSetChange(p);
+ rio_spin_unlock_irqrestore(&HostP->HostLock, flags);
+ return 1;
+}
+
+/*
+** Invalidate all the link interconnectivity of this unit, and of
+** all the units attached to it. This will mean that the entire
+** subnet will re-introduce itself.
+*/
+static int RIOIsolate(struct rio_info *p, struct Host *HostP, unsigned int UnitId)
+{
+ unsigned int link, unit;
+
+ UnitId--; /* this trick relies on the Unit Id being UNSIGNED! */
+
+ if (UnitId >= MAX_RUP) /* dontcha just lurv unsigned maths! */
+ return (0);
+
+ if (HostP->Mapping[UnitId].Flags & BEEN_HERE)
+ return (0);
+
+ HostP->Mapping[UnitId].Flags |= BEEN_HERE;
+
+ if (p->RIOPrintDisabled == DO_PRINT)
+ rio_dprintk(RIO_DEBUG_ROUTE, "RIOMesgIsolated %s", HostP->Mapping[UnitId].Name);
+
+ for (link = 0; link < LINKS_PER_UNIT; link++) {
+ unit = HostP->Mapping[UnitId].Topology[link].Unit;
+ HostP->Mapping[UnitId].Topology[link].Unit = ROUTE_DISCONNECT;
+ HostP->Mapping[UnitId].Topology[link].Link = NO_LINK;
+ RIOIsolate(p, HostP, unit);
+ }
+ HostP->Mapping[UnitId].Flags &= ~BEEN_HERE;
+ return 1;
+}
+
+static int RIOCheck(struct Host *HostP, unsigned int UnitId)
+{
+ unsigned char link;
+
+/* rio_dprint(RIO_DEBUG_ROUTE, ("Check to see if unit %d has a route to the host\n",UnitId)); */
+ rio_dprintk(RIO_DEBUG_ROUTE, "RIOCheck : UnitID = %d\n", UnitId);
+
+ if (UnitId == HOST_ID) {
+ /* rio_dprint(RIO_DEBUG_ROUTE, ("Unit %d is NOT isolated - it IS the host!\n", UnitId)); */
+ return 1;
+ }
+
+ UnitId--;
+
+ if (UnitId >= MAX_RUP) {
+ /* rio_dprint(RIO_DEBUG_ROUTE, ("Unit %d - ignored.\n", UnitId)); */
+ return 0;
+ }
+
+ for (link = 0; link < LINKS_PER_UNIT; link++) {
+ if (HostP->Mapping[UnitId].Topology[link].Unit == HOST_ID) {
+ /* rio_dprint(RIO_DEBUG_ROUTE, ("Unit %d is connected directly to host via link (%c).\n",
+ UnitId, 'A'+link)); */
+ return 1;
+ }
+ }
+
+ if (HostP->Mapping[UnitId].Flags & BEEN_HERE) {
+ /* rio_dprint(RIO_DEBUG_ROUTE, ("Been to Unit %d before - ignoring\n", UnitId)); */
+ return 0;
+ }
+
+ HostP->Mapping[UnitId].Flags |= BEEN_HERE;
+
+ for (link = 0; link < LINKS_PER_UNIT; link++) {
+ /* rio_dprint(RIO_DEBUG_ROUTE, ("Unit %d check link (%c)\n", UnitId,'A'+link)); */
+ if (RIOCheck(HostP, HostP->Mapping[UnitId].Topology[link].Unit)) {
+ /* rio_dprint(RIO_DEBUG_ROUTE, ("Unit %d is connected to something that knows the host via link (%c)\n", UnitId,link+'A')); */
+ HostP->Mapping[UnitId].Flags &= ~BEEN_HERE;
+ return 1;
+ }
+ }
+
+ HostP->Mapping[UnitId].Flags &= ~BEEN_HERE;
+
+ /* rio_dprint(RIO_DEBUG_ROUTE, ("Unit %d DOESNT KNOW THE HOST!\n", UnitId)); */
+
+ return 0;
+}
+
+/*
+** Returns the type of unit (host, 16/8 port RTA)
+*/
+
+unsigned int GetUnitType(unsigned int Uniq)
+{
+ switch ((Uniq >> 28) & 0xf) {
+ case RIO_AT:
+ case RIO_MCA:
+ case RIO_EISA:
+ case RIO_PCI:
+ rio_dprintk(RIO_DEBUG_ROUTE, "Unit type: Host\n");
+ return (TYPE_HOST);
+ case RIO_RTA_16:
+ rio_dprintk(RIO_DEBUG_ROUTE, "Unit type: 16 port RTA\n");
+ return (TYPE_RTA16);
+ case RIO_RTA:
+ rio_dprintk(RIO_DEBUG_ROUTE, "Unit type: 8 port RTA\n");
+ return (TYPE_RTA8);
+ default:
+ rio_dprintk(RIO_DEBUG_ROUTE, "Unit type: Unrecognised\n");
+ return (99);
+ }
+}
+
+int RIOSetChange(struct rio_info *p)
+{
+ if (p->RIOQuickCheck != NOT_CHANGED)
+ return (0);
+ p->RIOQuickCheck = CHANGED;
+ if (p->RIOSignalProcess) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "Send SIG-HUP");
+ /*
+ psignal( RIOSignalProcess, SIGHUP );
+ */
+ }
+ return (0);
+}
+
+static void RIOConCon(struct rio_info *p,
+ struct Host *HostP,
+ unsigned int FromId,
+ unsigned int FromLink,
+ unsigned int ToId,
+ unsigned int ToLink,
+ int Change)
+{
+ char *FromName;
+ char *FromType;
+ char *ToName;
+ char *ToType;
+ unsigned int tp;
+
+/*
+** 15.10.1998 ARG - ESIL 0759
+** (Part) fix for port being trashed when opened whilst RTA "disconnected"
+**
+** What's this doing in here anyway ?
+** It was causing the port to be 'unmapped' if opened whilst RTA "disconnected"
+**
+** 09.12.1998 ARG - ESIL 0776 - part fix
+** Okay, We've found out what this was all about now !
+** Someone had botched this to use RIOHalted to indicated the number of RTAs
+** 'disconnected'. The value in RIOHalted was then being used in the
+** 'RIO_QUICK_CHECK' ioctl. A none zero value indicating that a least one RTA
+** is 'disconnected'. The change was put in to satisfy a customer's needs.
+** Having taken this bit of code out 'RIO_QUICK_CHECK' now no longer works for
+** the customer.
+**
+ if (Change == CONNECT) {
+ if (p->RIOHalted) p->RIOHalted --;
+ }
+ else {
+ p->RIOHalted ++;
+ }
+**
+** So - we need to implement it slightly differently - a new member of the
+** rio_info struct - RIORtaDisCons (RIO RTA connections) keeps track of RTA
+** connections and disconnections.
+*/
+ if (Change == CONNECT) {
+ if (p->RIORtaDisCons)
+ p->RIORtaDisCons--;
+ } else {
+ p->RIORtaDisCons++;
+ }
+
+ if (p->RIOPrintDisabled == DONT_PRINT)
+ return;
+
+ if (FromId > ToId) {
+ tp = FromId;
+ FromId = ToId;
+ ToId = tp;
+ tp = FromLink;
+ FromLink = ToLink;
+ ToLink = tp;
+ }
+
+ FromName = FromId ? HostP->Mapping[FromId - 1].Name : HostP->Name;
+ FromType = FromId ? "RTA" : "HOST";
+ ToName = ToId ? HostP->Mapping[ToId - 1].Name : HostP->Name;
+ ToType = ToId ? "RTA" : "HOST";
+
+ rio_dprintk(RIO_DEBUG_ROUTE, "Link between %s '%s' (%c) and %s '%s' (%c) %s.\n", FromType, FromName, 'A' + FromLink, ToType, ToName, 'A' + ToLink, (Change == CONNECT) ? "established" : "disconnected");
+ printk(KERN_DEBUG "rio: Link between %s '%s' (%c) and %s '%s' (%c) %s.\n", FromType, FromName, 'A' + FromLink, ToType, ToName, 'A' + ToLink, (Change == CONNECT) ? "established" : "disconnected");
+}
+
+/*
+** RIORemoveFromSavedTable :
+**
+** Delete and RTA entry from the saved table given to us
+** by the configuration program.
+*/
+static int RIORemoveFromSavedTable(struct rio_info *p, struct Map *pMap)
+{
+ int entry;
+
+ /*
+ ** We loop for all entries even after finding an entry and
+ ** zeroing it because we may have two entries to delete if
+ ** it's a 16 port RTA.
+ */
+ for (entry = 0; entry < TOTAL_MAP_ENTRIES; entry++) {
+ if (p->RIOSavedTable[entry].RtaUniqueNum == pMap->RtaUniqueNum) {
+ memset(&p->RIOSavedTable[entry], 0, sizeof(struct Map));
+ }
+ }
+ return 0;
+}
+
+
+/*
+** RIOCheckDisconnected :
+**
+** Scan the unit links to and return zero if the unit is completely
+** disconnected.
+*/
+static int RIOFreeDisconnected(struct rio_info *p, struct Host *HostP, int unit)
+{
+ int link;
+
+
+ rio_dprintk(RIO_DEBUG_ROUTE, "RIOFreeDisconnect unit %d\n", unit);
+ /*
+ ** If the slot is tentative and does not belong to the
+ ** second half of a 16 port RTA then scan to see if
+ ** is disconnected.
+ */
+ for (link = 0; link < LINKS_PER_UNIT; link++) {
+ if (HostP->Mapping[unit].Topology[link].Unit != ROUTE_DISCONNECT)
+ break;
+ }
+
+ /*
+ ** If not all links are disconnected then we can forget about it.
+ */
+ if (link < LINKS_PER_UNIT)
+ return 1;
+
+#ifdef NEED_TO_FIX_THIS
+ /* Ok so all the links are disconnected. But we may have only just
+ ** made this slot tentative and not yet received a topology update.
+ ** Lets check how long ago we made it tentative.
+ */
+ rio_dprintk(RIO_DEBUG_ROUTE, "Just about to check LBOLT on entry %d\n", unit);
+ if (drv_getparm(LBOLT, (ulong_t *) & current_time))
+ rio_dprintk(RIO_DEBUG_ROUTE, "drv_getparm(LBOLT,....) Failed.\n");
+
+ elapse_time = current_time - TentTime[unit];
+ rio_dprintk(RIO_DEBUG_ROUTE, "elapse %d = current %d - tent %d (%d usec)\n", elapse_time, current_time, TentTime[unit], drv_hztousec(elapse_time));
+ if (drv_hztousec(elapse_time) < WAIT_TO_FINISH) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "Skipping slot %d, not timed out yet %d\n", unit, drv_hztousec(elapse_time));
+ return 1;
+ }
+#endif
+
+ /*
+ ** We have found an usable slot.
+ ** If it is half of a 16 port RTA then delete the other half.
+ */
+ if (HostP->Mapping[unit].ID2 != 0) {
+ int nOther = (HostP->Mapping[unit].ID2) - 1;
+
+ rio_dprintk(RIO_DEBUG_ROUTE, "RioFreedis second slot %d.\n", nOther);
+ memset(&HostP->Mapping[nOther], 0, sizeof(struct Map));
+ }
+ RIORemoveFromSavedTable(p, &HostP->Mapping[unit]);
+
+ return 0;
+}
+
+
+/*
+** RIOFindFreeID :
+**
+** This function scans the given host table for either one
+** or two free unit ID's.
+*/
+
+int RIOFindFreeID(struct rio_info *p, struct Host *HostP, unsigned int * pID1, unsigned int * pID2)
+{
+ int unit, tempID;
+
+ /*
+ ** Initialise the ID's to MAX_RUP.
+ ** We do this to make the loop for setting the ID's as simple as
+ ** possible.
+ */
+ *pID1 = MAX_RUP;
+ if (pID2 != NULL)
+ *pID2 = MAX_RUP;
+
+ /*
+ ** Scan all entries of the host mapping table for free slots.
+ ** We scan for free slots first and then if that is not successful
+ ** we start all over again looking for tentative slots we can re-use.
+ */
+ for (unit = 0; unit < MAX_RUP; unit++) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "Scanning unit %d\n", unit);
+ /*
+ ** If the flags are zero then the slot is empty.
+ */
+ if (HostP->Mapping[unit].Flags == 0) {
+ rio_dprintk(RIO_DEBUG_ROUTE, " This slot is empty.\n");
+ /*
+ ** If we haven't allocated the first ID then do it now.
+ */
+ if (*pID1 == MAX_RUP) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "Make tentative entry for first unit %d\n", unit);
+ *pID1 = unit;
+
+ /*
+ ** If the second ID is not needed then we can return
+ ** now.
+ */
+ if (pID2 == NULL)
+ return 0;
+ } else {
+ /*
+ ** Allocate the second slot and return.
+ */
+ rio_dprintk(RIO_DEBUG_ROUTE, "Make tentative entry for second unit %d\n", unit);
+ *pID2 = unit;
+ return 0;
+ }
+ }
+ }
+
+ /*
+ ** If we manage to come out of the free slot loop then we
+ ** need to start all over again looking for tentative slots
+ ** that we can re-use.
+ */
+ rio_dprintk(RIO_DEBUG_ROUTE, "Starting to scan for tentative slots\n");
+ for (unit = 0; unit < MAX_RUP; unit++) {
+ if (((HostP->Mapping[unit].Flags & SLOT_TENTATIVE) || (HostP->Mapping[unit].Flags == 0)) && !(HostP->Mapping[unit].Flags & RTA16_SECOND_SLOT)) {
+ rio_dprintk(RIO_DEBUG_ROUTE, " Slot %d looks promising.\n", unit);
+
+ if (unit == *pID1) {
+ rio_dprintk(RIO_DEBUG_ROUTE, " No it isn't, its the 1st half\n");
+ continue;
+ }
+
+ /*
+ ** Slot is Tentative or Empty, but not a tentative second
+ ** slot of a 16 porter.
+ ** Attempt to free up this slot (and its parnter if
+ ** it is a 16 port slot. The second slot will become
+ ** empty after a call to RIOFreeDisconnected so thats why
+ ** we look for empty slots above as well).
+ */
+ if (HostP->Mapping[unit].Flags != 0)
+ if (RIOFreeDisconnected(p, HostP, unit) != 0)
+ continue;
+ /*
+ ** If we haven't allocated the first ID then do it now.
+ */
+ if (*pID1 == MAX_RUP) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "Grab tentative entry for first unit %d\n", unit);
+ *pID1 = unit;
+
+ /*
+ ** Clear out this slot now that we intend to use it.
+ */
+ memset(&HostP->Mapping[unit], 0, sizeof(struct Map));
+
+ /*
+ ** If the second ID is not needed then we can return
+ ** now.
+ */
+ if (pID2 == NULL)
+ return 0;
+ } else {
+ /*
+ ** Allocate the second slot and return.
+ */
+ rio_dprintk(RIO_DEBUG_ROUTE, "Grab tentative/empty entry for second unit %d\n", unit);
+ *pID2 = unit;
+
+ /*
+ ** Clear out this slot now that we intend to use it.
+ */
+ memset(&HostP->Mapping[unit], 0, sizeof(struct Map));
+
+ /* At this point under the right(wrong?) conditions
+ ** we may have a first unit ID being higher than the
+ ** second unit ID. This is a bad idea if we are about
+ ** to fill the slots with a 16 port RTA.
+ ** Better check and swap them over.
+ */
+
+ if (*pID1 > *pID2) {
+ rio_dprintk(RIO_DEBUG_ROUTE, "Swapping IDS %d %d\n", *pID1, *pID2);
+ tempID = *pID1;
+ *pID1 = *pID2;
+ *pID2 = tempID;
+ }
+ return 0;
+ }
+ }
+ }
+
+ /*
+ ** If we manage to get to the end of the second loop then we
+ ** can give up and return a failure.
+ */
+ return 1;
+}
+
+
+/*
+** The link switch scenario.
+**
+** Rta Wun (A) is connected to Tuw (A).
+** The tables are all up to date, and the system is OK.
+**
+** If Wun (A) is now moved to Wun (B) before Wun (A) can
+** become disconnected, then the follow happens:
+**
+** Tuw (A) spots the change of unit:link at the other end
+** of its link and Tuw sends a topology packet reflecting
+** the change: Tuw (A) now disconnected from Wun (A), and
+** this is closely followed by a packet indicating that
+** Tuw (A) is now connected to Wun (B).
+**
+** Wun (B) will spot that it has now become connected, and
+** Wun will send a topology packet, which indicates that
+** both Wun (A) and Wun (B) is connected to Tuw (A).
+**
+** Eventually Wun (A) realises that it is now disconnected
+** and Wun will send out a topology packet indicating that
+** Wun (A) is now disconnected.
+*/
diff --git a/drivers/staging/generic_serial/rio/riospace.h b/drivers/staging/generic_serial/rio/riospace.h
new file mode 100644
index 000000000000..ffb31d4332b9
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/riospace.h
@@ -0,0 +1,154 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : riospace.h
+** SID : 1.2
+** Last Modified : 11/6/98 11:34:13
+** Retrieved : 11/6/98 11:34:22
+**
+** ident @(#)riospace.h 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rio_riospace_h__
+#define __rio_riospace_h__
+
+#define RIO_LOCATOR_LEN 16
+#define MAX_RIO_BOARDS 4
+
+/*
+** DONT change this file. At all. Unless you can rebuild the entire
+** device driver, which you probably can't, then the rest of the
+** driver won't see any changes you make here. So don't make any.
+** In particular, it won't be able to see changes to RIO_SLOTS
+*/
+
+struct Conf {
+ char Locator[24];
+ unsigned int StartupTime;
+ unsigned int SlowCook;
+ unsigned int IntrPollTime;
+ unsigned int BreakInterval;
+ unsigned int Timer;
+ unsigned int RtaLoadBase;
+ unsigned int HostLoadBase;
+ unsigned int XpHz;
+ unsigned int XpCps;
+ char *XpOn;
+ char *XpOff;
+ unsigned int MaxXpCps;
+ unsigned int MinXpCps;
+ unsigned int SpinCmds;
+ unsigned int FirstAddr;
+ unsigned int LastAddr;
+ unsigned int BufferSize;
+ unsigned int LowWater;
+ unsigned int LineLength;
+ unsigned int CmdTime;
+};
+
+/*
+** Board types - these MUST correspond to product codes!
+*/
+#define RIO_EMPTY 0x0
+#define RIO_EISA 0x3
+#define RIO_RTA_16 0x9
+#define RIO_AT 0xA
+#define RIO_MCA 0xB
+#define RIO_PCI 0xD
+#define RIO_RTA 0xE
+
+/*
+** Board data structure. This is used for configuration info
+*/
+struct Brd {
+ unsigned char Type; /* RIO_EISA, RIO_MCA, RIO_AT, RIO_EMPTY... */
+ unsigned char Ivec; /* POLLED or ivec number */
+ unsigned char Mode; /* Control stuff, see below */
+};
+
+struct Board {
+ char Locator[RIO_LOCATOR_LEN];
+ int NumSlots;
+ struct Brd Boards[MAX_RIO_BOARDS];
+};
+
+#define BOOT_FROM_LINK 0x00
+#define BOOT_FROM_RAM 0x01
+#define EXTERNAL_BUS_OFF 0x00
+#define EXTERNAL_BUS_ON 0x02
+#define INTERRUPT_DISABLE 0x00
+#define INTERRUPT_ENABLE 0x04
+#define BYTE_OPERATION 0x00
+#define WORD_OPERATION 0x08
+#define POLLED INTERRUPT_DISABLE
+#define IRQ_15 (0x00 | INTERRUPT_ENABLE)
+#define IRQ_12 (0x10 | INTERRUPT_ENABLE)
+#define IRQ_11 (0x20 | INTERRUPT_ENABLE)
+#define IRQ_9 (0x30 | INTERRUPT_ENABLE)
+#define SLOW_LINKS 0x00
+#define FAST_LINKS 0x40
+#define SLOW_AT_BUS 0x00
+#define FAST_AT_BUS 0x80
+#define SLOW_PCI_TP 0x00
+#define FAST_PCI_TP 0x80
+/*
+** Debug levels
+*/
+#define DBG_NONE 0x00000000
+
+#define DBG_INIT 0x00000001
+#define DBG_OPEN 0x00000002
+#define DBG_CLOSE 0x00000004
+#define DBG_IOCTL 0x00000008
+
+#define DBG_READ 0x00000010
+#define DBG_WRITE 0x00000020
+#define DBG_INTR 0x00000040
+#define DBG_PROC 0x00000080
+
+#define DBG_PARAM 0x00000100
+#define DBG_CMD 0x00000200
+#define DBG_XPRINT 0x00000400
+#define DBG_POLL 0x00000800
+
+#define DBG_DAEMON 0x00001000
+#define DBG_FAIL 0x00002000
+#define DBG_MODEM 0x00004000
+#define DBG_LIST 0x00008000
+
+#define DBG_ROUTE 0x00010000
+#define DBG_UTIL 0x00020000
+#define DBG_BOOT 0x00040000
+#define DBG_BUFFER 0x00080000
+
+#define DBG_MON 0x00100000
+#define DBG_SPECIAL 0x00200000
+#define DBG_VPIX 0x00400000
+#define DBG_FLUSH 0x00800000
+
+#define DBG_QENABLE 0x01000000
+
+#define DBG_ALWAYS 0x80000000
+
+#endif /* __rio_riospace_h__ */
diff --git a/drivers/staging/generic_serial/rio/riotable.c b/drivers/staging/generic_serial/rio/riotable.c
new file mode 100644
index 000000000000..3d15802dc0f3
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/riotable.c
@@ -0,0 +1,941 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : riotable.c
+** SID : 1.2
+** Last Modified : 11/6/98 10:33:47
+** Retrieved : 11/6/98 10:33:50
+**
+** ident @(#)riotable.c 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/termios.h>
+#include <linux/serial.h>
+
+#include <linux/generic_serial.h>
+
+
+#include "linux_compat.h"
+#include "rio_linux.h"
+#include "pkt.h"
+#include "daemon.h"
+#include "rio.h"
+#include "riospace.h"
+#include "cmdpkt.h"
+#include "map.h"
+#include "rup.h"
+#include "port.h"
+#include "riodrvr.h"
+#include "rioinfo.h"
+#include "func.h"
+#include "errors.h"
+#include "pci.h"
+
+#include "parmmap.h"
+#include "unixrup.h"
+#include "board.h"
+#include "host.h"
+#include "phb.h"
+#include "link.h"
+#include "cmdblk.h"
+#include "route.h"
+#include "cirrus.h"
+#include "rioioctl.h"
+#include "param.h"
+#include "protsts.h"
+
+/*
+** A configuration table has been loaded. It is now up to us
+** to sort it out and use the information contained therein.
+*/
+int RIONewTable(struct rio_info *p)
+{
+ int Host, Host1, Host2, NameIsUnique, Entry, SubEnt;
+ struct Map *MapP;
+ struct Map *HostMapP;
+ struct Host *HostP;
+
+ char *cptr;
+
+ /*
+ ** We have been sent a new table to install. We need to break
+ ** it down into little bits and spread it around a bit to see
+ ** what we have got.
+ */
+ /*
+ ** Things to check:
+ ** (things marked 'xx' aren't checked any more!)
+ ** (1) That there are no booted Hosts/RTAs out there.
+ ** (2) That the names are properly formed
+ ** (3) That blank entries really are.
+ ** xx (4) That hosts mentioned in the table actually exist. xx
+ ** (5) That the IDs are unique (per host).
+ ** (6) That host IDs are zero
+ ** (7) That port numbers are valid
+ ** (8) That port numbers aren't duplicated
+ ** (9) That names aren't duplicated
+ ** xx (10) That hosts that actually exist are mentioned in the table. xx
+ */
+ rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(1)\n");
+ if (p->RIOSystemUp) { /* (1) */
+ p->RIOError.Error = HOST_HAS_ALREADY_BEEN_BOOTED;
+ return -EBUSY;
+ }
+
+ p->RIOError.Error = NOTHING_WRONG_AT_ALL;
+ p->RIOError.Entry = -1;
+ p->RIOError.Other = -1;
+
+ for (Entry = 0; Entry < TOTAL_MAP_ENTRIES; Entry++) {
+ MapP = &p->RIOConnectTable[Entry];
+ if ((MapP->Flags & RTA16_SECOND_SLOT) == 0) {
+ rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(2)\n");
+ cptr = MapP->Name; /* (2) */
+ cptr[MAX_NAME_LEN - 1] = '\0';
+ if (cptr[0] == '\0') {
+ memcpy(MapP->Name, MapP->RtaUniqueNum ? "RTA NN" : "HOST NN", 8);
+ MapP->Name[5] = '0' + Entry / 10;
+ MapP->Name[6] = '0' + Entry % 10;
+ }
+ while (*cptr) {
+ if (*cptr < ' ' || *cptr > '~') {
+ p->RIOError.Error = BAD_CHARACTER_IN_NAME;
+ p->RIOError.Entry = Entry;
+ return -ENXIO;
+ }
+ cptr++;
+ }
+ }
+
+ /*
+ ** If the entry saved was a tentative entry then just forget
+ ** about it.
+ */
+ if (MapP->Flags & SLOT_TENTATIVE) {
+ MapP->HostUniqueNum = 0;
+ MapP->RtaUniqueNum = 0;
+ continue;
+ }
+
+ rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(3)\n");
+ if (!MapP->RtaUniqueNum && !MapP->HostUniqueNum) { /* (3) */
+ if (MapP->ID || MapP->SysPort || MapP->Flags) {
+ rio_dprintk(RIO_DEBUG_TABLE, "%s pretending to be empty but isn't\n", MapP->Name);
+ p->RIOError.Error = TABLE_ENTRY_ISNT_PROPERLY_NULL;
+ p->RIOError.Entry = Entry;
+ return -ENXIO;
+ }
+ rio_dprintk(RIO_DEBUG_TABLE, "!RIO: Daemon: test (3) passes\n");
+ continue;
+ }
+
+ rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(4)\n");
+ for (Host = 0; Host < p->RIONumHosts; Host++) { /* (4) */
+ if (p->RIOHosts[Host].UniqueNum == MapP->HostUniqueNum) {
+ HostP = &p->RIOHosts[Host];
+ /*
+ ** having done the lookup, we don't really want to do
+ ** it again, so hang the host number in a safe place
+ */
+ MapP->Topology[0].Unit = Host;
+ break;
+ }
+ }
+
+ if (Host >= p->RIONumHosts) {
+ rio_dprintk(RIO_DEBUG_TABLE, "RTA %s has unknown host unique number 0x%x\n", MapP->Name, MapP->HostUniqueNum);
+ MapP->HostUniqueNum = 0;
+ /* MapP->RtaUniqueNum = 0; */
+ /* MapP->ID = 0; */
+ /* MapP->Flags = 0; */
+ /* MapP->SysPort = 0; */
+ /* MapP->Name[0] = 0; */
+ continue;
+ }
+
+ rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(5)\n");
+ if (MapP->RtaUniqueNum) { /* (5) */
+ if (!MapP->ID) {
+ rio_dprintk(RIO_DEBUG_TABLE, "RIO: RTA %s has been allocated an ID of zero!\n", MapP->Name);
+ p->RIOError.Error = ZERO_RTA_ID;
+ p->RIOError.Entry = Entry;
+ return -ENXIO;
+ }
+ if (MapP->ID > MAX_RUP) {
+ rio_dprintk(RIO_DEBUG_TABLE, "RIO: RTA %s has been allocated an invalid ID %d\n", MapP->Name, MapP->ID);
+ p->RIOError.Error = ID_NUMBER_OUT_OF_RANGE;
+ p->RIOError.Entry = Entry;
+ return -ENXIO;
+ }
+ for (SubEnt = 0; SubEnt < Entry; SubEnt++) {
+ if (MapP->HostUniqueNum == p->RIOConnectTable[SubEnt].HostUniqueNum && MapP->ID == p->RIOConnectTable[SubEnt].ID) {
+ rio_dprintk(RIO_DEBUG_TABLE, "Dupl. ID number allocated to RTA %s and RTA %s\n", MapP->Name, p->RIOConnectTable[SubEnt].Name);
+ p->RIOError.Error = DUPLICATED_RTA_ID;
+ p->RIOError.Entry = Entry;
+ p->RIOError.Other = SubEnt;
+ return -ENXIO;
+ }
+ /*
+ ** If the RtaUniqueNum is the same, it may be looking at both
+ ** entries for a 16 port RTA, so check the ids
+ */
+ if ((MapP->RtaUniqueNum == p->RIOConnectTable[SubEnt].RtaUniqueNum)
+ && (MapP->ID2 != p->RIOConnectTable[SubEnt].ID)) {
+ rio_dprintk(RIO_DEBUG_TABLE, "RTA %s has duplicate unique number\n", MapP->Name);
+ rio_dprintk(RIO_DEBUG_TABLE, "RTA %s has duplicate unique number\n", p->RIOConnectTable[SubEnt].Name);
+ p->RIOError.Error = DUPLICATE_UNIQUE_NUMBER;
+ p->RIOError.Entry = Entry;
+ p->RIOError.Other = SubEnt;
+ return -ENXIO;
+ }
+ }
+ rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(7a)\n");
+ /* (7a) */
+ if ((MapP->SysPort != NO_PORT) && (MapP->SysPort % PORTS_PER_RTA)) {
+ rio_dprintk(RIO_DEBUG_TABLE, "TTY Port number %d-RTA %s is not a multiple of %d!\n", (int) MapP->SysPort, MapP->Name, PORTS_PER_RTA);
+ p->RIOError.Error = TTY_NUMBER_OUT_OF_RANGE;
+ p->RIOError.Entry = Entry;
+ return -ENXIO;
+ }
+ rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(7b)\n");
+ /* (7b) */
+ if ((MapP->SysPort != NO_PORT) && (MapP->SysPort >= RIO_PORTS)) {
+ rio_dprintk(RIO_DEBUG_TABLE, "TTY Port number %d for RTA %s is too big\n", (int) MapP->SysPort, MapP->Name);
+ p->RIOError.Error = TTY_NUMBER_OUT_OF_RANGE;
+ p->RIOError.Entry = Entry;
+ return -ENXIO;
+ }
+ for (SubEnt = 0; SubEnt < Entry; SubEnt++) {
+ if (p->RIOConnectTable[SubEnt].Flags & RTA16_SECOND_SLOT)
+ continue;
+ if (p->RIOConnectTable[SubEnt].RtaUniqueNum) {
+ rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(8)\n");
+ /* (8) */
+ if ((MapP->SysPort != NO_PORT) && (MapP->SysPort == p->RIOConnectTable[SubEnt].SysPort)) {
+ rio_dprintk(RIO_DEBUG_TABLE, "RTA %s:same TTY port # as RTA %s (%d)\n", MapP->Name, p->RIOConnectTable[SubEnt].Name, (int) MapP->SysPort);
+ p->RIOError.Error = TTY_NUMBER_IN_USE;
+ p->RIOError.Entry = Entry;
+ p->RIOError.Other = SubEnt;
+ return -ENXIO;
+ }
+ rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(9)\n");
+ if (strcmp(MapP->Name, p->RIOConnectTable[SubEnt].Name) == 0 && !(MapP->Flags & RTA16_SECOND_SLOT)) { /* (9) */
+ rio_dprintk(RIO_DEBUG_TABLE, "RTA name %s used twice\n", MapP->Name);
+ p->RIOError.Error = NAME_USED_TWICE;
+ p->RIOError.Entry = Entry;
+ p->RIOError.Other = SubEnt;
+ return -ENXIO;
+ }
+ }
+ }
+ } else { /* (6) */
+ rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: entering(6)\n");
+ if (MapP->ID) {
+ rio_dprintk(RIO_DEBUG_TABLE, "RIO:HOST %s has been allocated ID that isn't zero!\n", MapP->Name);
+ p->RIOError.Error = HOST_ID_NOT_ZERO;
+ p->RIOError.Entry = Entry;
+ return -ENXIO;
+ }
+ if (MapP->SysPort != NO_PORT) {
+ rio_dprintk(RIO_DEBUG_TABLE, "RIO: HOST %s has been allocated port numbers!\n", MapP->Name);
+ p->RIOError.Error = HOST_SYSPORT_BAD;
+ p->RIOError.Entry = Entry;
+ return -ENXIO;
+ }
+ }
+ }
+
+ /*
+ ** wow! if we get here then it's a goody!
+ */
+
+ /*
+ ** Zero the (old) entries for each host...
+ */
+ for (Host = 0; Host < RIO_HOSTS; Host++) {
+ for (Entry = 0; Entry < MAX_RUP; Entry++) {
+ memset(&p->RIOHosts[Host].Mapping[Entry], 0, sizeof(struct Map));
+ }
+ memset(&p->RIOHosts[Host].Name[0], 0, sizeof(p->RIOHosts[Host].Name));
+ }
+
+ /*
+ ** Copy in the new table entries
+ */
+ for (Entry = 0; Entry < TOTAL_MAP_ENTRIES; Entry++) {
+ rio_dprintk(RIO_DEBUG_TABLE, "RIONewTable: Copy table for Host entry %d\n", Entry);
+ MapP = &p->RIOConnectTable[Entry];
+
+ /*
+ ** Now, if it is an empty slot ignore it!
+ */
+ if (MapP->HostUniqueNum == 0)
+ continue;
+
+ /*
+ ** we saved the host number earlier, so grab it back
+ */
+ HostP = &p->RIOHosts[MapP->Topology[0].Unit];
+
+ /*
+ ** If it is a host, then we only need to fill in the name field.
+ */
+ if (MapP->ID == 0) {
+ rio_dprintk(RIO_DEBUG_TABLE, "Host entry found. Name %s\n", MapP->Name);
+ memcpy(HostP->Name, MapP->Name, MAX_NAME_LEN);
+ continue;
+ }
+
+ /*
+ ** Its an RTA entry, so fill in the host mapping entries for it
+ ** and the port mapping entries. Notice that entry zero is for
+ ** ID one.
+ */
+ HostMapP = &HostP->Mapping[MapP->ID - 1];
+
+ if (MapP->Flags & SLOT_IN_USE) {
+ rio_dprintk(RIO_DEBUG_TABLE, "Rta entry found. Name %s\n", MapP->Name);
+ /*
+ ** structure assign, then sort out the bits we shouldn't have done
+ */
+ *HostMapP = *MapP;
+
+ HostMapP->Flags = SLOT_IN_USE;
+ if (MapP->Flags & RTA16_SECOND_SLOT)
+ HostMapP->Flags |= RTA16_SECOND_SLOT;
+
+ RIOReMapPorts(p, HostP, HostMapP);
+ } else {
+ rio_dprintk(RIO_DEBUG_TABLE, "TENTATIVE Rta entry found. Name %s\n", MapP->Name);
+ }
+ }
+
+ for (Entry = 0; Entry < TOTAL_MAP_ENTRIES; Entry++) {
+ p->RIOSavedTable[Entry] = p->RIOConnectTable[Entry];
+ }
+
+ for (Host = 0; Host < p->RIONumHosts; Host++) {
+ for (SubEnt = 0; SubEnt < LINKS_PER_UNIT; SubEnt++) {
+ p->RIOHosts[Host].Topology[SubEnt].Unit = ROUTE_DISCONNECT;
+ p->RIOHosts[Host].Topology[SubEnt].Link = NO_LINK;
+ }
+ for (Entry = 0; Entry < MAX_RUP; Entry++) {
+ for (SubEnt = 0; SubEnt < LINKS_PER_UNIT; SubEnt++) {
+ p->RIOHosts[Host].Mapping[Entry].Topology[SubEnt].Unit = ROUTE_DISCONNECT;
+ p->RIOHosts[Host].Mapping[Entry].Topology[SubEnt].Link = NO_LINK;
+ }
+ }
+ if (!p->RIOHosts[Host].Name[0]) {
+ memcpy(p->RIOHosts[Host].Name, "HOST 1", 7);
+ p->RIOHosts[Host].Name[5] += Host;
+ }
+ /*
+ ** Check that default name assigned is unique.
+ */
+ Host1 = Host;
+ NameIsUnique = 0;
+ while (!NameIsUnique) {
+ NameIsUnique = 1;
+ for (Host2 = 0; Host2 < p->RIONumHosts; Host2++) {
+ if (Host2 == Host)
+ continue;
+ if (strcmp(p->RIOHosts[Host].Name, p->RIOHosts[Host2].Name)
+ == 0) {
+ NameIsUnique = 0;
+ Host1++;
+ if (Host1 >= p->RIONumHosts)
+ Host1 = 0;
+ p->RIOHosts[Host].Name[5] = '1' + Host1;
+ }
+ }
+ }
+ /*
+ ** Rename host if name already used.
+ */
+ if (Host1 != Host) {
+ rio_dprintk(RIO_DEBUG_TABLE, "Default name %s already used\n", p->RIOHosts[Host].Name);
+ memcpy(p->RIOHosts[Host].Name, "HOST 1", 7);
+ p->RIOHosts[Host].Name[5] += Host1;
+ }
+ rio_dprintk(RIO_DEBUG_TABLE, "Assigning default name %s\n", p->RIOHosts[Host].Name);
+ }
+ return 0;
+}
+
+/*
+** User process needs the config table - build it from first
+** principles.
+**
+* FIXME: SMP locking
+*/
+int RIOApel(struct rio_info *p)
+{
+ int Host;
+ int link;
+ int Rup;
+ int Next = 0;
+ struct Map *MapP;
+ struct Host *HostP;
+ unsigned long flags;
+
+ rio_dprintk(RIO_DEBUG_TABLE, "Generating a table to return to config.rio\n");
+
+ memset(&p->RIOConnectTable[0], 0, sizeof(struct Map) * TOTAL_MAP_ENTRIES);
+
+ for (Host = 0; Host < RIO_HOSTS; Host++) {
+ rio_dprintk(RIO_DEBUG_TABLE, "Processing host %d\n", Host);
+ HostP = &p->RIOHosts[Host];
+ rio_spin_lock_irqsave(&HostP->HostLock, flags);
+
+ MapP = &p->RIOConnectTable[Next++];
+ MapP->HostUniqueNum = HostP->UniqueNum;
+ if ((HostP->Flags & RUN_STATE) != RC_RUNNING) {
+ rio_spin_unlock_irqrestore(&HostP->HostLock, flags);
+ continue;
+ }
+ MapP->RtaUniqueNum = 0;
+ MapP->ID = 0;
+ MapP->Flags = SLOT_IN_USE;
+ MapP->SysPort = NO_PORT;
+ for (link = 0; link < LINKS_PER_UNIT; link++)
+ MapP->Topology[link] = HostP->Topology[link];
+ memcpy(MapP->Name, HostP->Name, MAX_NAME_LEN);
+ for (Rup = 0; Rup < MAX_RUP; Rup++) {
+ if (HostP->Mapping[Rup].Flags & (SLOT_IN_USE | SLOT_TENTATIVE)) {
+ p->RIOConnectTable[Next] = HostP->Mapping[Rup];
+ if (HostP->Mapping[Rup].Flags & SLOT_IN_USE)
+ p->RIOConnectTable[Next].Flags |= SLOT_IN_USE;
+ if (HostP->Mapping[Rup].Flags & SLOT_TENTATIVE)
+ p->RIOConnectTable[Next].Flags |= SLOT_TENTATIVE;
+ if (HostP->Mapping[Rup].Flags & RTA16_SECOND_SLOT)
+ p->RIOConnectTable[Next].Flags |= RTA16_SECOND_SLOT;
+ Next++;
+ }
+ }
+ rio_spin_unlock_irqrestore(&HostP->HostLock, flags);
+ }
+ return 0;
+}
+
+/*
+** config.rio has taken a dislike to one of the gross maps entries.
+** if the entry is suitably inactive, then we can gob on it and remove
+** it from the table.
+*/
+int RIODeleteRta(struct rio_info *p, struct Map *MapP)
+{
+ int host, entry, port, link;
+ int SysPort;
+ struct Host *HostP;
+ struct Map *HostMapP;
+ struct Port *PortP;
+ int work_done = 0;
+ unsigned long lock_flags, sem_flags;
+
+ rio_dprintk(RIO_DEBUG_TABLE, "Delete entry on host %x, rta %x\n", MapP->HostUniqueNum, MapP->RtaUniqueNum);
+
+ for (host = 0; host < p->RIONumHosts; host++) {
+ HostP = &p->RIOHosts[host];
+
+ rio_spin_lock_irqsave(&HostP->HostLock, lock_flags);
+
+ if ((HostP->Flags & RUN_STATE) != RC_RUNNING) {
+ rio_spin_unlock_irqrestore(&HostP->HostLock, lock_flags);
+ continue;
+ }
+
+ for (entry = 0; entry < MAX_RUP; entry++) {
+ if (MapP->RtaUniqueNum == HostP->Mapping[entry].RtaUniqueNum) {
+ HostMapP = &HostP->Mapping[entry];
+ rio_dprintk(RIO_DEBUG_TABLE, "Found entry offset %d on host %s\n", entry, HostP->Name);
+
+ /*
+ ** Check all four links of the unit are disconnected
+ */
+ for (link = 0; link < LINKS_PER_UNIT; link++) {
+ if (HostMapP->Topology[link].Unit != ROUTE_DISCONNECT) {
+ rio_dprintk(RIO_DEBUG_TABLE, "Entry is in use and cannot be deleted!\n");
+ p->RIOError.Error = UNIT_IS_IN_USE;
+ rio_spin_unlock_irqrestore(&HostP->HostLock, lock_flags);
+ return -EBUSY;
+ }
+ }
+ /*
+ ** Slot has been allocated, BUT not booted/routed/
+ ** connected/selected or anything else-ed
+ */
+ SysPort = HostMapP->SysPort;
+
+ if (SysPort != NO_PORT) {
+ for (port = SysPort; port < SysPort + PORTS_PER_RTA; port++) {
+ PortP = p->RIOPortp[port];
+ rio_dprintk(RIO_DEBUG_TABLE, "Unmap port\n");
+
+ rio_spin_lock_irqsave(&PortP->portSem, sem_flags);
+
+ PortP->Mapped = 0;
+
+ if (PortP->State & (RIO_MOPEN | RIO_LOPEN)) {
+
+ rio_dprintk(RIO_DEBUG_TABLE, "Gob on port\n");
+ PortP->TxBufferIn = PortP->TxBufferOut = 0;
+ /* What should I do
+ wakeup( &PortP->TxBufferIn );
+ wakeup( &PortP->TxBufferOut);
+ */
+ PortP->InUse = NOT_INUSE;
+ /* What should I do
+ wakeup( &PortP->InUse );
+ signal(PortP->TtyP->t_pgrp,SIGKILL);
+ ttyflush(PortP->TtyP,(FREAD|FWRITE));
+ */
+ PortP->State |= RIO_CLOSING | RIO_DELETED;
+ }
+
+ /*
+ ** For the second slot of a 16 port RTA, the
+ ** driver needs to reset the changes made to
+ ** the phb to port mappings in RIORouteRup.
+ */
+ if (PortP->SecondBlock) {
+ u16 dest_unit = HostMapP->ID;
+ u16 dest_port = port - SysPort;
+ u16 __iomem *TxPktP;
+ struct PKT __iomem *Pkt;
+
+ for (TxPktP = PortP->TxStart; TxPktP <= PortP->TxEnd; TxPktP++) {
+ /*
+ ** *TxPktP is the pointer to the
+ ** transmit packet on the host card.
+ ** This needs to be translated into
+ ** a 32 bit pointer so it can be
+ ** accessed from the driver.
+ */
+ Pkt = (struct PKT __iomem *) RIO_PTR(HostP->Caddr, readw(&*TxPktP));
+ rio_dprintk(RIO_DEBUG_TABLE, "Tx packet (%x) destination: Old %x:%x New %x:%x\n", readw(TxPktP), readb(&Pkt->dest_unit), readb(&Pkt->dest_port), dest_unit, dest_port);
+ writew(dest_unit, &Pkt->dest_unit);
+ writew(dest_port, &Pkt->dest_port);
+ }
+ rio_dprintk(RIO_DEBUG_TABLE, "Port %d phb destination: Old %x:%x New %x:%x\n", port, readb(&PortP->PhbP->destination) & 0xff, (readb(&PortP->PhbP->destination) >> 8) & 0xff, dest_unit, dest_port);
+ writew(dest_unit + (dest_port << 8), &PortP->PhbP->destination);
+ }
+ rio_spin_unlock_irqrestore(&PortP->portSem, sem_flags);
+ }
+ }
+ rio_dprintk(RIO_DEBUG_TABLE, "Entry nulled.\n");
+ memset(HostMapP, 0, sizeof(struct Map));
+ work_done++;
+ }
+ }
+ rio_spin_unlock_irqrestore(&HostP->HostLock, lock_flags);
+ }
+
+ /* XXXXX lock me up */
+ for (entry = 0; entry < TOTAL_MAP_ENTRIES; entry++) {
+ if (p->RIOSavedTable[entry].RtaUniqueNum == MapP->RtaUniqueNum) {
+ memset(&p->RIOSavedTable[entry], 0, sizeof(struct Map));
+ work_done++;
+ }
+ if (p->RIOConnectTable[entry].RtaUniqueNum == MapP->RtaUniqueNum) {
+ memset(&p->RIOConnectTable[entry], 0, sizeof(struct Map));
+ work_done++;
+ }
+ }
+ if (work_done)
+ return 0;
+
+ rio_dprintk(RIO_DEBUG_TABLE, "Couldn't find entry to be deleted\n");
+ p->RIOError.Error = COULDNT_FIND_ENTRY;
+ return -ENXIO;
+}
+
+int RIOAssignRta(struct rio_info *p, struct Map *MapP)
+{
+ int host;
+ struct Map *HostMapP;
+ char *sptr;
+ int link;
+
+
+ rio_dprintk(RIO_DEBUG_TABLE, "Assign entry on host %x, rta %x, ID %d, Sysport %d\n", MapP->HostUniqueNum, MapP->RtaUniqueNum, MapP->ID, (int) MapP->SysPort);
+
+ if ((MapP->ID != (u16) - 1) && ((int) MapP->ID < (int) 1 || (int) MapP->ID > MAX_RUP)) {
+ rio_dprintk(RIO_DEBUG_TABLE, "Bad ID in map entry!\n");
+ p->RIOError.Error = ID_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+ if (MapP->RtaUniqueNum == 0) {
+ rio_dprintk(RIO_DEBUG_TABLE, "Rta Unique number zero!\n");
+ p->RIOError.Error = RTA_UNIQUE_NUMBER_ZERO;
+ return -EINVAL;
+ }
+ if ((MapP->SysPort != NO_PORT) && (MapP->SysPort % PORTS_PER_RTA)) {
+ rio_dprintk(RIO_DEBUG_TABLE, "Port %d not multiple of %d!\n", (int) MapP->SysPort, PORTS_PER_RTA);
+ p->RIOError.Error = TTY_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+ if ((MapP->SysPort != NO_PORT) && (MapP->SysPort >= RIO_PORTS)) {
+ rio_dprintk(RIO_DEBUG_TABLE, "Port %d not valid!\n", (int) MapP->SysPort);
+ p->RIOError.Error = TTY_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ /*
+ ** Copy the name across to the map entry.
+ */
+ MapP->Name[MAX_NAME_LEN - 1] = '\0';
+ sptr = MapP->Name;
+ while (*sptr) {
+ if (*sptr < ' ' || *sptr > '~') {
+ rio_dprintk(RIO_DEBUG_TABLE, "Name entry contains non-printing characters!\n");
+ p->RIOError.Error = BAD_CHARACTER_IN_NAME;
+ return -EINVAL;
+ }
+ sptr++;
+ }
+
+ for (host = 0; host < p->RIONumHosts; host++) {
+ if (MapP->HostUniqueNum == p->RIOHosts[host].UniqueNum) {
+ if ((p->RIOHosts[host].Flags & RUN_STATE) != RC_RUNNING) {
+ p->RIOError.Error = HOST_NOT_RUNNING;
+ return -ENXIO;
+ }
+
+ /*
+ ** Now we have a host we need to allocate an ID
+ ** if the entry does not already have one.
+ */
+ if (MapP->ID == (u16) - 1) {
+ int nNewID;
+
+ rio_dprintk(RIO_DEBUG_TABLE, "Attempting to get a new ID for rta \"%s\"\n", MapP->Name);
+ /*
+ ** The idea here is to allow RTA's to be assigned
+ ** before they actually appear on the network.
+ ** This allows the addition of RTA's without having
+ ** to plug them in.
+ ** What we do is:
+ ** - Find a free ID and allocate it to the RTA.
+ ** - If this map entry is the second half of a
+ ** 16 port entry then find the other half and
+ ** make sure the 2 cross reference each other.
+ */
+ if (RIOFindFreeID(p, &p->RIOHosts[host], &nNewID, NULL) != 0) {
+ p->RIOError.Error = COULDNT_FIND_ENTRY;
+ return -EBUSY;
+ }
+ MapP->ID = (u16) nNewID + 1;
+ rio_dprintk(RIO_DEBUG_TABLE, "Allocated ID %d for this new RTA.\n", MapP->ID);
+ HostMapP = &p->RIOHosts[host].Mapping[nNewID];
+ HostMapP->RtaUniqueNum = MapP->RtaUniqueNum;
+ HostMapP->HostUniqueNum = MapP->HostUniqueNum;
+ HostMapP->ID = MapP->ID;
+ for (link = 0; link < LINKS_PER_UNIT; link++) {
+ HostMapP->Topology[link].Unit = ROUTE_DISCONNECT;
+ HostMapP->Topology[link].Link = NO_LINK;
+ }
+ if (MapP->Flags & RTA16_SECOND_SLOT) {
+ int unit;
+
+ for (unit = 0; unit < MAX_RUP; unit++)
+ if (p->RIOHosts[host].Mapping[unit].RtaUniqueNum == MapP->RtaUniqueNum)
+ break;
+ if (unit == MAX_RUP) {
+ p->RIOError.Error = COULDNT_FIND_ENTRY;
+ return -EBUSY;
+ }
+ HostMapP->Flags |= RTA16_SECOND_SLOT;
+ HostMapP->ID2 = MapP->ID2 = p->RIOHosts[host].Mapping[unit].ID;
+ p->RIOHosts[host].Mapping[unit].ID2 = MapP->ID;
+ rio_dprintk(RIO_DEBUG_TABLE, "Cross referenced id %d to ID %d.\n", MapP->ID, p->RIOHosts[host].Mapping[unit].ID);
+ }
+ }
+
+ HostMapP = &p->RIOHosts[host].Mapping[MapP->ID - 1];
+
+ if (HostMapP->Flags & SLOT_IN_USE) {
+ rio_dprintk(RIO_DEBUG_TABLE, "Map table slot for ID %d is already in use.\n", MapP->ID);
+ p->RIOError.Error = ID_ALREADY_IN_USE;
+ return -EBUSY;
+ }
+
+ /*
+ ** Assign the sys ports and the name, and mark the slot as
+ ** being in use.
+ */
+ HostMapP->SysPort = MapP->SysPort;
+ if ((MapP->Flags & RTA16_SECOND_SLOT) == 0)
+ memcpy(HostMapP->Name, MapP->Name, MAX_NAME_LEN);
+ HostMapP->Flags = SLOT_IN_USE | RTA_BOOTED;
+#ifdef NEED_TO_FIX
+ RIO_SV_BROADCAST(p->RIOHosts[host].svFlags[MapP->ID - 1]);
+#endif
+ if (MapP->Flags & RTA16_SECOND_SLOT)
+ HostMapP->Flags |= RTA16_SECOND_SLOT;
+
+ RIOReMapPorts(p, &p->RIOHosts[host], HostMapP);
+ /*
+ ** Adjust 2nd block of 8 phbs
+ */
+ if (MapP->Flags & RTA16_SECOND_SLOT)
+ RIOFixPhbs(p, &p->RIOHosts[host], HostMapP->ID - 1);
+
+ if (HostMapP->SysPort != NO_PORT) {
+ if (HostMapP->SysPort < p->RIOFirstPortsBooted)
+ p->RIOFirstPortsBooted = HostMapP->SysPort;
+ if (HostMapP->SysPort > p->RIOLastPortsBooted)
+ p->RIOLastPortsBooted = HostMapP->SysPort;
+ }
+ if (MapP->Flags & RTA16_SECOND_SLOT)
+ rio_dprintk(RIO_DEBUG_TABLE, "Second map of RTA %s added to configuration\n", p->RIOHosts[host].Mapping[MapP->ID2 - 1].Name);
+ else
+ rio_dprintk(RIO_DEBUG_TABLE, "RTA %s added to configuration\n", MapP->Name);
+ return 0;
+ }
+ }
+ p->RIOError.Error = UNKNOWN_HOST_NUMBER;
+ rio_dprintk(RIO_DEBUG_TABLE, "Unknown host %x\n", MapP->HostUniqueNum);
+ return -ENXIO;
+}
+
+
+int RIOReMapPorts(struct rio_info *p, struct Host *HostP, struct Map *HostMapP)
+{
+ struct Port *PortP;
+ unsigned int SubEnt;
+ unsigned int HostPort;
+ unsigned int SysPort;
+ u16 RtaType;
+ unsigned long flags;
+
+ rio_dprintk(RIO_DEBUG_TABLE, "Mapping sysport %d to id %d\n", (int) HostMapP->SysPort, HostMapP->ID);
+
+ /*
+ ** We need to tell the UnixRups which sysport the rup corresponds to
+ */
+ HostP->UnixRups[HostMapP->ID - 1].BaseSysPort = HostMapP->SysPort;
+
+ if (HostMapP->SysPort == NO_PORT)
+ return (0);
+
+ RtaType = GetUnitType(HostMapP->RtaUniqueNum);
+ rio_dprintk(RIO_DEBUG_TABLE, "Mapping sysport %d-%d\n", (int) HostMapP->SysPort, (int) HostMapP->SysPort + PORTS_PER_RTA - 1);
+
+ /*
+ ** now map each of its eight ports
+ */
+ for (SubEnt = 0; SubEnt < PORTS_PER_RTA; SubEnt++) {
+ rio_dprintk(RIO_DEBUG_TABLE, "subent = %d, HostMapP->SysPort = %d\n", SubEnt, (int) HostMapP->SysPort);
+ SysPort = HostMapP->SysPort + SubEnt; /* portnumber within system */
+ /* portnumber on host */
+
+ HostPort = (HostMapP->ID - 1) * PORTS_PER_RTA + SubEnt;
+
+ rio_dprintk(RIO_DEBUG_TABLE, "c1 p = %p, p->rioPortp = %p\n", p, p->RIOPortp);
+ PortP = p->RIOPortp[SysPort];
+ rio_dprintk(RIO_DEBUG_TABLE, "Map port\n");
+
+ /*
+ ** Point at all the real neat data structures
+ */
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ PortP->HostP = HostP;
+ PortP->Caddr = HostP->Caddr;
+
+ /*
+ ** The PhbP cannot be filled in yet
+ ** unless the host has been booted
+ */
+ if ((HostP->Flags & RUN_STATE) == RC_RUNNING) {
+ struct PHB __iomem *PhbP = PortP->PhbP = &HostP->PhbP[HostPort];
+ PortP->TxAdd = (u16 __iomem *) RIO_PTR(HostP->Caddr, readw(&PhbP->tx_add));
+ PortP->TxStart = (u16 __iomem *) RIO_PTR(HostP->Caddr, readw(&PhbP->tx_start));
+ PortP->TxEnd = (u16 __iomem *) RIO_PTR(HostP->Caddr, readw(&PhbP->tx_end));
+ PortP->RxRemove = (u16 __iomem *) RIO_PTR(HostP->Caddr, readw(&PhbP->rx_remove));
+ PortP->RxStart = (u16 __iomem *) RIO_PTR(HostP->Caddr, readw(&PhbP->rx_start));
+ PortP->RxEnd = (u16 __iomem *) RIO_PTR(HostP->Caddr, readw(&PhbP->rx_end));
+ } else
+ PortP->PhbP = NULL;
+
+ /*
+ ** port related flags
+ */
+ PortP->HostPort = HostPort;
+ /*
+ ** For each part of a 16 port RTA, RupNum is ID - 1.
+ */
+ PortP->RupNum = HostMapP->ID - 1;
+ if (HostMapP->Flags & RTA16_SECOND_SLOT) {
+ PortP->ID2 = HostMapP->ID2 - 1;
+ PortP->SecondBlock = 1;
+ } else {
+ PortP->ID2 = 0;
+ PortP->SecondBlock = 0;
+ }
+ PortP->RtaUniqueNum = HostMapP->RtaUniqueNum;
+
+ /*
+ ** If the port was already mapped then thats all we need to do.
+ */
+ if (PortP->Mapped) {
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ continue;
+ } else
+ HostMapP->Flags &= ~RTA_NEWBOOT;
+
+ PortP->State = 0;
+ PortP->Config = 0;
+ /*
+ ** Check out the module type - if it is special (read only etc.)
+ ** then we need to set flags in the PortP->Config.
+ ** Note: For 16 port RTA, all ports are of the same type.
+ */
+ if (RtaType == TYPE_RTA16) {
+ PortP->Config |= p->RIOModuleTypes[HostP->UnixRups[HostMapP->ID - 1].ModTypes].Flags[SubEnt % PORTS_PER_MODULE];
+ } else {
+ if (SubEnt < PORTS_PER_MODULE)
+ PortP->Config |= p->RIOModuleTypes[LONYBLE(HostP->UnixRups[HostMapP->ID - 1].ModTypes)].Flags[SubEnt % PORTS_PER_MODULE];
+ else
+ PortP->Config |= p->RIOModuleTypes[HINYBLE(HostP->UnixRups[HostMapP->ID - 1].ModTypes)].Flags[SubEnt % PORTS_PER_MODULE];
+ }
+
+ /*
+ ** more port related flags
+ */
+ PortP->PortState = 0;
+ PortP->ModemLines = 0;
+ PortP->ModemState = 0;
+ PortP->CookMode = COOK_WELL;
+ PortP->ParamSem = 0;
+ PortP->FlushCmdBodge = 0;
+ PortP->WflushFlag = 0;
+ PortP->MagicFlags = 0;
+ PortP->Lock = 0;
+ PortP->Store = 0;
+ PortP->FirstOpen = 1;
+
+ /*
+ ** Buffers 'n things
+ */
+ PortP->RxDataStart = 0;
+ PortP->Cor2Copy = 0;
+ PortP->Name = &HostMapP->Name[0];
+ PortP->statsGather = 0;
+ PortP->txchars = 0;
+ PortP->rxchars = 0;
+ PortP->opens = 0;
+ PortP->closes = 0;
+ PortP->ioctls = 0;
+ if (PortP->TxRingBuffer)
+ memset(PortP->TxRingBuffer, 0, p->RIOBufferSize);
+ else if (p->RIOBufferSize) {
+ PortP->TxRingBuffer = kzalloc(p->RIOBufferSize, GFP_KERNEL);
+ }
+ PortP->TxBufferOut = 0;
+ PortP->TxBufferIn = 0;
+ PortP->Debug = 0;
+ /*
+ ** LastRxTgl stores the state of the rx toggle bit for this
+ ** port, to be compared with the state of the next pkt received.
+ ** If the same, we have received the same rx pkt from the RTA
+ ** twice. Initialise to a value not equal to PHB_RX_TGL or 0.
+ */
+ PortP->LastRxTgl = ~(u8) PHB_RX_TGL;
+
+ /*
+ ** and mark the port as usable
+ */
+ PortP->Mapped = 1;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ }
+ if (HostMapP->SysPort < p->RIOFirstPortsMapped)
+ p->RIOFirstPortsMapped = HostMapP->SysPort;
+ if (HostMapP->SysPort > p->RIOLastPortsMapped)
+ p->RIOLastPortsMapped = HostMapP->SysPort;
+
+ return 0;
+}
+
+int RIOChangeName(struct rio_info *p, struct Map *MapP)
+{
+ int host;
+ struct Map *HostMapP;
+ char *sptr;
+
+ rio_dprintk(RIO_DEBUG_TABLE, "Change name entry on host %x, rta %x, ID %d, Sysport %d\n", MapP->HostUniqueNum, MapP->RtaUniqueNum, MapP->ID, (int) MapP->SysPort);
+
+ if (MapP->ID > MAX_RUP) {
+ rio_dprintk(RIO_DEBUG_TABLE, "Bad ID in map entry!\n");
+ p->RIOError.Error = ID_NUMBER_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ MapP->Name[MAX_NAME_LEN - 1] = '\0';
+ sptr = MapP->Name;
+
+ while (*sptr) {
+ if (*sptr < ' ' || *sptr > '~') {
+ rio_dprintk(RIO_DEBUG_TABLE, "Name entry contains non-printing characters!\n");
+ p->RIOError.Error = BAD_CHARACTER_IN_NAME;
+ return -EINVAL;
+ }
+ sptr++;
+ }
+
+ for (host = 0; host < p->RIONumHosts; host++) {
+ if (MapP->HostUniqueNum == p->RIOHosts[host].UniqueNum) {
+ if ((p->RIOHosts[host].Flags & RUN_STATE) != RC_RUNNING) {
+ p->RIOError.Error = HOST_NOT_RUNNING;
+ return -ENXIO;
+ }
+ if (MapP->ID == 0) {
+ memcpy(p->RIOHosts[host].Name, MapP->Name, MAX_NAME_LEN);
+ return 0;
+ }
+
+ HostMapP = &p->RIOHosts[host].Mapping[MapP->ID - 1];
+
+ if (HostMapP->RtaUniqueNum != MapP->RtaUniqueNum) {
+ p->RIOError.Error = RTA_NUMBER_WRONG;
+ return -ENXIO;
+ }
+ memcpy(HostMapP->Name, MapP->Name, MAX_NAME_LEN);
+ return 0;
+ }
+ }
+ p->RIOError.Error = UNKNOWN_HOST_NUMBER;
+ rio_dprintk(RIO_DEBUG_TABLE, "Unknown host %x\n", MapP->HostUniqueNum);
+ return -ENXIO;
+}
diff --git a/drivers/staging/generic_serial/rio/riotty.c b/drivers/staging/generic_serial/rio/riotty.c
new file mode 100644
index 000000000000..8a90393faf3c
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/riotty.c
@@ -0,0 +1,654 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : riotty.c
+** SID : 1.3
+** Last Modified : 11/6/98 10:33:47
+** Retrieved : 11/6/98 10:33:50
+**
+** ident @(#)riotty.c 1.3
+**
+** -----------------------------------------------------------------------------
+*/
+
+#define __EXPLICIT_DEF_H__
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/termios.h>
+
+#include <linux/serial.h>
+
+#include <linux/generic_serial.h>
+
+
+#include "linux_compat.h"
+#include "rio_linux.h"
+#include "pkt.h"
+#include "daemon.h"
+#include "rio.h"
+#include "riospace.h"
+#include "cmdpkt.h"
+#include "map.h"
+#include "rup.h"
+#include "port.h"
+#include "riodrvr.h"
+#include "rioinfo.h"
+#include "func.h"
+#include "errors.h"
+#include "pci.h"
+
+#include "parmmap.h"
+#include "unixrup.h"
+#include "board.h"
+#include "host.h"
+#include "phb.h"
+#include "link.h"
+#include "cmdblk.h"
+#include "route.h"
+#include "cirrus.h"
+#include "rioioctl.h"
+#include "param.h"
+
+static void RIOClearUp(struct Port *PortP);
+
+/* Below belongs in func.h */
+int RIOShortCommand(struct rio_info *p, struct Port *PortP, int command, int len, int arg);
+
+
+extern struct rio_info *p;
+
+
+int riotopen(struct tty_struct *tty, struct file *filp)
+{
+ unsigned int SysPort;
+ int repeat_this = 250;
+ struct Port *PortP; /* pointer to the port structure */
+ unsigned long flags;
+ int retval = 0;
+
+ func_enter();
+
+ /* Make sure driver_data is NULL in case the rio isn't booted jet. Else gs_close
+ is going to oops.
+ */
+ tty->driver_data = NULL;
+
+ SysPort = rio_minor(tty);
+
+ if (p->RIOFailed) {
+ rio_dprintk(RIO_DEBUG_TTY, "System initialisation failed\n");
+ func_exit();
+ return -ENXIO;
+ }
+
+ rio_dprintk(RIO_DEBUG_TTY, "port open SysPort %d (mapped:%d)\n", SysPort, p->RIOPortp[SysPort]->Mapped);
+
+ /*
+ ** Validate that we have received a legitimate request.
+ ** Currently, just check that we are opening a port on
+ ** a host card that actually exists, and that the port
+ ** has been mapped onto a host.
+ */
+ if (SysPort >= RIO_PORTS) { /* out of range ? */
+ rio_dprintk(RIO_DEBUG_TTY, "Illegal port number %d\n", SysPort);
+ func_exit();
+ return -ENXIO;
+ }
+
+ /*
+ ** Grab pointer to the port stucture
+ */
+ PortP = p->RIOPortp[SysPort]; /* Get control struc */
+ rio_dprintk(RIO_DEBUG_TTY, "PortP: %p\n", PortP);
+ if (!PortP->Mapped) { /* we aren't mapped yet! */
+ /*
+ ** The system doesn't know which RTA this port
+ ** corresponds to.
+ */
+ rio_dprintk(RIO_DEBUG_TTY, "port not mapped into system\n");
+ func_exit();
+ return -ENXIO;
+ }
+
+ tty->driver_data = PortP;
+
+ PortP->gs.port.tty = tty;
+ PortP->gs.port.count++;
+
+ rio_dprintk(RIO_DEBUG_TTY, "%d bytes in tx buffer\n", PortP->gs.xmit_cnt);
+
+ retval = gs_init_port(&PortP->gs);
+ if (retval) {
+ PortP->gs.port.count--;
+ return -ENXIO;
+ }
+ /*
+ ** If the host hasn't been booted yet, then
+ ** fail
+ */
+ if ((PortP->HostP->Flags & RUN_STATE) != RC_RUNNING) {
+ rio_dprintk(RIO_DEBUG_TTY, "Host not running\n");
+ func_exit();
+ return -ENXIO;
+ }
+
+ /*
+ ** If the RTA has not booted yet and the user has choosen to block
+ ** until the RTA is present then we must spin here waiting for
+ ** the RTA to boot.
+ */
+ /* I find the above code a bit hairy. I find the below code
+ easier to read and shorter. Now, if it works too that would
+ be great... -- REW
+ */
+ rio_dprintk(RIO_DEBUG_TTY, "Checking if RTA has booted... \n");
+ while (!(PortP->HostP->Mapping[PortP->RupNum].Flags & RTA_BOOTED)) {
+ if (!PortP->WaitUntilBooted) {
+ rio_dprintk(RIO_DEBUG_TTY, "RTA never booted\n");
+ func_exit();
+ return -ENXIO;
+ }
+
+ /* Under Linux you'd normally use a wait instead of this
+ busy-waiting. I'll stick with the old implementation for
+ now. --REW
+ */
+ if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_TTY, "RTA_wait_for_boot: EINTR in delay \n");
+ func_exit();
+ return -EINTR;
+ }
+ if (repeat_this-- <= 0) {
+ rio_dprintk(RIO_DEBUG_TTY, "Waiting for RTA to boot timeout\n");
+ func_exit();
+ return -EIO;
+ }
+ }
+ rio_dprintk(RIO_DEBUG_TTY, "RTA has been booted\n");
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ if (p->RIOHalted) {
+ goto bombout;
+ }
+
+ /*
+ ** If the port is in the final throws of being closed,
+ ** we should wait here (politely), waiting
+ ** for it to finish, so that it doesn't close us!
+ */
+ while ((PortP->State & RIO_CLOSING) && !p->RIOHalted) {
+ rio_dprintk(RIO_DEBUG_TTY, "Waiting for RIO_CLOSING to go away\n");
+ if (repeat_this-- <= 0) {
+ rio_dprintk(RIO_DEBUG_TTY, "Waiting for not idle closed broken by signal\n");
+ RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE);
+ retval = -EINTR;
+ goto bombout;
+ }
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) {
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ retval = -EINTR;
+ goto bombout;
+ }
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ }
+
+ if (!PortP->Mapped) {
+ rio_dprintk(RIO_DEBUG_TTY, "Port unmapped while closing!\n");
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ retval = -ENXIO;
+ func_exit();
+ return retval;
+ }
+
+ if (p->RIOHalted) {
+ goto bombout;
+ }
+
+/*
+** 15.10.1998 ARG - ESIL 0761 part fix
+** RIO has it's own CTSFLOW and RTSFLOW flags in 'Config' in the port structure,
+** we need to make sure that the flags are clear when the port is opened.
+*/
+ /* Uh? Suppose I turn these on and then another process opens
+ the port again? The flags get cleared! Not good. -- REW */
+ if (!(PortP->State & (RIO_LOPEN | RIO_MOPEN))) {
+ PortP->Config &= ~(RIO_CTSFLOW | RIO_RTSFLOW);
+ }
+
+ if (!(PortP->firstOpen)) { /* First time ? */
+ rio_dprintk(RIO_DEBUG_TTY, "First open for this port\n");
+
+
+ PortP->firstOpen++;
+ PortP->CookMode = 0; /* XXX RIOCookMode(tp); */
+ PortP->InUse = NOT_INUSE;
+
+ /* Tentative fix for bug PR27. Didn't work. */
+ /* PortP->gs.xmit_cnt = 0; */
+
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+
+ /* Someone explain to me why this delay/config is
+ here. If I read the docs correctly the "open"
+ command piggybacks the parameters immediately.
+ -- REW */
+ RIOParam(PortP, RIOC_OPEN, 1, OK_TO_SLEEP); /* Open the port */
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+
+ /*
+ ** wait for the port to be not closed.
+ */
+ while (!(PortP->PortState & PORT_ISOPEN) && !p->RIOHalted) {
+ rio_dprintk(RIO_DEBUG_TTY, "Waiting for PORT_ISOPEN-currently %x\n", PortP->PortState);
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_TTY, "Waiting for open to finish broken by signal\n");
+ RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE);
+ func_exit();
+ return -EINTR;
+ }
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ }
+
+ if (p->RIOHalted) {
+ retval = -EIO;
+ bombout:
+ /* RIOClearUp( PortP ); */
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return retval;
+ }
+ rio_dprintk(RIO_DEBUG_TTY, "PORT_ISOPEN found\n");
+ }
+ rio_dprintk(RIO_DEBUG_TTY, "Modem - test for carrier\n");
+ /*
+ ** ACTION
+ ** insert test for carrier here. -- ???
+ ** I already see that test here. What's the deal? -- REW
+ */
+ if ((PortP->gs.port.tty->termios->c_cflag & CLOCAL) ||
+ (PortP->ModemState & RIOC_MSVR1_CD)) {
+ rio_dprintk(RIO_DEBUG_TTY, "open(%d) Modem carr on\n", SysPort);
+ /*
+ tp->tm.c_state |= CARR_ON;
+ wakeup((caddr_t) &tp->tm.c_canq);
+ */
+ PortP->State |= RIO_CARR_ON;
+ wake_up_interruptible(&PortP->gs.port.open_wait);
+ } else { /* no carrier - wait for DCD */
+ /*
+ while (!(PortP->gs.port.tty->termios->c_state & CARR_ON) &&
+ !(filp->f_flags & O_NONBLOCK) && !p->RIOHalted )
+ */
+ while (!(PortP->State & RIO_CARR_ON) && !(filp->f_flags & O_NONBLOCK) && !p->RIOHalted) {
+ rio_dprintk(RIO_DEBUG_TTY, "open(%d) sleeping for carr on\n", SysPort);
+ /*
+ PortP->gs.port.tty->termios->c_state |= WOPEN;
+ */
+ PortP->State |= RIO_WOPEN;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) {
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ /*
+ ** ACTION: verify that this is a good thing
+ ** to do here. -- ???
+ ** I think it's OK. -- REW
+ */
+ rio_dprintk(RIO_DEBUG_TTY, "open(%d) sleeping for carr broken by signal\n", SysPort);
+ RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE);
+ /*
+ tp->tm.c_state &= ~WOPEN;
+ */
+ PortP->State &= ~RIO_WOPEN;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ func_exit();
+ return -EINTR;
+ }
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ }
+ PortP->State &= ~RIO_WOPEN;
+ }
+ if (p->RIOHalted)
+ goto bombout;
+ rio_dprintk(RIO_DEBUG_TTY, "Setting RIO_MOPEN\n");
+ PortP->State |= RIO_MOPEN;
+
+ if (p->RIOHalted)
+ goto bombout;
+
+ rio_dprintk(RIO_DEBUG_TTY, "high level open done\n");
+
+ /*
+ ** Count opens for port statistics reporting
+ */
+ if (PortP->statsGather)
+ PortP->opens++;
+
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ rio_dprintk(RIO_DEBUG_TTY, "Returning from open\n");
+ func_exit();
+ return 0;
+}
+
+/*
+** RIOClose the port.
+** The operating system thinks that this is last close for the device.
+** As there are two interfaces to the port (Modem and tty), we need to
+** check that both are closed before we close the device.
+*/
+int riotclose(void *ptr)
+{
+ struct Port *PortP = ptr; /* pointer to the port structure */
+ int deleted = 0;
+ int try = -1; /* Disable the timeouts by setting them to -1 */
+ int repeat_this = -1; /* Congrats to those having 15 years of
+ uptime! (You get to break the driver.) */
+ unsigned long end_time;
+ struct tty_struct *tty;
+ unsigned long flags;
+ int rv = 0;
+
+ rio_dprintk(RIO_DEBUG_TTY, "port close SysPort %d\n", PortP->PortNum);
+
+ /* PortP = p->RIOPortp[SysPort]; */
+ rio_dprintk(RIO_DEBUG_TTY, "Port is at address %p\n", PortP);
+ /* tp = PortP->TtyP; *//* Get tty */
+ tty = PortP->gs.port.tty;
+ rio_dprintk(RIO_DEBUG_TTY, "TTY is at address %p\n", tty);
+
+ if (PortP->gs.closing_wait)
+ end_time = jiffies + PortP->gs.closing_wait;
+ else
+ end_time = jiffies + MAX_SCHEDULE_TIMEOUT;
+
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+
+ /*
+ ** Setting this flag will make any process trying to open
+ ** this port block until we are complete closing it.
+ */
+ PortP->State |= RIO_CLOSING;
+
+ if ((PortP->State & RIO_DELETED)) {
+ rio_dprintk(RIO_DEBUG_TTY, "Close on deleted RTA\n");
+ deleted = 1;
+ }
+
+ if (p->RIOHalted) {
+ RIOClearUp(PortP);
+ rv = -EIO;
+ goto close_end;
+ }
+
+ rio_dprintk(RIO_DEBUG_TTY, "Clear bits\n");
+ /*
+ ** clear the open bits for this device
+ */
+ PortP->State &= ~RIO_MOPEN;
+ PortP->State &= ~RIO_CARR_ON;
+ PortP->ModemState &= ~RIOC_MSVR1_CD;
+ /*
+ ** If the device was open as both a Modem and a tty line
+ ** then we need to wimp out here, as the port has not really
+ ** been finally closed (gee, whizz!) The test here uses the
+ ** bit for the OTHER mode of operation, to see if THAT is
+ ** still active!
+ */
+ if ((PortP->State & (RIO_LOPEN | RIO_MOPEN))) {
+ /*
+ ** The port is still open for the other task -
+ ** return, pretending that we are still active.
+ */
+ rio_dprintk(RIO_DEBUG_TTY, "Channel %d still open !\n", PortP->PortNum);
+ PortP->State &= ~RIO_CLOSING;
+ if (PortP->firstOpen)
+ PortP->firstOpen--;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return -EIO;
+ }
+
+ rio_dprintk(RIO_DEBUG_TTY, "Closing down - everything must go!\n");
+
+ PortP->State &= ~RIO_DYNOROD;
+
+ /*
+ ** This is where we wait for the port
+ ** to drain down before closing. Bye-bye....
+ ** (We never meant to do this)
+ */
+ rio_dprintk(RIO_DEBUG_TTY, "Timeout 1 starts\n");
+
+ if (!deleted)
+ while ((PortP->InUse != NOT_INUSE) && !p->RIOHalted && (PortP->TxBufferIn != PortP->TxBufferOut)) {
+ if (repeat_this-- <= 0) {
+ rv = -EINTR;
+ rio_dprintk(RIO_DEBUG_TTY, "Waiting for not idle closed broken by signal\n");
+ RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE);
+ goto close_end;
+ }
+ rio_dprintk(RIO_DEBUG_TTY, "Calling timeout to flush in closing\n");
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ if (RIODelay_ni(PortP, HUNDRED_MS * 10) == RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_TTY, "RTA EINTR in delay \n");
+ rv = -EINTR;
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ goto close_end;
+ }
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ }
+
+ PortP->TxBufferIn = PortP->TxBufferOut = 0;
+ repeat_this = 0xff;
+
+ PortP->InUse = 0;
+ if ((PortP->State & (RIO_LOPEN | RIO_MOPEN))) {
+ /*
+ ** The port has been re-opened for the other task -
+ ** return, pretending that we are still active.
+ */
+ rio_dprintk(RIO_DEBUG_TTY, "Channel %d re-open!\n", PortP->PortNum);
+ PortP->State &= ~RIO_CLOSING;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ if (PortP->firstOpen)
+ PortP->firstOpen--;
+ return -EIO;
+ }
+
+ if (p->RIOHalted) {
+ RIOClearUp(PortP);
+ goto close_end;
+ }
+
+ /* Can't call RIOShortCommand with the port locked. */
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+
+ if (RIOShortCommand(p, PortP, RIOC_CLOSE, 1, 0) == RIO_FAIL) {
+ RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE);
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ goto close_end;
+ }
+
+ if (!deleted)
+ while (try && (PortP->PortState & PORT_ISOPEN)) {
+ try--;
+ if (time_after(jiffies, end_time)) {
+ rio_dprintk(RIO_DEBUG_TTY, "Run out of tries - force the bugger shut!\n");
+ RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE);
+ break;
+ }
+ rio_dprintk(RIO_DEBUG_TTY, "Close: PortState:ISOPEN is %d\n", PortP->PortState & PORT_ISOPEN);
+
+ if (p->RIOHalted) {
+ RIOClearUp(PortP);
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ goto close_end;
+ }
+ if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) {
+ rio_dprintk(RIO_DEBUG_TTY, "RTA EINTR in delay \n");
+ RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE);
+ break;
+ }
+ }
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ rio_dprintk(RIO_DEBUG_TTY, "Close: try was %d on completion\n", try);
+
+ /* RIOPreemptiveCmd(p, PortP, RIOC_FCLOSE); */
+
+/*
+** 15.10.1998 ARG - ESIL 0761 part fix
+** RIO has it's own CTSFLOW and RTSFLOW flags in 'Config' in the port structure,** we need to make sure that the flags are clear when the port is opened.
+*/
+ PortP->Config &= ~(RIO_CTSFLOW | RIO_RTSFLOW);
+
+ /*
+ ** Count opens for port statistics reporting
+ */
+ if (PortP->statsGather)
+ PortP->closes++;
+
+close_end:
+ /* XXX: Why would a "DELETED" flag be reset here? I'd have
+ thought that a "deleted" flag means that the port was
+ permanently gone, but here we can make it reappear by it
+ being in close during the "deletion".
+ */
+ PortP->State &= ~(RIO_CLOSING | RIO_DELETED);
+ if (PortP->firstOpen)
+ PortP->firstOpen--;
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ rio_dprintk(RIO_DEBUG_TTY, "Return from close\n");
+ return rv;
+}
+
+
+
+static void RIOClearUp(struct Port *PortP)
+{
+ rio_dprintk(RIO_DEBUG_TTY, "RIOHalted set\n");
+ PortP->Config = 0; /* Direct semaphore */
+ PortP->PortState = 0;
+ PortP->firstOpen = 0;
+ PortP->FlushCmdBodge = 0;
+ PortP->ModemState = PortP->CookMode = 0;
+ PortP->Mapped = 0;
+ PortP->WflushFlag = 0;
+ PortP->MagicFlags = 0;
+ PortP->RxDataStart = 0;
+ PortP->TxBufferIn = 0;
+ PortP->TxBufferOut = 0;
+}
+
+/*
+** Put a command onto a port.
+** The PortPointer, command, length and arg are passed.
+** The len is the length *inclusive* of the command byte,
+** and so for a command that takes no data, len==1.
+** The arg is a single byte, and is only used if len==2.
+** Other values of len aren't allowed, and will cause
+** a panic.
+*/
+int RIOShortCommand(struct rio_info *p, struct Port *PortP, int command, int len, int arg)
+{
+ struct PKT __iomem *PacketP;
+ int retries = 20; /* at 10 per second -> 2 seconds */
+ unsigned long flags;
+
+ rio_dprintk(RIO_DEBUG_TTY, "entering shortcommand.\n");
+
+ if (PortP->State & RIO_DELETED) {
+ rio_dprintk(RIO_DEBUG_TTY, "Short command to deleted RTA ignored\n");
+ return RIO_FAIL;
+ }
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+
+ /*
+ ** If the port is in use for pre-emptive command, then wait for it to
+ ** be free again.
+ */
+ while ((PortP->InUse != NOT_INUSE) && !p->RIOHalted) {
+ rio_dprintk(RIO_DEBUG_TTY, "Waiting for not in use (%d)\n", retries);
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ if (retries-- <= 0) {
+ return RIO_FAIL;
+ }
+ if (RIODelay_ni(PortP, HUNDRED_MS) == RIO_FAIL) {
+ return RIO_FAIL;
+ }
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ }
+ if (PortP->State & RIO_DELETED) {
+ rio_dprintk(RIO_DEBUG_TTY, "Short command to deleted RTA ignored\n");
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return RIO_FAIL;
+ }
+
+ while (!can_add_transmit(&PacketP, PortP) && !p->RIOHalted) {
+ rio_dprintk(RIO_DEBUG_TTY, "Waiting to add short command to queue (%d)\n", retries);
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ if (retries-- <= 0) {
+ rio_dprintk(RIO_DEBUG_TTY, "out of tries. Failing\n");
+ return RIO_FAIL;
+ }
+ if (RIODelay_ni(PortP, HUNDRED_MS) == RIO_FAIL) {
+ return RIO_FAIL;
+ }
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+ }
+
+ if (p->RIOHalted) {
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return RIO_FAIL;
+ }
+
+ /*
+ ** set the command byte and the argument byte
+ */
+ writeb(command, &PacketP->data[0]);
+
+ if (len == 2)
+ writeb(arg, &PacketP->data[1]);
+
+ /*
+ ** set the length of the packet and set the command bit.
+ */
+ writeb(PKT_CMD_BIT | len, &PacketP->len);
+
+ add_transmit(PortP);
+ /*
+ ** Count characters transmitted for port statistics reporting
+ */
+ if (PortP->statsGather)
+ PortP->txchars += len;
+
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+ return p->RIOHalted ? RIO_FAIL : ~RIO_FAIL;
+}
+
+
diff --git a/drivers/staging/generic_serial/rio/route.h b/drivers/staging/generic_serial/rio/route.h
new file mode 100644
index 000000000000..46e963771c30
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/route.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+ ******* *******
+ ******* R O U T E H E A D E R
+ ******* *******
+ ****************************************************************************
+
+ Author : Ian Nandhra / Jeremy Rolls
+ Date :
+
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+
+ Version : 0.01
+
+
+ Mods
+ ----------------------------------------------------------------------------
+ Date By Description
+ ----------------------------------------------------------------------------
+
+ ***************************************************************************/
+
+#ifndef _route_h
+#define _route_h
+
+#define MAX_LINKS 4
+#define MAX_NODES 17 /* Maximum nodes in a subnet */
+#define NODE_BYTES ((MAX_NODES / 8) + 1) /* Number of bytes needed for
+ 1 bit per node */
+#define ROUTE_DATA_SIZE (NODE_BYTES + 2) /* Number of bytes for complete
+ info about cost etc. */
+#define ROUTES_PER_PACKET ((PKT_MAX_DATA_LEN -2)/ ROUTE_DATA_SIZE)
+ /* Number of nodes we can squeeze
+ into one packet */
+#define MAX_TOPOLOGY_PACKETS (MAX_NODES / ROUTES_PER_PACKET + 1)
+/************************************************
+ * Define the types of command for the ROUTE RUP.
+ ************************************************/
+#define ROUTE_REQUEST 0 /* Request an ID */
+#define ROUTE_FOAD 1 /* Kill the RTA */
+#define ROUTE_ALREADY 2 /* ID given already */
+#define ROUTE_USED 3 /* All ID's used */
+#define ROUTE_ALLOCATE 4 /* Here it is */
+#define ROUTE_REQ_TOP 5 /* I bet you didn't expect....
+ the Topological Inquisition */
+#define ROUTE_TOPOLOGY 6 /* Topology request answered FD */
+/*******************************************************************
+ * Define the Route Map Structure
+ *
+ * The route map gives a pointer to a Link Structure to use.
+ * This allows Disconnected Links to be checked quickly
+ ******************************************************************/
+typedef struct COST_ROUTE COST_ROUTE;
+struct COST_ROUTE {
+ unsigned char cost; /* Cost down this link */
+ unsigned char route[NODE_BYTES]; /* Nodes through this route */
+};
+
+typedef struct ROUTE_STR ROUTE_STR;
+struct ROUTE_STR {
+ COST_ROUTE cost_route[MAX_LINKS];
+ /* cost / route for this link */
+ ushort favoured; /* favoured link */
+};
+
+
+#define NO_LINK (short) 5 /* Link unattached */
+#define ROUTE_NO_ID (short) 100 /* No Id */
+#define ROUTE_DISCONNECT (ushort) 0xff /* Not connected */
+#define ROUTE_INTERCONNECT (ushort) 0x40 /* Sub-net interconnect */
+
+
+#define SYNC_RUP (ushort) 255
+#define COMMAND_RUP (ushort) 254
+#define ERROR_RUP (ushort) 253
+#define POLL_RUP (ushort) 252
+#define BOOT_RUP (ushort) 251
+#define ROUTE_RUP (ushort) 250
+#define STATUS_RUP (ushort) 249
+#define POWER_RUP (ushort) 248
+
+#define HIGHEST_RUP (ushort) 255 /* Set to Top one */
+#define LOWEST_RUP (ushort) 248 /* Set to bottom one */
+
+#endif
+
+/*********** end of file ***********/
diff --git a/drivers/staging/generic_serial/rio/rup.h b/drivers/staging/generic_serial/rio/rup.h
new file mode 100644
index 000000000000..4ae90cb207a9
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/rup.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+ ******* *******
+ ******* R U P S T R U C T U R E
+ ******* *******
+ ****************************************************************************
+
+ Author : Ian Nandhra
+ Date :
+
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+
+ Version : 0.01
+
+
+ Mods
+ ----------------------------------------------------------------------------
+ Date By Description
+ ----------------------------------------------------------------------------
+
+ ***************************************************************************/
+
+#ifndef _rup_h
+#define _rup_h 1
+
+#define MAX_RUP ((short) 16)
+#define PKTS_PER_RUP ((short) 2) /* They are always used in pairs */
+
+/*************************************************
+ * Define all the packet request stuff
+ ************************************************/
+#define TX_RUP_INACTIVE 0 /* Nothing to transmit */
+#define TX_PACKET_READY 1 /* Transmit packet ready */
+#define TX_LOCK_RUP 2 /* Transmit side locked */
+
+#define RX_RUP_INACTIVE 0 /* Nothing received */
+#define RX_PACKET_READY 1 /* Packet received */
+
+#define RUP_NO_OWNER 0xff /* RUP not owned by any process */
+
+struct RUP {
+ u16 txpkt; /* Outgoing packet */
+ u16 rxpkt; /* Incoming packet */
+ u16 link; /* Which link to send down? */
+ u8 rup_dest_unit[2]; /* Destination unit */
+ u16 handshake; /* For handshaking */
+ u16 timeout; /* Timeout */
+ u16 status; /* Status */
+ u16 txcontrol; /* Transmit control */
+ u16 rxcontrol; /* Receive control */
+};
+
+#endif
+
+/*********** end of file ***********/
diff --git a/drivers/staging/generic_serial/rio/unixrup.h b/drivers/staging/generic_serial/rio/unixrup.h
new file mode 100644
index 000000000000..7abf0cba0f2c
--- /dev/null
+++ b/drivers/staging/generic_serial/rio/unixrup.h
@@ -0,0 +1,51 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+**
+** Module : unixrup.h
+** SID : 1.2
+** Last Modified : 11/6/98 11:34:20
+** Retrieved : 11/6/98 11:34:22
+**
+** ident @(#)unixrup.h 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+
+#ifndef __rio_unixrup_h__
+#define __rio_unixrup_h__
+
+/*
+** UnixRup data structure. This contains pointers to actual RUPs on the
+** host card, and all the command/boot control stuff.
+*/
+struct UnixRup {
+ struct CmdBlk *CmdsWaitingP; /* Commands waiting to be done */
+ struct CmdBlk *CmdPendingP; /* The command currently being sent */
+ struct RUP __iomem *RupP; /* the Rup to send it to */
+ unsigned int Id; /* Id number */
+ unsigned int BaseSysPort; /* SysPort of first tty on this RTA */
+ unsigned int ModTypes; /* Modules on this RTA */
+ spinlock_t RupLock; /* Lock structure for MPX */
+ /* struct lockb RupLock; *//* Lock structure for MPX */
+};
+
+#endif /* __rio_unixrup_h__ */
diff --git a/drivers/staging/generic_serial/ser_a2232.c b/drivers/staging/generic_serial/ser_a2232.c
new file mode 100644
index 000000000000..3f47c2ead8e5
--- /dev/null
+++ b/drivers/staging/generic_serial/ser_a2232.c
@@ -0,0 +1,831 @@
+/* drivers/char/ser_a2232.c */
+
+/* $Id: ser_a2232.c,v 0.4 2000/01/25 12:00:00 ehaase Exp $ */
+
+/* Linux serial driver for the Amiga A2232 board */
+
+/* This driver is MAINTAINED. Before applying any changes, please contact
+ * the author.
+ */
+
+/* Copyright (c) 2000-2001 Enver Haase <ehaase@inf.fu-berlin.de>
+ * alias The A2232 driver project <A2232@gmx.net>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+/***************************** Documentation ************************/
+/*
+ * This driver is in EXPERIMENTAL state. That means I could not find
+ * someone with five A2232 boards with 35 ports running at 19200 bps
+ * at the same time and test the machine's behaviour.
+ * However, I know that you can performance-tweak this driver (see
+ * the source code).
+ * One thing to consider is the time this driver consumes during the
+ * Amiga's vertical blank interrupt. Everything that is to be done
+ * _IS DONE_ when entering the vertical blank interrupt handler of
+ * this driver.
+ * However, it would be more sane to only do the job for only ONE card
+ * instead of ALL cards at a time; or, more generally, to handle only
+ * SOME ports instead of ALL ports at a time.
+ * However, as long as no-one runs into problems I guess I shouldn't
+ * change the driver as it runs fine for me :) .
+ *
+ * Version history of this file:
+ * 0.4 Resolved licensing issues.
+ * 0.3 Inclusion in the Linux/m68k tree, small fixes.
+ * 0.2 Added documentation, minor typo fixes.
+ * 0.1 Initial release.
+ *
+ * TO DO:
+ * - Handle incoming BREAK events. I guess "Stevens: Advanced
+ * Programming in the UNIX(R) Environment" is a good reference
+ * on what is to be done.
+ * - When installing as a module, don't simply 'printk' text, but
+ * send it to the TTY used by the user.
+ *
+ * THANKS TO:
+ * - Jukka Marin (65EC02 code).
+ * - The other NetBSD developers on whose A2232 driver I had a
+ * pretty close look. However, I didn't copy any code so it
+ * is okay to put my code under the GPL and include it into
+ * Linux.
+ */
+/***************************** End of Documentation *****************/
+
+/***************************** Defines ******************************/
+/*
+ * Enables experimental 115200 (normal) 230400 (turbo) baud rate.
+ * The A2232 specification states it can only operate at speeds up to
+ * 19200 bits per second, and I was not able to send a file via
+ * "sz"/"rz" and a null-modem cable from one A2232 port to another
+ * at 115200 bits per second.
+ * However, this might work for you.
+ */
+#undef A2232_SPEEDHACK
+/*
+ * Default is not to use RTS/CTS so you could be talked to death.
+ */
+#define A2232_SUPPRESS_RTSCTS_WARNING
+/************************* End of Defines ***************************/
+
+/***************************** Includes *****************************/
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+
+#include <asm/setup.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <linux/zorro.h>
+#include <asm/irq.h>
+#include <linux/mutex.h>
+
+#include <linux/delay.h>
+
+#include <linux/serial.h>
+#include <linux/generic_serial.h>
+#include <linux/tty_flip.h>
+
+#include "ser_a2232.h"
+#include "ser_a2232fw.h"
+/************************* End of Includes **************************/
+
+/***************************** Prototypes ***************************/
+/* The interrupt service routine */
+static irqreturn_t a2232_vbl_inter(int irq, void *data);
+/* Initialize the port structures */
+static void a2232_init_portstructs(void);
+/* Initialize and register TTY drivers. */
+/* returns 0 IFF successful */
+static int a2232_init_drivers(void);
+
+/* BEGIN GENERIC_SERIAL PROTOTYPES */
+static void a2232_disable_tx_interrupts(void *ptr);
+static void a2232_enable_tx_interrupts(void *ptr);
+static void a2232_disable_rx_interrupts(void *ptr);
+static void a2232_enable_rx_interrupts(void *ptr);
+static int a2232_carrier_raised(struct tty_port *port);
+static void a2232_shutdown_port(void *ptr);
+static int a2232_set_real_termios(void *ptr);
+static int a2232_chars_in_buffer(void *ptr);
+static void a2232_close(void *ptr);
+static void a2232_hungup(void *ptr);
+/* static void a2232_getserial (void *ptr, struct serial_struct *sp); */
+/* END GENERIC_SERIAL PROTOTYPES */
+
+/* Functions that the TTY driver struct expects */
+static int a2232_ioctl(struct tty_struct *tty,
+ unsigned int cmd, unsigned long arg);
+static void a2232_throttle(struct tty_struct *tty);
+static void a2232_unthrottle(struct tty_struct *tty);
+static int a2232_open(struct tty_struct * tty, struct file * filp);
+/************************* End of Prototypes ************************/
+
+/***************************** Global variables *********************/
+/*---------------------------------------------------------------------------
+ * Interface from generic_serial.c back here
+ *--------------------------------------------------------------------------*/
+static struct real_driver a2232_real_driver = {
+ a2232_disable_tx_interrupts,
+ a2232_enable_tx_interrupts,
+ a2232_disable_rx_interrupts,
+ a2232_enable_rx_interrupts,
+ a2232_shutdown_port,
+ a2232_set_real_termios,
+ a2232_chars_in_buffer,
+ a2232_close,
+ a2232_hungup,
+ NULL /* a2232_getserial */
+};
+
+static void *a2232_driver_ID = &a2232_driver_ID; // Some memory address WE own.
+
+/* Ports structs */
+static struct a2232_port a2232_ports[MAX_A2232_BOARDS*NUMLINES];
+
+/* TTY driver structs */
+static struct tty_driver *a2232_driver;
+
+/* nr of cards completely (all ports) and correctly configured */
+static int nr_a2232;
+
+/* zorro_dev structs for the A2232's */
+static struct zorro_dev *zd_a2232[MAX_A2232_BOARDS];
+/***************************** End of Global variables **************/
+
+/* Helper functions */
+
+static inline volatile struct a2232memory *a2232mem(unsigned int board)
+{
+ return (volatile struct a2232memory *)ZTWO_VADDR(zd_a2232[board]->resource.start);
+}
+
+static inline volatile struct a2232status *a2232stat(unsigned int board,
+ unsigned int portonboard)
+{
+ volatile struct a2232memory *mem = a2232mem(board);
+ return &(mem->Status[portonboard]);
+}
+
+static inline void a2232_receive_char(struct a2232_port *port, int ch, int err)
+{
+/* Mostly stolen from other drivers.
+ Maybe one could implement a more efficient version by not only
+ transferring one character at a time.
+*/
+ struct tty_struct *tty = port->gs.port.tty;
+
+#if 0
+ switch(err) {
+ case TTY_BREAK:
+ break;
+ case TTY_PARITY:
+ break;
+ case TTY_OVERRUN:
+ break;
+ case TTY_FRAME:
+ break;
+ }
+#endif
+
+ tty_insert_flip_char(tty, ch, err);
+ tty_flip_buffer_push(tty);
+}
+
+/***************************** Functions ****************************/
+/*** BEGIN OF REAL_DRIVER FUNCTIONS ***/
+
+static void a2232_disable_tx_interrupts(void *ptr)
+{
+ struct a2232_port *port;
+ volatile struct a2232status *stat;
+ unsigned long flags;
+
+ port = ptr;
+ stat = a2232stat(port->which_a2232, port->which_port_on_a2232);
+ stat->OutDisable = -1;
+
+ /* Does this here really have to be? */
+ local_irq_save(flags);
+ port->gs.port.flags &= ~GS_TX_INTEN;
+ local_irq_restore(flags);
+}
+
+static void a2232_enable_tx_interrupts(void *ptr)
+{
+ struct a2232_port *port;
+ volatile struct a2232status *stat;
+ unsigned long flags;
+
+ port = ptr;
+ stat = a2232stat(port->which_a2232, port->which_port_on_a2232);
+ stat->OutDisable = 0;
+
+ /* Does this here really have to be? */
+ local_irq_save(flags);
+ port->gs.port.flags |= GS_TX_INTEN;
+ local_irq_restore(flags);
+}
+
+static void a2232_disable_rx_interrupts(void *ptr)
+{
+ struct a2232_port *port;
+ port = ptr;
+ port->disable_rx = -1;
+}
+
+static void a2232_enable_rx_interrupts(void *ptr)
+{
+ struct a2232_port *port;
+ port = ptr;
+ port->disable_rx = 0;
+}
+
+static int a2232_carrier_raised(struct tty_port *port)
+{
+ struct a2232_port *ap = container_of(port, struct a2232_port, gs.port);
+ return ap->cd_status;
+}
+
+static void a2232_shutdown_port(void *ptr)
+{
+ struct a2232_port *port;
+ volatile struct a2232status *stat;
+ unsigned long flags;
+
+ port = ptr;
+ stat = a2232stat(port->which_a2232, port->which_port_on_a2232);
+
+ local_irq_save(flags);
+
+ port->gs.port.flags &= ~GS_ACTIVE;
+
+ if (port->gs.port.tty && port->gs.port.tty->termios->c_cflag & HUPCL) {
+ /* Set DTR and RTS to Low, flush output.
+ The NetBSD driver "msc.c" does it this way. */
+ stat->Command = ( (stat->Command & ~A2232CMD_CMask) |
+ A2232CMD_Close );
+ stat->OutFlush = -1;
+ stat->Setup = -1;
+ }
+
+ local_irq_restore(flags);
+
+ /* After analyzing control flow, I think a2232_shutdown_port
+ is actually the last call from the system when at application
+ level someone issues a "echo Hello >>/dev/ttyY0".
+ Therefore I think the MOD_DEC_USE_COUNT should be here and
+ not in "a2232_close()". See the comment in "sx.c", too.
+ If you run into problems, compile this driver into the
+ kernel instead of compiling it as a module. */
+}
+
+static int a2232_set_real_termios(void *ptr)
+{
+ unsigned int cflag, baud, chsize, stopb, parity, softflow;
+ int rate;
+ int a2232_param, a2232_cmd;
+ unsigned long flags;
+ unsigned int i;
+ struct a2232_port *port = ptr;
+ volatile struct a2232status *status;
+ volatile struct a2232memory *mem;
+
+ if (!port->gs.port.tty || !port->gs.port.tty->termios) return 0;
+
+ status = a2232stat(port->which_a2232, port->which_port_on_a2232);
+ mem = a2232mem(port->which_a2232);
+
+ a2232_param = a2232_cmd = 0;
+
+ // get baud rate
+ baud = port->gs.baud;
+ if (baud == 0) {
+ /* speed == 0 -> drop DTR, do nothing else */
+ local_irq_save(flags);
+ // Clear DTR (and RTS... mhhh).
+ status->Command = ( (status->Command & ~A2232CMD_CMask) |
+ A2232CMD_Close );
+ status->OutFlush = -1;
+ status->Setup = -1;
+
+ local_irq_restore(flags);
+ return 0;
+ }
+
+ rate = A2232_BAUD_TABLE_NOAVAIL;
+ for (i=0; i < A2232_BAUD_TABLE_NUM_RATES * 3; i += 3){
+ if (a2232_baud_table[i] == baud){
+ if (mem->Common.Crystal == A2232_TURBO) rate = a2232_baud_table[i+2];
+ else rate = a2232_baud_table[i+1];
+ }
+ }
+ if (rate == A2232_BAUD_TABLE_NOAVAIL){
+ printk("a2232: Board %d Port %d unsupported baud rate: %d baud. Using another.\n",port->which_a2232,port->which_port_on_a2232,baud);
+ // This is useful for both (turbo or normal) Crystal versions.
+ rate = A2232PARAM_B9600;
+ }
+ a2232_param |= rate;
+
+ cflag = port->gs.port.tty->termios->c_cflag;
+
+ // get character size
+ chsize = cflag & CSIZE;
+ switch (chsize){
+ case CS8: a2232_param |= A2232PARAM_8Bit; break;
+ case CS7: a2232_param |= A2232PARAM_7Bit; break;
+ case CS6: a2232_param |= A2232PARAM_6Bit; break;
+ case CS5: a2232_param |= A2232PARAM_5Bit; break;
+ default: printk("a2232: Board %d Port %d unsupported character size: %d. Using 8 data bits.\n",
+ port->which_a2232,port->which_port_on_a2232,chsize);
+ a2232_param |= A2232PARAM_8Bit; break;
+ }
+
+ // get number of stop bits
+ stopb = cflag & CSTOPB;
+ if (stopb){ // two stop bits instead of one
+ printk("a2232: Board %d Port %d 2 stop bits unsupported. Using 1 stop bit.\n",
+ port->which_a2232,port->which_port_on_a2232);
+ }
+
+ // Warn if RTS/CTS not wanted
+ if (!(cflag & CRTSCTS)){
+#ifndef A2232_SUPPRESS_RTSCTS_WARNING
+ printk("a2232: Board %d Port %d cannot switch off firmware-implemented RTS/CTS hardware flow control.\n",
+ port->which_a2232,port->which_port_on_a2232);
+#endif
+ }
+
+ /* I think this is correct.
+ However, IXOFF means _input_ flow control and I wonder
+ if one should care about IXON _output_ flow control,
+ too. If this makes problems, one should turn the A2232
+ firmware XON/XOFF "SoftFlow" flow control off and use
+ the conventional way of inserting START/STOP characters
+ by hand in throttle()/unthrottle().
+ */
+ softflow = !!( port->gs.port.tty->termios->c_iflag & IXOFF );
+
+ // get Parity (Enabled/Disabled? If Enabled, Odd or Even?)
+ parity = cflag & (PARENB | PARODD);
+ if (parity & PARENB){
+ if (parity & PARODD){
+ a2232_cmd |= A2232CMD_OddParity;
+ }
+ else{
+ a2232_cmd |= A2232CMD_EvenParity;
+ }
+ }
+ else a2232_cmd |= A2232CMD_NoParity;
+
+
+ /* Hmm. Maybe an own a2232_port structure
+ member would be cleaner? */
+ if (cflag & CLOCAL)
+ port->gs.port.flags &= ~ASYNC_CHECK_CD;
+ else
+ port->gs.port.flags |= ASYNC_CHECK_CD;
+
+
+ /* Now we have all parameters and can go to set them: */
+ local_irq_save(flags);
+
+ status->Param = a2232_param | A2232PARAM_RcvBaud;
+ status->Command = a2232_cmd | A2232CMD_Open | A2232CMD_Enable;
+ status->SoftFlow = softflow;
+ status->OutDisable = 0;
+ status->Setup = -1;
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+static int a2232_chars_in_buffer(void *ptr)
+{
+ struct a2232_port *port;
+ volatile struct a2232status *status;
+ unsigned char ret; /* we need modulo-256 arithmetics */
+ port = ptr;
+ status = a2232stat(port->which_a2232, port->which_port_on_a2232);
+#if A2232_IOBUFLEN != 256
+#error "Re-Implement a2232_chars_in_buffer()!"
+#endif
+ ret = (status->OutHead - status->OutTail);
+ return ret;
+}
+
+static void a2232_close(void *ptr)
+{
+ a2232_disable_tx_interrupts(ptr);
+ a2232_disable_rx_interrupts(ptr);
+ /* see the comment in a2232_shutdown_port above. */
+}
+
+static void a2232_hungup(void *ptr)
+{
+ a2232_close(ptr);
+}
+/*** END OF REAL_DRIVER FUNCTIONS ***/
+
+/*** BEGIN FUNCTIONS EXPECTED BY TTY DRIVER STRUCTS ***/
+static int a2232_ioctl( struct tty_struct *tty,
+ unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+static void a2232_throttle(struct tty_struct *tty)
+{
+/* Throttle: System cannot take another chars: Drop RTS or
+ send the STOP char or whatever.
+ The A2232 firmware does RTS/CTS anyway, and XON/XOFF
+ if switched on. So the only thing we can do at this
+ layer here is not taking any characters out of the
+ A2232 buffer any more. */
+ struct a2232_port *port = tty->driver_data;
+ port->throttle_input = -1;
+}
+
+static void a2232_unthrottle(struct tty_struct *tty)
+{
+/* Unthrottle: dual to "throttle()" above. */
+ struct a2232_port *port = tty->driver_data;
+ port->throttle_input = 0;
+}
+
+static int a2232_open(struct tty_struct * tty, struct file * filp)
+{
+/* More or less stolen from other drivers. */
+ int line;
+ int retval;
+ struct a2232_port *port;
+
+ line = tty->index;
+ port = &a2232_ports[line];
+
+ tty->driver_data = port;
+ port->gs.port.tty = tty;
+ port->gs.port.count++;
+ retval = gs_init_port(&port->gs);
+ if (retval) {
+ port->gs.port.count--;
+ return retval;
+ }
+ port->gs.port.flags |= GS_ACTIVE;
+ retval = gs_block_til_ready(port, filp);
+
+ if (retval) {
+ port->gs.port.count--;
+ return retval;
+ }
+
+ a2232_enable_rx_interrupts(port);
+
+ return 0;
+}
+/*** END OF FUNCTIONS EXPECTED BY TTY DRIVER STRUCTS ***/
+
+static irqreturn_t a2232_vbl_inter(int irq, void *data)
+{
+#if A2232_IOBUFLEN != 256
+#error "Re-Implement a2232_vbl_inter()!"
+#endif
+
+struct a2232_port *port;
+volatile struct a2232memory *mem;
+volatile struct a2232status *status;
+unsigned char newhead;
+unsigned char bufpos; /* Must be unsigned char. We need the modulo-256 arithmetics */
+unsigned char ncd, ocd, ccd; /* names consistent with the NetBSD driver */
+volatile u_char *ibuf, *cbuf, *obuf;
+int ch, err, n, p;
+ for (n = 0; n < nr_a2232; n++){ /* for every completely initialized A2232 board */
+ mem = a2232mem(n);
+ for (p = 0; p < NUMLINES; p++){ /* for every port on this board */
+ err = 0;
+ port = &a2232_ports[n*NUMLINES+p];
+ if ( port->gs.port.flags & GS_ACTIVE ){ /* if the port is used */
+
+ status = a2232stat(n,p);
+
+ if (!port->disable_rx && !port->throttle_input){ /* If input is not disabled */
+ newhead = status->InHead; /* 65EC02 write pointer */
+ bufpos = status->InTail;
+
+ /* check for input for this port */
+ if (newhead != bufpos) {
+ /* buffer for input chars/events */
+ ibuf = mem->InBuf[p];
+
+ /* data types of bytes in ibuf */
+ cbuf = mem->InCtl[p];
+
+ /* do for all chars */
+ while (bufpos != newhead) {
+ /* which type of input data? */
+ switch (cbuf[bufpos]) {
+ /* switch on input event (CD, BREAK, etc.) */
+ case A2232INCTL_EVENT:
+ switch (ibuf[bufpos++]) {
+ case A2232EVENT_Break:
+ /* TODO: Handle BREAK signal */
+ break;
+ /* A2232EVENT_CarrierOn and A2232EVENT_CarrierOff are
+ handled in a separate queue and should not occur here. */
+ case A2232EVENT_Sync:
+ printk("A2232: 65EC02 software sent SYNC event, don't know what to do. Ignoring.");
+ break;
+ default:
+ printk("A2232: 65EC02 software broken, unknown event type %d occurred.\n",ibuf[bufpos-1]);
+ } /* event type switch */
+ break;
+ case A2232INCTL_CHAR:
+ /* Receive incoming char */
+ a2232_receive_char(port, ibuf[bufpos], err);
+ bufpos++;
+ break;
+ default:
+ printk("A2232: 65EC02 software broken, unknown data type %d occurred.\n",cbuf[bufpos]);
+ bufpos++;
+ } /* switch on input data type */
+ } /* while there's something in the buffer */
+
+ status->InTail = bufpos; /* tell 65EC02 what we've read */
+
+ } /* if there was something in the buffer */
+ } /* If input is not disabled */
+
+ /* Now check if there's something to output */
+ obuf = mem->OutBuf[p];
+ bufpos = status->OutHead;
+ while ( (port->gs.xmit_cnt > 0) &&
+ (!port->gs.port.tty->stopped) &&
+ (!port->gs.port.tty->hw_stopped) ){ /* While there are chars to transmit */
+ if (((bufpos+1) & A2232_IOBUFLENMASK) != status->OutTail) { /* If the A2232 buffer is not full */
+ ch = port->gs.xmit_buf[port->gs.xmit_tail]; /* get the next char to transmit */
+ port->gs.xmit_tail = (port->gs.xmit_tail+1) & (SERIAL_XMIT_SIZE-1); /* modulo-addition for the gs.xmit_buf ring-buffer */
+ obuf[bufpos++] = ch; /* put it into the A2232 buffer */
+ port->gs.xmit_cnt--;
+ }
+ else{ /* If A2232 the buffer is full */
+ break; /* simply stop filling it. */
+ }
+ }
+ status->OutHead = bufpos;
+
+ /* WakeUp if output buffer runs low */
+ if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.port.tty) {
+ tty_wakeup(port->gs.port.tty);
+ }
+ } // if the port is used
+ } // for every port on the board
+
+ /* Now check the CD message queue */
+ newhead = mem->Common.CDHead;
+ bufpos = mem->Common.CDTail;
+ if (newhead != bufpos){ /* There are CD events in queue */
+ ocd = mem->Common.CDStatus; /* get old status bits */
+ while (newhead != bufpos){ /* read all events */
+ ncd = mem->CDBuf[bufpos++]; /* get one event */
+ ccd = ncd ^ ocd; /* mask of changed lines */
+ ocd = ncd; /* save new status bits */
+ for(p=0; p < NUMLINES; p++){ /* for all ports */
+ if (ccd & 1){ /* this one changed */
+
+ struct a2232_port *port = &a2232_ports[n*7+p];
+ port->cd_status = !(ncd & 1); /* ncd&1 <=> CD is now off */
+
+ if (!(port->gs.port.flags & ASYNC_CHECK_CD))
+ ; /* Don't report DCD changes */
+ else if (port->cd_status) { // if DCD on: DCD went UP!
+
+ /* Are we blocking in open?*/
+ wake_up_interruptible(&port->gs.port.open_wait);
+ }
+ else { // if DCD off: DCD went DOWN!
+ if (port->gs.port.tty)
+ tty_hangup (port->gs.port.tty);
+ }
+
+ } // if CD changed for this port
+ ccd >>= 1;
+ ncd >>= 1; /* Shift bits for next line */
+ } // for every port
+ } // while CD events in queue
+ mem->Common.CDStatus = ocd; /* save new status */
+ mem->Common.CDTail = bufpos; /* remove events */
+ } // if events in CD queue
+
+ } // for every completely initialized A2232 board
+ return IRQ_HANDLED;
+}
+
+static const struct tty_port_operations a2232_port_ops = {
+ .carrier_raised = a2232_carrier_raised,
+};
+
+static void a2232_init_portstructs(void)
+{
+ struct a2232_port *port;
+ int i;
+
+ for (i = 0; i < MAX_A2232_BOARDS*NUMLINES; i++) {
+ port = a2232_ports + i;
+ tty_port_init(&port->gs.port);
+ port->gs.port.ops = &a2232_port_ops;
+ port->which_a2232 = i/NUMLINES;
+ port->which_port_on_a2232 = i%NUMLINES;
+ port->disable_rx = port->throttle_input = port->cd_status = 0;
+ port->gs.magic = A2232_MAGIC;
+ port->gs.close_delay = HZ/2;
+ port->gs.closing_wait = 30 * HZ;
+ port->gs.rd = &a2232_real_driver;
+ }
+}
+
+static const struct tty_operations a2232_ops = {
+ .open = a2232_open,
+ .close = gs_close,
+ .write = gs_write,
+ .put_char = gs_put_char,
+ .flush_chars = gs_flush_chars,
+ .write_room = gs_write_room,
+ .chars_in_buffer = gs_chars_in_buffer,
+ .flush_buffer = gs_flush_buffer,
+ .ioctl = a2232_ioctl,
+ .throttle = a2232_throttle,
+ .unthrottle = a2232_unthrottle,
+ .set_termios = gs_set_termios,
+ .stop = gs_stop,
+ .start = gs_start,
+ .hangup = gs_hangup,
+};
+
+static int a2232_init_drivers(void)
+{
+ int error;
+
+ a2232_driver = alloc_tty_driver(NUMLINES * nr_a2232);
+ if (!a2232_driver)
+ return -ENOMEM;
+ a2232_driver->owner = THIS_MODULE;
+ a2232_driver->driver_name = "commodore_a2232";
+ a2232_driver->name = "ttyY";
+ a2232_driver->major = A2232_NORMAL_MAJOR;
+ a2232_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ a2232_driver->subtype = SERIAL_TYPE_NORMAL;
+ a2232_driver->init_termios = tty_std_termios;
+ a2232_driver->init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ a2232_driver->init_termios.c_ispeed = 9600;
+ a2232_driver->init_termios.c_ospeed = 9600;
+ a2232_driver->flags = TTY_DRIVER_REAL_RAW;
+ tty_set_operations(a2232_driver, &a2232_ops);
+ if ((error = tty_register_driver(a2232_driver))) {
+ printk(KERN_ERR "A2232: Couldn't register A2232 driver, error = %d\n",
+ error);
+ put_tty_driver(a2232_driver);
+ return 1;
+ }
+ return 0;
+}
+
+static int __init a2232board_init(void)
+{
+ struct zorro_dev *z;
+
+ unsigned int boardaddr;
+ int bcount;
+ short start;
+ u_char *from;
+ volatile u_char *to;
+ volatile struct a2232memory *mem;
+ int error, i;
+
+#ifdef CONFIG_SMP
+ return -ENODEV; /* This driver is not SMP aware. Is there an SMP ZorroII-bus-machine? */
+#endif
+
+ if (!MACH_IS_AMIGA){
+ return -ENODEV;
+ }
+
+ printk("Commodore A2232 driver initializing.\n"); /* Say that we're alive. */
+
+ z = NULL;
+ nr_a2232 = 0;
+ while ( (z = zorro_find_device(ZORRO_WILDCARD, z)) ){
+ if ( (z->id != ZORRO_PROD_CBM_A2232_PROTOTYPE) &&
+ (z->id != ZORRO_PROD_CBM_A2232) ){
+ continue; // The board found was no A2232
+ }
+ if (!zorro_request_device(z,"A2232 driver"))
+ continue;
+
+ printk("Commodore A2232 found (#%d).\n",nr_a2232);
+
+ zd_a2232[nr_a2232] = z;
+
+ boardaddr = ZTWO_VADDR( z->resource.start );
+ printk("Board is located at address 0x%x, size is 0x%x.\n", boardaddr, (unsigned int) ((z->resource.end+1) - (z->resource.start)));
+
+ mem = (volatile struct a2232memory *) boardaddr;
+
+ (void) mem->Enable6502Reset; /* copy the code across to the board */
+ to = (u_char *)mem; from = a2232_65EC02code; bcount = sizeof(a2232_65EC02code) - 2;
+ start = *(short *)from;
+ from += sizeof(start);
+ to += start;
+ while(bcount--) *to++ = *from++;
+ printk("65EC02 software uploaded to the A2232 memory.\n");
+
+ mem->Common.Crystal = A2232_UNKNOWN; /* use automatic speed check */
+
+ /* start 6502 running */
+ (void) mem->ResetBoard;
+ printk("A2232's 65EC02 CPU up and running.\n");
+
+ /* wait until speed detector has finished */
+ for (bcount = 0; bcount < 2000; bcount++) {
+ udelay(1000);
+ if (mem->Common.Crystal)
+ break;
+ }
+ printk((mem->Common.Crystal?"A2232 oscillator crystal detected by 65EC02 software: ":"65EC02 software could not determine A2232 oscillator crystal: "));
+ switch (mem->Common.Crystal){
+ case A2232_UNKNOWN:
+ printk("Unknown crystal.\n");
+ break;
+ case A2232_NORMAL:
+ printk ("Normal crystal.\n");
+ break;
+ case A2232_TURBO:
+ printk ("Turbo crystal.\n");
+ break;
+ default:
+ printk ("0x%x. Huh?\n",mem->Common.Crystal);
+ }
+
+ nr_a2232++;
+
+ }
+
+ printk("Total: %d A2232 boards initialized.\n", nr_a2232); /* Some status report if no card was found */
+
+ a2232_init_portstructs();
+
+ /*
+ a2232_init_drivers also registers the drivers. Must be here because all boards
+ have to be detected first.
+ */
+ if (a2232_init_drivers()) return -ENODEV; // maybe we should use a different -Exxx?
+
+ error = request_irq(IRQ_AMIGA_VERTB, a2232_vbl_inter, 0,
+ "A2232 serial VBL", a2232_driver_ID);
+ if (error) {
+ for (i = 0; i < nr_a2232; i++)
+ zorro_release_device(zd_a2232[i]);
+ tty_unregister_driver(a2232_driver);
+ put_tty_driver(a2232_driver);
+ }
+ return error;
+}
+
+static void __exit a2232board_exit(void)
+{
+ int i;
+
+ for (i = 0; i < nr_a2232; i++) {
+ zorro_release_device(zd_a2232[i]);
+ }
+
+ tty_unregister_driver(a2232_driver);
+ put_tty_driver(a2232_driver);
+ free_irq(IRQ_AMIGA_VERTB, a2232_driver_ID);
+}
+
+module_init(a2232board_init);
+module_exit(a2232board_exit);
+
+MODULE_AUTHOR("Enver Haase");
+MODULE_DESCRIPTION("Amiga A2232 multi-serial board driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/generic_serial/ser_a2232.h b/drivers/staging/generic_serial/ser_a2232.h
new file mode 100644
index 000000000000..bc09eb9e118b
--- /dev/null
+++ b/drivers/staging/generic_serial/ser_a2232.h
@@ -0,0 +1,202 @@
+/* drivers/char/ser_a2232.h */
+
+/* $Id: ser_a2232.h,v 0.4 2000/01/25 12:00:00 ehaase Exp $ */
+
+/* Linux serial driver for the Amiga A2232 board */
+
+/* This driver is MAINTAINED. Before applying any changes, please contact
+ * the author.
+ */
+
+/* Copyright (c) 2000-2001 Enver Haase <ehaase@inf.fu-berlin.de>
+ * alias The A2232 driver project <A2232@gmx.net>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _SER_A2232_H_
+#define _SER_A2232_H_
+
+/*
+ How many boards are to be supported at maximum;
+ "up to five A2232 Multiport Serial Cards may be installed in a
+ single Amiga 2000" states the A2232 User's Guide. If you have
+ more slots available, you might want to change the value below.
+*/
+#define MAX_A2232_BOARDS 5
+
+#ifndef A2232_NORMAL_MAJOR
+/* This allows overriding on the compiler commandline, or in a "major.h"
+ include or something like that */
+#define A2232_NORMAL_MAJOR 224 /* /dev/ttyY* */
+#define A2232_CALLOUT_MAJOR 225 /* /dev/cuy* */
+#endif
+
+/* Some magic is always good - Who knows :) */
+#define A2232_MAGIC 0x000a2232
+
+/* A2232 port structure to keep track of the
+ status of every single line used */
+struct a2232_port{
+ struct gs_port gs;
+ unsigned int which_a2232;
+ unsigned int which_port_on_a2232;
+ short disable_rx;
+ short throttle_input;
+ short cd_status;
+};
+
+#define NUMLINES 7 /* number of lines per board */
+#define A2232_IOBUFLEN 256 /* number of bytes per buffer */
+#define A2232_IOBUFLENMASK 0xff /* mask for maximum number of bytes */
+
+
+#define A2232_UNKNOWN 0 /* crystal not known */
+#define A2232_NORMAL 1 /* normal A2232 (1.8432 MHz oscillator) */
+#define A2232_TURBO 2 /* turbo A2232 (3.6864 MHz oscillator) */
+
+
+struct a2232common {
+ char Crystal; /* normal (1) or turbo (2) board? */
+ u_char Pad_a;
+ u_char TimerH; /* timer value after speed check */
+ u_char TimerL;
+ u_char CDHead; /* head pointer for CD message queue */
+ u_char CDTail; /* tail pointer for CD message queue */
+ u_char CDStatus;
+ u_char Pad_b;
+};
+
+struct a2232status {
+ u_char InHead; /* input queue head */
+ u_char InTail; /* input queue tail */
+ u_char OutDisable; /* disables output */
+ u_char OutHead; /* output queue head */
+ u_char OutTail; /* output queue tail */
+ u_char OutCtrl; /* soft flow control character to send */
+ u_char OutFlush; /* flushes output buffer */
+ u_char Setup; /* causes reconfiguration */
+ u_char Param; /* parameter byte - see A2232PARAM */
+ u_char Command; /* command byte - see A2232CMD */
+ u_char SoftFlow; /* enables xon/xoff flow control */
+ /* private 65EC02 fields: */
+ u_char XonOff; /* stores XON/XOFF enable/disable */
+};
+
+#define A2232_MEMPAD1 \
+ (0x0200 - NUMLINES * sizeof(struct a2232status) - \
+ sizeof(struct a2232common))
+#define A2232_MEMPAD2 (0x2000 - NUMLINES * A2232_IOBUFLEN - A2232_IOBUFLEN)
+
+struct a2232memory {
+ struct a2232status Status[NUMLINES]; /* 0x0000-0x006f status areas */
+ struct a2232common Common; /* 0x0070-0x0077 common flags */
+ u_char Dummy1[A2232_MEMPAD1]; /* 0x00XX-0x01ff */
+ u_char OutBuf[NUMLINES][A2232_IOBUFLEN];/* 0x0200-0x08ff output bufs */
+ u_char InBuf[NUMLINES][A2232_IOBUFLEN]; /* 0x0900-0x0fff input bufs */
+ u_char InCtl[NUMLINES][A2232_IOBUFLEN]; /* 0x1000-0x16ff control data */
+ u_char CDBuf[A2232_IOBUFLEN]; /* 0x1700-0x17ff CD event buffer */
+ u_char Dummy2[A2232_MEMPAD2]; /* 0x1800-0x2fff */
+ u_char Code[0x1000]; /* 0x3000-0x3fff code area */
+ u_short InterruptAck; /* 0x4000 intr ack */
+ u_char Dummy3[0x3ffe]; /* 0x4002-0x7fff */
+ u_short Enable6502Reset; /* 0x8000 Stop board, */
+ /* 6502 RESET line held low */
+ u_char Dummy4[0x3ffe]; /* 0x8002-0xbfff */
+ u_short ResetBoard; /* 0xc000 reset board & run, */
+ /* 6502 RESET line held high */
+};
+
+#undef A2232_MEMPAD1
+#undef A2232_MEMPAD2
+
+#define A2232INCTL_CHAR 0 /* corresponding byte in InBuf is a character */
+#define A2232INCTL_EVENT 1 /* corresponding byte in InBuf is an event */
+
+#define A2232EVENT_Break 1 /* break set */
+#define A2232EVENT_CarrierOn 2 /* carrier raised */
+#define A2232EVENT_CarrierOff 3 /* carrier dropped */
+#define A2232EVENT_Sync 4 /* don't know, defined in 2232.ax */
+
+#define A2232CMD_Enable 0x1 /* enable/DTR bit */
+#define A2232CMD_Close 0x2 /* close the device */
+#define A2232CMD_Open 0xb /* open the device */
+#define A2232CMD_CMask 0xf /* command mask */
+#define A2232CMD_RTSOff 0x0 /* turn off RTS */
+#define A2232CMD_RTSOn 0x8 /* turn on RTS */
+#define A2232CMD_Break 0xd /* transmit a break */
+#define A2232CMD_RTSMask 0xc /* mask for RTS stuff */
+#define A2232CMD_NoParity 0x00 /* don't use parity */
+#define A2232CMD_OddParity 0x20 /* odd parity */
+#define A2232CMD_EvenParity 0x60 /* even parity */
+#define A2232CMD_ParityMask 0xe0 /* parity mask */
+
+#define A2232PARAM_B115200 0x0 /* baud rates */
+#define A2232PARAM_B50 0x1
+#define A2232PARAM_B75 0x2
+#define A2232PARAM_B110 0x3
+#define A2232PARAM_B134 0x4
+#define A2232PARAM_B150 0x5
+#define A2232PARAM_B300 0x6
+#define A2232PARAM_B600 0x7
+#define A2232PARAM_B1200 0x8
+#define A2232PARAM_B1800 0x9
+#define A2232PARAM_B2400 0xa
+#define A2232PARAM_B3600 0xb
+#define A2232PARAM_B4800 0xc
+#define A2232PARAM_B7200 0xd
+#define A2232PARAM_B9600 0xe
+#define A2232PARAM_B19200 0xf
+#define A2232PARAM_BaudMask 0xf /* baud rate mask */
+#define A2232PARAM_RcvBaud 0x10 /* enable receive baud rate */
+#define A2232PARAM_8Bit 0x00 /* numbers of bits */
+#define A2232PARAM_7Bit 0x20
+#define A2232PARAM_6Bit 0x40
+#define A2232PARAM_5Bit 0x60
+#define A2232PARAM_BitMask 0x60 /* numbers of bits mask */
+
+
+/* Standard speeds tables, -1 means unavailable, -2 means 0 baud: switch off line */
+#define A2232_BAUD_TABLE_NOAVAIL -1
+#define A2232_BAUD_TABLE_NUM_RATES (18)
+static int a2232_baud_table[A2232_BAUD_TABLE_NUM_RATES*3] = {
+ //Baud //Normal //Turbo
+ 50, A2232PARAM_B50, A2232_BAUD_TABLE_NOAVAIL,
+ 75, A2232PARAM_B75, A2232_BAUD_TABLE_NOAVAIL,
+ 110, A2232PARAM_B110, A2232_BAUD_TABLE_NOAVAIL,
+ 134, A2232PARAM_B134, A2232_BAUD_TABLE_NOAVAIL,
+ 150, A2232PARAM_B150, A2232PARAM_B75,
+ 200, A2232_BAUD_TABLE_NOAVAIL, A2232_BAUD_TABLE_NOAVAIL,
+ 300, A2232PARAM_B300, A2232PARAM_B150,
+ 600, A2232PARAM_B600, A2232PARAM_B300,
+ 1200, A2232PARAM_B1200, A2232PARAM_B600,
+ 1800, A2232PARAM_B1800, A2232_BAUD_TABLE_NOAVAIL,
+ 2400, A2232PARAM_B2400, A2232PARAM_B1200,
+ 4800, A2232PARAM_B4800, A2232PARAM_B2400,
+ 9600, A2232PARAM_B9600, A2232PARAM_B4800,
+ 19200, A2232PARAM_B19200, A2232PARAM_B9600,
+ 38400, A2232_BAUD_TABLE_NOAVAIL, A2232PARAM_B19200,
+ 57600, A2232_BAUD_TABLE_NOAVAIL, A2232_BAUD_TABLE_NOAVAIL,
+#ifdef A2232_SPEEDHACK
+ 115200, A2232PARAM_B115200, A2232_BAUD_TABLE_NOAVAIL,
+ 230400, A2232_BAUD_TABLE_NOAVAIL, A2232PARAM_B115200
+#else
+ 115200, A2232_BAUD_TABLE_NOAVAIL, A2232_BAUD_TABLE_NOAVAIL,
+ 230400, A2232_BAUD_TABLE_NOAVAIL, A2232_BAUD_TABLE_NOAVAIL
+#endif
+};
+#endif
diff --git a/drivers/staging/generic_serial/ser_a2232fw.ax b/drivers/staging/generic_serial/ser_a2232fw.ax
new file mode 100644
index 000000000000..736438032768
--- /dev/null
+++ b/drivers/staging/generic_serial/ser_a2232fw.ax
@@ -0,0 +1,529 @@
+;.lib "axm"
+;
+;begin
+;title "A2232 serial board driver"
+;
+;set modules "2232"
+;set executable "2232.bin"
+;
+;;;;set nolink
+;
+;set temporary directory "t:"
+;
+;set assembly options "-m6502 -l60:t:list"
+;set link options "bin"; loadadr"
+;;;bin2c 2232.bin msc6502.h msc6502code
+;end
+;
+;
+; ### Commodore A2232 serial board driver for NetBSD by JM v1.3 ###
+;
+; - Created 950501 by JM -
+;
+;
+; Serial board driver software.
+;
+;
+% Copyright (c) 1995 Jukka Marin <jmarin@jmp.fi>.
+% All rights reserved.
+%
+% Redistribution and use in source and binary forms, with or without
+% modification, are permitted provided that the following conditions
+% are met:
+% 1. Redistributions of source code must retain the above copyright
+% notice, and the entire permission notice in its entirety,
+% including the disclaimer of warranties.
+% 2. Redistributions in binary form must reproduce the above copyright
+% notice, this list of conditions and the following disclaimer in the
+% documentation and/or other materials provided with the distribution.
+% 3. The name of the author may not be used to endorse or promote
+% products derived from this software without specific prior
+% written permission.
+%
+% ALTERNATIVELY, this product may be distributed under the terms of
+% the GNU General Public License, in which case the provisions of the
+% GPL are required INSTEAD OF the above restrictions. (This clause is
+% necessary due to a potential bad interaction between the GPL and
+% the restrictions contained in a BSD-style copyright.)
+%
+% THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+% WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+% OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+% DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+% INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+% (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+% SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+% HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+% STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+% OF THE POSSIBILITY OF SUCH DAMAGE.
+;
+;
+; Bugs:
+;
+; - Can't send a break yet
+;
+;
+;
+; Edited:
+;
+; - 950501 by JM -> v0.1 - Created this file.
+; - 951029 by JM -> v1.3 - Carrier Detect events now queued in a separate
+; queue.
+;
+;
+
+
+CODE equ $3800 ; start address for program code
+
+
+CTL_CHAR equ $00 ; byte in ibuf is a character
+CTL_EVENT equ $01 ; byte in ibuf is an event
+
+EVENT_BREAK equ $01
+EVENT_CDON equ $02
+EVENT_CDOFF equ $03
+EVENT_SYNC equ $04
+
+XON equ $11
+XOFF equ $13
+
+
+VARBASE macro *starting_address ; was VARINIT
+_varbase set \1
+ endm
+
+VARDEF macro *name space_needs
+\1 equ _varbase
+_varbase set _varbase+\2
+ endm
+
+
+stz macro * address
+ db $64,\1
+ endm
+
+stzax macro * address
+ db $9e,<\1,>\1
+ endm
+
+
+biti macro * immediate value
+ db $89,\1
+ endm
+
+smb0 macro * address
+ db $87,\1
+ endm
+smb1 macro * address
+ db $97,\1
+ endm
+smb2 macro * address
+ db $a7,\1
+ endm
+smb3 macro * address
+ db $b7,\1
+ endm
+smb4 macro * address
+ db $c7,\1
+ endm
+smb5 macro * address
+ db $d7,\1
+ endm
+smb6 macro * address
+ db $e7,\1
+ endm
+smb7 macro * address
+ db $f7,\1
+ endm
+
+
+
+;-----------------------------------------------------------------------;
+; ;
+; stuff common for all ports, non-critical (run once / loop) ;
+; ;
+DO_SLOW macro * port_number ;
+ .local ; ;
+ lda CIA+C_PA ; check all CD inputs ;
+ cmp CommonCDo ; changed from previous accptd? ;
+ beq =over ; nope, do nothing else here ;
+ ; ;
+ cmp CommonCDb ; bouncing? ;
+ beq =nobounce ; nope -> ;
+ ; ;
+ sta CommonCDb ; save current state ;
+ lda #64 ; reinitialize counter ;
+ sta CommonCDc ; ;
+ jmp =over ; skip CD save ;
+ ; ;
+=nobounce dec CommonCDc ; no, decrement bounce counter ;
+ bpl =over ; not done yet, so skip CD save ;
+ ; ;
+=saveCD ldx CDHead ; get write index ;
+ sta cdbuf,x ; save status in buffer ;
+ inx ; ;
+ cpx CDTail ; buffer full? ;
+ .if ne ; no: preserve status: ;
+ stx CDHead ; update index in RAM ;
+ sta CommonCDo ; save state for the next check ;
+ .end ; ;
+=over .end local ;
+ endm ;
+ ;
+;-----------------------------------------------------------------------;
+
+
+; port specific stuff (no data transfer)
+
+DO_PORT macro * port_number
+ .local ; ;
+ lda SetUp\1 ; reconfiguration request? ;
+ .if ne ; yes: ;
+ lda SoftFlow\1 ; get XON/XOFF flag ;
+ sta XonOff\1 ; save it ;
+ lda Param\1 ; get parameter ;
+ ora #%00010000 ; use baud generator for Rx ;
+ sta ACIA\1+A_CTRL ; store in control register ;
+ stz OutDisable\1 ; enable transmit output ;
+ stz SetUp\1 ; no reconfiguration no more ;
+ .end ; ;
+ ; ;
+ lda InHead\1 ; get write index ;
+ sbc InTail\1 ; buffer full soon? ;
+ cmp #200 ; 200 chars or more in buffer? ;
+ lda Command\1 ; get Command reg value ;
+ and #%11110011 ; turn RTS OFF by default ;
+ .if cc ; still room in buffer: ;
+ ora #%00001000 ; turn RTS ON ;
+ .end ; ;
+ sta ACIA\1+A_CMD ; set/clear RTS ;
+ ; ;
+ lda OutFlush\1 ; request to flush output buffer;
+ .if ne ; yessh! ;
+ lda OutHead\1 ; get head ;
+ sta OutTail\1 ; save as tail ;
+ stz OutDisable\1 ; enable transmit output ;
+ stz OutFlush\1 ; clear request ;
+ .end
+ .end local
+ endm
+
+
+DO_DATA macro * port number
+ .local
+ lda ACIA\1+A_SR ; read ACIA status register ;
+ biti [1<<3] ; something received? ;
+ .if ne ; yes: ;
+ biti [1<<1] ; framing error? ;
+ .if ne ; yes: ;
+ lda ACIA\1+A_DATA ; read received character ;
+ bne =SEND ; not break -> ignore it ;
+ ldx InHead\1 ; get write pointer ;
+ lda #CTL_EVENT ; get type of byte ;
+ sta ictl\1,x ; save it in InCtl buffer ;
+ lda #EVENT_BREAK ; event code ;
+ sta ibuf\1,x ; save it as well ;
+ inx ; ;
+ cpx InTail\1 ; still room in buffer? ;
+ .if ne ; absolutely: ;
+ stx InHead\1 ; update index in memory ;
+ .end ; ;
+ jmp =SEND ; go check if anything to send ;
+ .end ; ;
+ ; normal char received: ;
+ ldx InHead\1 ; get write index ;
+ lda ACIA\1+A_DATA ; read received character ;
+ sta ibuf\1,x ; save char in buffer ;
+ stzax ictl\1 ; set type to CTL_CHAR ;
+ inx ; ;
+ cpx InTail\1 ; buffer full? ;
+ .if ne ; no: preserve character: ;
+ stx InHead\1 ; update index in RAM ;
+ .end ; ;
+ and #$7f ; mask off parity if any ;
+ cmp #XOFF ; XOFF from remote host? ;
+ .if eq ; yes: ;
+ lda XonOff\1 ; if XON/XOFF handshaking.. ;
+ sta OutDisable\1 ; ..disable transmitter ;
+ .end ; ;
+ .end ; ;
+ ; ;
+ ; BUFFER FULL CHECK WAS HERE ;
+ ; ;
+=SEND lda ACIA\1+A_SR ; transmit register empty? ;
+ and #[1<<4] ; ;
+ .if ne ; yes: ;
+ ldx OutCtrl\1 ; sending out XON/XOFF? ;
+ .if ne ; yes: ;
+ lda CIA+C_PB ; check CTS signal ;
+ and #[1<<\1] ; (for this port only) ;
+ bne =DONE ; not allowed to send -> done ;
+ stx ACIA\1+A_DATA ; transmit control char ;
+ stz OutCtrl\1 ; clear flag ;
+ jmp =DONE ; and we're done ;
+ .end ; ;
+ ; ;
+ ldx OutTail\1 ; anything to transmit? ;
+ cpx OutHead\1 ; ;
+ .if ne ; yes: ;
+ lda OutDisable\1 ; allowed to transmit? ;
+ .if eq ; yes: ;
+ lda CIA+C_PB ; check CTS signal ;
+ and #[1<<\1] ; (for this port only) ;
+ bne =DONE ; not allowed to send -> done ;
+ lda obuf\1,x ; get a char from buffer ;
+ sta ACIA\1+A_DATA ; send it away ;
+ inc OutTail\1 ; update read index ;
+ .end ; ;
+ .end ; ;
+ .end ; ;
+=DONE .end local
+ endm
+
+
+
+PORTVAR macro * port number
+ VARDEF InHead\1 1
+ VARDEF InTail\1 1
+ VARDEF OutDisable\1 1
+ VARDEF OutHead\1 1
+ VARDEF OutTail\1 1
+ VARDEF OutCtrl\1 1
+ VARDEF OutFlush\1 1
+ VARDEF SetUp\1 1
+ VARDEF Param\1 1
+ VARDEF Command\1 1
+ VARDEF SoftFlow\1 1
+ ; private:
+ VARDEF XonOff\1 1
+ endm
+
+
+ VARBASE 0 ; start variables at address $0000
+ PORTVAR 0 ; define variables for port 0
+ PORTVAR 1 ; define variables for port 1
+ PORTVAR 2 ; define variables for port 2
+ PORTVAR 3 ; define variables for port 3
+ PORTVAR 4 ; define variables for port 4
+ PORTVAR 5 ; define variables for port 5
+ PORTVAR 6 ; define variables for port 6
+
+
+
+ VARDEF Crystal 1 ; 0 = unknown, 1 = normal, 2 = turbo
+ VARDEF Pad_a 1
+ VARDEF TimerH 1
+ VARDEF TimerL 1
+ VARDEF CDHead 1
+ VARDEF CDTail 1
+ VARDEF CDStatus 1
+ VARDEF Pad_b 1
+
+ VARDEF CommonCDo 1 ; for carrier detect optimization
+ VARDEF CommonCDc 1 ; for carrier detect debouncing
+ VARDEF CommonCDb 1 ; for carrier detect debouncing
+
+
+ VARBASE $0200
+ VARDEF obuf0 256 ; output data (characters only)
+ VARDEF obuf1 256
+ VARDEF obuf2 256
+ VARDEF obuf3 256
+ VARDEF obuf4 256
+ VARDEF obuf5 256
+ VARDEF obuf6 256
+
+ VARDEF ibuf0 256 ; input data (characters, events etc - see ictl)
+ VARDEF ibuf1 256
+ VARDEF ibuf2 256
+ VARDEF ibuf3 256
+ VARDEF ibuf4 256
+ VARDEF ibuf5 256
+ VARDEF ibuf6 256
+
+ VARDEF ictl0 256 ; input control information (type of data in ibuf)
+ VARDEF ictl1 256
+ VARDEF ictl2 256
+ VARDEF ictl3 256
+ VARDEF ictl4 256
+ VARDEF ictl5 256
+ VARDEF ictl6 256
+
+ VARDEF cdbuf 256 ; CD event queue
+
+
+ACIA0 equ $4400
+ACIA1 equ $4c00
+ACIA2 equ $5400
+ACIA3 equ $5c00
+ACIA4 equ $6400
+ACIA5 equ $6c00
+ACIA6 equ $7400
+
+A_DATA equ $00
+A_SR equ $02
+A_CMD equ $04
+A_CTRL equ $06
+; 00 write transmit data read received data
+; 02 reset ACIA read status register
+; 04 write command register read command register
+; 06 write control register read control register
+
+CIA equ $7c00 ; 8520 CIA
+C_PA equ $00 ; port A data register
+C_PB equ $02 ; port B data register
+C_DDRA equ $04 ; data direction register for port A
+C_DDRB equ $06 ; data direction register for port B
+C_TAL equ $08 ; timer A
+C_TAH equ $0a
+C_TBL equ $0c ; timer B
+C_TBH equ $0e
+C_TODL equ $10 ; TOD LSB
+C_TODM equ $12 ; TOD middle byte
+C_TODH equ $14 ; TOD MSB
+C_DATA equ $18 ; serial data register
+C_INTCTRL equ $1a ; interrupt control register
+C_CTRLA equ $1c ; control register A
+C_CTRLB equ $1e ; control register B
+
+
+
+
+
+ section main,code,CODE-2
+
+ db >CODE,<CODE
+
+;-----------------------------------------------------------------------;
+; here's the initialization code: ;
+; ;
+R_RESET ldx #$ff ;
+ txs ; initialize stack pointer ;
+ cld ; in case a 6502 is used... ;
+ ldx #0 ; ;
+ lda #0 ; ;
+ ldy #Crystal ; this many bytes to clear ;
+clr_loop sta 0,x ; clear zero page variables ;
+ inx ; ;
+ dey ; ;
+ bne clr_loop ; ;
+ ; ;
+ stz CommonCDo ; force CD test at boot ;
+ stz CommonCDb ; ;
+ stz CDHead ; clear queue ;
+ stz CDTail ; ;
+ ; ;
+ lda #0 ; ;
+ sta Pad_a ; ;
+ lda #170 ; test cmp ;
+ cmp #100 ; ;
+ .if cs ; ;
+ inc Pad_a ; C was set ;
+ .end ; ;
+ ;
+;-----------------------------------------------------------------------;
+; Speed check ;
+;-----------------------------------------------------------------------;
+ ;
+ lda Crystal ; speed already set? ;
+ beq DoSpeedy ; ;
+ jmp LOOP ; yes, skip speed test ;
+ ; ;
+DoSpeedy lda #%10011000 ; 8N1, 1200/2400 bps ;
+ sta ACIA0+A_CTRL ; ;
+ lda #%00001011 ; enable DTR ;
+ sta ACIA0+A_CMD ; ;
+ lda ACIA0+A_SR ; read status register ;
+ ; ;
+ lda #%10000000 ; disable all ints (unnecessary);
+ sta CIA+C_INTCTRL ; ;
+ lda #255 ; program the timer ;
+ sta CIA+C_TAL ; ;
+ sta CIA+C_TAH ; ;
+ ; ;
+ ldx #0 ; ;
+ stx ACIA0+A_DATA ; transmit a zero ;
+ nop ; ;
+ nop ; ;
+ lda ACIA0+A_SR ; read status ;
+ nop ; ;
+ nop ; ;
+ stx ACIA0+A_DATA ; transmit a zero ;
+Speedy1 lda ACIA0+A_SR ; read status ;
+ and #[1<<4] ; transmit data reg empty? ;
+ beq Speedy1 ; not yet, wait more ;
+ ; ;
+ lda #%00010001 ; load & start the timer ;
+ stx ACIA0+A_DATA ; transmit one more zero ;
+ sta CIA+C_CTRLA ; ;
+Speedy2 lda ACIA0+A_SR ; read status ;
+ and #[1<<4] ; transmit data reg empty? ;
+ beq Speedy2 ; not yet, wait more ;
+ stx CIA+C_CTRLA ; stop the timer ;
+ ; ;
+ lda CIA+C_TAL ; copy timer value for 68k ;
+ sta TimerL ; ;
+ lda CIA+C_TAH ; ;
+ sta TimerH ; ;
+ cmp #$d0 ; turbo or normal? ;
+ .if cs ; ;
+ lda #2 ; turbo! :-) ;
+ .else ; ;
+ lda #1 ; normal :-( ;
+ .end ; ;
+ sta Crystal ; ;
+ lda #0 ; ;
+ sta ACIA0+A_SR ; ;
+ sta ACIA0+A_CTRL ; reset UART ;
+ sta ACIA0+A_CMD ; ;
+ ;
+ jmp LOOP ;
+ ;
+; ;
+;-----------------------------------------------------------------------;
+; ;
+; The Real Thing: ;
+; ;
+LOOP DO_SLOW ; do non-critical things ;
+ jsr do_input ; check for received data
+ DO_PORT 0
+ jsr do_input
+ DO_PORT 1
+ jsr do_input
+ DO_PORT 2
+ jsr do_input
+ DO_PORT 3
+ jsr do_input
+ DO_PORT 4
+ jsr do_input
+ DO_PORT 5
+ jsr do_input
+ DO_PORT 6
+ jsr do_input
+ jmp LOOP
+
+
+do_input DO_DATA 0
+ DO_DATA 1
+ DO_DATA 2
+ DO_DATA 3
+ DO_DATA 4
+ DO_DATA 5
+ DO_DATA 6
+ rts
+
+
+;-----------------------------------------------------------------------;
+ section vectors,data,$3ffa
+ dw $d0d0
+ dw R_RESET
+ dw $c0ce
+;-----------------------------------------------------------------------;
+
+
+
+ end
+
+
+
diff --git a/drivers/staging/generic_serial/ser_a2232fw.h b/drivers/staging/generic_serial/ser_a2232fw.h
new file mode 100644
index 000000000000..e09a30acfe5c
--- /dev/null
+++ b/drivers/staging/generic_serial/ser_a2232fw.h
@@ -0,0 +1,306 @@
+/* drivers/char/ser_a2232fw.h */
+
+/* $Id: ser_a2232fw.h,v 0.4 2000/01/25 12:00:00 ehaase Exp $ */
+
+/*
+ * Copyright (c) 1995 Jukka Marin <jmarin@jmp.fi>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* This is the 65EC02 code by Jukka Marin that is executed by
+ the A2232's 65EC02 processor (base address: 0x3800)
+ Source file: ser_a2232fw.ax
+ Version: 1.3 (951029)
+ Known Bugs: Cannot send a break yet
+*/
+static unsigned char a2232_65EC02code[] = {
+ 0x38, 0x00, 0xA2, 0xFF, 0x9A, 0xD8, 0xA2, 0x00,
+ 0xA9, 0x00, 0xA0, 0x54, 0x95, 0x00, 0xE8, 0x88,
+ 0xD0, 0xFA, 0x64, 0x5C, 0x64, 0x5E, 0x64, 0x58,
+ 0x64, 0x59, 0xA9, 0x00, 0x85, 0x55, 0xA9, 0xAA,
+ 0xC9, 0x64, 0x90, 0x02, 0xE6, 0x55, 0xA5, 0x54,
+ 0xF0, 0x03, 0x4C, 0x92, 0x38, 0xA9, 0x98, 0x8D,
+ 0x06, 0x44, 0xA9, 0x0B, 0x8D, 0x04, 0x44, 0xAD,
+ 0x02, 0x44, 0xA9, 0x80, 0x8D, 0x1A, 0x7C, 0xA9,
+ 0xFF, 0x8D, 0x08, 0x7C, 0x8D, 0x0A, 0x7C, 0xA2,
+ 0x00, 0x8E, 0x00, 0x44, 0xEA, 0xEA, 0xAD, 0x02,
+ 0x44, 0xEA, 0xEA, 0x8E, 0x00, 0x44, 0xAD, 0x02,
+ 0x44, 0x29, 0x10, 0xF0, 0xF9, 0xA9, 0x11, 0x8E,
+ 0x00, 0x44, 0x8D, 0x1C, 0x7C, 0xAD, 0x02, 0x44,
+ 0x29, 0x10, 0xF0, 0xF9, 0x8E, 0x1C, 0x7C, 0xAD,
+ 0x08, 0x7C, 0x85, 0x57, 0xAD, 0x0A, 0x7C, 0x85,
+ 0x56, 0xC9, 0xD0, 0x90, 0x05, 0xA9, 0x02, 0x4C,
+ 0x82, 0x38, 0xA9, 0x01, 0x85, 0x54, 0xA9, 0x00,
+ 0x8D, 0x02, 0x44, 0x8D, 0x06, 0x44, 0x8D, 0x04,
+ 0x44, 0x4C, 0x92, 0x38, 0xAD, 0x00, 0x7C, 0xC5,
+ 0x5C, 0xF0, 0x1F, 0xC5, 0x5E, 0xF0, 0x09, 0x85,
+ 0x5E, 0xA9, 0x40, 0x85, 0x5D, 0x4C, 0xB8, 0x38,
+ 0xC6, 0x5D, 0x10, 0x0E, 0xA6, 0x58, 0x9D, 0x00,
+ 0x17, 0xE8, 0xE4, 0x59, 0xF0, 0x04, 0x86, 0x58,
+ 0x85, 0x5C, 0x20, 0x23, 0x3A, 0xA5, 0x07, 0xF0,
+ 0x0F, 0xA5, 0x0A, 0x85, 0x0B, 0xA5, 0x08, 0x09,
+ 0x10, 0x8D, 0x06, 0x44, 0x64, 0x02, 0x64, 0x07,
+ 0xA5, 0x00, 0xE5, 0x01, 0xC9, 0xC8, 0xA5, 0x09,
+ 0x29, 0xF3, 0xB0, 0x02, 0x09, 0x08, 0x8D, 0x04,
+ 0x44, 0xA5, 0x06, 0xF0, 0x08, 0xA5, 0x03, 0x85,
+ 0x04, 0x64, 0x02, 0x64, 0x06, 0x20, 0x23, 0x3A,
+ 0xA5, 0x13, 0xF0, 0x0F, 0xA5, 0x16, 0x85, 0x17,
+ 0xA5, 0x14, 0x09, 0x10, 0x8D, 0x06, 0x4C, 0x64,
+ 0x0E, 0x64, 0x13, 0xA5, 0x0C, 0xE5, 0x0D, 0xC9,
+ 0xC8, 0xA5, 0x15, 0x29, 0xF3, 0xB0, 0x02, 0x09,
+ 0x08, 0x8D, 0x04, 0x4C, 0xA5, 0x12, 0xF0, 0x08,
+ 0xA5, 0x0F, 0x85, 0x10, 0x64, 0x0E, 0x64, 0x12,
+ 0x20, 0x23, 0x3A, 0xA5, 0x1F, 0xF0, 0x0F, 0xA5,
+ 0x22, 0x85, 0x23, 0xA5, 0x20, 0x09, 0x10, 0x8D,
+ 0x06, 0x54, 0x64, 0x1A, 0x64, 0x1F, 0xA5, 0x18,
+ 0xE5, 0x19, 0xC9, 0xC8, 0xA5, 0x21, 0x29, 0xF3,
+ 0xB0, 0x02, 0x09, 0x08, 0x8D, 0x04, 0x54, 0xA5,
+ 0x1E, 0xF0, 0x08, 0xA5, 0x1B, 0x85, 0x1C, 0x64,
+ 0x1A, 0x64, 0x1E, 0x20, 0x23, 0x3A, 0xA5, 0x2B,
+ 0xF0, 0x0F, 0xA5, 0x2E, 0x85, 0x2F, 0xA5, 0x2C,
+ 0x09, 0x10, 0x8D, 0x06, 0x5C, 0x64, 0x26, 0x64,
+ 0x2B, 0xA5, 0x24, 0xE5, 0x25, 0xC9, 0xC8, 0xA5,
+ 0x2D, 0x29, 0xF3, 0xB0, 0x02, 0x09, 0x08, 0x8D,
+ 0x04, 0x5C, 0xA5, 0x2A, 0xF0, 0x08, 0xA5, 0x27,
+ 0x85, 0x28, 0x64, 0x26, 0x64, 0x2A, 0x20, 0x23,
+ 0x3A, 0xA5, 0x37, 0xF0, 0x0F, 0xA5, 0x3A, 0x85,
+ 0x3B, 0xA5, 0x38, 0x09, 0x10, 0x8D, 0x06, 0x64,
+ 0x64, 0x32, 0x64, 0x37, 0xA5, 0x30, 0xE5, 0x31,
+ 0xC9, 0xC8, 0xA5, 0x39, 0x29, 0xF3, 0xB0, 0x02,
+ 0x09, 0x08, 0x8D, 0x04, 0x64, 0xA5, 0x36, 0xF0,
+ 0x08, 0xA5, 0x33, 0x85, 0x34, 0x64, 0x32, 0x64,
+ 0x36, 0x20, 0x23, 0x3A, 0xA5, 0x43, 0xF0, 0x0F,
+ 0xA5, 0x46, 0x85, 0x47, 0xA5, 0x44, 0x09, 0x10,
+ 0x8D, 0x06, 0x6C, 0x64, 0x3E, 0x64, 0x43, 0xA5,
+ 0x3C, 0xE5, 0x3D, 0xC9, 0xC8, 0xA5, 0x45, 0x29,
+ 0xF3, 0xB0, 0x02, 0x09, 0x08, 0x8D, 0x04, 0x6C,
+ 0xA5, 0x42, 0xF0, 0x08, 0xA5, 0x3F, 0x85, 0x40,
+ 0x64, 0x3E, 0x64, 0x42, 0x20, 0x23, 0x3A, 0xA5,
+ 0x4F, 0xF0, 0x0F, 0xA5, 0x52, 0x85, 0x53, 0xA5,
+ 0x50, 0x09, 0x10, 0x8D, 0x06, 0x74, 0x64, 0x4A,
+ 0x64, 0x4F, 0xA5, 0x48, 0xE5, 0x49, 0xC9, 0xC8,
+ 0xA5, 0x51, 0x29, 0xF3, 0xB0, 0x02, 0x09, 0x08,
+ 0x8D, 0x04, 0x74, 0xA5, 0x4E, 0xF0, 0x08, 0xA5,
+ 0x4B, 0x85, 0x4C, 0x64, 0x4A, 0x64, 0x4E, 0x20,
+ 0x23, 0x3A, 0x4C, 0x92, 0x38, 0xAD, 0x02, 0x44,
+ 0x89, 0x08, 0xF0, 0x3B, 0x89, 0x02, 0xF0, 0x1B,
+ 0xAD, 0x00, 0x44, 0xD0, 0x32, 0xA6, 0x00, 0xA9,
+ 0x01, 0x9D, 0x00, 0x10, 0xA9, 0x01, 0x9D, 0x00,
+ 0x09, 0xE8, 0xE4, 0x01, 0xF0, 0x02, 0x86, 0x00,
+ 0x4C, 0x65, 0x3A, 0xA6, 0x00, 0xAD, 0x00, 0x44,
+ 0x9D, 0x00, 0x09, 0x9E, 0x00, 0x10, 0xE8, 0xE4,
+ 0x01, 0xF0, 0x02, 0x86, 0x00, 0x29, 0x7F, 0xC9,
+ 0x13, 0xD0, 0x04, 0xA5, 0x0B, 0x85, 0x02, 0xAD,
+ 0x02, 0x44, 0x29, 0x10, 0xF0, 0x2C, 0xA6, 0x05,
+ 0xF0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x01, 0xD0,
+ 0x21, 0x8E, 0x00, 0x44, 0x64, 0x05, 0x4C, 0x98,
+ 0x3A, 0xA6, 0x04, 0xE4, 0x03, 0xF0, 0x13, 0xA5,
+ 0x02, 0xD0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x01,
+ 0xD0, 0x08, 0xBD, 0x00, 0x02, 0x8D, 0x00, 0x44,
+ 0xE6, 0x04, 0xAD, 0x02, 0x4C, 0x89, 0x08, 0xF0,
+ 0x3B, 0x89, 0x02, 0xF0, 0x1B, 0xAD, 0x00, 0x4C,
+ 0xD0, 0x32, 0xA6, 0x0C, 0xA9, 0x01, 0x9D, 0x00,
+ 0x11, 0xA9, 0x01, 0x9D, 0x00, 0x0A, 0xE8, 0xE4,
+ 0x0D, 0xF0, 0x02, 0x86, 0x0C, 0x4C, 0xDA, 0x3A,
+ 0xA6, 0x0C, 0xAD, 0x00, 0x4C, 0x9D, 0x00, 0x0A,
+ 0x9E, 0x00, 0x11, 0xE8, 0xE4, 0x0D, 0xF0, 0x02,
+ 0x86, 0x0C, 0x29, 0x7F, 0xC9, 0x13, 0xD0, 0x04,
+ 0xA5, 0x17, 0x85, 0x0E, 0xAD, 0x02, 0x4C, 0x29,
+ 0x10, 0xF0, 0x2C, 0xA6, 0x11, 0xF0, 0x0F, 0xAD,
+ 0x02, 0x7C, 0x29, 0x02, 0xD0, 0x21, 0x8E, 0x00,
+ 0x4C, 0x64, 0x11, 0x4C, 0x0D, 0x3B, 0xA6, 0x10,
+ 0xE4, 0x0F, 0xF0, 0x13, 0xA5, 0x0E, 0xD0, 0x0F,
+ 0xAD, 0x02, 0x7C, 0x29, 0x02, 0xD0, 0x08, 0xBD,
+ 0x00, 0x03, 0x8D, 0x00, 0x4C, 0xE6, 0x10, 0xAD,
+ 0x02, 0x54, 0x89, 0x08, 0xF0, 0x3B, 0x89, 0x02,
+ 0xF0, 0x1B, 0xAD, 0x00, 0x54, 0xD0, 0x32, 0xA6,
+ 0x18, 0xA9, 0x01, 0x9D, 0x00, 0x12, 0xA9, 0x01,
+ 0x9D, 0x00, 0x0B, 0xE8, 0xE4, 0x19, 0xF0, 0x02,
+ 0x86, 0x18, 0x4C, 0x4F, 0x3B, 0xA6, 0x18, 0xAD,
+ 0x00, 0x54, 0x9D, 0x00, 0x0B, 0x9E, 0x00, 0x12,
+ 0xE8, 0xE4, 0x19, 0xF0, 0x02, 0x86, 0x18, 0x29,
+ 0x7F, 0xC9, 0x13, 0xD0, 0x04, 0xA5, 0x23, 0x85,
+ 0x1A, 0xAD, 0x02, 0x54, 0x29, 0x10, 0xF0, 0x2C,
+ 0xA6, 0x1D, 0xF0, 0x0F, 0xAD, 0x02, 0x7C, 0x29,
+ 0x04, 0xD0, 0x21, 0x8E, 0x00, 0x54, 0x64, 0x1D,
+ 0x4C, 0x82, 0x3B, 0xA6, 0x1C, 0xE4, 0x1B, 0xF0,
+ 0x13, 0xA5, 0x1A, 0xD0, 0x0F, 0xAD, 0x02, 0x7C,
+ 0x29, 0x04, 0xD0, 0x08, 0xBD, 0x00, 0x04, 0x8D,
+ 0x00, 0x54, 0xE6, 0x1C, 0xAD, 0x02, 0x5C, 0x89,
+ 0x08, 0xF0, 0x3B, 0x89, 0x02, 0xF0, 0x1B, 0xAD,
+ 0x00, 0x5C, 0xD0, 0x32, 0xA6, 0x24, 0xA9, 0x01,
+ 0x9D, 0x00, 0x13, 0xA9, 0x01, 0x9D, 0x00, 0x0C,
+ 0xE8, 0xE4, 0x25, 0xF0, 0x02, 0x86, 0x24, 0x4C,
+ 0xC4, 0x3B, 0xA6, 0x24, 0xAD, 0x00, 0x5C, 0x9D,
+ 0x00, 0x0C, 0x9E, 0x00, 0x13, 0xE8, 0xE4, 0x25,
+ 0xF0, 0x02, 0x86, 0x24, 0x29, 0x7F, 0xC9, 0x13,
+ 0xD0, 0x04, 0xA5, 0x2F, 0x85, 0x26, 0xAD, 0x02,
+ 0x5C, 0x29, 0x10, 0xF0, 0x2C, 0xA6, 0x29, 0xF0,
+ 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x08, 0xD0, 0x21,
+ 0x8E, 0x00, 0x5C, 0x64, 0x29, 0x4C, 0xF7, 0x3B,
+ 0xA6, 0x28, 0xE4, 0x27, 0xF0, 0x13, 0xA5, 0x26,
+ 0xD0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x08, 0xD0,
+ 0x08, 0xBD, 0x00, 0x05, 0x8D, 0x00, 0x5C, 0xE6,
+ 0x28, 0xAD, 0x02, 0x64, 0x89, 0x08, 0xF0, 0x3B,
+ 0x89, 0x02, 0xF0, 0x1B, 0xAD, 0x00, 0x64, 0xD0,
+ 0x32, 0xA6, 0x30, 0xA9, 0x01, 0x9D, 0x00, 0x14,
+ 0xA9, 0x01, 0x9D, 0x00, 0x0D, 0xE8, 0xE4, 0x31,
+ 0xF0, 0x02, 0x86, 0x30, 0x4C, 0x39, 0x3C, 0xA6,
+ 0x30, 0xAD, 0x00, 0x64, 0x9D, 0x00, 0x0D, 0x9E,
+ 0x00, 0x14, 0xE8, 0xE4, 0x31, 0xF0, 0x02, 0x86,
+ 0x30, 0x29, 0x7F, 0xC9, 0x13, 0xD0, 0x04, 0xA5,
+ 0x3B, 0x85, 0x32, 0xAD, 0x02, 0x64, 0x29, 0x10,
+ 0xF0, 0x2C, 0xA6, 0x35, 0xF0, 0x0F, 0xAD, 0x02,
+ 0x7C, 0x29, 0x10, 0xD0, 0x21, 0x8E, 0x00, 0x64,
+ 0x64, 0x35, 0x4C, 0x6C, 0x3C, 0xA6, 0x34, 0xE4,
+ 0x33, 0xF0, 0x13, 0xA5, 0x32, 0xD0, 0x0F, 0xAD,
+ 0x02, 0x7C, 0x29, 0x10, 0xD0, 0x08, 0xBD, 0x00,
+ 0x06, 0x8D, 0x00, 0x64, 0xE6, 0x34, 0xAD, 0x02,
+ 0x6C, 0x89, 0x08, 0xF0, 0x3B, 0x89, 0x02, 0xF0,
+ 0x1B, 0xAD, 0x00, 0x6C, 0xD0, 0x32, 0xA6, 0x3C,
+ 0xA9, 0x01, 0x9D, 0x00, 0x15, 0xA9, 0x01, 0x9D,
+ 0x00, 0x0E, 0xE8, 0xE4, 0x3D, 0xF0, 0x02, 0x86,
+ 0x3C, 0x4C, 0xAE, 0x3C, 0xA6, 0x3C, 0xAD, 0x00,
+ 0x6C, 0x9D, 0x00, 0x0E, 0x9E, 0x00, 0x15, 0xE8,
+ 0xE4, 0x3D, 0xF0, 0x02, 0x86, 0x3C, 0x29, 0x7F,
+ 0xC9, 0x13, 0xD0, 0x04, 0xA5, 0x47, 0x85, 0x3E,
+ 0xAD, 0x02, 0x6C, 0x29, 0x10, 0xF0, 0x2C, 0xA6,
+ 0x41, 0xF0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x20,
+ 0xD0, 0x21, 0x8E, 0x00, 0x6C, 0x64, 0x41, 0x4C,
+ 0xE1, 0x3C, 0xA6, 0x40, 0xE4, 0x3F, 0xF0, 0x13,
+ 0xA5, 0x3E, 0xD0, 0x0F, 0xAD, 0x02, 0x7C, 0x29,
+ 0x20, 0xD0, 0x08, 0xBD, 0x00, 0x07, 0x8D, 0x00,
+ 0x6C, 0xE6, 0x40, 0xAD, 0x02, 0x74, 0x89, 0x08,
+ 0xF0, 0x3B, 0x89, 0x02, 0xF0, 0x1B, 0xAD, 0x00,
+ 0x74, 0xD0, 0x32, 0xA6, 0x48, 0xA9, 0x01, 0x9D,
+ 0x00, 0x16, 0xA9, 0x01, 0x9D, 0x00, 0x0F, 0xE8,
+ 0xE4, 0x49, 0xF0, 0x02, 0x86, 0x48, 0x4C, 0x23,
+ 0x3D, 0xA6, 0x48, 0xAD, 0x00, 0x74, 0x9D, 0x00,
+ 0x0F, 0x9E, 0x00, 0x16, 0xE8, 0xE4, 0x49, 0xF0,
+ 0x02, 0x86, 0x48, 0x29, 0x7F, 0xC9, 0x13, 0xD0,
+ 0x04, 0xA5, 0x53, 0x85, 0x4A, 0xAD, 0x02, 0x74,
+ 0x29, 0x10, 0xF0, 0x2C, 0xA6, 0x4D, 0xF0, 0x0F,
+ 0xAD, 0x02, 0x7C, 0x29, 0x40, 0xD0, 0x21, 0x8E,
+ 0x00, 0x74, 0x64, 0x4D, 0x4C, 0x56, 0x3D, 0xA6,
+ 0x4C, 0xE4, 0x4B, 0xF0, 0x13, 0xA5, 0x4A, 0xD0,
+ 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x40, 0xD0, 0x08,
+ 0xBD, 0x00, 0x08, 0x8D, 0x00, 0x74, 0xE6, 0x4C,
+ 0x60, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xD0, 0x00, 0x38,
+ 0xCE, 0xC0,
+};
diff --git a/drivers/staging/generic_serial/sx.c b/drivers/staging/generic_serial/sx.c
new file mode 100644
index 000000000000..1291462bcddb
--- /dev/null
+++ b/drivers/staging/generic_serial/sx.c
@@ -0,0 +1,2894 @@
+/* sx.c -- driver for the Specialix SX series cards.
+ *
+ * This driver will also support the older SI, and XIO cards.
+ *
+ *
+ * (C) 1998 - 2004 R.E.Wolff@BitWizard.nl
+ *
+ * Simon Allen (simonallen@cix.compulink.co.uk) wrote a previous
+ * version of this driver. Some fragments may have been copied. (none
+ * yet :-)
+ *
+ * Specialix pays for the development and support of this driver.
+ * Please DO contact support@specialix.co.uk if you require
+ * support. But please read the documentation (sx.txt) first.
+ *
+ *
+ *
+ * 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.33 2000/03/09 10:00:00 pvdl,wolff
+ * - Fixed module and port counting
+ * - Fixed signal handling
+ * - Fixed an Ooops
+ *
+ * Revision 1.32 2000/03/07 09:00:00 wolff,pvdl
+ * - Fixed some sx_dprintk typos
+ * - added detection for an invalid board/module configuration
+ *
+ * Revision 1.31 2000/03/06 12:00:00 wolff,pvdl
+ * - Added support for EISA
+ *
+ * Revision 1.30 2000/01/21 17:43:06 wolff
+ * - Added support for SX+
+ *
+ * Revision 1.26 1999/08/05 15:22:14 wolff
+ * - Port to 2.3.x
+ * - Reformatted to Linus' liking.
+ *
+ * Revision 1.25 1999/07/30 14:24:08 wolff
+ * Had accidentally left "gs_debug" set to "-1" instead of "off" (=0).
+ *
+ * Revision 1.24 1999/07/28 09:41:52 wolff
+ * - I noticed the remark about use-count straying in sx.txt. I checked
+ * sx_open, and found a few places where that could happen. I hope it's
+ * fixed now.
+ *
+ * Revision 1.23 1999/07/28 08:56:06 wolff
+ * - Fixed crash when sx_firmware run twice.
+ * - Added sx_slowpoll as a module parameter (I guess nobody really wanted
+ * to change it from the default... )
+ * - Fixed a stupid editing problem I introduced in 1.22.
+ * - Fixed dropping characters on a termios change.
+ *
+ * Revision 1.22 1999/07/26 21:01:43 wolff
+ * Russell Brown noticed that I had overlooked 4 out of six modem control
+ * signals in sx_getsignals. Ooops.
+ *
+ * Revision 1.21 1999/07/23 09:11:33 wolff
+ * I forgot to free dynamically allocated memory when the driver is unloaded.
+ *
+ * Revision 1.20 1999/07/20 06:25:26 wolff
+ * The "closing wait" wasn't honoured. Thanks to James Griffiths for
+ * reporting this.
+ *
+ * Revision 1.19 1999/07/11 08:59:59 wolff
+ * Fixed an oops in close, when an open was pending. Changed the memtest
+ * a bit. Should also test the board in word-mode, however my card fails the
+ * memtest then. I still have to figure out what is wrong...
+ *
+ * Revision 1.18 1999/06/10 09:38:42 wolff
+ * Changed the format of the firmware revision from %04x to %x.%02x .
+ *
+ * Revision 1.17 1999/06/04 09:44:35 wolff
+ * fixed problem: reference to pci stuff when config_pci was off...
+ * Thanks to Jorge Novo for noticing this.
+ *
+ * Revision 1.16 1999/06/02 08:30:15 wolff
+ * added/removed the workaround for the DCD bug in the Firmware.
+ * A bit more debugging code to locate that...
+ *
+ * Revision 1.15 1999/06/01 11:35:30 wolff
+ * when DCD is left low (floating?), on TA's the firmware first tells us
+ * that DCD is high, but after a short while suddenly comes to the
+ * conclusion that it is low. All this would be fine, if it weren't that
+ * Unix requires us to send a "hangup" signal in that case. This usually
+ * all happens BEFORE the program has had a chance to ioctl the device
+ * into clocal mode..
+ *
+ * Revision 1.14 1999/05/25 11:18:59 wolff
+ * Added PCI-fix.
+ * Added checks for return code of sx_sendcommand.
+ * Don't issue "reconfig" if port isn't open yet. (bit us on TA modules...)
+ *
+ * Revision 1.13 1999/04/29 15:18:01 wolff
+ * Fixed an "oops" that showed on SuSE 6.0 systems.
+ * Activate DTR again after stty 0.
+ *
+ * Revision 1.12 1999/04/29 07:49:52 wolff
+ * Improved "stty 0" handling a bit. (used to change baud to 9600 assuming
+ * the connection would be dropped anyway. That is not always the case,
+ * and confuses people).
+ * Told the card to always monitor the modem signals.
+ * Added support for dynamic gs_debug adjustments.
+ * Now tells the rest of the system the number of ports.
+ *
+ * Revision 1.11 1999/04/24 11:11:30 wolff
+ * Fixed two stupid typos in the memory test.
+ *
+ * Revision 1.10 1999/04/24 10:53:39 wolff
+ * Added some of Christian's suggestions.
+ * Fixed an HW_COOK_IN bug (ISIG was not in I_OTHER. We used to trust the
+ * card to send the signal to the process.....)
+ *
+ * Revision 1.9 1999/04/23 07:26:38 wolff
+ * Included Christian Lademann's 2.0 compile-warning fixes and interrupt
+ * assignment redesign.
+ * Cleanup of some other stuff.
+ *
+ * Revision 1.8 1999/04/16 13:05:30 wolff
+ * fixed a DCD change unnoticed bug.
+ *
+ * Revision 1.7 1999/04/14 22:19:51 wolff
+ * Fixed typo that showed up in 2.0.x builds (get_user instead of Get_user!)
+ *
+ * Revision 1.6 1999/04/13 18:40:20 wolff
+ * changed misc-minor to 161, as assigned by HPA.
+ *
+ * Revision 1.5 1999/04/13 15:12:25 wolff
+ * Fixed use-count leak when "hangup" occurred.
+ * Added workaround for a stupid-PCIBIOS bug.
+ *
+ *
+ * Revision 1.4 1999/04/01 22:47:40 wolff
+ * Fixed < 1M linux-2.0 problem.
+ * (vremap isn't compatible with ioremap in that case)
+ *
+ * Revision 1.3 1999/03/31 13:45:45 wolff
+ * Firmware loading is now done through a separate IOCTL.
+ *
+ * Revision 1.2 1999/03/28 12:22:29 wolff
+ * rcs cleanup
+ *
+ * Revision 1.1 1999/03/28 12:10:34 wolff
+ * Readying for release on 2.0.x (sorry David, 1.01 becomes 1.1 for RCS).
+ *
+ * Revision 0.12 1999/03/28 09:20:10 wolff
+ * Fixed problem in 0.11, continueing cleanup.
+ *
+ * Revision 0.11 1999/03/28 08:46:44 wolff
+ * cleanup. Not good.
+ *
+ * Revision 0.10 1999/03/28 08:09:43 wolff
+ * Fixed loosing characters on close.
+ *
+ * Revision 0.9 1999/03/21 22:52:01 wolff
+ * Ported back to 2.2.... (minor things)
+ *
+ * Revision 0.8 1999/03/21 22:40:33 wolff
+ * Port to 2.0
+ *
+ * Revision 0.7 1999/03/21 19:06:34 wolff
+ * Fixed hangup processing.
+ *
+ * Revision 0.6 1999/02/05 08:45:14 wolff
+ * fixed real_raw problems. Inclusion into kernel imminent.
+ *
+ * Revision 0.5 1998/12/21 23:51:06 wolff
+ * Snatched a nasty bug: sx_transmit_chars was getting re-entered, and it
+ * shouldn't have. THATs why I want to have transmit interrupts even when
+ * the buffer is empty.
+ *
+ * Revision 0.4 1998/12/17 09:34:46 wolff
+ * PPP works. ioctl works. Basically works!
+ *
+ * Revision 0.3 1998/12/15 13:05:18 wolff
+ * It works! Wow! Gotta start implementing IOCTL and stuff....
+ *
+ * Revision 0.2 1998/12/01 08:33:53 wolff
+ * moved over to 2.1.130
+ *
+ * Revision 0.1 1998/11/03 21:23:51 wolff
+ * Initial revision. Detects SX card.
+ *
+ * */
+
+#define SX_VERSION 1.33
+
+#include <linux/module.h>
+#include <linux/kdev_t.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/eisa.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+/* The 3.0.0 version of sxboards/sxwindow.h uses BYTE and WORD.... */
+#define BYTE u8
+#define WORD u16
+
+/* .... but the 3.0.4 version uses _u8 and _u16. */
+#define _u8 u8
+#define _u16 u16
+
+#include "sxboards.h"
+#include "sxwindow.h"
+
+#include <linux/generic_serial.h>
+#include "sx.h"
+
+/* I don't think that this driver can handle more than 256 ports on
+ one machine. You'll have to increase the number of boards in sx.h
+ if you want more than 4 boards. */
+
+#ifndef PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8
+#define PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8 0x2000
+#endif
+
+/* Configurable options:
+ (Don't be too sure that it'll work if you toggle them) */
+
+/* Am I paranoid or not ? ;-) */
+#undef SX_PARANOIA_CHECK
+
+/* 20 -> 2000 per second. The card should rate-limit interrupts at 100
+ Hz, but it is user configurable. I don't recommend going above 1000
+ Hz. The interrupt ratelimit might trigger if the interrupt is
+ shared with a very active other device. */
+#define IRQ_RATE_LIMIT 20
+
+/* Sharing interrupts is possible now. If the other device wants more
+ than 2000 interrupts per second, we'd gracefully decline further
+ interrupts. That's not what we want. On the other hand, if the
+ other device interrupts 2000 times a second, don't use the SX
+ interrupt. Use polling. */
+#undef IRQ_RATE_LIMIT
+
+#if 0
+/* Not implemented */
+/*
+ * The following defines are mostly for testing purposes. But if you need
+ * some nice reporting in your syslog, you can define them also.
+ */
+#define SX_REPORT_FIFO
+#define SX_REPORT_OVERRUN
+#endif
+
+/* Function prototypes */
+static void sx_disable_tx_interrupts(void *ptr);
+static void sx_enable_tx_interrupts(void *ptr);
+static void sx_disable_rx_interrupts(void *ptr);
+static void sx_enable_rx_interrupts(void *ptr);
+static int sx_carrier_raised(struct tty_port *port);
+static void sx_shutdown_port(void *ptr);
+static int sx_set_real_termios(void *ptr);
+static void sx_close(void *ptr);
+static int sx_chars_in_buffer(void *ptr);
+static int sx_init_board(struct sx_board *board);
+static int sx_init_portstructs(int nboards, int nports);
+static long sx_fw_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg);
+static int sx_init_drivers(void);
+
+static struct tty_driver *sx_driver;
+
+static DEFINE_MUTEX(sx_boards_lock);
+static struct sx_board boards[SX_NBOARDS];
+static struct sx_port *sx_ports;
+static int sx_initialized;
+static int sx_nports;
+static int sx_debug;
+
+/* You can have the driver poll your card.
+ - Set sx_poll to 1 to poll every timer tick (10ms on Intel).
+ This is used when the card cannot use an interrupt for some reason.
+
+ - set sx_slowpoll to 100 to do an extra poll once a second (on Intel). If
+ the driver misses an interrupt (report this if it DOES happen to you!)
+ everything will continue to work....
+ */
+static int sx_poll = 1;
+static int sx_slowpoll;
+
+/* The card limits the number of interrupts per second.
+ At 115k2 "100" should be sufficient.
+ If you're using higher baudrates, you can increase this...
+ */
+
+static int sx_maxints = 100;
+
+#ifdef CONFIG_ISA
+
+/* These are the only open spaces in my computer. Yours may have more
+ or less.... -- REW
+ duh: Card at 0xa0000 is possible on HP Netserver?? -- pvdl
+*/
+static int sx_probe_addrs[] = {
+ 0xc0000, 0xd0000, 0xe0000,
+ 0xc8000, 0xd8000, 0xe8000
+};
+static int si_probe_addrs[] = {
+ 0xc0000, 0xd0000, 0xe0000,
+ 0xc8000, 0xd8000, 0xe8000, 0xa0000
+};
+static int si1_probe_addrs[] = {
+ 0xd0000
+};
+
+#define NR_SX_ADDRS ARRAY_SIZE(sx_probe_addrs)
+#define NR_SI_ADDRS ARRAY_SIZE(si_probe_addrs)
+#define NR_SI1_ADDRS ARRAY_SIZE(si1_probe_addrs)
+
+module_param_array(sx_probe_addrs, int, NULL, 0);
+module_param_array(si_probe_addrs, int, NULL, 0);
+#endif
+
+/* Set the mask to all-ones. This alas, only supports 32 interrupts.
+ Some architectures may need more. */
+static int sx_irqmask = -1;
+
+module_param(sx_poll, int, 0);
+module_param(sx_slowpoll, int, 0);
+module_param(sx_maxints, int, 0);
+module_param(sx_debug, int, 0);
+module_param(sx_irqmask, int, 0);
+
+MODULE_LICENSE("GPL");
+
+static struct real_driver sx_real_driver = {
+ sx_disable_tx_interrupts,
+ sx_enable_tx_interrupts,
+ sx_disable_rx_interrupts,
+ sx_enable_rx_interrupts,
+ sx_shutdown_port,
+ sx_set_real_termios,
+ sx_chars_in_buffer,
+ sx_close,
+};
+
+/*
+ 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
+
+#ifdef DEBUG
+#define sx_dprintk(f, str...) if (sx_debug & f) printk (str)
+#else
+#define sx_dprintk(f, str...) /* nothing */
+#endif
+
+#define func_enter() sx_dprintk(SX_DEBUG_FLOW, "sx: enter %s\n",__func__)
+#define func_exit() sx_dprintk(SX_DEBUG_FLOW, "sx: exit %s\n",__func__)
+
+#define func_enter2() sx_dprintk(SX_DEBUG_FLOW, "sx: enter %s (port %d)\n", \
+ __func__, port->line)
+
+/*
+ * Firmware loader driver specific routines
+ *
+ */
+
+static const struct file_operations sx_fw_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = sx_fw_ioctl,
+ .llseek = noop_llseek,
+};
+
+static struct miscdevice sx_fw_device = {
+ SXCTL_MISC_MINOR, "sxctl", &sx_fw_fops
+};
+
+#ifdef SX_PARANOIA_CHECK
+
+/* This doesn't work. Who's paranoid around here? Not me! */
+
+static inline int sx_paranoia_check(struct sx_port const *port,
+ char *name, const char *routine)
+{
+ static const char *badmagic = KERN_ERR "sx: Warning: bad sx port magic "
+ "number for device %s in %s\n";
+ static const char *badinfo = KERN_ERR "sx: Warning: null sx port for "
+ "device %s in %s\n";
+
+ if (!port) {
+ printk(badinfo, name, routine);
+ return 1;
+ }
+ if (port->magic != SX_MAGIC) {
+ printk(badmagic, name, routine);
+ return 1;
+ }
+
+ return 0;
+}
+#else
+#define sx_paranoia_check(a,b,c) 0
+#endif
+
+/* The timeouts. First try 30 times as fast as possible. Then give
+ the card some time to breathe between accesses. (Otherwise the
+ processor on the card might not be able to access its OWN bus... */
+
+#define TIMEOUT_1 30
+#define TIMEOUT_2 1000000
+
+#ifdef DEBUG
+static void my_hd_io(void __iomem *p, int len)
+{
+ int i, j, ch;
+ unsigned char __iomem *addr = p;
+
+ for (i = 0; i < len; i += 16) {
+ printk("%p ", addr + i);
+ for (j = 0; j < 16; j++) {
+ printk("%02x %s", readb(addr + j + i),
+ (j == 7) ? " " : "");
+ }
+ for (j = 0; j < 16; j++) {
+ ch = readb(addr + j + i);
+ printk("%c", (ch < 0x20) ? '.' :
+ ((ch > 0x7f) ? '.' : ch));
+ }
+ printk("\n");
+ }
+}
+static void my_hd(void *p, int len)
+{
+ int i, j, ch;
+ unsigned char *addr = p;
+
+ for (i = 0; i < len; i += 16) {
+ printk("%p ", addr + i);
+ for (j = 0; j < 16; j++) {
+ printk("%02x %s", addr[j + i], (j == 7) ? " " : "");
+ }
+ for (j = 0; j < 16; j++) {
+ ch = addr[j + i];
+ printk("%c", (ch < 0x20) ? '.' :
+ ((ch > 0x7f) ? '.' : ch));
+ }
+ printk("\n");
+ }
+}
+#endif
+
+/* This needs redoing for Alpha -- REW -- Done. */
+
+static inline void write_sx_byte(struct sx_board *board, int offset, u8 byte)
+{
+ writeb(byte, board->base + offset);
+}
+
+static inline u8 read_sx_byte(struct sx_board *board, int offset)
+{
+ return readb(board->base + offset);
+}
+
+static inline void write_sx_word(struct sx_board *board, int offset, u16 word)
+{
+ writew(word, board->base + offset);
+}
+
+static inline u16 read_sx_word(struct sx_board *board, int offset)
+{
+ return readw(board->base + offset);
+}
+
+static int sx_busy_wait_eq(struct sx_board *board,
+ int offset, int mask, int correctval)
+{
+ int i;
+
+ func_enter();
+
+ for (i = 0; i < TIMEOUT_1; i++)
+ if ((read_sx_byte(board, offset) & mask) == correctval) {
+ func_exit();
+ return 1;
+ }
+
+ for (i = 0; i < TIMEOUT_2; i++) {
+ if ((read_sx_byte(board, offset) & mask) == correctval) {
+ func_exit();
+ return 1;
+ }
+ udelay(1);
+ }
+
+ func_exit();
+ return 0;
+}
+
+static int sx_busy_wait_neq(struct sx_board *board,
+ int offset, int mask, int badval)
+{
+ int i;
+
+ func_enter();
+
+ for (i = 0; i < TIMEOUT_1; i++)
+ if ((read_sx_byte(board, offset) & mask) != badval) {
+ func_exit();
+ return 1;
+ }
+
+ for (i = 0; i < TIMEOUT_2; i++) {
+ if ((read_sx_byte(board, offset) & mask) != badval) {
+ func_exit();
+ return 1;
+ }
+ udelay(1);
+ }
+
+ func_exit();
+ return 0;
+}
+
+/* 5.6.4 of 6210028 r2.3 */
+static int sx_reset(struct sx_board *board)
+{
+ func_enter();
+
+ if (IS_SX_BOARD(board)) {
+
+ write_sx_byte(board, SX_CONFIG, 0);
+ write_sx_byte(board, SX_RESET, 1); /* Value doesn't matter */
+
+ if (!sx_busy_wait_eq(board, SX_RESET_STATUS, 1, 0)) {
+ printk(KERN_INFO "sx: Card doesn't respond to "
+ "reset...\n");
+ return 0;
+ }
+ } else if (IS_EISA_BOARD(board)) {
+ outb(board->irq << 4, board->eisa_base + 0xc02);
+ } else if (IS_SI1_BOARD(board)) {
+ write_sx_byte(board, SI1_ISA_RESET, 0); /*value doesn't matter*/
+ } else {
+ /* Gory details of the SI/ISA board */
+ write_sx_byte(board, SI2_ISA_RESET, SI2_ISA_RESET_SET);
+ write_sx_byte(board, SI2_ISA_IRQ11, SI2_ISA_IRQ11_CLEAR);
+ write_sx_byte(board, SI2_ISA_IRQ12, SI2_ISA_IRQ12_CLEAR);
+ write_sx_byte(board, SI2_ISA_IRQ15, SI2_ISA_IRQ15_CLEAR);
+ write_sx_byte(board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_CLEAR);
+ write_sx_byte(board, SI2_ISA_IRQSET, SI2_ISA_IRQSET_CLEAR);
+ }
+
+ func_exit();
+ return 1;
+}
+
+/* This doesn't work on machines where "NULL" isn't 0 */
+/* If you have one of those, someone will need to write
+ the equivalent of this, which will amount to about 3 lines. I don't
+ want to complicate this right now. -- REW
+ (See, I do write comments every now and then :-) */
+#define OFFSETOF(strct, elem) ((long)&(((struct strct *)NULL)->elem))
+
+#define CHAN_OFFSET(port,elem) (port->ch_base + OFFSETOF (_SXCHANNEL, elem))
+#define MODU_OFFSET(board,addr,elem) (addr + OFFSETOF (_SXMODULE, elem))
+#define BRD_OFFSET(board,elem) (OFFSETOF (_SXCARD, elem))
+
+#define sx_write_channel_byte(port, elem, val) \
+ write_sx_byte (port->board, CHAN_OFFSET (port, elem), val)
+
+#define sx_read_channel_byte(port, elem) \
+ read_sx_byte (port->board, CHAN_OFFSET (port, elem))
+
+#define sx_write_channel_word(port, elem, val) \
+ write_sx_word (port->board, CHAN_OFFSET (port, elem), val)
+
+#define sx_read_channel_word(port, elem) \
+ read_sx_word (port->board, CHAN_OFFSET (port, elem))
+
+#define sx_write_module_byte(board, addr, elem, val) \
+ write_sx_byte (board, MODU_OFFSET (board, addr, elem), val)
+
+#define sx_read_module_byte(board, addr, elem) \
+ read_sx_byte (board, MODU_OFFSET (board, addr, elem))
+
+#define sx_write_module_word(board, addr, elem, val) \
+ write_sx_word (board, MODU_OFFSET (board, addr, elem), val)
+
+#define sx_read_module_word(board, addr, elem) \
+ read_sx_word (board, MODU_OFFSET (board, addr, elem))
+
+#define sx_write_board_byte(board, elem, val) \
+ write_sx_byte (board, BRD_OFFSET (board, elem), val)
+
+#define sx_read_board_byte(board, elem) \
+ read_sx_byte (board, BRD_OFFSET (board, elem))
+
+#define sx_write_board_word(board, elem, val) \
+ write_sx_word (board, BRD_OFFSET (board, elem), val)
+
+#define sx_read_board_word(board, elem) \
+ read_sx_word (board, BRD_OFFSET (board, elem))
+
+static int sx_start_board(struct sx_board *board)
+{
+ if (IS_SX_BOARD(board)) {
+ write_sx_byte(board, SX_CONFIG, SX_CONF_BUSEN);
+ } else if (IS_EISA_BOARD(board)) {
+ write_sx_byte(board, SI2_EISA_OFF, SI2_EISA_VAL);
+ outb((board->irq << 4) | 4, board->eisa_base + 0xc02);
+ } else if (IS_SI1_BOARD(board)) {
+ write_sx_byte(board, SI1_ISA_RESET_CLEAR, 0);
+ write_sx_byte(board, SI1_ISA_INTCL, 0);
+ } else {
+ /* Don't bug me about the clear_set.
+ I haven't the foggiest idea what it's about -- REW */
+ write_sx_byte(board, SI2_ISA_RESET, SI2_ISA_RESET_CLEAR);
+ write_sx_byte(board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_SET);
+ }
+ return 1;
+}
+
+#define SX_IRQ_REG_VAL(board) \
+ ((board->flags & SX_ISA_BOARD) ? (board->irq << 4) : 0)
+
+/* Note. The SX register is write-only. Therefore, we have to enable the
+ bus too. This is a no-op, if you don't mess with this driver... */
+static int sx_start_interrupts(struct sx_board *board)
+{
+
+ /* Don't call this with board->irq == 0 */
+
+ if (IS_SX_BOARD(board)) {
+ write_sx_byte(board, SX_CONFIG, SX_IRQ_REG_VAL(board) |
+ SX_CONF_BUSEN | SX_CONF_HOSTIRQ);
+ } else if (IS_EISA_BOARD(board)) {
+ inb(board->eisa_base + 0xc03);
+ } else if (IS_SI1_BOARD(board)) {
+ write_sx_byte(board, SI1_ISA_INTCL, 0);
+ write_sx_byte(board, SI1_ISA_INTCL_CLEAR, 0);
+ } else {
+ switch (board->irq) {
+ case 11:
+ write_sx_byte(board, SI2_ISA_IRQ11, SI2_ISA_IRQ11_SET);
+ break;
+ case 12:
+ write_sx_byte(board, SI2_ISA_IRQ12, SI2_ISA_IRQ12_SET);
+ break;
+ case 15:
+ write_sx_byte(board, SI2_ISA_IRQ15, SI2_ISA_IRQ15_SET);
+ break;
+ default:
+ printk(KERN_INFO "sx: SI/XIO card doesn't support "
+ "interrupt %d.\n", board->irq);
+ return 0;
+ }
+ write_sx_byte(board, SI2_ISA_INTCLEAR, SI2_ISA_INTCLEAR_SET);
+ }
+
+ return 1;
+}
+
+static int sx_send_command(struct sx_port *port,
+ int command, int mask, int newstat)
+{
+ func_enter2();
+ write_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat), command);
+ func_exit();
+ return sx_busy_wait_eq(port->board, CHAN_OFFSET(port, hi_hstat), mask,
+ newstat);
+}
+
+static char *mod_type_s(int module_type)
+{
+ switch (module_type) {
+ case TA4:
+ return "TA4";
+ case TA8:
+ return "TA8";
+ case TA4_ASIC:
+ return "TA4_ASIC";
+ case TA8_ASIC:
+ return "TA8_ASIC";
+ case MTA_CD1400:
+ return "MTA_CD1400";
+ case SXDC:
+ return "SXDC";
+ default:
+ return "Unknown/invalid";
+ }
+}
+
+static char *pan_type_s(int pan_type)
+{
+ switch (pan_type) {
+ case MOD_RS232DB25:
+ return "MOD_RS232DB25";
+ case MOD_RS232RJ45:
+ return "MOD_RS232RJ45";
+ case MOD_RS422DB25:
+ return "MOD_RS422DB25";
+ case MOD_PARALLEL:
+ return "MOD_PARALLEL";
+ case MOD_2_RS232DB25:
+ return "MOD_2_RS232DB25";
+ case MOD_2_RS232RJ45:
+ return "MOD_2_RS232RJ45";
+ case MOD_2_RS422DB25:
+ return "MOD_2_RS422DB25";
+ case MOD_RS232DB25MALE:
+ return "MOD_RS232DB25MALE";
+ case MOD_2_PARALLEL:
+ return "MOD_2_PARALLEL";
+ case MOD_BLANK:
+ return "empty";
+ default:
+ return "invalid";
+ }
+}
+
+static int mod_compat_type(int module_type)
+{
+ return module_type >> 4;
+}
+
+static void sx_reconfigure_port(struct sx_port *port)
+{
+ if (sx_read_channel_byte(port, hi_hstat) == HS_IDLE_OPEN) {
+ if (sx_send_command(port, HS_CONFIG, -1, HS_IDLE_OPEN) != 1) {
+ printk(KERN_WARNING "sx: Sent reconfigure command, but "
+ "card didn't react.\n");
+ }
+ } else {
+ sx_dprintk(SX_DEBUG_TERMIOS, "sx: Not sending reconfigure: "
+ "port isn't open (%02x).\n",
+ sx_read_channel_byte(port, hi_hstat));
+ }
+}
+
+static void sx_setsignals(struct sx_port *port, int dtr, int rts)
+{
+ int t;
+ func_enter2();
+
+ t = sx_read_channel_byte(port, hi_op);
+ if (dtr >= 0)
+ t = dtr ? (t | OP_DTR) : (t & ~OP_DTR);
+ if (rts >= 0)
+ t = rts ? (t | OP_RTS) : (t & ~OP_RTS);
+ sx_write_channel_byte(port, hi_op, t);
+ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "setsignals: %d/%d\n", dtr, rts);
+
+ func_exit();
+}
+
+static int sx_getsignals(struct sx_port *port)
+{
+ int i_stat, o_stat;
+
+ o_stat = sx_read_channel_byte(port, hi_op);
+ i_stat = sx_read_channel_byte(port, hi_ip);
+
+ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "getsignals: %d/%d (%d/%d) "
+ "%02x/%02x\n",
+ (o_stat & OP_DTR) != 0, (o_stat & OP_RTS) != 0,
+ port->c_dcd, tty_port_carrier_raised(&port->gs.port),
+ sx_read_channel_byte(port, hi_ip),
+ sx_read_channel_byte(port, hi_state));
+
+ return (((o_stat & OP_DTR) ? TIOCM_DTR : 0) |
+ ((o_stat & OP_RTS) ? TIOCM_RTS : 0) |
+ ((i_stat & IP_CTS) ? TIOCM_CTS : 0) |
+ ((i_stat & IP_DCD) ? TIOCM_CAR : 0) |
+ ((i_stat & IP_DSR) ? TIOCM_DSR : 0) |
+ ((i_stat & IP_RI) ? TIOCM_RNG : 0));
+}
+
+static void sx_set_baud(struct sx_port *port)
+{
+ int t;
+
+ if (port->board->ta_type == MOD_SXDC) {
+ switch (port->gs.baud) {
+ /* Save some typing work... */
+#define e(x) case x: t = BAUD_ ## x; break
+ e(50);
+ e(75);
+ e(110);
+ e(150);
+ e(200);
+ e(300);
+ e(600);
+ e(1200);
+ e(1800);
+ e(2000);
+ e(2400);
+ e(4800);
+ e(7200);
+ e(9600);
+ e(14400);
+ e(19200);
+ e(28800);
+ e(38400);
+ e(56000);
+ e(57600);
+ e(64000);
+ e(76800);
+ e(115200);
+ e(128000);
+ e(150000);
+ e(230400);
+ e(256000);
+ e(460800);
+ e(921600);
+ case 134:
+ t = BAUD_134_5;
+ break;
+ case 0:
+ t = -1;
+ break;
+ default:
+ /* Can I return "invalid"? */
+ t = BAUD_9600;
+ printk(KERN_INFO "sx: unsupported baud rate: %d.\n",
+ port->gs.baud);
+ break;
+ }
+#undef e
+ if (t > 0) {
+/* The baud rate is not set to 0, so we're enabeling DTR... -- REW */
+ sx_setsignals(port, 1, -1);
+ /* XXX This is not TA & MTA compatible */
+ sx_write_channel_byte(port, hi_csr, 0xff);
+
+ sx_write_channel_byte(port, hi_txbaud, t);
+ sx_write_channel_byte(port, hi_rxbaud, t);
+ } else {
+ sx_setsignals(port, 0, -1);
+ }
+ } else {
+ switch (port->gs.baud) {
+#define e(x) case x: t = CSR_ ## x; break
+ e(75);
+ e(150);
+ e(300);
+ e(600);
+ e(1200);
+ e(2400);
+ e(4800);
+ e(1800);
+ e(9600);
+ e(19200);
+ e(57600);
+ e(38400);
+/* TA supports 110, but not 115200, MTA supports 115200, but not 110 */
+ case 110:
+ if (port->board->ta_type == MOD_TA) {
+ t = CSR_110;
+ break;
+ } else {
+ t = CSR_9600;
+ printk(KERN_INFO "sx: Unsupported baud rate: "
+ "%d.\n", port->gs.baud);
+ break;
+ }
+ case 115200:
+ if (port->board->ta_type == MOD_TA) {
+ t = CSR_9600;
+ printk(KERN_INFO "sx: Unsupported baud rate: "
+ "%d.\n", port->gs.baud);
+ break;
+ } else {
+ t = CSR_110;
+ break;
+ }
+ case 0:
+ t = -1;
+ break;
+ default:
+ t = CSR_9600;
+ printk(KERN_INFO "sx: Unsupported baud rate: %d.\n",
+ port->gs.baud);
+ break;
+ }
+#undef e
+ if (t >= 0) {
+ sx_setsignals(port, 1, -1);
+ sx_write_channel_byte(port, hi_csr, t * 0x11);
+ } else {
+ sx_setsignals(port, 0, -1);
+ }
+ }
+}
+
+/* Simon Allen's version of this routine was 225 lines long. 85 is a lot
+ better. -- REW */
+
+static int sx_set_real_termios(void *ptr)
+{
+ struct sx_port *port = ptr;
+
+ func_enter2();
+
+ if (!port->gs.port.tty)
+ return 0;
+
+ /* What is this doing here? -- REW
+ Ha! figured it out. It is to allow you to get DTR active again
+ if you've dropped it with stty 0. Moved to set_baud, where it
+ belongs (next to the drop dtr if baud == 0) -- REW */
+ /* sx_setsignals (port, 1, -1); */
+
+ sx_set_baud(port);
+
+#define CFLAG port->gs.port.tty->termios->c_cflag
+ sx_write_channel_byte(port, hi_mr1,
+ (C_PARENB(port->gs.port.tty) ? MR1_WITH : MR1_NONE) |
+ (C_PARODD(port->gs.port.tty) ? MR1_ODD : MR1_EVEN) |
+ (C_CRTSCTS(port->gs.port.tty) ? MR1_RTS_RXFLOW : 0) |
+ (((CFLAG & CSIZE) == CS8) ? MR1_8_BITS : 0) |
+ (((CFLAG & CSIZE) == CS7) ? MR1_7_BITS : 0) |
+ (((CFLAG & CSIZE) == CS6) ? MR1_6_BITS : 0) |
+ (((CFLAG & CSIZE) == CS5) ? MR1_5_BITS : 0));
+
+ sx_write_channel_byte(port, hi_mr2,
+ (C_CRTSCTS(port->gs.port.tty) ? MR2_CTS_TXFLOW : 0) |
+ (C_CSTOPB(port->gs.port.tty) ? MR2_2_STOP :
+ MR2_1_STOP));
+
+ switch (CFLAG & CSIZE) {
+ case CS8:
+ sx_write_channel_byte(port, hi_mask, 0xff);
+ break;
+ case CS7:
+ sx_write_channel_byte(port, hi_mask, 0x7f);
+ break;
+ case CS6:
+ sx_write_channel_byte(port, hi_mask, 0x3f);
+ break;
+ case CS5:
+ sx_write_channel_byte(port, hi_mask, 0x1f);
+ break;
+ default:
+ printk(KERN_INFO "sx: Invalid wordsize: %u\n",
+ (unsigned int)CFLAG & CSIZE);
+ break;
+ }
+
+ sx_write_channel_byte(port, hi_prtcl,
+ (I_IXON(port->gs.port.tty) ? SP_TXEN : 0) |
+ (I_IXOFF(port->gs.port.tty) ? SP_RXEN : 0) |
+ (I_IXANY(port->gs.port.tty) ? SP_TANY : 0) | SP_DCEN);
+
+ sx_write_channel_byte(port, hi_break,
+ (I_IGNBRK(port->gs.port.tty) ? BR_IGN : 0 |
+ I_BRKINT(port->gs.port.tty) ? BR_INT : 0));
+
+ sx_write_channel_byte(port, hi_txon, START_CHAR(port->gs.port.tty));
+ sx_write_channel_byte(port, hi_rxon, START_CHAR(port->gs.port.tty));
+ sx_write_channel_byte(port, hi_txoff, STOP_CHAR(port->gs.port.tty));
+ sx_write_channel_byte(port, hi_rxoff, STOP_CHAR(port->gs.port.tty));
+
+ sx_reconfigure_port(port);
+
+ /* Tell line discipline whether we will do input cooking */
+ if (I_OTHER(port->gs.port.tty)) {
+ clear_bit(TTY_HW_COOK_IN, &port->gs.port.tty->flags);
+ } else {
+ set_bit(TTY_HW_COOK_IN, &port->gs.port.tty->flags);
+ }
+ sx_dprintk(SX_DEBUG_TERMIOS, "iflags: %x(%d) ",
+ (unsigned int)port->gs.port.tty->termios->c_iflag,
+ I_OTHER(port->gs.port.tty));
+
+/* Tell line discipline whether we will do output cooking.
+ * If OPOST is set and no other output flags are set then we can do output
+ * processing. Even if only *one* other flag in the O_OTHER group is set
+ * we do cooking in software.
+ */
+ if (O_OPOST(port->gs.port.tty) && !O_OTHER(port->gs.port.tty)) {
+ set_bit(TTY_HW_COOK_OUT, &port->gs.port.tty->flags);
+ } else {
+ clear_bit(TTY_HW_COOK_OUT, &port->gs.port.tty->flags);
+ }
+ sx_dprintk(SX_DEBUG_TERMIOS, "oflags: %x(%d)\n",
+ (unsigned int)port->gs.port.tty->termios->c_oflag,
+ O_OTHER(port->gs.port.tty));
+ /* port->c_dcd = sx_get_CD (port); */
+ func_exit();
+ return 0;
+}
+
+/* ********************************************************************** *
+ * the interrupt related routines *
+ * ********************************************************************** */
+
+/* Note:
+ Other drivers use the macro "MIN" to calculate how much to copy.
+ This has the disadvantage that it will evaluate parts twice. That's
+ expensive when it's IO (and the compiler cannot optimize those away!).
+ Moreover, I'm not sure that you're race-free.
+
+ I assign a value, and then only allow the value to decrease. This
+ is always safe. This makes the code a few lines longer, and you
+ know I'm dead against that, but I think it is required in this
+ case. */
+
+static void sx_transmit_chars(struct sx_port *port)
+{
+ int c;
+ int tx_ip;
+ int txroom;
+
+ func_enter2();
+ sx_dprintk(SX_DEBUG_TRANSMIT, "Port %p: transmit %d chars\n",
+ port, port->gs.xmit_cnt);
+
+ if (test_and_set_bit(SX_PORT_TRANSMIT_LOCK, &port->locks)) {
+ return;
+ }
+
+ while (1) {
+ c = port->gs.xmit_cnt;
+
+ sx_dprintk(SX_DEBUG_TRANSMIT, "Copying %d ", c);
+ tx_ip = sx_read_channel_byte(port, hi_txipos);
+
+ /* Took me 5 minutes to deduce this formula.
+ Luckily it is literally in the manual in section 6.5.4.3.5 */
+ txroom = (sx_read_channel_byte(port, hi_txopos) - tx_ip - 1) &
+ 0xff;
+
+ /* Don't copy more bytes than there is room for in the buffer */
+ if (c > txroom)
+ c = txroom;
+ sx_dprintk(SX_DEBUG_TRANSMIT, " %d(%d) ", c, txroom);
+
+ /* Don't copy past the end of the hardware transmit buffer */
+ if (c > 0x100 - tx_ip)
+ c = 0x100 - tx_ip;
+
+ sx_dprintk(SX_DEBUG_TRANSMIT, " %d(%d) ", c, 0x100 - tx_ip);
+
+ /* Don't copy pas the end of the source buffer */
+ if (c > SERIAL_XMIT_SIZE - port->gs.xmit_tail)
+ c = SERIAL_XMIT_SIZE - port->gs.xmit_tail;
+
+ sx_dprintk(SX_DEBUG_TRANSMIT, " %d(%ld) \n",
+ c, SERIAL_XMIT_SIZE - port->gs.xmit_tail);
+
+ /* If for one reason or another, we can't copy more data, we're
+ done! */
+ if (c == 0)
+ break;
+
+ memcpy_toio(port->board->base + CHAN_OFFSET(port, hi_txbuf) +
+ tx_ip, port->gs.xmit_buf + port->gs.xmit_tail, c);
+
+ /* Update the pointer in the card */
+ sx_write_channel_byte(port, hi_txipos, (tx_ip + c) & 0xff);
+
+ /* Update the kernel buffer end */
+ port->gs.xmit_tail = (port->gs.xmit_tail + c) &
+ (SERIAL_XMIT_SIZE - 1);
+
+ /* This one last. (this is essential)
+ It would allow others to start putting more data into the
+ buffer! */
+ port->gs.xmit_cnt -= c;
+ }
+
+ if (port->gs.xmit_cnt == 0) {
+ sx_disable_tx_interrupts(port);
+ }
+
+ if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.port.tty) {
+ tty_wakeup(port->gs.port.tty);
+ sx_dprintk(SX_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n",
+ port->gs.wakeup_chars);
+ }
+
+ clear_bit(SX_PORT_TRANSMIT_LOCK, &port->locks);
+ func_exit();
+}
+
+/* Note the symmetry between receiving chars and transmitting them!
+ Note: The kernel should have implemented both a receive buffer and
+ a transmit buffer. */
+
+/* Inlined: Called only once. Remove the inline when you add another call */
+static inline void sx_receive_chars(struct sx_port *port)
+{
+ int c;
+ int rx_op;
+ struct tty_struct *tty;
+ int copied = 0;
+ unsigned char *rp;
+
+ func_enter2();
+ tty = port->gs.port.tty;
+ while (1) {
+ rx_op = sx_read_channel_byte(port, hi_rxopos);
+ c = (sx_read_channel_byte(port, hi_rxipos) - rx_op) & 0xff;
+
+ sx_dprintk(SX_DEBUG_RECEIVE, "rxop=%d, c = %d.\n", rx_op, c);
+
+ /* Don't copy past the end of the hardware receive buffer */
+ if (rx_op + c > 0x100)
+ c = 0x100 - rx_op;
+
+ sx_dprintk(SX_DEBUG_RECEIVE, "c = %d.\n", c);
+
+ /* Don't copy more bytes than there is room for in the buffer */
+
+ c = tty_prepare_flip_string(tty, &rp, c);
+
+ sx_dprintk(SX_DEBUG_RECEIVE, "c = %d.\n", c);
+
+ /* If for one reason or another, we can't copy more data, we're done! */
+ if (c == 0)
+ break;
+
+ sx_dprintk(SX_DEBUG_RECEIVE, "Copying over %d chars. First is "
+ "%d at %lx\n", c, read_sx_byte(port->board,
+ CHAN_OFFSET(port, hi_rxbuf) + rx_op),
+ CHAN_OFFSET(port, hi_rxbuf));
+ memcpy_fromio(rp, port->board->base +
+ CHAN_OFFSET(port, hi_rxbuf) + rx_op, c);
+
+ /* This one last. ( Not essential.)
+ It allows the card to start putting more data into the
+ buffer!
+ Update the pointer in the card */
+ sx_write_channel_byte(port, hi_rxopos, (rx_op + c) & 0xff);
+
+ copied += c;
+ }
+ if (copied) {
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ sx_dprintk(SX_DEBUG_RECEIVE, "pushing flipq port %d (%3d "
+ "chars): %d.%06d (%d/%d)\n", port->line,
+ copied, (int)(tv.tv_sec % 60), (int)tv.tv_usec,
+ tty->raw, tty->real_raw);
+
+ /* Tell the rest of the system the news. Great news. New
+ characters! */
+ tty_flip_buffer_push(tty);
+ /* tty_schedule_flip (tty); */
+ }
+
+ func_exit();
+}
+
+/* Inlined: it is called only once. Remove the inline if you add another
+ call */
+static inline void sx_check_modem_signals(struct sx_port *port)
+{
+ int hi_state;
+ int c_dcd;
+
+ hi_state = sx_read_channel_byte(port, hi_state);
+ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "Checking modem signals (%d/%d)\n",
+ port->c_dcd, tty_port_carrier_raised(&port->gs.port));
+
+ if (hi_state & ST_BREAK) {
+ hi_state &= ~ST_BREAK;
+ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "got a break.\n");
+ sx_write_channel_byte(port, hi_state, hi_state);
+ gs_got_break(&port->gs);
+ }
+ if (hi_state & ST_DCD) {
+ hi_state &= ~ST_DCD;
+ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "got a DCD change.\n");
+ sx_write_channel_byte(port, hi_state, hi_state);
+ c_dcd = tty_port_carrier_raised(&port->gs.port);
+ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD is now %d\n", c_dcd);
+ if (c_dcd != port->c_dcd) {
+ port->c_dcd = c_dcd;
+ if (tty_port_carrier_raised(&port->gs.port)) {
+ /* DCD went UP */
+ if ((sx_read_channel_byte(port, hi_hstat) !=
+ HS_IDLE_CLOSED) &&
+ !(port->gs.port.tty->termios->
+ c_cflag & CLOCAL)) {
+ /* Are we blocking in open? */
+ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD "
+ "active, unblocking open\n");
+ wake_up_interruptible(&port->gs.port.
+ open_wait);
+ } else {
+ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD "
+ "raised. Ignoring.\n");
+ }
+ } else {
+ /* DCD went down! */
+ if (!(port->gs.port.tty->termios->c_cflag & CLOCAL)){
+ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD "
+ "dropped. hanging up....\n");
+ tty_hangup(port->gs.port.tty);
+ } else {
+ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD "
+ "dropped. ignoring.\n");
+ }
+ }
+ } else {
+ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "Hmmm. card told us "
+ "DCD changed, but it didn't.\n");
+ }
+ }
+}
+
+/* This is what an interrupt routine should look like.
+ * Small, elegant, clear.
+ */
+
+static irqreturn_t sx_interrupt(int irq, void *ptr)
+{
+ struct sx_board *board = ptr;
+ struct sx_port *port;
+ int i;
+
+ func_enter();
+ sx_dprintk(SX_DEBUG_FLOW, "sx: enter sx_interrupt (%d/%d)\n", irq,
+ board->irq);
+
+ /* AAargh! The order in which to do these things is essential and
+ not trivial.
+
+ - Rate limit goes before "recursive". Otherwise a series of
+ recursive calls will hang the machine in the interrupt routine.
+
+ - hardware twiddling goes before "recursive". Otherwise when we
+ poll the card, and a recursive interrupt happens, we won't
+ ack the card, so it might keep on interrupting us. (especially
+ level sensitive interrupt systems like PCI).
+
+ - Rate limit goes before hardware twiddling. Otherwise we won't
+ catch a card that has gone bonkers.
+
+ - The "initialized" test goes after the hardware twiddling. Otherwise
+ the card will stick us in the interrupt routine again.
+
+ - The initialized test goes before recursive.
+ */
+
+#ifdef IRQ_RATE_LIMIT
+ /* Aaargh! I'm ashamed. This costs more lines-of-code than the
+ actual interrupt routine!. (Well, used to when I wrote that
+ comment) */
+ {
+ static int lastjif;
+ static int nintr = 0;
+
+ if (lastjif == jiffies) {
+ if (++nintr > IRQ_RATE_LIMIT) {
+ free_irq(board->irq, board);
+ printk(KERN_ERR "sx: Too many interrupts. "
+ "Turning off interrupt %d.\n",
+ board->irq);
+ }
+ } else {
+ lastjif = jiffies;
+ nintr = 0;
+ }
+ }
+#endif
+
+ if (board->irq == irq) {
+ /* Tell the card we've noticed the interrupt. */
+
+ sx_write_board_word(board, cc_int_pending, 0);
+ if (IS_SX_BOARD(board)) {
+ write_sx_byte(board, SX_RESET_IRQ, 1);
+ } else if (IS_EISA_BOARD(board)) {
+ inb(board->eisa_base + 0xc03);
+ write_sx_word(board, 8, 0);
+ } else {
+ write_sx_byte(board, SI2_ISA_INTCLEAR,
+ SI2_ISA_INTCLEAR_CLEAR);
+ write_sx_byte(board, SI2_ISA_INTCLEAR,
+ SI2_ISA_INTCLEAR_SET);
+ }
+ }
+
+ if (!sx_initialized)
+ return IRQ_HANDLED;
+ if (!(board->flags & SX_BOARD_INITIALIZED))
+ return IRQ_HANDLED;
+
+ if (test_and_set_bit(SX_BOARD_INTR_LOCK, &board->locks)) {
+ printk(KERN_ERR "Recursive interrupt! (%d)\n", board->irq);
+ return IRQ_HANDLED;
+ }
+
+ for (i = 0; i < board->nports; i++) {
+ port = &board->ports[i];
+ if (port->gs.port.flags & GS_ACTIVE) {
+ if (sx_read_channel_byte(port, hi_state)) {
+ sx_dprintk(SX_DEBUG_INTERRUPTS, "Port %d: "
+ "modem signal change?... \n",i);
+ sx_check_modem_signals(port);
+ }
+ if (port->gs.xmit_cnt) {
+ sx_transmit_chars(port);
+ }
+ if (!(port->gs.port.flags & SX_RX_THROTTLE)) {
+ sx_receive_chars(port);
+ }
+ }
+ }
+
+ clear_bit(SX_BOARD_INTR_LOCK, &board->locks);
+
+ sx_dprintk(SX_DEBUG_FLOW, "sx: exit sx_interrupt (%d/%d)\n", irq,
+ board->irq);
+ func_exit();
+ return IRQ_HANDLED;
+}
+
+static void sx_pollfunc(unsigned long data)
+{
+ struct sx_board *board = (struct sx_board *)data;
+
+ func_enter();
+
+ sx_interrupt(0, board);
+
+ mod_timer(&board->timer, jiffies + sx_poll);
+ func_exit();
+}
+
+/* ********************************************************************** *
+ * Here are the routines that actually *
+ * interface with the generic_serial driver *
+ * ********************************************************************** */
+
+/* Ehhm. I don't know how to fiddle with interrupts on the SX card. --REW */
+/* Hmm. Ok I figured it out. You don't. */
+
+static void sx_disable_tx_interrupts(void *ptr)
+{
+ struct sx_port *port = ptr;
+ func_enter2();
+
+ port->gs.port.flags &= ~GS_TX_INTEN;
+
+ func_exit();
+}
+
+static void sx_enable_tx_interrupts(void *ptr)
+{
+ struct sx_port *port = ptr;
+ int data_in_buffer;
+ func_enter2();
+
+ /* First transmit the characters that we're supposed to */
+ sx_transmit_chars(port);
+
+ /* The sx card will never interrupt us if we don't fill the buffer
+ past 25%. So we keep considering interrupts off if that's the case. */
+ data_in_buffer = (sx_read_channel_byte(port, hi_txipos) -
+ sx_read_channel_byte(port, hi_txopos)) & 0xff;
+
+ /* XXX Must be "HIGH_WATER" for SI card according to doc. */
+ if (data_in_buffer < LOW_WATER)
+ port->gs.port.flags &= ~GS_TX_INTEN;
+
+ func_exit();
+}
+
+static void sx_disable_rx_interrupts(void *ptr)
+{
+ /* struct sx_port *port = ptr; */
+ func_enter();
+
+ func_exit();
+}
+
+static void sx_enable_rx_interrupts(void *ptr)
+{
+ /* struct sx_port *port = ptr; */
+ func_enter();
+
+ func_exit();
+}
+
+/* Jeez. Isn't this simple? */
+static int sx_carrier_raised(struct tty_port *port)
+{
+ struct sx_port *sp = container_of(port, struct sx_port, gs.port);
+ return ((sx_read_channel_byte(sp, hi_ip) & IP_DCD) != 0);
+}
+
+/* Jeez. Isn't this simple? */
+static int sx_chars_in_buffer(void *ptr)
+{
+ struct sx_port *port = ptr;
+ func_enter2();
+
+ func_exit();
+ return ((sx_read_channel_byte(port, hi_txipos) -
+ sx_read_channel_byte(port, hi_txopos)) & 0xff);
+}
+
+static void sx_shutdown_port(void *ptr)
+{
+ struct sx_port *port = ptr;
+
+ func_enter();
+
+ port->gs.port.flags &= ~GS_ACTIVE;
+ if (port->gs.port.tty && (port->gs.port.tty->termios->c_cflag & HUPCL)) {
+ sx_setsignals(port, 0, 0);
+ sx_reconfigure_port(port);
+ }
+
+ func_exit();
+}
+
+/* ********************************************************************** *
+ * Here are the routines that actually *
+ * interface with the rest of the system *
+ * ********************************************************************** */
+
+static int sx_open(struct tty_struct *tty, struct file *filp)
+{
+ struct sx_port *port;
+ int retval, line;
+ unsigned long flags;
+
+ func_enter();
+
+ if (!sx_initialized) {
+ return -EIO;
+ }
+
+ line = tty->index;
+ sx_dprintk(SX_DEBUG_OPEN, "%d: opening line %d. tty=%p ctty=%p, "
+ "np=%d)\n", task_pid_nr(current), line, tty,
+ current->signal->tty, sx_nports);
+
+ if ((line < 0) || (line >= SX_NPORTS) || (line >= sx_nports))
+ return -ENODEV;
+
+ port = &sx_ports[line];
+ port->c_dcd = 0; /* Make sure that the first interrupt doesn't detect a
+ 1 -> 0 transition. */
+
+ sx_dprintk(SX_DEBUG_OPEN, "port = %p c_dcd = %d\n", port, port->c_dcd);
+
+ spin_lock_irqsave(&port->gs.driver_lock, flags);
+
+ tty->driver_data = port;
+ port->gs.port.tty = tty;
+ port->gs.port.count++;
+ spin_unlock_irqrestore(&port->gs.driver_lock, flags);
+
+ sx_dprintk(SX_DEBUG_OPEN, "starting port\n");
+
+ /*
+ * Start up serial port
+ */
+ retval = gs_init_port(&port->gs);
+ sx_dprintk(SX_DEBUG_OPEN, "done gs_init\n");
+ if (retval) {
+ port->gs.port.count--;
+ return retval;
+ }
+
+ port->gs.port.flags |= GS_ACTIVE;
+ if (port->gs.port.count <= 1)
+ sx_setsignals(port, 1, 1);
+
+#if 0
+ if (sx_debug & SX_DEBUG_OPEN)
+ my_hd(port, sizeof(*port));
+#else
+ if (sx_debug & SX_DEBUG_OPEN)
+ my_hd_io(port->board->base + port->ch_base, sizeof(*port));
+#endif
+
+ if (port->gs.port.count <= 1) {
+ if (sx_send_command(port, HS_LOPEN, -1, HS_IDLE_OPEN) != 1) {
+ printk(KERN_ERR "sx: Card didn't respond to LOPEN "
+ "command.\n");
+ spin_lock_irqsave(&port->gs.driver_lock, flags);
+ port->gs.port.count--;
+ spin_unlock_irqrestore(&port->gs.driver_lock, flags);
+ return -EIO;
+ }
+ }
+
+ retval = gs_block_til_ready(port, filp);
+ sx_dprintk(SX_DEBUG_OPEN, "Block til ready returned %d. Count=%d\n",
+ retval, port->gs.port.count);
+
+ if (retval) {
+/*
+ * Don't lower gs.port.count here because sx_close() will be called later
+ */
+
+ return retval;
+ }
+ /* tty->low_latency = 1; */
+
+ port->c_dcd = sx_carrier_raised(&port->gs.port);
+ sx_dprintk(SX_DEBUG_OPEN, "at open: cd=%d\n", port->c_dcd);
+
+ func_exit();
+ return 0;
+
+}
+
+static void sx_close(void *ptr)
+{
+ struct sx_port *port = ptr;
+ /* Give the port 5 seconds to close down. */
+ int to = 5 * HZ;
+
+ func_enter();
+
+ sx_setsignals(port, 0, 0);
+ sx_reconfigure_port(port);
+ sx_send_command(port, HS_CLOSE, 0, 0);
+
+ while (to-- && (sx_read_channel_byte(port, hi_hstat) != HS_IDLE_CLOSED))
+ if (msleep_interruptible(10))
+ break;
+ if (sx_read_channel_byte(port, hi_hstat) != HS_IDLE_CLOSED) {
+ if (sx_send_command(port, HS_FORCE_CLOSED, -1, HS_IDLE_CLOSED)
+ != 1) {
+ printk(KERN_ERR "sx: sent the force_close command, but "
+ "card didn't react\n");
+ } else
+ sx_dprintk(SX_DEBUG_CLOSE, "sent the force_close "
+ "command.\n");
+ }
+
+ sx_dprintk(SX_DEBUG_CLOSE, "waited %d jiffies for close. count=%d\n",
+ 5 * HZ - to - 1, port->gs.port.count);
+
+ if (port->gs.port.count) {
+ sx_dprintk(SX_DEBUG_CLOSE, "WARNING port count:%d\n",
+ port->gs.port.count);
+ /*printk("%s SETTING port count to zero: %p count: %d\n",
+ __func__, port, port->gs.port.count);
+ port->gs.port.count = 0;*/
+ }
+
+ func_exit();
+}
+
+/* This is relatively thorough. But then again it is only 20 lines. */
+#define MARCHUP for (i = min; i < max; i++)
+#define MARCHDOWN for (i = max - 1; i >= min; i--)
+#define W0 write_sx_byte(board, i, 0x55)
+#define W1 write_sx_byte(board, i, 0xaa)
+#define R0 if (read_sx_byte(board, i) != 0x55) return 1
+#define R1 if (read_sx_byte(board, i) != 0xaa) return 1
+
+/* This memtest takes a human-noticable time. You normally only do it
+ once a boot, so I guess that it is worth it. */
+static int do_memtest(struct sx_board *board, int min, int max)
+{
+ int i;
+
+ /* This is a marchb. Theoretically, marchb catches much more than
+ simpler tests. In practise, the longer test just catches more
+ intermittent errors. -- REW
+ (For the theory behind memory testing see:
+ Testing Semiconductor Memories by A.J. van de Goor.) */
+ MARCHUP {
+ W0;
+ }
+ MARCHUP {
+ R0;
+ W1;
+ R1;
+ W0;
+ R0;
+ W1;
+ }
+ MARCHUP {
+ R1;
+ W0;
+ W1;
+ }
+ MARCHDOWN {
+ R1;
+ W0;
+ W1;
+ W0;
+ }
+ MARCHDOWN {
+ R0;
+ W1;
+ W0;
+ }
+
+ return 0;
+}
+
+#undef MARCHUP
+#undef MARCHDOWN
+#undef W0
+#undef W1
+#undef R0
+#undef R1
+
+#define MARCHUP for (i = min; i < max; i += 2)
+#define MARCHDOWN for (i = max - 1; i >= min; i -= 2)
+#define W0 write_sx_word(board, i, 0x55aa)
+#define W1 write_sx_word(board, i, 0xaa55)
+#define R0 if (read_sx_word(board, i) != 0x55aa) return 1
+#define R1 if (read_sx_word(board, i) != 0xaa55) return 1
+
+#if 0
+/* This memtest takes a human-noticable time. You normally only do it
+ once a boot, so I guess that it is worth it. */
+static int do_memtest_w(struct sx_board *board, int min, int max)
+{
+ int i;
+
+ MARCHUP {
+ W0;
+ }
+ MARCHUP {
+ R0;
+ W1;
+ R1;
+ W0;
+ R0;
+ W1;
+ }
+ MARCHUP {
+ R1;
+ W0;
+ W1;
+ }
+ MARCHDOWN {
+ R1;
+ W0;
+ W1;
+ W0;
+ }
+ MARCHDOWN {
+ R0;
+ W1;
+ W0;
+ }
+
+ return 0;
+}
+#endif
+
+static long sx_fw_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ long rc = 0;
+ int __user *descr = (int __user *)arg;
+ int i;
+ static struct sx_board *board = NULL;
+ int nbytes, offset;
+ unsigned long data;
+ char *tmp;
+
+ func_enter();
+
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ tty_lock();
+
+ sx_dprintk(SX_DEBUG_FIRMWARE, "IOCTL %x: %lx\n", cmd, arg);
+
+ if (!board)
+ board = &boards[0];
+ if (board->flags & SX_BOARD_PRESENT) {
+ sx_dprintk(SX_DEBUG_FIRMWARE, "Board present! (%x)\n",
+ board->flags);
+ } else {
+ sx_dprintk(SX_DEBUG_FIRMWARE, "Board not present! (%x) all:",
+ board->flags);
+ for (i = 0; i < SX_NBOARDS; i++)
+ sx_dprintk(SX_DEBUG_FIRMWARE, "<%x> ", boards[i].flags);
+ sx_dprintk(SX_DEBUG_FIRMWARE, "\n");
+ rc = -EIO;
+ goto out;
+ }
+
+ switch (cmd) {
+ case SXIO_SET_BOARD:
+ sx_dprintk(SX_DEBUG_FIRMWARE, "set board to %ld\n", arg);
+ rc = -EIO;
+ if (arg >= SX_NBOARDS)
+ break;
+ sx_dprintk(SX_DEBUG_FIRMWARE, "not out of range\n");
+ if (!(boards[arg].flags & SX_BOARD_PRESENT))
+ break;
+ sx_dprintk(SX_DEBUG_FIRMWARE, ".. and present!\n");
+ board = &boards[arg];
+ rc = 0;
+ /* FIXME: And this does ... nothing?? */
+ break;
+ case SXIO_GET_TYPE:
+ rc = -ENOENT; /* If we manage to miss one, return error. */
+ if (IS_SX_BOARD(board))
+ rc = SX_TYPE_SX;
+ if (IS_CF_BOARD(board))
+ rc = SX_TYPE_CF;
+ if (IS_SI_BOARD(board))
+ rc = SX_TYPE_SI;
+ if (IS_SI1_BOARD(board))
+ rc = SX_TYPE_SI;
+ if (IS_EISA_BOARD(board))
+ rc = SX_TYPE_SI;
+ sx_dprintk(SX_DEBUG_FIRMWARE, "returning type= %ld\n", rc);
+ break;
+ case SXIO_DO_RAMTEST:
+ if (sx_initialized) { /* Already initialized: better not ramtest the board. */
+ rc = -EPERM;
+ break;
+ }
+ if (IS_SX_BOARD(board)) {
+ rc = do_memtest(board, 0, 0x7000);
+ if (!rc)
+ rc = do_memtest(board, 0, 0x7000);
+ /*if (!rc) rc = do_memtest_w (board, 0, 0x7000); */
+ } else {
+ rc = do_memtest(board, 0, 0x7ff8);
+ /* if (!rc) rc = do_memtest_w (board, 0, 0x7ff8); */
+ }
+ sx_dprintk(SX_DEBUG_FIRMWARE,
+ "returning memtest result= %ld\n", rc);
+ break;
+ case SXIO_DOWNLOAD:
+ if (sx_initialized) {/* Already initialized */
+ rc = -EEXIST;
+ break;
+ }
+ if (!sx_reset(board)) {
+ rc = -EIO;
+ break;
+ }
+ sx_dprintk(SX_DEBUG_INIT, "reset the board...\n");
+
+ tmp = kmalloc(SX_CHUNK_SIZE, GFP_USER);
+ if (!tmp) {
+ rc = -ENOMEM;
+ break;
+ }
+ /* FIXME: check returns */
+ get_user(nbytes, descr++);
+ get_user(offset, descr++);
+ get_user(data, descr++);
+ while (nbytes && data) {
+ for (i = 0; i < nbytes; i += SX_CHUNK_SIZE) {
+ if (copy_from_user(tmp, (char __user *)data + i,
+ (i + SX_CHUNK_SIZE > nbytes) ?
+ nbytes - i : SX_CHUNK_SIZE)) {
+ kfree(tmp);
+ rc = -EFAULT;
+ goto out;
+ }
+ memcpy_toio(board->base2 + offset + i, tmp,
+ (i + SX_CHUNK_SIZE > nbytes) ?
+ nbytes - i : SX_CHUNK_SIZE);
+ }
+
+ get_user(nbytes, descr++);
+ get_user(offset, descr++);
+ get_user(data, descr++);
+ }
+ kfree(tmp);
+ sx_nports += sx_init_board(board);
+ rc = sx_nports;
+ break;
+ case SXIO_INIT:
+ if (sx_initialized) { /* Already initialized */
+ rc = -EEXIST;
+ break;
+ }
+ /* This is not allowed until all boards are initialized... */
+ for (i = 0; i < SX_NBOARDS; i++) {
+ if ((boards[i].flags & SX_BOARD_PRESENT) &&
+ !(boards[i].flags & SX_BOARD_INITIALIZED)) {
+ rc = -EIO;
+ break;
+ }
+ }
+ for (i = 0; i < SX_NBOARDS; i++)
+ if (!(boards[i].flags & SX_BOARD_PRESENT))
+ break;
+
+ sx_dprintk(SX_DEBUG_FIRMWARE, "initing portstructs, %d boards, "
+ "%d channels, first board: %d ports\n",
+ i, sx_nports, boards[0].nports);
+ rc = sx_init_portstructs(i, sx_nports);
+ sx_init_drivers();
+ if (rc >= 0)
+ sx_initialized++;
+ break;
+ case SXIO_SETDEBUG:
+ sx_debug = arg;
+ break;
+ case SXIO_GETDEBUG:
+ rc = sx_debug;
+ break;
+ case SXIO_GETGSDEBUG:
+ case SXIO_SETGSDEBUG:
+ rc = -EINVAL;
+ break;
+ case SXIO_GETNPORTS:
+ rc = sx_nports;
+ break;
+ default:
+ rc = -ENOTTY;
+ break;
+ }
+out:
+ tty_unlock();
+ func_exit();
+ return rc;
+}
+
+static int sx_break(struct tty_struct *tty, int flag)
+{
+ struct sx_port *port = tty->driver_data;
+ int rv;
+
+ func_enter();
+ tty_lock();
+
+ if (flag)
+ rv = sx_send_command(port, HS_START, -1, HS_IDLE_BREAK);
+ else
+ rv = sx_send_command(port, HS_STOP, -1, HS_IDLE_OPEN);
+ if (rv != 1)
+ printk(KERN_ERR "sx: couldn't send break (%x).\n",
+ read_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat)));
+ tty_unlock();
+ func_exit();
+ return 0;
+}
+
+static int sx_tiocmget(struct tty_struct *tty)
+{
+ struct sx_port *port = tty->driver_data;
+ return sx_getsignals(port);
+}
+
+static int sx_tiocmset(struct tty_struct *tty,
+ unsigned int set, unsigned int clear)
+{
+ struct sx_port *port = tty->driver_data;
+ int rts = -1, dtr = -1;
+
+ if (set & TIOCM_RTS)
+ rts = 1;
+ if (set & TIOCM_DTR)
+ dtr = 1;
+ if (clear & TIOCM_RTS)
+ rts = 0;
+ if (clear & TIOCM_DTR)
+ dtr = 0;
+
+ sx_setsignals(port, dtr, rts);
+ sx_reconfigure_port(port);
+ return 0;
+}
+
+static int sx_ioctl(struct tty_struct *tty,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc;
+ struct sx_port *port = tty->driver_data;
+ void __user *argp = (void __user *)arg;
+
+ /* func_enter2(); */
+
+ rc = 0;
+ tty_lock();
+ switch (cmd) {
+ case TIOCGSERIAL:
+ rc = gs_getserial(&port->gs, argp);
+ break;
+ case TIOCSSERIAL:
+ rc = gs_setserial(&port->gs, argp);
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+ tty_unlock();
+
+ /* func_exit(); */
+ return rc;
+}
+
+/* The throttle/unthrottle scheme for the Specialix card is different
+ * from other drivers and deserves some explanation.
+ * The Specialix hardware takes care of XON/XOFF
+ * and CTS/RTS flow control itself. This means that all we have to
+ * do when signalled by the upper tty layer to throttle/unthrottle is
+ * to make a note of it here. When we come to read characters from the
+ * rx buffers on the card (sx_receive_chars()) we look to see if the
+ * upper layer can accept more (as noted here in sx_rx_throt[]).
+ * If it can't we simply don't remove chars from the cards buffer.
+ * When the tty layer can accept chars, we again note that here and when
+ * sx_receive_chars() is called it will remove them from the cards buffer.
+ * The card will notice that a ports buffer has drained below some low
+ * water mark and will unflow control the line itself, using whatever
+ * flow control scheme is in use for that port. -- Simon Allen
+ */
+
+static void sx_throttle(struct tty_struct *tty)
+{
+ struct sx_port *port = tty->driver_data;
+
+ func_enter2();
+ /* If the port is using any type of input flow
+ * control then throttle the port.
+ */
+ if ((tty->termios->c_cflag & CRTSCTS) || (I_IXOFF(tty))) {
+ port->gs.port.flags |= SX_RX_THROTTLE;
+ }
+ func_exit();
+}
+
+static void sx_unthrottle(struct tty_struct *tty)
+{
+ struct sx_port *port = tty->driver_data;
+
+ func_enter2();
+ /* Always unthrottle even if flow control is not enabled on
+ * this port in case we disabled flow control while the port
+ * was throttled
+ */
+ port->gs.port.flags &= ~SX_RX_THROTTLE;
+ func_exit();
+ return;
+}
+
+/* ********************************************************************** *
+ * Here are the initialization routines. *
+ * ********************************************************************** */
+
+static int sx_init_board(struct sx_board *board)
+{
+ int addr;
+ int chans;
+ int type;
+
+ func_enter();
+
+ /* This is preceded by downloading the download code. */
+
+ board->flags |= SX_BOARD_INITIALIZED;
+
+ if (read_sx_byte(board, 0))
+ /* CF boards may need this. */
+ write_sx_byte(board, 0, 0);
+
+ /* This resets the processor again, to make sure it didn't do any
+ foolish things while we were downloading the image */
+ if (!sx_reset(board))
+ return 0;
+
+ sx_start_board(board);
+ udelay(10);
+ if (!sx_busy_wait_neq(board, 0, 0xff, 0)) {
+ printk(KERN_ERR "sx: Ooops. Board won't initialize.\n");
+ return 0;
+ }
+
+ /* Ok. So now the processor on the card is running. It gathered
+ some info for us... */
+ sx_dprintk(SX_DEBUG_INIT, "The sxcard structure:\n");
+ if (sx_debug & SX_DEBUG_INIT)
+ my_hd_io(board->base, 0x10);
+ sx_dprintk(SX_DEBUG_INIT, "the first sx_module structure:\n");
+ if (sx_debug & SX_DEBUG_INIT)
+ my_hd_io(board->base + 0x80, 0x30);
+
+ sx_dprintk(SX_DEBUG_INIT, "init_status: %x, %dk memory, firmware "
+ "V%x.%02x,\n",
+ read_sx_byte(board, 0), read_sx_byte(board, 1),
+ read_sx_byte(board, 5), read_sx_byte(board, 4));
+
+ if (read_sx_byte(board, 0) == 0xff) {
+ printk(KERN_INFO "sx: No modules found. Sorry.\n");
+ board->nports = 0;
+ return 0;
+ }
+
+ chans = 0;
+
+ if (IS_SX_BOARD(board)) {
+ sx_write_board_word(board, cc_int_count, sx_maxints);
+ } else {
+ if (sx_maxints)
+ sx_write_board_word(board, cc_int_count,
+ SI_PROCESSOR_CLOCK / 8 / sx_maxints);
+ }
+
+ /* grab the first module type... */
+ /* board->ta_type = mod_compat_type (read_sx_byte (board, 0x80 + 0x08)); */
+ board->ta_type = mod_compat_type(sx_read_module_byte(board, 0x80,
+ mc_chip));
+
+ /* XXX byteorder */
+ for (addr = 0x80; addr != 0; addr = read_sx_word(board, addr) & 0x7fff){
+ type = sx_read_module_byte(board, addr, mc_chip);
+ sx_dprintk(SX_DEBUG_INIT, "Module at %x: %d channels\n",
+ addr, read_sx_byte(board, addr + 2));
+
+ chans += sx_read_module_byte(board, addr, mc_type);
+
+ sx_dprintk(SX_DEBUG_INIT, "module is an %s, which has %s/%s "
+ "panels\n",
+ mod_type_s(type),
+ pan_type_s(sx_read_module_byte(board, addr,
+ mc_mods) & 0xf),
+ pan_type_s(sx_read_module_byte(board, addr,
+ mc_mods) >> 4));
+
+ sx_dprintk(SX_DEBUG_INIT, "CD1400 versions: %x/%x, ASIC "
+ "version: %x\n",
+ sx_read_module_byte(board, addr, mc_rev1),
+ sx_read_module_byte(board, addr, mc_rev2),
+ sx_read_module_byte(board, addr, mc_mtaasic_rev));
+
+ /* The following combinations are illegal: It should theoretically
+ work, but timing problems make the bus HANG. */
+
+ if (mod_compat_type(type) != board->ta_type) {
+ printk(KERN_ERR "sx: This is an invalid "
+ "configuration.\nDon't mix TA/MTA/SXDC on the "
+ "same hostadapter.\n");
+ chans = 0;
+ break;
+ }
+ if ((IS_EISA_BOARD(board) ||
+ IS_SI_BOARD(board)) &&
+ (mod_compat_type(type) == 4)) {
+ printk(KERN_ERR "sx: This is an invalid "
+ "configuration.\nDon't use SXDCs on an SI/XIO "
+ "adapter.\n");
+ chans = 0;
+ break;
+ }
+#if 0 /* Problem fixed: firmware 3.05 */
+ if (IS_SX_BOARD(board) && (type == TA8)) {
+ /* There are some issues with the firmware and the DCD/RTS
+ lines. It might work if you tie them together or something.
+ It might also work if you get a newer sx_firmware. Therefore
+ this is just a warning. */
+ printk(KERN_WARNING
+ "sx: The SX host doesn't work too well "
+ "with the TA8 adapters.\nSpecialix is working on it.\n");
+ }
+#endif
+ }
+
+ if (chans) {
+ if (board->irq > 0) {
+ /* fixed irq, probably PCI */
+ if (sx_irqmask & (1 << board->irq)) { /* may we use this irq? */
+ if (request_irq(board->irq, sx_interrupt,
+ IRQF_SHARED | IRQF_DISABLED,
+ "sx", board)) {
+ printk(KERN_ERR "sx: Cannot allocate "
+ "irq %d.\n", board->irq);
+ board->irq = 0;
+ }
+ } else
+ board->irq = 0;
+ } else if (board->irq < 0 && sx_irqmask) {
+ /* auto-allocate irq */
+ int irqnr;
+ int irqmask = sx_irqmask & (IS_SX_BOARD(board) ?
+ SX_ISA_IRQ_MASK : SI2_ISA_IRQ_MASK);
+ for (irqnr = 15; irqnr > 0; irqnr--)
+ if (irqmask & (1 << irqnr))
+ if (!request_irq(irqnr, sx_interrupt,
+ IRQF_SHARED | IRQF_DISABLED,
+ "sx", board))
+ break;
+ if (!irqnr)
+ printk(KERN_ERR "sx: Cannot allocate IRQ.\n");
+ board->irq = irqnr;
+ } else
+ board->irq = 0;
+
+ if (board->irq) {
+ /* Found a valid interrupt, start up interrupts! */
+ sx_dprintk(SX_DEBUG_INIT, "Using irq %d.\n",
+ board->irq);
+ sx_start_interrupts(board);
+ board->poll = sx_slowpoll;
+ board->flags |= SX_IRQ_ALLOCATED;
+ } else {
+ /* no irq: setup board for polled operation */
+ board->poll = sx_poll;
+ sx_dprintk(SX_DEBUG_INIT, "Using poll-interval %d.\n",
+ board->poll);
+ }
+
+ /* The timer should be initialized anyway: That way we can
+ safely del_timer it when the module is unloaded. */
+ setup_timer(&board->timer, sx_pollfunc, (unsigned long)board);
+
+ if (board->poll)
+ mod_timer(&board->timer, jiffies + board->poll);
+ } else {
+ board->irq = 0;
+ }
+
+ board->nports = chans;
+ sx_dprintk(SX_DEBUG_INIT, "returning %d ports.", board->nports);
+
+ func_exit();
+ return chans;
+}
+
+static void __devinit printheader(void)
+{
+ static int header_printed;
+
+ if (!header_printed) {
+ printk(KERN_INFO "Specialix SX driver "
+ "(C) 1998/1999 R.E.Wolff@BitWizard.nl\n");
+ printk(KERN_INFO "sx: version " __stringify(SX_VERSION) "\n");
+ header_printed = 1;
+ }
+}
+
+static int __devinit probe_sx(struct sx_board *board)
+{
+ struct vpd_prom vpdp;
+ char *p;
+ int i;
+
+ func_enter();
+
+ if (!IS_CF_BOARD(board)) {
+ sx_dprintk(SX_DEBUG_PROBE, "Going to verify vpd prom at %p.\n",
+ board->base + SX_VPD_ROM);
+
+ if (sx_debug & SX_DEBUG_PROBE)
+ my_hd_io(board->base + SX_VPD_ROM, 0x40);
+
+ p = (char *)&vpdp;
+ for (i = 0; i < sizeof(struct vpd_prom); i++)
+ *p++ = read_sx_byte(board, SX_VPD_ROM + i * 2);
+
+ if (sx_debug & SX_DEBUG_PROBE)
+ my_hd(&vpdp, 0x20);
+
+ sx_dprintk(SX_DEBUG_PROBE, "checking identifier...\n");
+
+ if (strncmp(vpdp.identifier, SX_VPD_IDENT_STRING, 16) != 0) {
+ sx_dprintk(SX_DEBUG_PROBE, "Got non-SX identifier: "
+ "'%s'\n", vpdp.identifier);
+ return 0;
+ }
+ }
+
+ printheader();
+
+ if (!IS_CF_BOARD(board)) {
+ printk(KERN_DEBUG "sx: Found an SX board at %lx\n",
+ board->hw_base);
+ printk(KERN_DEBUG "sx: hw_rev: %d, assembly level: %d, "
+ "uniq ID:%08x, ",
+ vpdp.hwrev, vpdp.hwass, vpdp.uniqid);
+ printk("Manufactured: %d/%d\n", 1970 + vpdp.myear, vpdp.mweek);
+
+ if ((((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) !=
+ SX_PCI_UNIQUEID1) && (((vpdp.uniqid >> 24) &
+ SX_UNIQUEID_MASK) != SX_ISA_UNIQUEID1)) {
+ /* This might be a bit harsh. This was the primary
+ reason the SX/ISA card didn't work at first... */
+ printk(KERN_ERR "sx: Hmm. Not an SX/PCI or SX/ISA "
+ "card. Sorry: giving up.\n");
+ return (0);
+ }
+
+ if (((vpdp.uniqid >> 24) & SX_UNIQUEID_MASK) ==
+ SX_ISA_UNIQUEID1) {
+ if (((unsigned long)board->hw_base) & 0x8000) {
+ printk(KERN_WARNING "sx: Warning: There may be "
+ "hardware problems with the card at "
+ "%lx.\n", board->hw_base);
+ printk(KERN_WARNING "sx: Read sx.txt for more "
+ "info.\n");
+ }
+ }
+ }
+
+ board->nports = -1;
+
+ /* This resets the processor, and keeps it off the bus. */
+ if (!sx_reset(board))
+ return 0;
+ sx_dprintk(SX_DEBUG_INIT, "reset the board...\n");
+
+ func_exit();
+ return 1;
+}
+
+#if defined(CONFIG_ISA) || defined(CONFIG_EISA)
+
+/* Specialix probes for this card at 32k increments from 640k to 16M.
+ I consider machines with less than 16M unlikely nowadays, so I'm
+ not probing above 1Mb. Also, 0xa0000, 0xb0000, are taken by the VGA
+ card. 0xe0000 and 0xf0000 are taken by the BIOS. That only leaves
+ 0xc0000, 0xc8000, 0xd0000 and 0xd8000 . */
+
+static int __devinit probe_si(struct sx_board *board)
+{
+ int i;
+
+ func_enter();
+ sx_dprintk(SX_DEBUG_PROBE, "Going to verify SI signature hw %lx at "
+ "%p.\n", board->hw_base, board->base + SI2_ISA_ID_BASE);
+
+ if (sx_debug & SX_DEBUG_PROBE)
+ my_hd_io(board->base + SI2_ISA_ID_BASE, 0x8);
+
+ if (!IS_EISA_BOARD(board)) {
+ if (IS_SI1_BOARD(board)) {
+ for (i = 0; i < 8; i++) {
+ write_sx_byte(board, SI2_ISA_ID_BASE + 7 - i,i);
+ }
+ }
+ for (i = 0; i < 8; i++) {
+ if ((read_sx_byte(board, SI2_ISA_ID_BASE + 7 - i) & 7)
+ != i) {
+ func_exit();
+ return 0;
+ }
+ }
+ }
+
+ /* Now we're pretty much convinced that there is an SI board here,
+ but to prevent trouble, we'd better double check that we don't
+ have an SI1 board when we're probing for an SI2 board.... */
+
+ write_sx_byte(board, SI2_ISA_ID_BASE, 0x10);
+ if (IS_SI1_BOARD(board)) {
+ /* This should be an SI1 board, which has this
+ location writable... */
+ if (read_sx_byte(board, SI2_ISA_ID_BASE) != 0x10) {
+ func_exit();
+ return 0;
+ }
+ } else {
+ /* This should be an SI2 board, which has the bottom
+ 3 bits non-writable... */
+ if (read_sx_byte(board, SI2_ISA_ID_BASE) == 0x10) {
+ func_exit();
+ return 0;
+ }
+ }
+
+ /* Now we're pretty much convinced that there is an SI board here,
+ but to prevent trouble, we'd better double check that we don't
+ have an SI1 board when we're probing for an SI2 board.... */
+
+ write_sx_byte(board, SI2_ISA_ID_BASE, 0x10);
+ if (IS_SI1_BOARD(board)) {
+ /* This should be an SI1 board, which has this
+ location writable... */
+ if (read_sx_byte(board, SI2_ISA_ID_BASE) != 0x10) {
+ func_exit();
+ return 0;
+ }
+ } else {
+ /* This should be an SI2 board, which has the bottom
+ 3 bits non-writable... */
+ if (read_sx_byte(board, SI2_ISA_ID_BASE) == 0x10) {
+ func_exit();
+ return 0;
+ }
+ }
+
+ printheader();
+
+ printk(KERN_DEBUG "sx: Found an SI board at %lx\n", board->hw_base);
+ /* Compared to the SX boards, it is a complete guess as to what
+ this card is up to... */
+
+ board->nports = -1;
+
+ /* This resets the processor, and keeps it off the bus. */
+ if (!sx_reset(board))
+ return 0;
+ sx_dprintk(SX_DEBUG_INIT, "reset the board...\n");
+
+ func_exit();
+ return 1;
+}
+#endif
+
+static const struct tty_operations sx_ops = {
+ .break_ctl = sx_break,
+ .open = sx_open,
+ .close = gs_close,
+ .write = gs_write,
+ .put_char = gs_put_char,
+ .flush_chars = gs_flush_chars,
+ .write_room = gs_write_room,
+ .chars_in_buffer = gs_chars_in_buffer,
+ .flush_buffer = gs_flush_buffer,
+ .ioctl = sx_ioctl,
+ .throttle = sx_throttle,
+ .unthrottle = sx_unthrottle,
+ .set_termios = gs_set_termios,
+ .stop = gs_stop,
+ .start = gs_start,
+ .hangup = gs_hangup,
+ .tiocmget = sx_tiocmget,
+ .tiocmset = sx_tiocmset,
+};
+
+static const struct tty_port_operations sx_port_ops = {
+ .carrier_raised = sx_carrier_raised,
+};
+
+static int sx_init_drivers(void)
+{
+ int error;
+
+ func_enter();
+
+ sx_driver = alloc_tty_driver(sx_nports);
+ if (!sx_driver)
+ return 1;
+ sx_driver->owner = THIS_MODULE;
+ sx_driver->driver_name = "specialix_sx";
+ sx_driver->name = "ttyX";
+ sx_driver->major = SX_NORMAL_MAJOR;
+ sx_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ sx_driver->subtype = SERIAL_TYPE_NORMAL;
+ sx_driver->init_termios = tty_std_termios;
+ sx_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ sx_driver->init_termios.c_ispeed = 9600;
+ sx_driver->init_termios.c_ospeed = 9600;
+ sx_driver->flags = TTY_DRIVER_REAL_RAW;
+ tty_set_operations(sx_driver, &sx_ops);
+
+ if ((error = tty_register_driver(sx_driver))) {
+ put_tty_driver(sx_driver);
+ printk(KERN_ERR "sx: Couldn't register sx driver, error = %d\n",
+ error);
+ return 1;
+ }
+ func_exit();
+ return 0;
+}
+
+static int sx_init_portstructs(int nboards, int nports)
+{
+ struct sx_board *board;
+ struct sx_port *port;
+ int i, j;
+ int addr, chans;
+ int portno;
+
+ func_enter();
+
+ /* Many drivers statically allocate the maximum number of ports
+ There is no reason not to allocate them dynamically.
+ Is there? -- REW */
+ sx_ports = kcalloc(nports, sizeof(struct sx_port), GFP_KERNEL);
+ if (!sx_ports)
+ return -ENOMEM;
+
+ port = sx_ports;
+ for (i = 0; i < nboards; i++) {
+ board = &boards[i];
+ board->ports = port;
+ for (j = 0; j < boards[i].nports; j++) {
+ sx_dprintk(SX_DEBUG_INIT, "initing port %d\n", j);
+ tty_port_init(&port->gs.port);
+ port->gs.port.ops = &sx_port_ops;
+ port->gs.magic = SX_MAGIC;
+ port->gs.close_delay = HZ / 2;
+ port->gs.closing_wait = 30 * HZ;
+ port->board = board;
+ port->gs.rd = &sx_real_driver;
+#ifdef NEW_WRITE_LOCKING
+ port->gs.port_write_mutex = MUTEX;
+#endif
+ spin_lock_init(&port->gs.driver_lock);
+ /*
+ * Initializing wait queue
+ */
+ port++;
+ }
+ }
+
+ port = sx_ports;
+ portno = 0;
+ for (i = 0; i < nboards; i++) {
+ board = &boards[i];
+ board->port_base = portno;
+ /* Possibly the configuration was rejected. */
+ sx_dprintk(SX_DEBUG_PROBE, "Board has %d channels\n",
+ board->nports);
+ if (board->nports <= 0)
+ continue;
+ /* XXX byteorder ?? */
+ for (addr = 0x80; addr != 0;
+ addr = read_sx_word(board, addr) & 0x7fff) {
+ chans = sx_read_module_byte(board, addr, mc_type);
+ sx_dprintk(SX_DEBUG_PROBE, "Module at %x: %d "
+ "channels\n", addr, chans);
+ sx_dprintk(SX_DEBUG_PROBE, "Port at");
+ for (j = 0; j < chans; j++) {
+ /* The "sx-way" is the way it SHOULD be done.
+ That way in the future, the firmware may for
+ example pack the structures a bit more
+ efficient. Neil tells me it isn't going to
+ happen anytime soon though. */
+ if (IS_SX_BOARD(board))
+ port->ch_base = sx_read_module_word(
+ board, addr + j * 2,
+ mc_chan_pointer);
+ else
+ port->ch_base = addr + 0x100 + 0x300 *j;
+
+ sx_dprintk(SX_DEBUG_PROBE, " %x",
+ port->ch_base);
+ port->line = portno++;
+ port++;
+ }
+ sx_dprintk(SX_DEBUG_PROBE, "\n");
+ }
+ /* This has to be done earlier. */
+ /* board->flags |= SX_BOARD_INITIALIZED; */
+ }
+
+ func_exit();
+ return 0;
+}
+
+static unsigned int sx_find_free_board(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < SX_NBOARDS; i++)
+ if (!(boards[i].flags & SX_BOARD_PRESENT))
+ break;
+
+ return i;
+}
+
+static void __exit sx_release_drivers(void)
+{
+ func_enter();
+ tty_unregister_driver(sx_driver);
+ put_tty_driver(sx_driver);
+ func_exit();
+}
+
+static void __devexit sx_remove_card(struct sx_board *board,
+ struct pci_dev *pdev)
+{
+ if (board->flags & SX_BOARD_INITIALIZED) {
+ /* The board should stop messing with us. (actually I mean the
+ interrupt) */
+ sx_reset(board);
+ if ((board->irq) && (board->flags & SX_IRQ_ALLOCATED))
+ free_irq(board->irq, board);
+
+ /* It is safe/allowed to del_timer a non-active timer */
+ del_timer(&board->timer);
+ if (pdev) {
+#ifdef CONFIG_PCI
+ iounmap(board->base2);
+ pci_release_region(pdev, IS_CF_BOARD(board) ? 3 : 2);
+#endif
+ } else {
+ iounmap(board->base);
+ release_region(board->hw_base, board->hw_len);
+ }
+
+ board->flags &= ~(SX_BOARD_INITIALIZED | SX_BOARD_PRESENT);
+ }
+}
+
+#ifdef CONFIG_EISA
+
+static int __devinit sx_eisa_probe(struct device *dev)
+{
+ struct eisa_device *edev = to_eisa_device(dev);
+ struct sx_board *board;
+ unsigned long eisa_slot = edev->base_addr;
+ unsigned int i;
+ int retval = -EIO;
+
+ mutex_lock(&sx_boards_lock);
+ i = sx_find_free_board();
+ if (i == SX_NBOARDS) {
+ mutex_unlock(&sx_boards_lock);
+ goto err;
+ }
+ board = &boards[i];
+ board->flags |= SX_BOARD_PRESENT;
+ mutex_unlock(&sx_boards_lock);
+
+ dev_info(dev, "XIO : Signature found in EISA slot %lu, "
+ "Product %d Rev %d (REPORT THIS TO LKLM)\n",
+ eisa_slot >> 12,
+ inb(eisa_slot + EISA_VENDOR_ID_OFFSET + 2),
+ inb(eisa_slot + EISA_VENDOR_ID_OFFSET + 3));
+
+ board->eisa_base = eisa_slot;
+ board->flags &= ~SX_BOARD_TYPE;
+ board->flags |= SI_EISA_BOARD;
+
+ board->hw_base = ((inb(eisa_slot + 0xc01) << 8) +
+ inb(eisa_slot + 0xc00)) << 16;
+ board->hw_len = SI2_EISA_WINDOW_LEN;
+ if (!request_region(board->hw_base, board->hw_len, "sx")) {
+ dev_err(dev, "can't request region\n");
+ goto err_flag;
+ }
+ board->base2 =
+ board->base = ioremap_nocache(board->hw_base, SI2_EISA_WINDOW_LEN);
+ if (!board->base) {
+ dev_err(dev, "can't remap memory\n");
+ goto err_reg;
+ }
+
+ sx_dprintk(SX_DEBUG_PROBE, "IO hw_base address: %lx\n", board->hw_base);
+ sx_dprintk(SX_DEBUG_PROBE, "base: %p\n", board->base);
+ board->irq = inb(eisa_slot + 0xc02) >> 4;
+ sx_dprintk(SX_DEBUG_PROBE, "IRQ: %d\n", board->irq);
+
+ if (!probe_si(board))
+ goto err_unmap;
+
+ dev_set_drvdata(dev, board);
+
+ return 0;
+err_unmap:
+ iounmap(board->base);
+err_reg:
+ release_region(board->hw_base, board->hw_len);
+err_flag:
+ board->flags &= ~SX_BOARD_PRESENT;
+err:
+ return retval;
+}
+
+static int __devexit sx_eisa_remove(struct device *dev)
+{
+ struct sx_board *board = dev_get_drvdata(dev);
+
+ sx_remove_card(board, NULL);
+
+ return 0;
+}
+
+static struct eisa_device_id sx_eisa_tbl[] = {
+ { "SLX" },
+ { "" }
+};
+
+MODULE_DEVICE_TABLE(eisa, sx_eisa_tbl);
+
+static struct eisa_driver sx_eisadriver = {
+ .id_table = sx_eisa_tbl,
+ .driver = {
+ .name = "sx",
+ .probe = sx_eisa_probe,
+ .remove = __devexit_p(sx_eisa_remove),
+ }
+};
+
+#endif
+
+#ifdef CONFIG_PCI
+ /********************************************************
+ * Setting bit 17 in the CNTRL register of the PLX 9050 *
+ * chip forces a retry on writes while a read is pending.*
+ * This is to prevent the card locking up on Intel Xeon *
+ * multiprocessor systems with the NX chipset. -- NV *
+ ********************************************************/
+
+/* Newer cards are produced with this bit set from the configuration
+ EEprom. As the bit is read/write for the CPU, we can fix it here,
+ if we detect that it isn't set correctly. -- REW */
+
+static void __devinit fix_sx_pci(struct pci_dev *pdev, struct sx_board *board)
+{
+ unsigned int hwbase;
+ void __iomem *rebase;
+ unsigned int t;
+
+#define CNTRL_REG_OFFSET 0x50
+#define CNTRL_REG_GOODVALUE 0x18260000
+
+ pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &hwbase);
+ hwbase &= PCI_BASE_ADDRESS_MEM_MASK;
+ rebase = ioremap_nocache(hwbase, 0x80);
+ t = readl(rebase + CNTRL_REG_OFFSET);
+ if (t != CNTRL_REG_GOODVALUE) {
+ printk(KERN_DEBUG "sx: performing cntrl reg fix: %08x -> "
+ "%08x\n", t, CNTRL_REG_GOODVALUE);
+ writel(CNTRL_REG_GOODVALUE, rebase + CNTRL_REG_OFFSET);
+ }
+ iounmap(rebase);
+}
+#endif
+
+static int __devinit sx_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+#ifdef CONFIG_PCI
+ struct sx_board *board;
+ unsigned int i, reg;
+ int retval = -EIO;
+
+ mutex_lock(&sx_boards_lock);
+ i = sx_find_free_board();
+ if (i == SX_NBOARDS) {
+ mutex_unlock(&sx_boards_lock);
+ goto err;
+ }
+ board = &boards[i];
+ board->flags |= SX_BOARD_PRESENT;
+ mutex_unlock(&sx_boards_lock);
+
+ retval = pci_enable_device(pdev);
+ if (retval)
+ goto err_flag;
+
+ board->flags &= ~SX_BOARD_TYPE;
+ board->flags |= (pdev->subsystem_vendor == 0x200) ? SX_PCI_BOARD :
+ SX_CFPCI_BOARD;
+
+ /* CF boards use base address 3.... */
+ reg = IS_CF_BOARD(board) ? 3 : 2;
+ retval = pci_request_region(pdev, reg, "sx");
+ if (retval) {
+ dev_err(&pdev->dev, "can't request region\n");
+ goto err_flag;
+ }
+ board->hw_base = pci_resource_start(pdev, reg);
+ board->base2 =
+ board->base = ioremap_nocache(board->hw_base, WINDOW_LEN(board));
+ if (!board->base) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ goto err_reg;
+ }
+
+ /* Most of the stuff on the CF board is offset by 0x18000 .... */
+ if (IS_CF_BOARD(board))
+ board->base += 0x18000;
+
+ board->irq = pdev->irq;
+
+ dev_info(&pdev->dev, "Got a specialix card: %p(%d) %x.\n", board->base,
+ board->irq, board->flags);
+
+ if (!probe_sx(board)) {
+ retval = -EIO;
+ goto err_unmap;
+ }
+
+ fix_sx_pci(pdev, board);
+
+ pci_set_drvdata(pdev, board);
+
+ return 0;
+err_unmap:
+ iounmap(board->base2);
+err_reg:
+ pci_release_region(pdev, reg);
+err_flag:
+ board->flags &= ~SX_BOARD_PRESENT;
+err:
+ return retval;
+#else
+ return -ENODEV;
+#endif
+}
+
+static void __devexit sx_pci_remove(struct pci_dev *pdev)
+{
+ struct sx_board *board = pci_get_drvdata(pdev);
+
+ sx_remove_card(board, pdev);
+}
+
+/* Specialix has a whole bunch of cards with 0x2000 as the device ID. They say
+ its because the standard requires it. So check for SUBVENDOR_ID. */
+static struct pci_device_id sx_pci_tbl[] = {
+ { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8,
+ .subvendor = PCI_ANY_ID, .subdevice = 0x0200 },
+ { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8,
+ .subvendor = PCI_ANY_ID, .subdevice = 0x0300 },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, sx_pci_tbl);
+
+static struct pci_driver sx_pcidriver = {
+ .name = "sx",
+ .id_table = sx_pci_tbl,
+ .probe = sx_pci_probe,
+ .remove = __devexit_p(sx_pci_remove)
+};
+
+static int __init sx_init(void)
+{
+#ifdef CONFIG_EISA
+ int retval1;
+#endif
+#ifdef CONFIG_ISA
+ struct sx_board *board;
+ unsigned int i;
+#endif
+ unsigned int found = 0;
+ int retval;
+
+ func_enter();
+ sx_dprintk(SX_DEBUG_INIT, "Initing sx module... (sx_debug=%d)\n",
+ sx_debug);
+ if (abs((long)(&sx_debug) - sx_debug) < 0x10000) {
+ printk(KERN_WARNING "sx: sx_debug is an address, instead of a "
+ "value. Assuming -1.\n(%p)\n", &sx_debug);
+ sx_debug = -1;
+ }
+
+ if (misc_register(&sx_fw_device) < 0) {
+ printk(KERN_ERR "SX: Unable to register firmware loader "
+ "driver.\n");
+ return -EIO;
+ }
+#ifdef CONFIG_ISA
+ for (i = 0; i < NR_SX_ADDRS; i++) {
+ board = &boards[found];
+ board->hw_base = sx_probe_addrs[i];
+ board->hw_len = SX_WINDOW_LEN;
+ if (!request_region(board->hw_base, board->hw_len, "sx"))
+ continue;
+ board->base2 =
+ board->base = ioremap_nocache(board->hw_base, board->hw_len);
+ if (!board->base)
+ goto err_sx_reg;
+ board->flags &= ~SX_BOARD_TYPE;
+ board->flags |= SX_ISA_BOARD;
+ board->irq = sx_irqmask ? -1 : 0;
+
+ if (probe_sx(board)) {
+ board->flags |= SX_BOARD_PRESENT;
+ found++;
+ } else {
+ iounmap(board->base);
+err_sx_reg:
+ release_region(board->hw_base, board->hw_len);
+ }
+ }
+
+ for (i = 0; i < NR_SI_ADDRS; i++) {
+ board = &boards[found];
+ board->hw_base = si_probe_addrs[i];
+ board->hw_len = SI2_ISA_WINDOW_LEN;
+ if (!request_region(board->hw_base, board->hw_len, "sx"))
+ continue;
+ board->base2 =
+ board->base = ioremap_nocache(board->hw_base, board->hw_len);
+ if (!board->base)
+ goto err_si_reg;
+ board->flags &= ~SX_BOARD_TYPE;
+ board->flags |= SI_ISA_BOARD;
+ board->irq = sx_irqmask ? -1 : 0;
+
+ if (probe_si(board)) {
+ board->flags |= SX_BOARD_PRESENT;
+ found++;
+ } else {
+ iounmap(board->base);
+err_si_reg:
+ release_region(board->hw_base, board->hw_len);
+ }
+ }
+ for (i = 0; i < NR_SI1_ADDRS; i++) {
+ board = &boards[found];
+ board->hw_base = si1_probe_addrs[i];
+ board->hw_len = SI1_ISA_WINDOW_LEN;
+ if (!request_region(board->hw_base, board->hw_len, "sx"))
+ continue;
+ board->base2 =
+ board->base = ioremap_nocache(board->hw_base, board->hw_len);
+ if (!board->base)
+ goto err_si1_reg;
+ board->flags &= ~SX_BOARD_TYPE;
+ board->flags |= SI1_ISA_BOARD;
+ board->irq = sx_irqmask ? -1 : 0;
+
+ if (probe_si(board)) {
+ board->flags |= SX_BOARD_PRESENT;
+ found++;
+ } else {
+ iounmap(board->base);
+err_si1_reg:
+ release_region(board->hw_base, board->hw_len);
+ }
+ }
+#endif
+#ifdef CONFIG_EISA
+ retval1 = eisa_driver_register(&sx_eisadriver);
+#endif
+ retval = pci_register_driver(&sx_pcidriver);
+
+ if (found) {
+ printk(KERN_INFO "sx: total of %d boards detected.\n", found);
+ retval = 0;
+ } else if (retval) {
+#ifdef CONFIG_EISA
+ retval = retval1;
+ if (retval1)
+#endif
+ misc_deregister(&sx_fw_device);
+ }
+
+ func_exit();
+ return retval;
+}
+
+static void __exit sx_exit(void)
+{
+ int i;
+
+ func_enter();
+#ifdef CONFIG_EISA
+ eisa_driver_unregister(&sx_eisadriver);
+#endif
+ pci_unregister_driver(&sx_pcidriver);
+
+ for (i = 0; i < SX_NBOARDS; i++)
+ sx_remove_card(&boards[i], NULL);
+
+ if (misc_deregister(&sx_fw_device) < 0) {
+ printk(KERN_INFO "sx: couldn't deregister firmware loader "
+ "device\n");
+ }
+ sx_dprintk(SX_DEBUG_CLEANUP, "Cleaning up drivers (%d)\n",
+ sx_initialized);
+ if (sx_initialized)
+ sx_release_drivers();
+
+ kfree(sx_ports);
+ func_exit();
+}
+
+module_init(sx_init);
+module_exit(sx_exit);
diff --git a/drivers/staging/generic_serial/sx.h b/drivers/staging/generic_serial/sx.h
new file mode 100644
index 000000000000..87c2defdead7
--- /dev/null
+++ b/drivers/staging/generic_serial/sx.h
@@ -0,0 +1,201 @@
+
+/*
+ * sx.h
+ *
+ * Copyright (C) 1998/1999 R.E.Wolff@BitWizard.nl
+ *
+ * SX serial driver.
+ * -- Supports SI, XIO and SX host cards.
+ * -- Supports TAs, MTAs and SXDCs.
+ *
+ * Version 1.3 -- March, 1999.
+ *
+ */
+
+#define SX_NBOARDS 4
+#define SX_PORTSPERBOARD 32
+#define SX_NPORTS (SX_NBOARDS * SX_PORTSPERBOARD)
+
+#ifdef __KERNEL__
+
+#define SX_MAGIC 0x12345678
+
+struct sx_port {
+ struct gs_port gs;
+ struct wait_queue *shutdown_wait;
+ int ch_base;
+ int c_dcd;
+ struct sx_board *board;
+ int line;
+ unsigned long locks;
+};
+
+struct sx_board {
+ int magic;
+ void __iomem *base;
+ void __iomem *base2;
+ unsigned long hw_base;
+ resource_size_t hw_len;
+ int eisa_base;
+ int port_base; /* Number of the first port */
+ struct sx_port *ports;
+ int nports;
+ int flags;
+ int irq;
+ int poll;
+ int ta_type;
+ struct timer_list timer;
+ unsigned long locks;
+};
+
+struct vpd_prom {
+ unsigned short id;
+ char hwrev;
+ char hwass;
+ int uniqid;
+ char myear;
+ char mweek;
+ char hw_feature[5];
+ char oem_id;
+ char identifier[16];
+};
+
+#ifndef MOD_RS232DB25MALE
+#define MOD_RS232DB25MALE 0x0a
+#endif
+
+#define SI_ISA_BOARD 0x00000001
+#define SX_ISA_BOARD 0x00000002
+#define SX_PCI_BOARD 0x00000004
+#define SX_CFPCI_BOARD 0x00000008
+#define SX_CFISA_BOARD 0x00000010
+#define SI_EISA_BOARD 0x00000020
+#define SI1_ISA_BOARD 0x00000040
+
+#define SX_BOARD_PRESENT 0x00001000
+#define SX_BOARD_INITIALIZED 0x00002000
+#define SX_IRQ_ALLOCATED 0x00004000
+
+#define SX_BOARD_TYPE 0x000000ff
+
+#define IS_SX_BOARD(board) (board->flags & (SX_PCI_BOARD | SX_CFPCI_BOARD | \
+ SX_ISA_BOARD | SX_CFISA_BOARD))
+
+#define IS_SI_BOARD(board) (board->flags & SI_ISA_BOARD)
+#define IS_SI1_BOARD(board) (board->flags & SI1_ISA_BOARD)
+
+#define IS_EISA_BOARD(board) (board->flags & SI_EISA_BOARD)
+
+#define IS_CF_BOARD(board) (board->flags & (SX_CFISA_BOARD | SX_CFPCI_BOARD))
+
+/* The SI processor clock is required to calculate the cc_int_count register
+ value for the SI cards. */
+#define SI_PROCESSOR_CLOCK 25000000
+
+
+/* port flags */
+/* Make sure these don't clash with gs flags or async flags */
+#define SX_RX_THROTTLE 0x0000001
+
+
+
+#define SX_PORT_TRANSMIT_LOCK 0
+#define SX_BOARD_INTR_LOCK 0
+
+
+
+/* Debug flags. Add these together to get more debug info. */
+
+#define SX_DEBUG_OPEN 0x00000001
+#define SX_DEBUG_SETTING 0x00000002
+#define SX_DEBUG_FLOW 0x00000004
+#define SX_DEBUG_MODEMSIGNALS 0x00000008
+#define SX_DEBUG_TERMIOS 0x00000010
+#define SX_DEBUG_TRANSMIT 0x00000020
+#define SX_DEBUG_RECEIVE 0x00000040
+#define SX_DEBUG_INTERRUPTS 0x00000080
+#define SX_DEBUG_PROBE 0x00000100
+#define SX_DEBUG_INIT 0x00000200
+#define SX_DEBUG_CLEANUP 0x00000400
+#define SX_DEBUG_CLOSE 0x00000800
+#define SX_DEBUG_FIRMWARE 0x00001000
+#define SX_DEBUG_MEMTEST 0x00002000
+
+#define SX_DEBUG_ALL 0xffffffff
+
+
+#define O_OTHER(tty) \
+ ((O_OLCUC(tty)) ||\
+ (O_ONLCR(tty)) ||\
+ (O_OCRNL(tty)) ||\
+ (O_ONOCR(tty)) ||\
+ (O_ONLRET(tty)) ||\
+ (O_OFILL(tty)) ||\
+ (O_OFDEL(tty)) ||\
+ (O_NLDLY(tty)) ||\
+ (O_CRDLY(tty)) ||\
+ (O_TABDLY(tty)) ||\
+ (O_BSDLY(tty)) ||\
+ (O_VTDLY(tty)) ||\
+ (O_FFDLY(tty)))
+
+/* Same for input. */
+#define I_OTHER(tty) \
+ ((I_INLCR(tty)) ||\
+ (I_IGNCR(tty)) ||\
+ (I_ICRNL(tty)) ||\
+ (I_IUCLC(tty)) ||\
+ (L_ISIG(tty)))
+
+#define MOD_TA ( TA>>4)
+#define MOD_MTA (MTA_CD1400>>4)
+#define MOD_SXDC ( SXDC>>4)
+
+
+/* We copy the download code over to the card in chunks of ... bytes */
+#define SX_CHUNK_SIZE 128
+
+#endif /* __KERNEL__ */
+
+
+
+/* Specialix document 6210046-11 page 3 */
+#define SPX(X) (('S'<<24) | ('P' << 16) | (X))
+
+/* Specialix-Linux specific IOCTLS. */
+#define SPXL(X) (SPX(('L' << 8) | (X)))
+
+
+#define SXIO_SET_BOARD SPXL(0x01)
+#define SXIO_GET_TYPE SPXL(0x02)
+#define SXIO_DOWNLOAD SPXL(0x03)
+#define SXIO_INIT SPXL(0x04)
+#define SXIO_SETDEBUG SPXL(0x05)
+#define SXIO_GETDEBUG SPXL(0x06)
+#define SXIO_DO_RAMTEST SPXL(0x07)
+#define SXIO_SETGSDEBUG SPXL(0x08)
+#define SXIO_GETGSDEBUG SPXL(0x09)
+#define SXIO_GETNPORTS SPXL(0x0a)
+
+
+#ifndef SXCTL_MISC_MINOR
+/* Allow others to gather this into "major.h" or something like that */
+#define SXCTL_MISC_MINOR 167
+#endif
+
+#ifndef SX_NORMAL_MAJOR
+/* This allows overriding on the compiler commandline, or in a "major.h"
+ include or something like that */
+#define SX_NORMAL_MAJOR 32
+#define SX_CALLOUT_MAJOR 33
+#endif
+
+
+#define SX_TYPE_SX 0x01
+#define SX_TYPE_SI 0x02
+#define SX_TYPE_CF 0x03
+
+
+#define WINDOW_LEN(board) (IS_CF_BOARD(board)?0x20000:SX_WINDOW_LEN)
+/* Need a #define for ^^^^^^^ !!! */
+
diff --git a/drivers/staging/generic_serial/sxboards.h b/drivers/staging/generic_serial/sxboards.h
new file mode 100644
index 000000000000..427927dc7dbf
--- /dev/null
+++ b/drivers/staging/generic_serial/sxboards.h
@@ -0,0 +1,206 @@
+/************************************************************************/
+/* */
+/* Title : SX/SI/XIO Board Hardware Definitions */
+/* */
+/* Author : N.P.Vassallo */
+/* */
+/* Creation : 16th March 1998 */
+/* */
+/* Version : 3.0.0 */
+/* */
+/* Copyright : (c) Specialix International Ltd. 1998 */
+/* */
+/* Description : Prototypes, structures and definitions */
+/* describing the SX/SI/XIO board hardware */
+/* */
+/************************************************************************/
+
+/* History...
+
+3.0.0 16/03/98 NPV Creation.
+
+*/
+
+#ifndef _sxboards_h /* If SXBOARDS.H not already defined */
+#define _sxboards_h 1
+
+/*****************************************************************************
+******************************* ******************************
+******************************* Board Types ******************************
+******************************* ******************************
+*****************************************************************************/
+
+/* BUS types... */
+#define BUS_ISA 0
+#define BUS_MCA 1
+#define BUS_EISA 2
+#define BUS_PCI 3
+
+/* Board phases... */
+#define SI1_Z280 1
+#define SI2_Z280 2
+#define SI3_T225 3
+
+/* Board types... */
+#define CARD_TYPE(bus,phase) (bus<<4|phase)
+#define CARD_BUS(type) ((type>>4)&0xF)
+#define CARD_PHASE(type) (type&0xF)
+
+#define TYPE_SI1_ISA CARD_TYPE(BUS_ISA,SI1_Z280)
+#define TYPE_SI2_ISA CARD_TYPE(BUS_ISA,SI2_Z280)
+#define TYPE_SI2_EISA CARD_TYPE(BUS_EISA,SI2_Z280)
+#define TYPE_SI2_PCI CARD_TYPE(BUS_PCI,SI2_Z280)
+
+#define TYPE_SX_ISA CARD_TYPE(BUS_ISA,SI3_T225)
+#define TYPE_SX_PCI CARD_TYPE(BUS_PCI,SI3_T225)
+/*****************************************************************************
+****************************** ******************************
+****************************** Phase 1 Z280 ******************************
+****************************** ******************************
+*****************************************************************************/
+
+/* ISA board details... */
+#define SI1_ISA_WINDOW_LEN 0x10000 /* 64 Kbyte shared memory window */
+//#define SI1_ISA_MEMORY_LEN 0x8000 /* Usable memory - unused define*/
+//#define SI1_ISA_ADDR_LOW 0x0A0000 /* Lowest address = 640 Kbyte */
+//#define SI1_ISA_ADDR_HIGH 0xFF8000 /* Highest address = 16Mbyte - 32Kbyte */
+//#define SI2_ISA_ADDR_STEP SI2_ISA_WINDOW_LEN/* ISA board address step */
+//#define SI2_ISA_IRQ_MASK 0x9800 /* IRQs 15,12,11 */
+
+/* ISA board, register definitions... */
+//#define SI2_ISA_ID_BASE 0x7FF8 /* READ: Board ID string */
+#define SI1_ISA_RESET 0x8000 /* WRITE: Host Reset */
+#define SI1_ISA_RESET_CLEAR 0xc000 /* WRITE: Host Reset clear*/
+#define SI1_ISA_WAIT 0x9000 /* WRITE: Host wait */
+#define SI1_ISA_WAIT_CLEAR 0xd000 /* WRITE: Host wait clear */
+#define SI1_ISA_INTCL 0xa000 /* WRITE: Host Reset */
+#define SI1_ISA_INTCL_CLEAR 0xe000 /* WRITE: Host Reset */
+
+
+/*****************************************************************************
+****************************** ******************************
+****************************** Phase 2 Z280 ******************************
+****************************** ******************************
+*****************************************************************************/
+
+/* ISA board details... */
+#define SI2_ISA_WINDOW_LEN 0x8000 /* 32 Kbyte shared memory window */
+#define SI2_ISA_MEMORY_LEN 0x7FF8 /* Usable memory */
+#define SI2_ISA_ADDR_LOW 0x0A0000 /* Lowest address = 640 Kbyte */
+#define SI2_ISA_ADDR_HIGH 0xFF8000 /* Highest address = 16Mbyte - 32Kbyte */
+#define SI2_ISA_ADDR_STEP SI2_ISA_WINDOW_LEN/* ISA board address step */
+#define SI2_ISA_IRQ_MASK 0x9800 /* IRQs 15,12,11 */
+
+/* ISA board, register definitions... */
+#define SI2_ISA_ID_BASE 0x7FF8 /* READ: Board ID string */
+#define SI2_ISA_RESET SI2_ISA_ID_BASE /* WRITE: Host Reset */
+#define SI2_ISA_IRQ11 (SI2_ISA_ID_BASE+1) /* WRITE: Set IRQ11 */
+#define SI2_ISA_IRQ12 (SI2_ISA_ID_BASE+2) /* WRITE: Set IRQ12 */
+#define SI2_ISA_IRQ15 (SI2_ISA_ID_BASE+3) /* WRITE: Set IRQ15 */
+#define SI2_ISA_IRQSET (SI2_ISA_ID_BASE+4) /* WRITE: Set Host Interrupt */
+#define SI2_ISA_INTCLEAR (SI2_ISA_ID_BASE+5) /* WRITE: Enable Host Interrupt */
+
+#define SI2_ISA_IRQ11_SET 0x10
+#define SI2_ISA_IRQ11_CLEAR 0x00
+#define SI2_ISA_IRQ12_SET 0x10
+#define SI2_ISA_IRQ12_CLEAR 0x00
+#define SI2_ISA_IRQ15_SET 0x10
+#define SI2_ISA_IRQ15_CLEAR 0x00
+#define SI2_ISA_INTCLEAR_SET 0x10
+#define SI2_ISA_INTCLEAR_CLEAR 0x00
+#define SI2_ISA_IRQSET_CLEAR 0x10
+#define SI2_ISA_IRQSET_SET 0x00
+#define SI2_ISA_RESET_SET 0x00
+#define SI2_ISA_RESET_CLEAR 0x10
+
+/* PCI board details... */
+#define SI2_PCI_WINDOW_LEN 0x100000 /* 1 Mbyte memory window */
+
+/* PCI board register definitions... */
+#define SI2_PCI_SET_IRQ 0x40001 /* Set Host Interrupt */
+#define SI2_PCI_RESET 0xC0001 /* Host Reset */
+
+/*****************************************************************************
+****************************** ******************************
+****************************** Phase 3 T225 ******************************
+****************************** ******************************
+*****************************************************************************/
+
+/* General board details... */
+#define SX_WINDOW_LEN 64*1024 /* 64 Kbyte memory window */
+
+/* ISA board details... */
+#define SX_ISA_ADDR_LOW 0x0A0000 /* Lowest address = 640 Kbyte */
+#define SX_ISA_ADDR_HIGH 0xFF8000 /* Highest address = 16Mbyte - 32Kbyte */
+#define SX_ISA_ADDR_STEP SX_WINDOW_LEN /* ISA board address step */
+#define SX_ISA_IRQ_MASK 0x9E00 /* IRQs 15,12,11,10,9 */
+
+/* Hardware register definitions... */
+#define SX_EVENT_STATUS 0x7800 /* READ: T225 Event Status */
+#define SX_EVENT_STROBE 0x7800 /* WRITE: T225 Event Strobe */
+#define SX_EVENT_ENABLE 0x7880 /* WRITE: T225 Event Enable */
+#define SX_VPD_ROM 0x7C00 /* READ: Vital Product Data ROM */
+#define SX_CONFIG 0x7C00 /* WRITE: Host Configuration Register */
+#define SX_IRQ_STATUS 0x7C80 /* READ: Host Interrupt Status */
+#define SX_SET_IRQ 0x7C80 /* WRITE: Set Host Interrupt */
+#define SX_RESET_STATUS 0x7D00 /* READ: Host Reset Status */
+#define SX_RESET 0x7D00 /* WRITE: Host Reset */
+#define SX_RESET_IRQ 0x7D80 /* WRITE: Reset Host Interrupt */
+
+/* SX_VPD_ROM definitions... */
+#define SX_VPD_SLX_ID1 0x00
+#define SX_VPD_SLX_ID2 0x01
+#define SX_VPD_HW_REV 0x02
+#define SX_VPD_HW_ASSEM 0x03
+#define SX_VPD_UNIQUEID4 0x04
+#define SX_VPD_UNIQUEID3 0x05
+#define SX_VPD_UNIQUEID2 0x06
+#define SX_VPD_UNIQUEID1 0x07
+#define SX_VPD_MANU_YEAR 0x08
+#define SX_VPD_MANU_WEEK 0x09
+#define SX_VPD_IDENT 0x10
+#define SX_VPD_IDENT_STRING "JET HOST BY KEV#"
+
+/* SX unique identifiers... */
+#define SX_UNIQUEID_MASK 0xF0
+#define SX_ISA_UNIQUEID1 0x20
+#define SX_PCI_UNIQUEID1 0x50
+
+/* SX_CONFIG definitions... */
+#define SX_CONF_BUSEN 0x02 /* Enable T225 memory and I/O */
+#define SX_CONF_HOSTIRQ 0x04 /* Enable board to host interrupt */
+
+/* SX bootstrap... */
+#define SX_BOOTSTRAP "\x28\x20\x21\x02\x60\x0a"
+#define SX_BOOTSTRAP_SIZE 6
+#define SX_BOOTSTRAP_ADDR (0x8000-SX_BOOTSTRAP_SIZE)
+
+/*****************************************************************************
+********************************** **********************************
+********************************** EISA **********************************
+********************************** **********************************
+*****************************************************************************/
+
+#define SI2_EISA_OFF 0x42
+#define SI2_EISA_VAL 0x01
+#define SI2_EISA_WINDOW_LEN 0x10000
+
+/*****************************************************************************
+*********************************** **********************************
+*********************************** PCI **********************************
+*********************************** **********************************
+*****************************************************************************/
+
+/* General definitions... */
+
+#define SPX_VENDOR_ID 0x11CB /* Assigned by the PCI SIG */
+#define SPX_DEVICE_ID 0x4000 /* SI/XIO boards */
+#define SPX_PLXDEVICE_ID 0x2000 /* SX boards */
+
+#define SPX_SUB_VENDOR_ID SPX_VENDOR_ID /* Same as vendor id */
+#define SI2_SUB_SYS_ID 0x400 /* Phase 2 (Z280) board */
+#define SX_SUB_SYS_ID 0x200 /* Phase 3 (t225) board */
+
+#endif /*_sxboards_h */
+
+/* End of SXBOARDS.H */
diff --git a/drivers/staging/generic_serial/sxwindow.h b/drivers/staging/generic_serial/sxwindow.h
new file mode 100644
index 000000000000..cf01b662aefc
--- /dev/null
+++ b/drivers/staging/generic_serial/sxwindow.h
@@ -0,0 +1,393 @@
+/************************************************************************/
+/* */
+/* Title : SX Shared Memory Window Structure */
+/* */
+/* Author : N.P.Vassallo */
+/* */
+/* Creation : 16th March 1998 */
+/* */
+/* Version : 3.0.0 */
+/* */
+/* Copyright : (c) Specialix International Ltd. 1998 */
+/* */
+/* Description : Prototypes, structures and definitions */
+/* describing the SX/SI/XIO cards shared */
+/* memory window structure: */
+/* SXCARD */
+/* SXMODULE */
+/* SXCHANNEL */
+/* */
+/************************************************************************/
+
+/* History...
+
+3.0.0 16/03/98 NPV Creation. (based on STRUCT.H)
+
+*/
+
+#ifndef _sxwindow_h /* If SXWINDOW.H not already defined */
+#define _sxwindow_h 1
+
+/*****************************************************************************
+*************************** ***************************
+*************************** Common Definitions ***************************
+*************************** ***************************
+*****************************************************************************/
+
+typedef struct _SXCARD *PSXCARD; /* SXCARD structure pointer */
+typedef struct _SXMODULE *PMOD; /* SXMODULE structure pointer */
+typedef struct _SXCHANNEL *PCHAN; /* SXCHANNEL structure pointer */
+
+/*****************************************************************************
+********************************* *********************************
+********************************* SXCARD *********************************
+********************************* *********************************
+*****************************************************************************/
+
+typedef struct _SXCARD
+{
+ BYTE cc_init_status; /* 0x00 Initialisation status */
+ BYTE cc_mem_size; /* 0x01 Size of memory on card */
+ WORD cc_int_count; /* 0x02 Interrupt count */
+ WORD cc_revision; /* 0x04 Download code revision */
+ BYTE cc_isr_count; /* 0x06 Count when ISR is run */
+ BYTE cc_main_count; /* 0x07 Count when main loop is run */
+ WORD cc_int_pending; /* 0x08 Interrupt pending */
+ WORD cc_poll_count; /* 0x0A Count when poll is run */
+ BYTE cc_int_set_count; /* 0x0C Count when host interrupt is set */
+ BYTE cc_rfu[0x80 - 0x0D]; /* 0x0D Pad structure to 128 bytes (0x80) */
+
+} SXCARD;
+
+/* SXCARD.cc_init_status definitions... */
+#define ADAPTERS_FOUND (BYTE)0x01
+#define NO_ADAPTERS_FOUND (BYTE)0xFF
+
+/* SXCARD.cc_mem_size definitions... */
+#define SX_MEMORY_SIZE (BYTE)0x40
+
+/* SXCARD.cc_int_count definitions... */
+#define INT_COUNT_DEFAULT 100 /* Hz */
+
+/*****************************************************************************
+******************************** ********************************
+******************************** SXMODULE ********************************
+******************************** ********************************
+*****************************************************************************/
+
+#define TOP_POINTER(a) ((a)|0x8000) /* Sets top bit of word */
+#define UNTOP_POINTER(a) ((a)&~0x8000) /* Clears top bit of word */
+
+typedef struct _SXMODULE
+{
+ WORD mc_next; /* 0x00 Next module "pointer" (ORed with 0x8000) */
+ BYTE mc_type; /* 0x02 Type of TA in terms of number of channels */
+ BYTE mc_mod_no; /* 0x03 Module number on SI bus cable (0 closest to card) */
+ BYTE mc_dtr; /* 0x04 Private DTR copy (TA only) */
+ BYTE mc_rfu1; /* 0x05 Reserved */
+ WORD mc_uart; /* 0x06 UART base address for this module */
+ BYTE mc_chip; /* 0x08 Chip type / number of ports */
+ BYTE mc_current_uart; /* 0x09 Current uart selected for this module */
+#ifdef DOWNLOAD
+ PCHAN mc_chan_pointer[8]; /* 0x0A Pointer to each channel structure */
+#else
+ WORD mc_chan_pointer[8]; /* 0x0A Define as WORD if not compiling into download */
+#endif
+ WORD mc_rfu2; /* 0x1A Reserved */
+ BYTE mc_opens1; /* 0x1C Number of open ports on first four ports on MTA/SXDC */
+ BYTE mc_opens2; /* 0x1D Number of open ports on second four ports on MTA/SXDC */
+ BYTE mc_mods; /* 0x1E Types of connector module attached to MTA/SXDC */
+ BYTE mc_rev1; /* 0x1F Revision of first CD1400 on MTA/SXDC */
+ BYTE mc_rev2; /* 0x20 Revision of second CD1400 on MTA/SXDC */
+ BYTE mc_mtaasic_rev; /* 0x21 Revision of MTA ASIC 1..4 -> A, B, C, D */
+ BYTE mc_rfu3[0x100 - 0x22]; /* 0x22 Pad structure to 256 bytes (0x100) */
+
+} SXMODULE;
+
+/* SXMODULE.mc_type definitions... */
+#define FOUR_PORTS (BYTE)4
+#define EIGHT_PORTS (BYTE)8
+
+/* SXMODULE.mc_chip definitions... */
+#define CHIP_MASK 0xF0
+#define TA (BYTE)0
+#define TA4 (TA | FOUR_PORTS)
+#define TA8 (TA | EIGHT_PORTS)
+#define TA4_ASIC (BYTE)0x0A
+#define TA8_ASIC (BYTE)0x0B
+#define MTA_CD1400 (BYTE)0x28
+#define SXDC (BYTE)0x48
+
+/* SXMODULE.mc_mods definitions... */
+#define MOD_RS232DB25 0x00 /* RS232 DB25 (socket/plug) */
+#define MOD_RS232RJ45 0x01 /* RS232 RJ45 (shielded/opto-isolated) */
+#define MOD_RESERVED_2 0x02 /* Reserved (RS485) */
+#define MOD_RS422DB25 0x03 /* RS422 DB25 Socket */
+#define MOD_RESERVED_4 0x04 /* Reserved */
+#define MOD_PARALLEL 0x05 /* Parallel */
+#define MOD_RESERVED_6 0x06 /* Reserved (RS423) */
+#define MOD_RESERVED_7 0x07 /* Reserved */
+#define MOD_2_RS232DB25 0x08 /* Rev 2.0 RS232 DB25 (socket/plug) */
+#define MOD_2_RS232RJ45 0x09 /* Rev 2.0 RS232 RJ45 */
+#define MOD_RESERVED_A 0x0A /* Rev 2.0 Reserved */
+#define MOD_2_RS422DB25 0x0B /* Rev 2.0 RS422 DB25 */
+#define MOD_RESERVED_C 0x0C /* Rev 2.0 Reserved */
+#define MOD_2_PARALLEL 0x0D /* Rev 2.0 Parallel */
+#define MOD_RESERVED_E 0x0E /* Rev 2.0 Reserved */
+#define MOD_BLANK 0x0F /* Blank Panel */
+
+/*****************************************************************************
+******************************** *******************************
+******************************** SXCHANNEL *******************************
+******************************** *******************************
+*****************************************************************************/
+
+#define TX_BUFF_OFFSET 0x60 /* Transmit buffer offset in channel structure */
+#define BUFF_POINTER(a) (((a)+TX_BUFF_OFFSET)|0x8000)
+#define UNBUFF_POINTER(a) (jet_channel*)(((a)&~0x8000)-TX_BUFF_OFFSET)
+#define BUFFER_SIZE 256
+#define HIGH_WATER ((BUFFER_SIZE / 4) * 3)
+#define LOW_WATER (BUFFER_SIZE / 4)
+
+typedef struct _SXCHANNEL
+{
+ WORD next_item; /* 0x00 Offset from window base of next channels hi_txbuf (ORred with 0x8000) */
+ WORD addr_uart; /* 0x02 INTERNAL pointer to uart address. Includes FASTPATH bit */
+ WORD module; /* 0x04 Offset from window base of parent SXMODULE structure */
+ BYTE type; /* 0x06 Chip type / number of ports (copy of mc_chip) */
+ BYTE chan_number; /* 0x07 Channel number on the TA/MTA/SXDC */
+ WORD xc_status; /* 0x08 Flow control and I/O status */
+ BYTE hi_rxipos; /* 0x0A Receive buffer input index */
+ BYTE hi_rxopos; /* 0x0B Receive buffer output index */
+ BYTE hi_txopos; /* 0x0C Transmit buffer output index */
+ BYTE hi_txipos; /* 0x0D Transmit buffer input index */
+ BYTE hi_hstat; /* 0x0E Command register */
+ BYTE dtr_bit; /* 0x0F INTERNAL DTR control byte (TA only) */
+ BYTE txon; /* 0x10 INTERNAL copy of hi_txon */
+ BYTE txoff; /* 0x11 INTERNAL copy of hi_txoff */
+ BYTE rxon; /* 0x12 INTERNAL copy of hi_rxon */
+ BYTE rxoff; /* 0x13 INTERNAL copy of hi_rxoff */
+ BYTE hi_mr1; /* 0x14 Mode Register 1 (databits,parity,RTS rx flow)*/
+ BYTE hi_mr2; /* 0x15 Mode Register 2 (stopbits,local,CTS tx flow)*/
+ BYTE hi_csr; /* 0x16 Clock Select Register (baud rate) */
+ BYTE hi_op; /* 0x17 Modem Output Signal */
+ BYTE hi_ip; /* 0x18 Modem Input Signal */
+ BYTE hi_state; /* 0x19 Channel status */
+ BYTE hi_prtcl; /* 0x1A Channel protocol (flow control) */
+ BYTE hi_txon; /* 0x1B Transmit XON character */
+ BYTE hi_txoff; /* 0x1C Transmit XOFF character */
+ BYTE hi_rxon; /* 0x1D Receive XON character */
+ BYTE hi_rxoff; /* 0x1E Receive XOFF character */
+ BYTE close_prev; /* 0x1F INTERNAL channel previously closed flag */
+ BYTE hi_break; /* 0x20 Break and error control */
+ BYTE break_state; /* 0x21 INTERNAL copy of hi_break */
+ BYTE hi_mask; /* 0x22 Mask for received data */
+ BYTE mask; /* 0x23 INTERNAL copy of hi_mask */
+ BYTE mod_type; /* 0x24 MTA/SXDC hardware module type */
+ BYTE ccr_state; /* 0x25 INTERNAL MTA/SXDC state of CCR register */
+ BYTE ip_mask; /* 0x26 Input handshake mask */
+ BYTE hi_parallel; /* 0x27 Parallel port flag */
+ BYTE par_error; /* 0x28 Error code for parallel loopback test */
+ BYTE any_sent; /* 0x29 INTERNAL data sent flag */
+ BYTE asic_txfifo_size; /* 0x2A INTERNAL SXDC transmit FIFO size */
+ BYTE rfu1[2]; /* 0x2B Reserved */
+ BYTE csr; /* 0x2D INTERNAL copy of hi_csr */
+#ifdef DOWNLOAD
+ PCHAN nextp; /* 0x2E Offset from window base of next channel structure */
+#else
+ WORD nextp; /* 0x2E Define as WORD if not compiling into download */
+#endif
+ BYTE prtcl; /* 0x30 INTERNAL copy of hi_prtcl */
+ BYTE mr1; /* 0x31 INTERNAL copy of hi_mr1 */
+ BYTE mr2; /* 0x32 INTERNAL copy of hi_mr2 */
+ BYTE hi_txbaud; /* 0x33 Extended transmit baud rate (SXDC only if((hi_csr&0x0F)==0x0F) */
+ BYTE hi_rxbaud; /* 0x34 Extended receive baud rate (SXDC only if((hi_csr&0xF0)==0xF0) */
+ BYTE txbreak_state; /* 0x35 INTERNAL MTA/SXDC transmit break state */
+ BYTE txbaud; /* 0x36 INTERNAL copy of hi_txbaud */
+ BYTE rxbaud; /* 0x37 INTERNAL copy of hi_rxbaud */
+ WORD err_framing; /* 0x38 Count of receive framing errors */
+ WORD err_parity; /* 0x3A Count of receive parity errors */
+ WORD err_overrun; /* 0x3C Count of receive overrun errors */
+ WORD err_overflow; /* 0x3E Count of receive buffer overflow errors */
+ BYTE rfu2[TX_BUFF_OFFSET - 0x40]; /* 0x40 Reserved until hi_txbuf */
+ BYTE hi_txbuf[BUFFER_SIZE]; /* 0x060 Transmit buffer */
+ BYTE hi_rxbuf[BUFFER_SIZE]; /* 0x160 Receive buffer */
+ BYTE rfu3[0x300 - 0x260]; /* 0x260 Reserved until 768 bytes (0x300) */
+
+} SXCHANNEL;
+
+/* SXCHANNEL.addr_uart definitions... */
+#define FASTPATH 0x1000 /* Set to indicate fast rx/tx processing (TA only) */
+
+/* SXCHANNEL.xc_status definitions... */
+#define X_TANY 0x0001 /* XON is any character (TA only) */
+#define X_TION 0x0001 /* Tx interrupts on (MTA only) */
+#define X_TXEN 0x0002 /* Tx XON/XOFF enabled (TA only) */
+#define X_RTSEN 0x0002 /* RTS FLOW enabled (MTA only) */
+#define X_TXRC 0x0004 /* XOFF received (TA only) */
+#define X_RTSLOW 0x0004 /* RTS dropped (MTA only) */
+#define X_RXEN 0x0008 /* Rx XON/XOFF enabled */
+#define X_ANYXO 0x0010 /* XOFF pending/sent or RTS dropped */
+#define X_RXSE 0x0020 /* Rx XOFF sent */
+#define X_NPEND 0x0040 /* Rx XON pending or XOFF pending */
+#define X_FPEND 0x0080 /* Rx XOFF pending */
+#define C_CRSE 0x0100 /* Carriage return sent (TA only) */
+#define C_TEMR 0x0100 /* Tx empty requested (MTA only) */
+#define C_TEMA 0x0200 /* Tx empty acked (MTA only) */
+#define C_ANYP 0x0200 /* Any protocol bar tx XON/XOFF (TA only) */
+#define C_EN 0x0400 /* Cooking enabled (on MTA means port is also || */
+#define C_HIGH 0x0800 /* Buffer previously hit high water */
+#define C_CTSEN 0x1000 /* CTS automatic flow-control enabled */
+#define C_DCDEN 0x2000 /* DCD/DTR checking enabled */
+#define C_BREAK 0x4000 /* Break detected */
+#define C_RTSEN 0x8000 /* RTS automatic flow control enabled (MTA only) */
+#define C_PARITY 0x8000 /* Parity checking enabled (TA only) */
+
+/* SXCHANNEL.hi_hstat definitions... */
+#define HS_IDLE_OPEN 0x00 /* Channel open state */
+#define HS_LOPEN 0x02 /* Local open command (no modem monitoring) */
+#define HS_MOPEN 0x04 /* Modem open command (wait for DCD signal) */
+#define HS_IDLE_MPEND 0x06 /* Waiting for DCD signal state */
+#define HS_CONFIG 0x08 /* Configuration command */
+#define HS_CLOSE 0x0A /* Close command */
+#define HS_START 0x0C /* Start transmit break command */
+#define HS_STOP 0x0E /* Stop transmit break command */
+#define HS_IDLE_CLOSED 0x10 /* Closed channel state */
+#define HS_IDLE_BREAK 0x12 /* Transmit break state */
+#define HS_FORCE_CLOSED 0x14 /* Force close command */
+#define HS_RESUME 0x16 /* Clear pending XOFF command */
+#define HS_WFLUSH 0x18 /* Flush transmit buffer command */
+#define HS_RFLUSH 0x1A /* Flush receive buffer command */
+#define HS_SUSPEND 0x1C /* Suspend output command (like XOFF received) */
+#define PARALLEL 0x1E /* Parallel port loopback test command (Diagnostics Only) */
+#define ENABLE_RX_INTS 0x20 /* Enable receive interrupts command (Diagnostics Only) */
+#define ENABLE_TX_INTS 0x22 /* Enable transmit interrupts command (Diagnostics Only) */
+#define ENABLE_MDM_INTS 0x24 /* Enable modem interrupts command (Diagnostics Only) */
+#define DISABLE_INTS 0x26 /* Disable interrupts command (Diagnostics Only) */
+
+/* SXCHANNEL.hi_mr1 definitions... */
+#define MR1_BITS 0x03 /* Data bits mask */
+#define MR1_5_BITS 0x00 /* 5 data bits */
+#define MR1_6_BITS 0x01 /* 6 data bits */
+#define MR1_7_BITS 0x02 /* 7 data bits */
+#define MR1_8_BITS 0x03 /* 8 data bits */
+#define MR1_PARITY 0x1C /* Parity mask */
+#define MR1_ODD 0x04 /* Odd parity */
+#define MR1_EVEN 0x00 /* Even parity */
+#define MR1_WITH 0x00 /* Parity enabled */
+#define MR1_FORCE 0x08 /* Force parity */
+#define MR1_NONE 0x10 /* No parity */
+#define MR1_NOPARITY MR1_NONE /* No parity */
+#define MR1_ODDPARITY (MR1_WITH|MR1_ODD) /* Odd parity */
+#define MR1_EVENPARITY (MR1_WITH|MR1_EVEN) /* Even parity */
+#define MR1_MARKPARITY (MR1_FORCE|MR1_ODD) /* Mark parity */
+#define MR1_SPACEPARITY (MR1_FORCE|MR1_EVEN) /* Space parity */
+#define MR1_RTS_RXFLOW 0x80 /* RTS receive flow control */
+
+/* SXCHANNEL.hi_mr2 definitions... */
+#define MR2_STOP 0x0F /* Stop bits mask */
+#define MR2_1_STOP 0x07 /* 1 stop bit */
+#define MR2_2_STOP 0x0F /* 2 stop bits */
+#define MR2_CTS_TXFLOW 0x10 /* CTS transmit flow control */
+#define MR2_RTS_TOGGLE 0x20 /* RTS toggle on transmit */
+#define MR2_NORMAL 0x00 /* Normal mode */
+#define MR2_AUTO 0x40 /* Auto-echo mode (TA only) */
+#define MR2_LOCAL 0x80 /* Local echo mode */
+#define MR2_REMOTE 0xC0 /* Remote echo mode (TA only) */
+
+/* SXCHANNEL.hi_csr definitions... */
+#define CSR_75 0x0 /* 75 baud */
+#define CSR_110 0x1 /* 110 baud (TA), 115200 (MTA/SXDC) */
+#define CSR_38400 0x2 /* 38400 baud */
+#define CSR_150 0x3 /* 150 baud */
+#define CSR_300 0x4 /* 300 baud */
+#define CSR_600 0x5 /* 600 baud */
+#define CSR_1200 0x6 /* 1200 baud */
+#define CSR_2000 0x7 /* 2000 baud */
+#define CSR_2400 0x8 /* 2400 baud */
+#define CSR_4800 0x9 /* 4800 baud */
+#define CSR_1800 0xA /* 1800 baud */
+#define CSR_9600 0xB /* 9600 baud */
+#define CSR_19200 0xC /* 19200 baud */
+#define CSR_57600 0xD /* 57600 baud */
+#define CSR_EXTBAUD 0xF /* Extended baud rate (hi_txbaud/hi_rxbaud) */
+
+/* SXCHANNEL.hi_op definitions... */
+#define OP_RTS 0x01 /* RTS modem output signal */
+#define OP_DTR 0x02 /* DTR modem output signal */
+
+/* SXCHANNEL.hi_ip definitions... */
+#define IP_CTS 0x02 /* CTS modem input signal */
+#define IP_DCD 0x04 /* DCD modem input signal */
+#define IP_DSR 0x20 /* DTR modem input signal */
+#define IP_RI 0x40 /* RI modem input signal */
+
+/* SXCHANNEL.hi_state definitions... */
+#define ST_BREAK 0x01 /* Break received (clear with config) */
+#define ST_DCD 0x02 /* DCD signal changed state */
+
+/* SXCHANNEL.hi_prtcl definitions... */
+#define SP_TANY 0x01 /* Transmit XON/XANY (if SP_TXEN enabled) */
+#define SP_TXEN 0x02 /* Transmit XON/XOFF flow control */
+#define SP_CEN 0x04 /* Cooking enabled */
+#define SP_RXEN 0x08 /* Rx XON/XOFF enabled */
+#define SP_DCEN 0x20 /* DCD / DTR check */
+#define SP_DTR_RXFLOW 0x40 /* DTR receive flow control */
+#define SP_PAEN 0x80 /* Parity checking enabled */
+
+/* SXCHANNEL.hi_break definitions... */
+#define BR_IGN 0x01 /* Ignore any received breaks */
+#define BR_INT 0x02 /* Interrupt on received break */
+#define BR_PARMRK 0x04 /* Enable parmrk parity error processing */
+#define BR_PARIGN 0x08 /* Ignore chars with parity errors */
+#define BR_ERRINT 0x80 /* Treat parity/framing/overrun errors as exceptions */
+
+/* SXCHANNEL.par_error definitions.. */
+#define DIAG_IRQ_RX 0x01 /* Indicate serial receive interrupt (diags only) */
+#define DIAG_IRQ_TX 0x02 /* Indicate serial transmit interrupt (diags only) */
+#define DIAG_IRQ_MD 0x04 /* Indicate serial modem interrupt (diags only) */
+
+/* SXCHANNEL.hi_txbaud/hi_rxbaud definitions... (SXDC only) */
+#define BAUD_75 0x00 /* 75 baud */
+#define BAUD_115200 0x01 /* 115200 baud */
+#define BAUD_38400 0x02 /* 38400 baud */
+#define BAUD_150 0x03 /* 150 baud */
+#define BAUD_300 0x04 /* 300 baud */
+#define BAUD_600 0x05 /* 600 baud */
+#define BAUD_1200 0x06 /* 1200 baud */
+#define BAUD_2000 0x07 /* 2000 baud */
+#define BAUD_2400 0x08 /* 2400 baud */
+#define BAUD_4800 0x09 /* 4800 baud */
+#define BAUD_1800 0x0A /* 1800 baud */
+#define BAUD_9600 0x0B /* 9600 baud */
+#define BAUD_19200 0x0C /* 19200 baud */
+#define BAUD_57600 0x0D /* 57600 baud */
+#define BAUD_230400 0x0E /* 230400 baud */
+#define BAUD_460800 0x0F /* 460800 baud */
+#define BAUD_921600 0x10 /* 921600 baud */
+#define BAUD_50 0x11 /* 50 baud */
+#define BAUD_110 0x12 /* 110 baud */
+#define BAUD_134_5 0x13 /* 134.5 baud */
+#define BAUD_200 0x14 /* 200 baud */
+#define BAUD_7200 0x15 /* 7200 baud */
+#define BAUD_56000 0x16 /* 56000 baud */
+#define BAUD_64000 0x17 /* 64000 baud */
+#define BAUD_76800 0x18 /* 76800 baud */
+#define BAUD_128000 0x19 /* 128000 baud */
+#define BAUD_150000 0x1A /* 150000 baud */
+#define BAUD_14400 0x1B /* 14400 baud */
+#define BAUD_256000 0x1C /* 256000 baud */
+#define BAUD_28800 0x1D /* 28800 baud */
+
+/* SXCHANNEL.txbreak_state definiions... */
+#define TXBREAK_OFF 0 /* Not sending break */
+#define TXBREAK_START 1 /* Begin sending break */
+#define TXBREAK_START1 2 /* Begin sending break, part 1 */
+#define TXBREAK_ON 3 /* Sending break */
+#define TXBREAK_STOP 4 /* Stop sending break */
+#define TXBREAK_STOP1 5 /* Stop sending break, part 1 */
+
+#endif /* _sxwindow_h */
+
+/* End of SXWINDOW.H */
+
diff --git a/drivers/staging/generic_serial/vme_scc.c b/drivers/staging/generic_serial/vme_scc.c
new file mode 100644
index 000000000000..96838640f575
--- /dev/null
+++ b/drivers/staging/generic_serial/vme_scc.c
@@ -0,0 +1,1145 @@
+/*
+ * drivers/char/vme_scc.c: MVME147, MVME162, BVME6000 SCC serial ports
+ * implementation.
+ * Copyright 1999 Richard Hirst <richard@sleepie.demon.co.uk>
+ *
+ * Based on atari_SCC.c which was
+ * Copyright 1994-95 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kdev_t.h>
+#include <asm/io.h>
+#include <linux/kernel.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/miscdevice.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <asm/setup.h>
+#include <asm/bootinfo.h>
+
+#ifdef CONFIG_MVME147_SCC
+#include <asm/mvme147hw.h>
+#endif
+#ifdef CONFIG_MVME162_SCC
+#include <asm/mvme16xhw.h>
+#endif
+#ifdef CONFIG_BVME6000_SCC
+#include <asm/bvme6000hw.h>
+#endif
+
+#include <linux/generic_serial.h>
+#include "scc.h"
+
+
+#define CHANNEL_A 0
+#define CHANNEL_B 1
+
+#define SCC_MINOR_BASE 64
+
+/* Shadows for all SCC write registers */
+static unsigned char scc_shadow[2][16];
+
+/* Location to access for SCC register access delay */
+static volatile unsigned char *scc_del = NULL;
+
+/* To keep track of STATUS_REG state for detection of Ext/Status int source */
+static unsigned char scc_last_status_reg[2];
+
+/***************************** Prototypes *****************************/
+
+/* Function prototypes */
+static void scc_disable_tx_interrupts(void * ptr);
+static void scc_enable_tx_interrupts(void * ptr);
+static void scc_disable_rx_interrupts(void * ptr);
+static void scc_enable_rx_interrupts(void * ptr);
+static int scc_carrier_raised(struct tty_port *port);
+static void scc_shutdown_port(void * ptr);
+static int scc_set_real_termios(void *ptr);
+static void scc_hungup(void *ptr);
+static void scc_close(void *ptr);
+static int scc_chars_in_buffer(void * ptr);
+static int scc_open(struct tty_struct * tty, struct file * filp);
+static int scc_ioctl(struct tty_struct * tty,
+ unsigned int cmd, unsigned long arg);
+static void scc_throttle(struct tty_struct *tty);
+static void scc_unthrottle(struct tty_struct *tty);
+static irqreturn_t scc_tx_int(int irq, void *data);
+static irqreturn_t scc_rx_int(int irq, void *data);
+static irqreturn_t scc_stat_int(int irq, void *data);
+static irqreturn_t scc_spcond_int(int irq, void *data);
+static void scc_setsignals(struct scc_port *port, int dtr, int rts);
+static int scc_break_ctl(struct tty_struct *tty, int break_state);
+
+static struct tty_driver *scc_driver;
+
+static struct scc_port scc_ports[2];
+
+/*---------------------------------------------------------------------------
+ * Interface from generic_serial.c back here
+ *--------------------------------------------------------------------------*/
+
+static struct real_driver scc_real_driver = {
+ scc_disable_tx_interrupts,
+ scc_enable_tx_interrupts,
+ scc_disable_rx_interrupts,
+ scc_enable_rx_interrupts,
+ scc_shutdown_port,
+ scc_set_real_termios,
+ scc_chars_in_buffer,
+ scc_close,
+ scc_hungup,
+ NULL
+};
+
+
+static const struct tty_operations scc_ops = {
+ .open = scc_open,
+ .close = gs_close,
+ .write = gs_write,
+ .put_char = gs_put_char,
+ .flush_chars = gs_flush_chars,
+ .write_room = gs_write_room,
+ .chars_in_buffer = gs_chars_in_buffer,
+ .flush_buffer = gs_flush_buffer,
+ .ioctl = scc_ioctl,
+ .throttle = scc_throttle,
+ .unthrottle = scc_unthrottle,
+ .set_termios = gs_set_termios,
+ .stop = gs_stop,
+ .start = gs_start,
+ .hangup = gs_hangup,
+ .break_ctl = scc_break_ctl,
+};
+
+static const struct tty_port_operations scc_port_ops = {
+ .carrier_raised = scc_carrier_raised,
+};
+
+/*----------------------------------------------------------------------------
+ * vme_scc_init() and support functions
+ *---------------------------------------------------------------------------*/
+
+static int __init scc_init_drivers(void)
+{
+ int error;
+
+ scc_driver = alloc_tty_driver(2);
+ if (!scc_driver)
+ return -ENOMEM;
+ scc_driver->owner = THIS_MODULE;
+ scc_driver->driver_name = "scc";
+ scc_driver->name = "ttyS";
+ scc_driver->major = TTY_MAJOR;
+ scc_driver->minor_start = SCC_MINOR_BASE;
+ scc_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ scc_driver->subtype = SERIAL_TYPE_NORMAL;
+ scc_driver->init_termios = tty_std_termios;
+ scc_driver->init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ scc_driver->init_termios.c_ispeed = 9600;
+ scc_driver->init_termios.c_ospeed = 9600;
+ scc_driver->flags = TTY_DRIVER_REAL_RAW;
+ tty_set_operations(scc_driver, &scc_ops);
+
+ if ((error = tty_register_driver(scc_driver))) {
+ printk(KERN_ERR "scc: Couldn't register scc driver, error = %d\n",
+ error);
+ put_tty_driver(scc_driver);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* ports[] array is indexed by line no (i.e. [0] for ttyS0, [1] for ttyS1).
+ */
+
+static void __init scc_init_portstructs(void)
+{
+ struct scc_port *port;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ port = scc_ports + i;
+ tty_port_init(&port->gs.port);
+ port->gs.port.ops = &scc_port_ops;
+ port->gs.magic = SCC_MAGIC;
+ port->gs.close_delay = HZ/2;
+ port->gs.closing_wait = 30 * HZ;
+ port->gs.rd = &scc_real_driver;
+#ifdef NEW_WRITE_LOCKING
+ port->gs.port_write_mutex = MUTEX;
+#endif
+ init_waitqueue_head(&port->gs.port.open_wait);
+ init_waitqueue_head(&port->gs.port.close_wait);
+ }
+}
+
+
+#ifdef CONFIG_MVME147_SCC
+static int __init mvme147_scc_init(void)
+{
+ struct scc_port *port;
+ int error;
+
+ printk(KERN_INFO "SCC: MVME147 Serial Driver\n");
+ /* Init channel A */
+ port = &scc_ports[0];
+ port->channel = CHANNEL_A;
+ port->ctrlp = (volatile unsigned char *)M147_SCC_A_ADDR;
+ port->datap = port->ctrlp + 1;
+ port->port_a = &scc_ports[0];
+ port->port_b = &scc_ports[1];
+ error = request_irq(MVME147_IRQ_SCCA_TX, scc_tx_int, IRQF_DISABLED,
+ "SCC-A TX", port);
+ if (error)
+ goto fail;
+ error = request_irq(MVME147_IRQ_SCCA_STAT, scc_stat_int, IRQF_DISABLED,
+ "SCC-A status", port);
+ if (error)
+ goto fail_free_a_tx;
+ error = request_irq(MVME147_IRQ_SCCA_RX, scc_rx_int, IRQF_DISABLED,
+ "SCC-A RX", port);
+ if (error)
+ goto fail_free_a_stat;
+ error = request_irq(MVME147_IRQ_SCCA_SPCOND, scc_spcond_int,
+ IRQF_DISABLED, "SCC-A special cond", port);
+ if (error)
+ goto fail_free_a_rx;
+
+ {
+ SCC_ACCESS_INIT(port);
+
+ /* disable interrupts for this channel */
+ SCCwrite(INT_AND_DMA_REG, 0);
+ /* Set the interrupt vector */
+ SCCwrite(INT_VECTOR_REG, MVME147_IRQ_SCC_BASE);
+ /* Interrupt parameters: vector includes status, status low */
+ SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT);
+ SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB);
+ }
+
+ /* Init channel B */
+ port = &scc_ports[1];
+ port->channel = CHANNEL_B;
+ port->ctrlp = (volatile unsigned char *)M147_SCC_B_ADDR;
+ port->datap = port->ctrlp + 1;
+ port->port_a = &scc_ports[0];
+ port->port_b = &scc_ports[1];
+ error = request_irq(MVME147_IRQ_SCCB_TX, scc_tx_int, IRQF_DISABLED,
+ "SCC-B TX", port);
+ if (error)
+ goto fail_free_a_spcond;
+ error = request_irq(MVME147_IRQ_SCCB_STAT, scc_stat_int, IRQF_DISABLED,
+ "SCC-B status", port);
+ if (error)
+ goto fail_free_b_tx;
+ error = request_irq(MVME147_IRQ_SCCB_RX, scc_rx_int, IRQF_DISABLED,
+ "SCC-B RX", port);
+ if (error)
+ goto fail_free_b_stat;
+ error = request_irq(MVME147_IRQ_SCCB_SPCOND, scc_spcond_int,
+ IRQF_DISABLED, "SCC-B special cond", port);
+ if (error)
+ goto fail_free_b_rx;
+
+ {
+ SCC_ACCESS_INIT(port);
+
+ /* disable interrupts for this channel */
+ SCCwrite(INT_AND_DMA_REG, 0);
+ }
+
+ /* Ensure interrupts are enabled in the PCC chip */
+ m147_pcc->serial_cntrl=PCC_LEVEL_SERIAL|PCC_INT_ENAB;
+
+ /* Initialise the tty driver structures and register */
+ scc_init_portstructs();
+ scc_init_drivers();
+
+ return 0;
+
+fail_free_b_rx:
+ free_irq(MVME147_IRQ_SCCB_RX, port);
+fail_free_b_stat:
+ free_irq(MVME147_IRQ_SCCB_STAT, port);
+fail_free_b_tx:
+ free_irq(MVME147_IRQ_SCCB_TX, port);
+fail_free_a_spcond:
+ free_irq(MVME147_IRQ_SCCA_SPCOND, port);
+fail_free_a_rx:
+ free_irq(MVME147_IRQ_SCCA_RX, port);
+fail_free_a_stat:
+ free_irq(MVME147_IRQ_SCCA_STAT, port);
+fail_free_a_tx:
+ free_irq(MVME147_IRQ_SCCA_TX, port);
+fail:
+ return error;
+}
+#endif
+
+
+#ifdef CONFIG_MVME162_SCC
+static int __init mvme162_scc_init(void)
+{
+ struct scc_port *port;
+ int error;
+
+ if (!(mvme16x_config & MVME16x_CONFIG_GOT_SCCA))
+ return (-ENODEV);
+
+ printk(KERN_INFO "SCC: MVME162 Serial Driver\n");
+ /* Init channel A */
+ port = &scc_ports[0];
+ port->channel = CHANNEL_A;
+ port->ctrlp = (volatile unsigned char *)MVME_SCC_A_ADDR;
+ port->datap = port->ctrlp + 2;
+ port->port_a = &scc_ports[0];
+ port->port_b = &scc_ports[1];
+ error = request_irq(MVME162_IRQ_SCCA_TX, scc_tx_int, IRQF_DISABLED,
+ "SCC-A TX", port);
+ if (error)
+ goto fail;
+ error = request_irq(MVME162_IRQ_SCCA_STAT, scc_stat_int, IRQF_DISABLED,
+ "SCC-A status", port);
+ if (error)
+ goto fail_free_a_tx;
+ error = request_irq(MVME162_IRQ_SCCA_RX, scc_rx_int, IRQF_DISABLED,
+ "SCC-A RX", port);
+ if (error)
+ goto fail_free_a_stat;
+ error = request_irq(MVME162_IRQ_SCCA_SPCOND, scc_spcond_int,
+ IRQF_DISABLED, "SCC-A special cond", port);
+ if (error)
+ goto fail_free_a_rx;
+
+ {
+ SCC_ACCESS_INIT(port);
+
+ /* disable interrupts for this channel */
+ SCCwrite(INT_AND_DMA_REG, 0);
+ /* Set the interrupt vector */
+ SCCwrite(INT_VECTOR_REG, MVME162_IRQ_SCC_BASE);
+ /* Interrupt parameters: vector includes status, status low */
+ SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT);
+ SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB);
+ }
+
+ /* Init channel B */
+ port = &scc_ports[1];
+ port->channel = CHANNEL_B;
+ port->ctrlp = (volatile unsigned char *)MVME_SCC_B_ADDR;
+ port->datap = port->ctrlp + 2;
+ port->port_a = &scc_ports[0];
+ port->port_b = &scc_ports[1];
+ error = request_irq(MVME162_IRQ_SCCB_TX, scc_tx_int, IRQF_DISABLED,
+ "SCC-B TX", port);
+ if (error)
+ goto fail_free_a_spcond;
+ error = request_irq(MVME162_IRQ_SCCB_STAT, scc_stat_int, IRQF_DISABLED,
+ "SCC-B status", port);
+ if (error)
+ goto fail_free_b_tx;
+ error = request_irq(MVME162_IRQ_SCCB_RX, scc_rx_int, IRQF_DISABLED,
+ "SCC-B RX", port);
+ if (error)
+ goto fail_free_b_stat;
+ error = request_irq(MVME162_IRQ_SCCB_SPCOND, scc_spcond_int,
+ IRQF_DISABLED, "SCC-B special cond", port);
+ if (error)
+ goto fail_free_b_rx;
+
+ {
+ SCC_ACCESS_INIT(port); /* Either channel will do */
+
+ /* disable interrupts for this channel */
+ SCCwrite(INT_AND_DMA_REG, 0);
+ }
+
+ /* Ensure interrupts are enabled in the MC2 chip */
+ *(volatile char *)0xfff4201d = 0x14;
+
+ /* Initialise the tty driver structures and register */
+ scc_init_portstructs();
+ scc_init_drivers();
+
+ return 0;
+
+fail_free_b_rx:
+ free_irq(MVME162_IRQ_SCCB_RX, port);
+fail_free_b_stat:
+ free_irq(MVME162_IRQ_SCCB_STAT, port);
+fail_free_b_tx:
+ free_irq(MVME162_IRQ_SCCB_TX, port);
+fail_free_a_spcond:
+ free_irq(MVME162_IRQ_SCCA_SPCOND, port);
+fail_free_a_rx:
+ free_irq(MVME162_IRQ_SCCA_RX, port);
+fail_free_a_stat:
+ free_irq(MVME162_IRQ_SCCA_STAT, port);
+fail_free_a_tx:
+ free_irq(MVME162_IRQ_SCCA_TX, port);
+fail:
+ return error;
+}
+#endif
+
+
+#ifdef CONFIG_BVME6000_SCC
+static int __init bvme6000_scc_init(void)
+{
+ struct scc_port *port;
+ int error;
+
+ printk(KERN_INFO "SCC: BVME6000 Serial Driver\n");
+ /* Init channel A */
+ port = &scc_ports[0];
+ port->channel = CHANNEL_A;
+ port->ctrlp = (volatile unsigned char *)BVME_SCC_A_ADDR;
+ port->datap = port->ctrlp + 4;
+ port->port_a = &scc_ports[0];
+ port->port_b = &scc_ports[1];
+ error = request_irq(BVME_IRQ_SCCA_TX, scc_tx_int, IRQF_DISABLED,
+ "SCC-A TX", port);
+ if (error)
+ goto fail;
+ error = request_irq(BVME_IRQ_SCCA_STAT, scc_stat_int, IRQF_DISABLED,
+ "SCC-A status", port);
+ if (error)
+ goto fail_free_a_tx;
+ error = request_irq(BVME_IRQ_SCCA_RX, scc_rx_int, IRQF_DISABLED,
+ "SCC-A RX", port);
+ if (error)
+ goto fail_free_a_stat;
+ error = request_irq(BVME_IRQ_SCCA_SPCOND, scc_spcond_int,
+ IRQF_DISABLED, "SCC-A special cond", port);
+ if (error)
+ goto fail_free_a_rx;
+
+ {
+ SCC_ACCESS_INIT(port);
+
+ /* disable interrupts for this channel */
+ SCCwrite(INT_AND_DMA_REG, 0);
+ /* Set the interrupt vector */
+ SCCwrite(INT_VECTOR_REG, BVME_IRQ_SCC_BASE);
+ /* Interrupt parameters: vector includes status, status low */
+ SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT);
+ SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB);
+ }
+
+ /* Init channel B */
+ port = &scc_ports[1];
+ port->channel = CHANNEL_B;
+ port->ctrlp = (volatile unsigned char *)BVME_SCC_B_ADDR;
+ port->datap = port->ctrlp + 4;
+ port->port_a = &scc_ports[0];
+ port->port_b = &scc_ports[1];
+ error = request_irq(BVME_IRQ_SCCB_TX, scc_tx_int, IRQF_DISABLED,
+ "SCC-B TX", port);
+ if (error)
+ goto fail_free_a_spcond;
+ error = request_irq(BVME_IRQ_SCCB_STAT, scc_stat_int, IRQF_DISABLED,
+ "SCC-B status", port);
+ if (error)
+ goto fail_free_b_tx;
+ error = request_irq(BVME_IRQ_SCCB_RX, scc_rx_int, IRQF_DISABLED,
+ "SCC-B RX", port);
+ if (error)
+ goto fail_free_b_stat;
+ error = request_irq(BVME_IRQ_SCCB_SPCOND, scc_spcond_int,
+ IRQF_DISABLED, "SCC-B special cond", port);
+ if (error)
+ goto fail_free_b_rx;
+
+ {
+ SCC_ACCESS_INIT(port); /* Either channel will do */
+
+ /* disable interrupts for this channel */
+ SCCwrite(INT_AND_DMA_REG, 0);
+ }
+
+ /* Initialise the tty driver structures and register */
+ scc_init_portstructs();
+ scc_init_drivers();
+
+ return 0;
+
+fail:
+ free_irq(BVME_IRQ_SCCA_STAT, port);
+fail_free_a_tx:
+ free_irq(BVME_IRQ_SCCA_RX, port);
+fail_free_a_stat:
+ free_irq(BVME_IRQ_SCCA_SPCOND, port);
+fail_free_a_rx:
+ free_irq(BVME_IRQ_SCCB_TX, port);
+fail_free_a_spcond:
+ free_irq(BVME_IRQ_SCCB_STAT, port);
+fail_free_b_tx:
+ free_irq(BVME_IRQ_SCCB_RX, port);
+fail_free_b_stat:
+ free_irq(BVME_IRQ_SCCB_SPCOND, port);
+fail_free_b_rx:
+ return error;
+}
+#endif
+
+
+static int __init vme_scc_init(void)
+{
+ int res = -ENODEV;
+
+#ifdef CONFIG_MVME147_SCC
+ if (MACH_IS_MVME147)
+ res = mvme147_scc_init();
+#endif
+#ifdef CONFIG_MVME162_SCC
+ if (MACH_IS_MVME16x)
+ res = mvme162_scc_init();
+#endif
+#ifdef CONFIG_BVME6000_SCC
+ if (MACH_IS_BVME6000)
+ res = bvme6000_scc_init();
+#endif
+ return res;
+}
+
+module_init(vme_scc_init);
+
+
+/*---------------------------------------------------------------------------
+ * Interrupt handlers
+ *--------------------------------------------------------------------------*/
+
+static irqreturn_t scc_rx_int(int irq, void *data)
+{
+ unsigned char ch;
+ struct scc_port *port = data;
+ struct tty_struct *tty = port->gs.port.tty;
+ SCC_ACCESS_INIT(port);
+
+ ch = SCCread_NB(RX_DATA_REG);
+ if (!tty) {
+ printk(KERN_WARNING "scc_rx_int with NULL tty!\n");
+ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
+ return IRQ_HANDLED;
+ }
+ tty_insert_flip_char(tty, ch, 0);
+
+ /* Check if another character is already ready; in that case, the
+ * spcond_int() function must be used, because this character may have an
+ * error condition that isn't signalled by the interrupt vector used!
+ */
+ if (SCCread(INT_PENDING_REG) &
+ (port->channel == CHANNEL_A ? IPR_A_RX : IPR_B_RX)) {
+ scc_spcond_int (irq, data);
+ return IRQ_HANDLED;
+ }
+
+ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
+
+ tty_flip_buffer_push(tty);
+ return IRQ_HANDLED;
+}
+
+
+static irqreturn_t scc_spcond_int(int irq, void *data)
+{
+ struct scc_port *port = data;
+ struct tty_struct *tty = port->gs.port.tty;
+ unsigned char stat, ch, err;
+ int int_pending_mask = port->channel == CHANNEL_A ?
+ IPR_A_RX : IPR_B_RX;
+ SCC_ACCESS_INIT(port);
+
+ if (!tty) {
+ printk(KERN_WARNING "scc_spcond_int with NULL tty!\n");
+ SCCwrite(COMMAND_REG, CR_ERROR_RESET);
+ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
+ return IRQ_HANDLED;
+ }
+ do {
+ stat = SCCread(SPCOND_STATUS_REG);
+ ch = SCCread_NB(RX_DATA_REG);
+
+ if (stat & SCSR_RX_OVERRUN)
+ err = TTY_OVERRUN;
+ else if (stat & SCSR_PARITY_ERR)
+ err = TTY_PARITY;
+ else if (stat & SCSR_CRC_FRAME_ERR)
+ err = TTY_FRAME;
+ else
+ err = 0;
+
+ tty_insert_flip_char(tty, ch, err);
+
+ /* ++TeSche: *All* errors have to be cleared manually,
+ * else the condition persists for the next chars
+ */
+ if (err)
+ SCCwrite(COMMAND_REG, CR_ERROR_RESET);
+
+ } while(SCCread(INT_PENDING_REG) & int_pending_mask);
+
+ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
+
+ tty_flip_buffer_push(tty);
+ return IRQ_HANDLED;
+}
+
+
+static irqreturn_t scc_tx_int(int irq, void *data)
+{
+ struct scc_port *port = data;
+ SCC_ACCESS_INIT(port);
+
+ if (!port->gs.port.tty) {
+ printk(KERN_WARNING "scc_tx_int with NULL tty!\n");
+ SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
+ SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET);
+ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
+ return IRQ_HANDLED;
+ }
+ while ((SCCread_NB(STATUS_REG) & SR_TX_BUF_EMPTY)) {
+ if (port->x_char) {
+ SCCwrite(TX_DATA_REG, port->x_char);
+ port->x_char = 0;
+ }
+ else if ((port->gs.xmit_cnt <= 0) ||
+ port->gs.port.tty->stopped ||
+ port->gs.port.tty->hw_stopped)
+ break;
+ else {
+ SCCwrite(TX_DATA_REG, port->gs.xmit_buf[port->gs.xmit_tail++]);
+ port->gs.xmit_tail = port->gs.xmit_tail & (SERIAL_XMIT_SIZE-1);
+ if (--port->gs.xmit_cnt <= 0)
+ break;
+ }
+ }
+ if ((port->gs.xmit_cnt <= 0) || port->gs.port.tty->stopped ||
+ port->gs.port.tty->hw_stopped) {
+ /* disable tx interrupts */
+ SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
+ SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); /* disable tx_int on next tx underrun? */
+ port->gs.port.flags &= ~GS_TX_INTEN;
+ }
+ if (port->gs.port.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars)
+ tty_wakeup(port->gs.port.tty);
+
+ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
+ return IRQ_HANDLED;
+}
+
+
+static irqreturn_t scc_stat_int(int irq, void *data)
+{
+ struct scc_port *port = data;
+ unsigned channel = port->channel;
+ unsigned char last_sr, sr, changed;
+ SCC_ACCESS_INIT(port);
+
+ last_sr = scc_last_status_reg[channel];
+ sr = scc_last_status_reg[channel] = SCCread_NB(STATUS_REG);
+ changed = last_sr ^ sr;
+
+ if (changed & SR_DCD) {
+ port->c_dcd = !!(sr & SR_DCD);
+ if (!(port->gs.port.flags & ASYNC_CHECK_CD))
+ ; /* Don't report DCD changes */
+ else if (port->c_dcd) {
+ wake_up_interruptible(&port->gs.port.open_wait);
+ }
+ else {
+ if (port->gs.port.tty)
+ tty_hangup (port->gs.port.tty);
+ }
+ }
+ SCCwrite(COMMAND_REG, CR_EXTSTAT_RESET);
+ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
+ return IRQ_HANDLED;
+}
+
+
+/*---------------------------------------------------------------------------
+ * generic_serial.c callback funtions
+ *--------------------------------------------------------------------------*/
+
+static void scc_disable_tx_interrupts(void *ptr)
+{
+ struct scc_port *port = ptr;
+ unsigned long flags;
+ SCC_ACCESS_INIT(port);
+
+ local_irq_save(flags);
+ SCCmod(INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
+ port->gs.port.flags &= ~GS_TX_INTEN;
+ local_irq_restore(flags);
+}
+
+
+static void scc_enable_tx_interrupts(void *ptr)
+{
+ struct scc_port *port = ptr;
+ unsigned long flags;
+ SCC_ACCESS_INIT(port);
+
+ local_irq_save(flags);
+ SCCmod(INT_AND_DMA_REG, 0xff, IDR_TX_INT_ENAB);
+ /* restart the transmitter */
+ scc_tx_int (0, port);
+ local_irq_restore(flags);
+}
+
+
+static void scc_disable_rx_interrupts(void *ptr)
+{
+ struct scc_port *port = ptr;
+ unsigned long flags;
+ SCC_ACCESS_INIT(port);
+
+ local_irq_save(flags);
+ SCCmod(INT_AND_DMA_REG,
+ ~(IDR_RX_INT_MASK|IDR_PARERR_AS_SPCOND|IDR_EXTSTAT_INT_ENAB), 0);
+ local_irq_restore(flags);
+}
+
+
+static void scc_enable_rx_interrupts(void *ptr)
+{
+ struct scc_port *port = ptr;
+ unsigned long flags;
+ SCC_ACCESS_INIT(port);
+
+ local_irq_save(flags);
+ SCCmod(INT_AND_DMA_REG, 0xff,
+ IDR_EXTSTAT_INT_ENAB|IDR_PARERR_AS_SPCOND|IDR_RX_INT_ALL);
+ local_irq_restore(flags);
+}
+
+
+static int scc_carrier_raised(struct tty_port *port)
+{
+ struct scc_port *sc = container_of(port, struct scc_port, gs.port);
+ unsigned channel = sc->channel;
+
+ return !!(scc_last_status_reg[channel] & SR_DCD);
+}
+
+
+static void scc_shutdown_port(void *ptr)
+{
+ struct scc_port *port = ptr;
+
+ port->gs.port.flags &= ~ GS_ACTIVE;
+ if (port->gs.port.tty && (port->gs.port.tty->termios->c_cflag & HUPCL)) {
+ scc_setsignals (port, 0, 0);
+ }
+}
+
+
+static int scc_set_real_termios (void *ptr)
+{
+ /* the SCC has char sizes 5,7,6,8 in that order! */
+ static int chsize_map[4] = { 0, 2, 1, 3 };
+ unsigned cflag, baud, chsize, channel, brgval = 0;
+ unsigned long flags;
+ struct scc_port *port = ptr;
+ SCC_ACCESS_INIT(port);
+
+ if (!port->gs.port.tty || !port->gs.port.tty->termios) return 0;
+
+ channel = port->channel;
+
+ if (channel == CHANNEL_A)
+ return 0; /* Settings controlled by boot PROM */
+
+ cflag = port->gs.port.tty->termios->c_cflag;
+ baud = port->gs.baud;
+ chsize = (cflag & CSIZE) >> 4;
+
+ if (baud == 0) {
+ /* speed == 0 -> drop DTR */
+ local_irq_save(flags);
+ SCCmod(TX_CTRL_REG, ~TCR_DTR, 0);
+ local_irq_restore(flags);
+ return 0;
+ }
+ else if ((MACH_IS_MVME16x && (baud < 50 || baud > 38400)) ||
+ (MACH_IS_MVME147 && (baud < 50 || baud > 19200)) ||
+ (MACH_IS_BVME6000 &&(baud < 50 || baud > 76800))) {
+ printk(KERN_NOTICE "SCC: Bad speed requested, %d\n", baud);
+ return 0;
+ }
+
+ if (cflag & CLOCAL)
+ port->gs.port.flags &= ~ASYNC_CHECK_CD;
+ else
+ port->gs.port.flags |= ASYNC_CHECK_CD;
+
+#ifdef CONFIG_MVME147_SCC
+ if (MACH_IS_MVME147)
+ brgval = (M147_SCC_PCLK + baud/2) / (16 * 2 * baud) - 2;
+#endif
+#ifdef CONFIG_MVME162_SCC
+ if (MACH_IS_MVME16x)
+ brgval = (MVME_SCC_PCLK + baud/2) / (16 * 2 * baud) - 2;
+#endif
+#ifdef CONFIG_BVME6000_SCC
+ if (MACH_IS_BVME6000)
+ brgval = (BVME_SCC_RTxC + baud/2) / (16 * 2 * baud) - 2;
+#endif
+ /* Now we have all parameters and can go to set them: */
+ local_irq_save(flags);
+
+ /* receiver's character size and auto-enables */
+ SCCmod(RX_CTRL_REG, ~(RCR_CHSIZE_MASK|RCR_AUTO_ENAB_MODE),
+ (chsize_map[chsize] << 6) |
+ ((cflag & CRTSCTS) ? RCR_AUTO_ENAB_MODE : 0));
+ /* parity and stop bits (both, Tx and Rx), clock mode never changes */
+ SCCmod (AUX1_CTRL_REG,
+ ~(A1CR_PARITY_MASK | A1CR_MODE_MASK),
+ ((cflag & PARENB
+ ? (cflag & PARODD ? A1CR_PARITY_ODD : A1CR_PARITY_EVEN)
+ : A1CR_PARITY_NONE)
+ | (cflag & CSTOPB ? A1CR_MODE_ASYNC_2 : A1CR_MODE_ASYNC_1)));
+ /* sender's character size, set DTR for valid baud rate */
+ SCCmod(TX_CTRL_REG, ~TCR_CHSIZE_MASK, chsize_map[chsize] << 5 | TCR_DTR);
+ /* clock sources never change */
+ /* disable BRG before changing the value */
+ SCCmod(DPLL_CTRL_REG, ~DCR_BRG_ENAB, 0);
+ /* BRG value */
+ SCCwrite(TIMER_LOW_REG, brgval & 0xff);
+ SCCwrite(TIMER_HIGH_REG, (brgval >> 8) & 0xff);
+ /* BRG enable, and clock source never changes */
+ SCCmod(DPLL_CTRL_REG, 0xff, DCR_BRG_ENAB);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+
+static int scc_chars_in_buffer (void *ptr)
+{
+ struct scc_port *port = ptr;
+ SCC_ACCESS_INIT(port);
+
+ return (SCCread (SPCOND_STATUS_REG) & SCSR_ALL_SENT) ? 0 : 1;
+}
+
+
+/* Comment taken from sx.c (2.4.0):
+ I haven't the foggiest why the decrement use count has to happen
+ here. The whole linux serial drivers stuff needs to be redesigned.
+ My guess is that this is a hack to minimize the impact of a bug
+ elsewhere. Thinking about it some more. (try it sometime) Try
+ running minicom on a serial port that is driven by a modularized
+ driver. Have the modem hangup. Then remove the driver module. Then
+ exit minicom. I expect an "oops". -- REW */
+
+static void scc_hungup(void *ptr)
+{
+ scc_disable_tx_interrupts(ptr);
+ scc_disable_rx_interrupts(ptr);
+}
+
+
+static void scc_close(void *ptr)
+{
+ scc_disable_tx_interrupts(ptr);
+ scc_disable_rx_interrupts(ptr);
+}
+
+
+/*---------------------------------------------------------------------------
+ * Internal support functions
+ *--------------------------------------------------------------------------*/
+
+static void scc_setsignals(struct scc_port *port, int dtr, int rts)
+{
+ unsigned long flags;
+ unsigned char t;
+ SCC_ACCESS_INIT(port);
+
+ local_irq_save(flags);
+ t = SCCread(TX_CTRL_REG);
+ if (dtr >= 0) t = dtr? (t | TCR_DTR): (t & ~TCR_DTR);
+ if (rts >= 0) t = rts? (t | TCR_RTS): (t & ~TCR_RTS);
+ SCCwrite(TX_CTRL_REG, t);
+ local_irq_restore(flags);
+}
+
+
+static void scc_send_xchar(struct tty_struct *tty, char ch)
+{
+ struct scc_port *port = tty->driver_data;
+
+ port->x_char = ch;
+ if (ch)
+ scc_enable_tx_interrupts(port);
+}
+
+
+/*---------------------------------------------------------------------------
+ * Driver entrypoints referenced from above
+ *--------------------------------------------------------------------------*/
+
+static int scc_open (struct tty_struct * tty, struct file * filp)
+{
+ int line = tty->index;
+ int retval;
+ struct scc_port *port = &scc_ports[line];
+ int i, channel = port->channel;
+ unsigned long flags;
+ SCC_ACCESS_INIT(port);
+#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_MVME147_SCC)
+ static const struct {
+ unsigned reg, val;
+ } mvme_init_tab[] = {
+ /* Values for MVME162 and MVME147 */
+ /* no parity, 1 stop bit, async, 1:16 */
+ { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 },
+ /* parity error is special cond, ints disabled, no DMA */
+ { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },
+ /* Rx 8 bits/char, no auto enable, Rx off */
+ { RX_CTRL_REG, RCR_CHSIZE_8 },
+ /* DTR off, Tx 8 bits/char, RTS off, Tx off */
+ { TX_CTRL_REG, TCR_CHSIZE_8 },
+ /* special features off */
+ { AUX2_CTRL_REG, 0 },
+ { CLK_CTRL_REG, CCR_RXCLK_BRG | CCR_TXCLK_BRG },
+ { DPLL_CTRL_REG, DCR_BRG_ENAB | DCR_BRG_USE_PCLK },
+ /* Start Rx */
+ { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
+ /* Start Tx */
+ { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
+ /* Ext/Stat ints: DCD only */
+ { INT_CTRL_REG, ICR_ENAB_DCD_INT },
+ /* Reset Ext/Stat ints */
+ { COMMAND_REG, CR_EXTSTAT_RESET },
+ /* ...again */
+ { COMMAND_REG, CR_EXTSTAT_RESET },
+ };
+#endif
+#if defined(CONFIG_BVME6000_SCC)
+ static const struct {
+ unsigned reg, val;
+ } bvme_init_tab[] = {
+ /* Values for BVME6000 */
+ /* no parity, 1 stop bit, async, 1:16 */
+ { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 },
+ /* parity error is special cond, ints disabled, no DMA */
+ { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },
+ /* Rx 8 bits/char, no auto enable, Rx off */
+ { RX_CTRL_REG, RCR_CHSIZE_8 },
+ /* DTR off, Tx 8 bits/char, RTS off, Tx off */
+ { TX_CTRL_REG, TCR_CHSIZE_8 },
+ /* special features off */
+ { AUX2_CTRL_REG, 0 },
+ { CLK_CTRL_REG, CCR_RTxC_XTAL | CCR_RXCLK_BRG | CCR_TXCLK_BRG },
+ { DPLL_CTRL_REG, DCR_BRG_ENAB },
+ /* Start Rx */
+ { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
+ /* Start Tx */
+ { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
+ /* Ext/Stat ints: DCD only */
+ { INT_CTRL_REG, ICR_ENAB_DCD_INT },
+ /* Reset Ext/Stat ints */
+ { COMMAND_REG, CR_EXTSTAT_RESET },
+ /* ...again */
+ { COMMAND_REG, CR_EXTSTAT_RESET },
+ };
+#endif
+ if (!(port->gs.port.flags & ASYNC_INITIALIZED)) {
+ local_irq_save(flags);
+#if defined(CONFIG_MVME147_SCC) || defined(CONFIG_MVME162_SCC)
+ if (MACH_IS_MVME147 || MACH_IS_MVME16x) {
+ for (i = 0; i < ARRAY_SIZE(mvme_init_tab); ++i)
+ SCCwrite(mvme_init_tab[i].reg, mvme_init_tab[i].val);
+ }
+#endif
+#if defined(CONFIG_BVME6000_SCC)
+ if (MACH_IS_BVME6000) {
+ for (i = 0; i < ARRAY_SIZE(bvme_init_tab); ++i)
+ SCCwrite(bvme_init_tab[i].reg, bvme_init_tab[i].val);
+ }
+#endif
+
+ /* remember status register for detection of DCD and CTS changes */
+ scc_last_status_reg[channel] = SCCread(STATUS_REG);
+
+ port->c_dcd = 0; /* Prevent initial 1->0 interrupt */
+ scc_setsignals (port, 1,1);
+ local_irq_restore(flags);
+ }
+
+ tty->driver_data = port;
+ port->gs.port.tty = tty;
+ port->gs.port.count++;
+ retval = gs_init_port(&port->gs);
+ if (retval) {
+ port->gs.port.count--;
+ return retval;
+ }
+ port->gs.port.flags |= GS_ACTIVE;
+ retval = gs_block_til_ready(port, filp);
+
+ if (retval) {
+ port->gs.port.count--;
+ return retval;
+ }
+
+ port->c_dcd = tty_port_carrier_raised(&port->gs.port);
+
+ scc_enable_rx_interrupts(port);
+
+ return 0;
+}
+
+
+static void scc_throttle (struct tty_struct * tty)
+{
+ struct scc_port *port = tty->driver_data;
+ unsigned long flags;
+ SCC_ACCESS_INIT(port);
+
+ if (tty->termios->c_cflag & CRTSCTS) {
+ local_irq_save(flags);
+ SCCmod(TX_CTRL_REG, ~TCR_RTS, 0);
+ local_irq_restore(flags);
+ }
+ if (I_IXOFF(tty))
+ scc_send_xchar(tty, STOP_CHAR(tty));
+}
+
+
+static void scc_unthrottle (struct tty_struct * tty)
+{
+ struct scc_port *port = tty->driver_data;
+ unsigned long flags;
+ SCC_ACCESS_INIT(port);
+
+ if (tty->termios->c_cflag & CRTSCTS) {
+ local_irq_save(flags);
+ SCCmod(TX_CTRL_REG, 0xff, TCR_RTS);
+ local_irq_restore(flags);
+ }
+ if (I_IXOFF(tty))
+ scc_send_xchar(tty, START_CHAR(tty));
+}
+
+
+static int scc_ioctl(struct tty_struct *tty,
+ unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+
+static int scc_break_ctl(struct tty_struct *tty, int break_state)
+{
+ struct scc_port *port = tty->driver_data;
+ unsigned long flags;
+ SCC_ACCESS_INIT(port);
+
+ local_irq_save(flags);
+ SCCmod(TX_CTRL_REG, ~TCR_SEND_BREAK,
+ break_state ? TCR_SEND_BREAK : 0);
+ local_irq_restore(flags);
+ return 0;
+}
+
+
+/*---------------------------------------------------------------------------
+ * Serial console stuff...
+ *--------------------------------------------------------------------------*/
+
+#define scc_delay() do { __asm__ __volatile__ (" nop; nop"); } while (0)
+
+static void scc_ch_write (char ch)
+{
+ volatile char *p = NULL;
+
+#ifdef CONFIG_MVME147_SCC
+ if (MACH_IS_MVME147)
+ p = (volatile char *)M147_SCC_A_ADDR;
+#endif
+#ifdef CONFIG_MVME162_SCC
+ if (MACH_IS_MVME16x)
+ p = (volatile char *)MVME_SCC_A_ADDR;
+#endif
+#ifdef CONFIG_BVME6000_SCC
+ if (MACH_IS_BVME6000)
+ p = (volatile char *)BVME_SCC_A_ADDR;
+#endif
+
+ do {
+ scc_delay();
+ }
+ while (!(*p & 4));
+ scc_delay();
+ *p = 8;
+ scc_delay();
+ *p = ch;
+}
+
+/* The console must be locked when we get here. */
+
+static void scc_console_write (struct console *co, const char *str, unsigned count)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ while (count--)
+ {
+ if (*str == '\n')
+ scc_ch_write ('\r');
+ scc_ch_write (*str++);
+ }
+ local_irq_restore(flags);
+}
+
+static struct tty_driver *scc_console_device(struct console *c, int *index)
+{
+ *index = c->index;
+ return scc_driver;
+}
+
+static struct console sercons = {
+ .name = "ttyS",
+ .write = scc_console_write,
+ .device = scc_console_device,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+
+static int __init vme_scc_console_init(void)
+{
+ if (vme_brdtype == VME_TYPE_MVME147 ||
+ vme_brdtype == VME_TYPE_MVME162 ||
+ vme_brdtype == VME_TYPE_MVME172 ||
+ vme_brdtype == VME_TYPE_BVME4000 ||
+ vme_brdtype == VME_TYPE_BVME6000)
+ register_console(&sercons);
+ return 0;
+}
+console_initcall(vme_scc_console_init);
diff --git a/drivers/staging/quatech_usb2/quatech_usb2.c b/drivers/staging/quatech_usb2/quatech_usb2.c
index ed58f482c963..36b18e302718 100644
--- a/drivers/staging/quatech_usb2/quatech_usb2.c
+++ b/drivers/staging/quatech_usb2/quatech_usb2.c
@@ -852,7 +852,7 @@ static int qt2_chars_in_buffer(struct tty_struct *tty)
* TIOCMGET and TIOCMSET are filtered off to their own methods before they get
* here, so we don't have to handle them.
*/
-static int qt2_ioctl(struct tty_struct *tty, struct file *file,
+static int qt2_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
@@ -1078,7 +1078,7 @@ static void qt2_set_termios(struct tty_struct *tty,
}
}
-static int qt2_tiocmget(struct tty_struct *tty, struct file *file)
+static int qt2_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
@@ -1121,7 +1121,7 @@ static int qt2_tiocmget(struct tty_struct *tty, struct file *file)
}
}
-static int qt2_tiocmset(struct tty_struct *tty, struct file *file,
+static int qt2_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
diff --git a/drivers/staging/serqt_usb2/serqt_usb2.c b/drivers/staging/serqt_usb2/serqt_usb2.c
index 27841ef6a568..e0aae86a8809 100644
--- a/drivers/staging/serqt_usb2/serqt_usb2.c
+++ b/drivers/staging/serqt_usb2/serqt_usb2.c
@@ -1191,7 +1191,7 @@ static int qt_write_room(struct tty_struct *tty)
}
-static int qt_ioctl(struct tty_struct *tty, struct file *file,
+static int qt_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
@@ -1383,7 +1383,7 @@ static void qt_break(struct tty_struct *tty, int break_state)
static inline int qt_real_tiocmget(struct tty_struct *tty,
struct usb_serial_port *port,
- struct file *file, struct usb_serial *serial)
+ struct usb_serial *serial)
{
u8 mcr;
@@ -1425,7 +1425,6 @@ static inline int qt_real_tiocmget(struct tty_struct *tty,
static inline int qt_real_tiocmset(struct tty_struct *tty,
struct usb_serial_port *port,
- struct file *file,
struct usb_serial *serial,
unsigned int value)
{
@@ -1462,7 +1461,7 @@ static inline int qt_real_tiocmset(struct tty_struct *tty,
return 0;
}
-static int qt_tiocmget(struct tty_struct *tty, struct file *file)
+static int qt_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = get_usb_serial(port, __func__);
@@ -1480,13 +1479,13 @@ static int qt_tiocmget(struct tty_struct *tty, struct file *file)
dbg("%s - port %d\n", __func__, port->number);
dbg("%s - port->RxHolding = %d\n", __func__, qt_port->RxHolding);
- retval = qt_real_tiocmget(tty, port, file, serial);
+ retval = qt_real_tiocmget(tty, port, serial);
spin_unlock_irqrestore(&qt_port->lock, flags);
return retval;
}
-static int qt_tiocmset(struct tty_struct *tty, struct file *file,
+static int qt_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
@@ -1506,7 +1505,7 @@ static int qt_tiocmset(struct tty_struct *tty, struct file *file,
dbg("%s - port %d\n", __func__, port->number);
dbg("%s - qt_port->RxHolding = %d\n", __func__, qt_port->RxHolding);
- retval = qt_real_tiocmset(tty, port, file, serial, set);
+ retval = qt_real_tiocmset(tty, port, serial, set);
spin_unlock_irqrestore(&qt_port->lock, flags);
return retval;
diff --git a/drivers/staging/tty/Kconfig b/drivers/staging/tty/Kconfig
new file mode 100644
index 000000000000..77103a07abbd
--- /dev/null
+++ b/drivers/staging/tty/Kconfig
@@ -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
index 000000000000..ac57c105611b
--- /dev/null
+++ b/drivers/staging/tty/Makefile
@@ -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
index 000000000000..88756453ac6c
--- /dev/null
+++ b/drivers/staging/tty/TODO
@@ -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/cd1865.h b/drivers/staging/tty/cd1865.h
new file mode 100644
index 000000000000..9940966e7a1d
--- /dev/null
+++ b/drivers/staging/tty/cd1865.h
@@ -0,0 +1,263 @@
+/*
+ * linux/drivers/char/cd1865.h -- Definitions relating to the CD1865
+ * for the 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.
+ *
+ */
+
+/*
+ * Definitions for Driving CD180/CD1864/CD1865 based eightport serial cards.
+ */
+
+
+/* Values of choice for Interrupt ACKs */
+/* These values are "obligatory" if you use the register based
+ * interrupt acknowledgements. See page 99-101 of V2.0 of the CD1865
+ * databook */
+#define SX_ACK_MINT 0x75 /* goes to PILR1 */
+#define SX_ACK_TINT 0x76 /* goes to PILR2 */
+#define SX_ACK_RINT 0x77 /* goes to PILR3 */
+
+/* Chip ID (is used when chips ar daisy chained.) */
+#define SX_ID 0x10
+
+/* Definitions for Cirrus Logic CL-CD186x 8-port async mux chip */
+
+#define CD186x_NCH 8 /* Total number of channels */
+#define CD186x_TPC 16 /* Ticks per character */
+#define CD186x_NFIFO 8 /* TX FIFO size */
+
+
+/* Global registers */
+
+#define CD186x_GIVR 0x40 /* Global Interrupt Vector Register */
+#define CD186x_GICR 0x41 /* Global Interrupting Channel Register */
+#define CD186x_PILR1 0x61 /* Priority Interrupt Level Register 1 */
+#define CD186x_PILR2 0x62 /* Priority Interrupt Level Register 2 */
+#define CD186x_PILR3 0x63 /* Priority Interrupt Level Register 3 */
+#define CD186x_CAR 0x64 /* Channel Access Register */
+#define CD186x_SRSR 0x65 /* Channel Access Register */
+#define CD186x_GFRCR 0x6b /* Global Firmware Revision Code Register */
+#define CD186x_PPRH 0x70 /* Prescaler Period Register High */
+#define CD186x_PPRL 0x71 /* Prescaler Period Register Low */
+#define CD186x_RDR 0x78 /* Receiver Data Register */
+#define CD186x_RCSR 0x7a /* Receiver Character Status Register */
+#define CD186x_TDR 0x7b /* Transmit Data Register */
+#define CD186x_EOIR 0x7f /* End of Interrupt Register */
+#define CD186x_MRAR 0x75 /* Modem Request Acknowledge register */
+#define CD186x_TRAR 0x76 /* Transmit Request Acknowledge register */
+#define CD186x_RRAR 0x77 /* Receive Request Acknowledge register */
+#define CD186x_SRCR 0x66 /* Service Request Configuration register */
+
+/* Channel Registers */
+
+#define CD186x_CCR 0x01 /* Channel Command Register */
+#define CD186x_IER 0x02 /* Interrupt Enable Register */
+#define CD186x_COR1 0x03 /* Channel Option Register 1 */
+#define CD186x_COR2 0x04 /* Channel Option Register 2 */
+#define CD186x_COR3 0x05 /* Channel Option Register 3 */
+#define CD186x_CCSR 0x06 /* Channel Control Status Register */
+#define CD186x_RDCR 0x07 /* Receive Data Count Register */
+#define CD186x_SCHR1 0x09 /* Special Character Register 1 */
+#define CD186x_SCHR2 0x0a /* Special Character Register 2 */
+#define CD186x_SCHR3 0x0b /* Special Character Register 3 */
+#define CD186x_SCHR4 0x0c /* Special Character Register 4 */
+#define CD186x_MCOR1 0x10 /* Modem Change Option 1 Register */
+#define CD186x_MCOR2 0x11 /* Modem Change Option 2 Register */
+#define CD186x_MCR 0x12 /* Modem Change Register */
+#define CD186x_RTPR 0x18 /* Receive Timeout Period Register */
+#define CD186x_MSVR 0x28 /* Modem Signal Value Register */
+#define CD186x_MSVRTS 0x29 /* Modem Signal Value Register */
+#define CD186x_MSVDTR 0x2a /* Modem Signal Value Register */
+#define CD186x_RBPRH 0x31 /* Receive Baud Rate Period Register High */
+#define CD186x_RBPRL 0x32 /* Receive Baud Rate Period Register Low */
+#define CD186x_TBPRH 0x39 /* Transmit Baud Rate Period Register High */
+#define CD186x_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 shift */
+
+
+/* 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 CD186x_C_ESC 0x00 /* Escape character */
+#define CD186x_C_SBRK 0x81 /* Start sending BREAK */
+#define CD186x_C_DELAY 0x82 /* Delay output */
+#define CD186x_C_EBRK 0x83 /* Stop sending BREAK */
+
+#define SRSR_RREQint 0x10 /* This chip wants "rec" serviced */
+#define SRSR_TREQint 0x04 /* This chip wants "transmit" serviced */
+#define SRSR_MREQint 0x01 /* This chip wants "mdm change" serviced */
+
+
+
+#define SRCR_PKGTYPE 0x80
+#define SRCR_REGACKEN 0x40
+#define SRCR_DAISYEN 0x20
+#define SRCR_GLOBPRI 0x10
+#define SRCR_UNFAIR 0x08
+#define SRCR_AUTOPRI 0x02
+#define SRCR_PRISEL 0x01
+
+
diff --git a/drivers/staging/tty/digi1.h b/drivers/staging/tty/digi1.h
new file mode 100644
index 000000000000..94d4eab5d3ca
--- /dev/null
+++ b/drivers/staging/tty/digi1.h
@@ -0,0 +1,100 @@
+/* Definitions for DigiBoard ditty(1) command. */
+
+#if !defined(TIOCMODG)
+#define TIOCMODG (('d'<<8) | 250) /* get modem ctrl state */
+#define TIOCMODS (('d'<<8) | 251) /* set modem ctrl state */
+#endif
+
+#if !defined(TIOCMSET)
+#define TIOCMSET (('d'<<8) | 252) /* set modem ctrl state */
+#define TIOCMGET (('d'<<8) | 253) /* set modem ctrl state */
+#endif
+
+#if !defined(TIOCMBIC)
+#define TIOCMBIC (('d'<<8) | 254) /* set modem ctrl state */
+#define TIOCMBIS (('d'<<8) | 255) /* set modem ctrl state */
+#endif
+
+#if !defined(TIOCSDTR)
+#define TIOCSDTR (('e'<<8) | 0) /* set DTR */
+#define TIOCCDTR (('e'<<8) | 1) /* clear DTR */
+#endif
+
+/************************************************************************
+ * Ioctl command arguments for DIGI parameters.
+ ************************************************************************/
+#define DIGI_GETA (('e'<<8) | 94) /* Read params */
+
+#define DIGI_SETA (('e'<<8) | 95) /* Set params */
+#define DIGI_SETAW (('e'<<8) | 96) /* Drain & set params */
+#define DIGI_SETAF (('e'<<8) | 97) /* Drain, flush & set params */
+
+#define DIGI_GETFLOW (('e'<<8) | 99) /* Get startc/stopc flow */
+ /* control characters */
+#define DIGI_SETFLOW (('e'<<8) | 100) /* Set startc/stopc flow */
+ /* control characters */
+#define DIGI_GETAFLOW (('e'<<8) | 101) /* Get Aux. startc/stopc */
+ /* flow control chars */
+#define DIGI_SETAFLOW (('e'<<8) | 102) /* Set Aux. startc/stopc */
+ /* flow control chars */
+
+#define DIGI_GETINFO (('e'<<8) | 103) /* Fill in digi_info */
+#define DIGI_POLLER (('e'<<8) | 104) /* Turn on/off poller */
+#define DIGI_INIT (('e'<<8) | 105) /* Allow things to run. */
+
+struct digiflow_struct
+{
+ unsigned char startc; /* flow cntl start char */
+ unsigned char stopc; /* flow cntl stop char */
+};
+
+typedef struct digiflow_struct digiflow_t;
+
+
+/************************************************************************
+ * Values for digi_flags
+ ************************************************************************/
+#define DIGI_IXON 0x0001 /* Handle IXON in the FEP */
+#define DIGI_FAST 0x0002 /* Fast baud rates */
+#define RTSPACE 0x0004 /* RTS input flow control */
+#define CTSPACE 0x0008 /* CTS output flow control */
+#define DSRPACE 0x0010 /* DSR output flow control */
+#define DCDPACE 0x0020 /* DCD output flow control */
+#define DTRPACE 0x0040 /* DTR input flow control */
+#define DIGI_FORCEDCD 0x0100 /* Force carrier */
+#define DIGI_ALTPIN 0x0200 /* Alternate RJ-45 pin config */
+#define DIGI_AIXON 0x0400 /* Aux flow control in fep */
+
+
+/************************************************************************
+ * Values for digiDload
+ ************************************************************************/
+#define NORMAL 0
+#define PCI_CTL 1
+
+#define SIZE8 0
+#define SIZE16 1
+#define SIZE32 2
+
+/************************************************************************
+ * Structure used with ioctl commands for DIGI parameters.
+ ************************************************************************/
+struct digi_struct
+{
+ unsigned short digi_flags; /* Flags (see above) */
+};
+
+typedef struct digi_struct digi_t;
+
+struct digi_info
+{
+ unsigned long board; /* Which board is this ? */
+ unsigned char status; /* Alive or dead */
+ unsigned char type; /* see epca.h */
+ unsigned char subtype; /* For future XEM, XR, etc ... */
+ unsigned short numports; /* Number of ports configured */
+ unsigned char *port; /* I/O Address */
+ unsigned char *membase; /* DPR Address */
+ unsigned char *version; /* For future ... */
+ unsigned short windowData; /* For future ... */
+} ;
diff --git a/drivers/staging/tty/digiFep1.h b/drivers/staging/tty/digiFep1.h
new file mode 100644
index 000000000000..3c1f1922c798
--- /dev/null
+++ b/drivers/staging/tty/digiFep1.h
@@ -0,0 +1,136 @@
+
+#define CSTART 0x400L
+#define CMAX 0x800L
+#define ISTART 0x800L
+#define IMAX 0xC00L
+#define CIN 0xD10L
+#define GLOBAL 0xD10L
+#define EIN 0xD18L
+#define FEPSTAT 0xD20L
+#define CHANSTRUCT 0x1000L
+#define RXTXBUF 0x4000L
+
+
+struct global_data
+{
+ u16 cin;
+ u16 cout;
+ u16 cstart;
+ u16 cmax;
+ u16 ein;
+ u16 eout;
+ u16 istart;
+ u16 imax;
+};
+
+
+struct board_chan
+{
+ u32 filler1;
+ u32 filler2;
+ u16 tseg;
+ u16 tin;
+ u16 tout;
+ u16 tmax;
+
+ u16 rseg;
+ u16 rin;
+ u16 rout;
+ u16 rmax;
+
+ u16 tlow;
+ u16 rlow;
+ u16 rhigh;
+ u16 incr;
+
+ u16 etime;
+ u16 edelay;
+ unchar *dev;
+
+ u16 iflag;
+ u16 oflag;
+ u16 cflag;
+ u16 gmask;
+
+ u16 col;
+ u16 delay;
+ u16 imask;
+ u16 tflush;
+
+ u32 filler3;
+ u32 filler4;
+ u32 filler5;
+ u32 filler6;
+
+ u8 num;
+ u8 ract;
+ u8 bstat;
+ u8 tbusy;
+ u8 iempty;
+ u8 ilow;
+ u8 idata;
+ u8 eflag;
+
+ u8 tflag;
+ u8 rflag;
+ u8 xmask;
+ u8 xval;
+ u8 mstat;
+ u8 mchange;
+ u8 mint;
+ u8 lstat;
+
+ u8 mtran;
+ u8 orun;
+ u8 startca;
+ u8 stopca;
+ u8 startc;
+ u8 stopc;
+ u8 vnext;
+ u8 hflow;
+
+ u8 fillc;
+ u8 ochar;
+ u8 omask;
+
+ u8 filler7;
+ u8 filler8[28];
+};
+
+
+#define SRXLWATER 0xE0
+#define SRXHWATER 0xE1
+#define STOUT 0xE2
+#define PAUSETX 0xE3
+#define RESUMETX 0xE4
+#define SAUXONOFFC 0xE6
+#define SENDBREAK 0xE8
+#define SETMODEM 0xE9
+#define SETIFLAGS 0xEA
+#define SONOFFC 0xEB
+#define STXLWATER 0xEC
+#define PAUSERX 0xEE
+#define RESUMERX 0xEF
+#define SETBUFFER 0xF2
+#define SETCOOKED 0xF3
+#define SETHFLOW 0xF4
+#define SETCTRLFLAGS 0xF5
+#define SETVNEXT 0xF6
+
+
+
+#define BREAK_IND 0x01
+#define LOWTX_IND 0x02
+#define EMPTYTX_IND 0x04
+#define DATA_IND 0x08
+#define MODEMCHG_IND 0x20
+
+#define FEP_HUPCL 0002000
+#if 0
+#define RTS 0x02
+#define CD 0x08
+#define DSR 0x10
+#define CTS 0x20
+#define RI 0x40
+#define DTR 0x80
+#endif
diff --git a/drivers/staging/tty/digiPCI.h b/drivers/staging/tty/digiPCI.h
new file mode 100644
index 000000000000..6ca7819e5069
--- /dev/null
+++ b/drivers/staging/tty/digiPCI.h
@@ -0,0 +1,42 @@
+/*************************************************************************
+ * Defines and structure definitions for PCI BIOS Interface
+ *************************************************************************/
+#define PCIMAX 32 /* maximum number of PCI boards */
+
+
+#define PCI_VENDOR_DIGI 0x114F
+#define PCI_DEVICE_EPC 0x0002
+#define PCI_DEVICE_RIGHTSWITCH 0x0003 /* For testing */
+#define PCI_DEVICE_XEM 0x0004
+#define PCI_DEVICE_XR 0x0005
+#define PCI_DEVICE_CX 0x0006
+#define PCI_DEVICE_XRJ 0x0009 /* Jupiter boards with */
+#define PCI_DEVICE_EPCJ 0x000a /* PLX 9060 chip for PCI */
+
+
+/*
+ * On the PCI boards, there is no IO space allocated
+ * The I/O registers will be in the first 3 bytes of the
+ * upper 2MB of the 4MB memory space. The board memory
+ * will be mapped into the low 2MB of the 4MB memory space
+ */
+
+/* Potential location of PCI Bios from E0000 to FFFFF*/
+#define PCI_BIOS_SIZE 0x00020000
+
+/* Size of Memory and I/O for PCI (4MB) */
+#define PCI_RAM_SIZE 0x00400000
+
+/* Size of Memory (2MB) */
+#define PCI_MEM_SIZE 0x00200000
+
+/* Offset of I/0 in Memory (2MB) */
+#define PCI_IO_OFFSET 0x00200000
+
+#define MEMOUTB(basemem, pnum, setmemval) *(caddr_t)((basemem) + ( PCI_IO_OFFSET | pnum << 4 | pnum )) = (setmemval)
+#define MEMINB(basemem, pnum) *(caddr_t)((basemem) + (PCI_IO_OFFSET | pnum << 4 | pnum )) /* for PCI I/O */
+
+
+
+
+
diff --git a/drivers/staging/tty/epca.c b/drivers/staging/tty/epca.c
new file mode 100644
index 000000000000..7ad3638967ae
--- /dev/null
+++ b/drivers/staging/tty/epca.c
@@ -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
index 000000000000..d414bf2dbf7c
--- /dev/null
+++ b/drivers/staging/tty/epca.h
@@ -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
index 000000000000..55dec067078e
--- /dev/null
+++ b/drivers/staging/tty/epcaconfig.h
@@ -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
index 000000000000..7b78e0dfc5b0
--- /dev/null
+++ b/drivers/staging/tty/ip2/Makefile
@@ -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
index 000000000000..e7af647800b6
--- /dev/null
+++ b/drivers/staging/tty/ip2/i2cmd.c
@@ -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
index 000000000000..29277ec6b8ed
--- /dev/null
+++ b/drivers/staging/tty/ip2/i2cmd.h
@@ -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
index 000000000000..29db44de399f
--- /dev/null
+++ b/drivers/staging/tty/ip2/i2ellis.c
@@ -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
index 000000000000..fb6df2456018
--- /dev/null
+++ b/drivers/staging/tty/ip2/i2ellis.h
@@ -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
index 000000000000..c0ba6c05f0cd
--- /dev/null
+++ b/drivers/staging/tty/ip2/i2hw.h
@@ -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
index 000000000000..0d10b89218ed
--- /dev/null
+++ b/drivers/staging/tty/ip2/i2lib.c
@@ -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
index 000000000000..e559e9bac06d
--- /dev/null
+++ b/drivers/staging/tty/ip2/i2lib.h
@@ -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
index 000000000000..00342a677c90
--- /dev/null
+++ b/drivers/staging/tty/ip2/i2pack.h
@@ -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
index 000000000000..936ccc533949
--- /dev/null
+++ b/drivers/staging/tty/ip2/ip2.h
@@ -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
index 000000000000..aa0a9da85e05
--- /dev/null
+++ b/drivers/staging/tty/ip2/ip2ioctl.h
@@ -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
index 000000000000..ea7a8fb08283
--- /dev/null
+++ b/drivers/staging/tty/ip2/ip2main.c
@@ -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
index 000000000000..da20435dc8a6
--- /dev/null
+++ b/drivers/staging/tty/ip2/ip2trace.h
@@ -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
index 000000000000..9d67b260b2f6
--- /dev/null
+++ b/drivers/staging/tty/ip2/ip2types.h
@@ -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
index 000000000000..0b266272cccd
--- /dev/null
+++ b/drivers/staging/tty/istallion.c
@@ -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
index 000000000000..602643a40b4b
--- /dev/null
+++ b/drivers/staging/tty/riscom8.c
@@ -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
index 000000000000..c9876b3f9714
--- /dev/null
+++ b/drivers/staging/tty/riscom8.h
@@ -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
index 000000000000..a32475ed0d18
--- /dev/null
+++ b/drivers/staging/tty/riscom8_reg.h
@@ -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
index 000000000000..674af6933978
--- /dev/null
+++ b/drivers/staging/tty/serial167.c
@@ -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
index 000000000000..47e5753f732a
--- /dev/null
+++ b/drivers/staging/tty/specialix.c
@@ -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
index 000000000000..c63005274d9b
--- /dev/null
+++ b/drivers/staging/tty/specialix_io8.h
@@ -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
index 000000000000..4fff5cd3b163
--- /dev/null
+++ b/drivers/staging/tty/stallion.c
@@ -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");