diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /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.c | 5276 |
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); + } |