aboutsummaryrefslogtreecommitdiff
path: root/drivers/char/istallion.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/istallion.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/char/istallion.c')
-rw-r--r--drivers/char/istallion.c5276
1 files changed, 5276 insertions, 0 deletions
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
new file mode 100644
index 00000000000..21aed0e8779
--- /dev/null
+++ b/drivers/char/istallion.c
@@ -0,0 +1,5276 @@
+/*****************************************************************************/
+
+/*
+ * 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.
+ *
+ * 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/config.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.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/devfs_fs_kernel.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+
+/*****************************************************************************/
+
+/*
+ * 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_BRUMBY8 5
+#define BRD_BRUMBY16 6
+#define BRD_ONBOARDE 7
+#define BRD_ONBOARD32 9
+#define BRD_ONBOARD2_32 10
+#define BRD_ONBOARDRS 11
+#define BRD_EASYIO 20
+#define BRD_ECH 21
+#define BRD_ECHMC 22
+#define BRD_ECP 23
+#define BRD_ECPE 24
+#define BRD_ECPMC 25
+#define BRD_ECHPCI 26
+#define BRD_ECH64PCI 27
+#define BRD_EASYIOPCI 28
+#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.
+ */
+
+typedef struct {
+ int brdtype;
+ int ioaddr1;
+ int ioaddr2;
+ unsigned long memaddr;
+ int irq;
+ int irqtype;
+} stlconf_t;
+
+static stlconf_t stli_brdconf[] = {
+ /*{ BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },*/
+};
+
+static int stli_nrbrds = sizeof(stli_brdconf) / sizeof(stlconf_t);
+
+/*
+ * 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;
+
+/*
+ * We will need to allocate a temporary write buffer for chars that
+ * come direct from user space. The problem is that a copy from user
+ * space might cause a page fault (typically on a system that is
+ * swapping!). All ports will share one buffer - since if the system
+ * is already swapping a shared buffer won't make things any worse.
+ */
+static char *stli_tmpwritebuf;
+static DECLARE_MUTEX(stli_tmpwritesem);
+
+#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 termios stli_deftermios = {
+ .c_cflag = (B9600 | CS8 | CREAD | HUPCL | CLOCAL),
+ .c_cc = INIT_C_CC,
+};
+
+/*
+ * 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 asystats_t stli_cdkstats;
+static stlibrd_t stli_dummybrd;
+static stliport_t stli_dummyport;
+
+/*****************************************************************************/
+
+static stlibrd_t *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 0x1
+#define BST_STARTED 0x2
+
+/*
+ * 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_INITIALIZING 1
+#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",
+ (char *) NULL,
+ "ONboard",
+ "ONboard-MC",
+ "ONboard-MC",
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) NULL,
+ (char *) 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",
+};
+
+/*****************************************************************************/
+
+#ifdef MODULE
+/*
+ * 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.
+ */
+
+typedef struct stlibrdtype {
+ char *name;
+ int type;
+} stlibrdtype_t;
+
+static stlibrdtype_t 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_PARM(board0, "1-3s");
+MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,memaddr]");
+MODULE_PARM(board1, "1-3s");
+MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,memaddr]");
+MODULE_PARM(board2, "1-3s");
+MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,memaddr]");
+MODULE_PARM(board3, "1-3s");
+MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,memaddr]");
+
+#endif
+
+/*
+ * 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 = sizeof(stli_eisamemprobeaddrs) / sizeof(unsigned long);
+int stli_eisaprobe = STLI_EISAPROBE;
+
+/*
+ * Define the Stallion PCI vendor and device IDs.
+ */
+#ifdef CONFIG_PCI
+#ifndef PCI_VENDOR_ID_STALLION
+#define PCI_VENDOR_ID_STALLION 0x124d
+#endif
+#ifndef PCI_DEVICE_ID_ECRA
+#define PCI_DEVICE_ID_ECRA 0x0004
+#endif
+
+static struct pci_device_id istallion_pci_tbl[] = {
+ { PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECRA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, istallion_pci_tbl);
+
+#endif /* CONFIG_PCI */
+
+/*****************************************************************************/
+
+/*
+ * 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)
+
+/*
+ * 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 stli_baudrates[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
+};
+
+/*****************************************************************************/
+
+/*
+ * Define some handy local macros...
+ */
+#undef MIN
+#define MIN(a,b) (((a) <= (b)) ? (a) : (b))
+
+#undef TOLOWER
+#define TOLOWER(x) ((((x) >= 'A') && ((x) <= 'Z')) ? ((x) + 0x20) : (x))
+
+/*****************************************************************************/
+
+/*
+ * Prototype all functions in this driver!
+ */
+
+#ifdef MODULE
+static void stli_argbrds(void);
+static int stli_parsebrd(stlconf_t *confp, char **argp);
+
+static unsigned long stli_atol(char *str);
+#endif
+
+int stli_init(void);
+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 void 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, struct file *file, unsigned int cmd, unsigned long arg);
+static void stli_settermios(struct tty_struct *tty, struct termios *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 void 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_portinfo(stlibrd_t *brdp, stliport_t *portp, int portnr, char *pos);
+
+static int stli_brdinit(stlibrd_t *brdp);
+static int stli_startbrd(stlibrd_t *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 int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
+static void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp);
+static void stli_poll(unsigned long arg);
+static int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp);
+static int stli_initopen(stlibrd_t *brdp, stliport_t *portp);
+static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait);
+static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait);
+static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *filp);
+static void stli_dohangup(void *arg);
+static int stli_setport(stliport_t *portp);
+static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback);
+static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback);
+static void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp);
+static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp);
+static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts);
+static long stli_mktiocm(unsigned long sigvalue);
+static void stli_read(stlibrd_t *brdp, stliport_t *portp);
+static int stli_getserial(stliport_t *portp, struct serial_struct __user *sp);
+static int stli_setserial(stliport_t *portp, struct serial_struct __user *sp);
+static int stli_getbrdstats(combrd_t __user *bp);
+static int stli_getportstats(stliport_t *portp, comstats_t __user *cp);
+static int stli_portcmdstats(stliport_t *portp);
+static int stli_clrportstats(stliport_t *portp, comstats_t __user *cp);
+static int stli_getportstruct(stliport_t __user *arg);
+static int stli_getbrdstruct(stlibrd_t __user *arg);
+static void *stli_memalloc(int len);
+static stlibrd_t *stli_allocbrd(void);
+
+static void stli_ecpinit(stlibrd_t *brdp);
+static void stli_ecpenable(stlibrd_t *brdp);
+static void stli_ecpdisable(stlibrd_t *brdp);
+static char *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_ecpreset(stlibrd_t *brdp);
+static void stli_ecpintr(stlibrd_t *brdp);
+static void stli_ecpeiinit(stlibrd_t *brdp);
+static void stli_ecpeienable(stlibrd_t *brdp);
+static void stli_ecpeidisable(stlibrd_t *brdp);
+static char *stli_ecpeigetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_ecpeireset(stlibrd_t *brdp);
+static void stli_ecpmcenable(stlibrd_t *brdp);
+static void stli_ecpmcdisable(stlibrd_t *brdp);
+static char *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_ecpmcreset(stlibrd_t *brdp);
+static void stli_ecppciinit(stlibrd_t *brdp);
+static char *stli_ecppcigetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_ecppcireset(stlibrd_t *brdp);
+
+static void stli_onbinit(stlibrd_t *brdp);
+static void stli_onbenable(stlibrd_t *brdp);
+static void stli_onbdisable(stlibrd_t *brdp);
+static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_onbreset(stlibrd_t *brdp);
+static void stli_onbeinit(stlibrd_t *brdp);
+static void stli_onbeenable(stlibrd_t *brdp);
+static void stli_onbedisable(stlibrd_t *brdp);
+static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_onbereset(stlibrd_t *brdp);
+static void stli_bbyinit(stlibrd_t *brdp);
+static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_bbyreset(stlibrd_t *brdp);
+static void stli_stalinit(stlibrd_t *brdp);
+static char *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset, int line);
+static void stli_stalreset(stlibrd_t *brdp);
+
+static stliport_t *stli_getport(int brdnr, int panelnr, int portnr);
+
+static int stli_initecp(stlibrd_t *brdp);
+static int stli_initonb(stlibrd_t *brdp);
+static int stli_eisamemprobe(stlibrd_t *brdp);
+static int stli_initports(stlibrd_t *brdp);
+
+#ifdef CONFIG_PCI
+static int stli_initpcibrd(int brdtype, struct pci_dev *devp);
+#endif
+
+/*****************************************************************************/
+
+/*
+ * 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 struct file_operations stli_fsiomem = {
+ .owner = THIS_MODULE,
+ .read = stli_memread,
+ .write = stli_memwrite,
+ .ioctl = stli_memioctl,
+};
+
+/*****************************************************************************/
+
+/*
+ * 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 struct timer_list stli_timerlist = TIMER_INITIALIZER(stli_poll, 0, 0);
+
+static int stli_timeron;
+
+/*
+ * Define the calculation for the timeout routine.
+ */
+#define STLI_TIMEOUT (jiffies + 1)
+
+/*****************************************************************************/
+
+static struct class_simple *istallion_class;
+
+#ifdef MODULE
+
+/*
+ * Loadable module initialization stuff.
+ */
+
+static int __init istallion_module_init(void)
+{
+ unsigned long flags;
+
+#ifdef DEBUG
+ printk("init_module()\n");
+#endif
+
+ save_flags(flags);
+ cli();
+ stli_init();
+ restore_flags(flags);
+
+ return(0);
+}
+
+/*****************************************************************************/
+
+static void __exit istallion_module_exit(void)
+{
+ stlibrd_t *brdp;
+ stliport_t *portp;
+ unsigned long flags;
+ int i, j;
+
+#ifdef DEBUG
+ printk("cleanup_module()\n");
+#endif
+
+ printk(KERN_INFO "Unloading %s: version %s\n", stli_drvtitle,
+ stli_drvversion);
+
+ save_flags(flags);
+ cli();
+
+/*
+ * Free up all allocated resources used by the ports. This includes
+ * memory and interrupts.
+ */
+ if (stli_timeron) {
+ stli_timeron = 0;
+ del_timer(&stli_timerlist);
+ }
+
+ i = tty_unregister_driver(stli_serial);
+ if (i) {
+ printk("STALLION: failed to un-register tty driver, "
+ "errno=%d\n", -i);
+ restore_flags(flags);
+ return;
+ }
+ put_tty_driver(stli_serial);
+ for (i = 0; i < 4; i++) {
+ devfs_remove("staliomem/%d", i);
+ class_simple_device_remove(MKDEV(STL_SIOMEMMAJOR, i));
+ }
+ devfs_remove("staliomem");
+ class_simple_destroy(istallion_class);
+ if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
+ printk("STALLION: failed to un-register serial memory device, "
+ "errno=%d\n", -i);
+ if (stli_tmpwritebuf != (char *) NULL)
+ kfree(stli_tmpwritebuf);
+ if (stli_txcookbuf != (char *) NULL)
+ kfree(stli_txcookbuf);
+
+ for (i = 0; (i < stli_nrbrds); i++) {
+ if ((brdp = stli_brds[i]) == (stlibrd_t *) NULL)
+ continue;
+ for (j = 0; (j < STL_MAXPORTS); j++) {
+ portp = brdp->ports[j];
+ if (portp != (stliport_t *) NULL) {
+ if (portp->tty != (struct tty_struct *) NULL)
+ tty_hangup(portp->tty);
+ kfree(portp);
+ }
+ }
+
+ iounmap(brdp->membase);
+ if (brdp->iosize > 0)
+ release_region(brdp->iobase, brdp->iosize);
+ kfree(brdp);
+ stli_brds[i] = (stlibrd_t *) NULL;
+ }
+
+ restore_flags(flags);
+}
+
+module_init(istallion_module_init);
+module_exit(istallion_module_exit);
+
+/*****************************************************************************/
+
+/*
+ * Check for any arguments passed in on the module load command line.
+ */
+
+static void stli_argbrds(void)
+{
+ stlconf_t conf;
+ stlibrd_t *brdp;
+ int nrargs, i;
+
+#ifdef DEBUG
+ printk("stli_argbrds()\n");
+#endif
+
+ nrargs = sizeof(stli_brdsp) / sizeof(char **);
+
+ for (i = stli_nrbrds; (i < nrargs); i++) {
+ memset(&conf, 0, sizeof(conf));
+ if (stli_parsebrd(&conf, stli_brdsp[i]) == 0)
+ continue;
+ if ((brdp = stli_allocbrd()) == (stlibrd_t *) NULL)
+ continue;
+ stli_nrbrds = i + 1;
+ brdp->brdnr = i;
+ brdp->brdtype = conf.brdtype;
+ brdp->iobase = conf.ioaddr1;
+ brdp->memaddr = conf.memaddr;
+ stli_brdinit(brdp);
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Convert an ascii string number into an unsigned long.
+ */
+
+static unsigned long stli_atol(char *str)
+{
+ unsigned long val;
+ int base, c;
+ char *sp;
+
+ val = 0;
+ sp = str;
+ if ((*sp == '0') && (*(sp+1) == 'x')) {
+ base = 16;
+ sp += 2;
+ } else if (*sp == '0') {
+ base = 8;
+ sp++;
+ } else {
+ base = 10;
+ }
+
+ for (; (*sp != 0); sp++) {
+ c = (*sp > '9') ? (TOLOWER(*sp) - 'a' + 10) : (*sp - '0');
+ if ((c < 0) || (c >= base)) {
+ printk("STALLION: invalid argument %s\n", str);
+ val = 0;
+ break;
+ }
+ val = (val * base) + c;
+ }
+ return(val);
+}
+
+/*****************************************************************************/
+
+/*
+ * Parse the supplied argument string, into the board conf struct.
+ */
+
+static int stli_parsebrd(stlconf_t *confp, char **argp)
+{
+ char *sp;
+ int nrbrdnames, i;
+
+#ifdef DEBUG
+ printk("stli_parsebrd(confp=%x,argp=%x)\n", (int) confp, (int) argp);
+#endif
+
+ if ((argp[0] == (char *) NULL) || (*argp[0] == 0))
+ return(0);
+
+ for (sp = argp[0], i = 0; ((*sp != 0) && (i < 25)); sp++, i++)
+ *sp = TOLOWER(*sp);
+
+ nrbrdnames = sizeof(stli_brdstr) / sizeof(stlibrdtype_t);
+ for (i = 0; (i < nrbrdnames); i++) {
+ if (strcmp(stli_brdstr[i].name, argp[0]) == 0)
+ break;
+ }
+ if (i >= nrbrdnames) {
+ printk("STALLION: unknown board name, %s?\n", argp[0]);
+ return(0);
+ }
+
+ confp->brdtype = stli_brdstr[i].type;
+ if ((argp[1] != (char *) NULL) && (*argp[1] != 0))
+ confp->ioaddr1 = stli_atol(argp[1]);
+ if ((argp[2] != (char *) NULL) && (*argp[2] != 0))
+ confp->memaddr = stli_atol(argp[2]);
+ return(1);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/*
+ * Local driver kernel malloc routine.
+ */
+
+static void *stli_memalloc(int len)
+{
+ return((void *) kmalloc(len, GFP_KERNEL));
+}
+
+/*****************************************************************************/
+
+static int stli_open(struct tty_struct *tty, struct file *filp)
+{
+ stlibrd_t *brdp;
+ stliport_t *portp;
+ unsigned int minordev;
+ int brdnr, portnr, rc;
+
+#ifdef DEBUG
+ printk("stli_open(tty=%x,filp=%x): device=%s\n", (int) tty,
+ (int) filp, tty->name);
+#endif
+
+ minordev = tty->index;
+ brdnr = MINOR2BRD(minordev);
+ if (brdnr >= stli_nrbrds)
+ return(-ENODEV);
+ brdp = stli_brds[brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return(-ENODEV);
+ if ((brdp->state & BST_STARTED) == 0)
+ return(-ENODEV);
+ portnr = MINOR2PORT(minordev);
+ if ((portnr < 0) || (portnr > brdp->nrports))
+ return(-ENODEV);
+
+ portp = brdp->ports[portnr];
+ if (portp == (stliport_t *) NULL)
+ return(-ENODEV);
+ if (portp->devnr < 1)
+ return(-ENODEV);
+
+
+/*
+ * Check if this port is in the middle of closing. If so then wait
+ * until it is closed then return error status based on flag settings.
+ * The sleep here does not need interrupt protection since the wakeup
+ * for it is done with the same context.
+ */
+ if (portp->flags & ASYNC_CLOSING) {
+ interruptible_sleep_on(&portp->close_wait);
+ if (portp->flags & ASYNC_HUP_NOTIFY)
+ return(-EAGAIN);
+ return(-ERESTARTSYS);
+ }
+
+/*
+ * 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.
+ */
+ portp->tty = tty;
+ tty->driver_data = portp;
+ portp->refcount++;
+
+ wait_event_interruptible(portp->raw_wait,
+ !test_bit(ST_INITIALIZING, &portp->state));
+ if (signal_pending(current))
+ return(-ERESTARTSYS);
+
+ if ((portp->flags & ASYNC_INITIALIZED) == 0) {
+ set_bit(ST_INITIALIZING, &portp->state);
+ if ((rc = stli_initopen(brdp, portp)) >= 0) {
+ portp->flags |= ASYNC_INITIALIZED;
+ clear_bit(TTY_IO_ERROR, &tty->flags);
+ }
+ clear_bit(ST_INITIALIZING, &portp->state);
+ wake_up_interruptible(&portp->raw_wait);
+ if (rc < 0)
+ return(rc);
+ }
+
+/*
+ * Check if this port is in the middle of closing. If so then wait
+ * until it is closed then return error status, based on flag settings.
+ * The sleep here does not need interrupt protection since the wakeup
+ * for it is done with the same context.
+ */
+ if (portp->flags & ASYNC_CLOSING) {
+ interruptible_sleep_on(&portp->close_wait);
+ if (portp->flags & ASYNC_HUP_NOTIFY)
+ return(-EAGAIN);
+ return(-ERESTARTSYS);
+ }