/drivers/char/istallion.c
https://bitbucket.org/evzijst/gittest · C · 5276 lines · 3590 code · 777 blank · 909 comment · 755 complexity · 35cbe6dab5652e5cf384df0804d64b1d MD5 · raw file
Large files are truncated click here to view the full file
- /*****************************************************************************/
- /*
- * 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);
- }
- /*
- * Based on type of open being done check if it can overlap with any
- * previous opens still in effect. If we are a normal serial device
- * then also we might have to wait for carrier.
- */
- if (!(filp->f_flags & O_NONBLOCK)) {
- if ((rc = stli_waitcarrier(brdp, portp, filp)) != 0)
- return(rc);
- }
- portp->flags |= ASYNC_NORMAL_ACTIVE;
- return(0);
- }
- /*****************************************************************************/
- static void stli_close(struct tty_struct *tty, struct file *filp)
- {
- stlibrd_t *brdp;
- stliport_t *portp;
- unsigned long flags;
- #ifdef DEBUG
- printk("stli_close(tty=%x,filp=%x)\n", (int) tty, (int) filp);
- #endif
- portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
- return;
- save_flags(flags);
- cli();
- if (tty_hung_up_p(filp)) {
- restore_flags(flags);
- return;
- }
- if ((tty->count == 1) && (portp->refcount != 1))
- portp->refcount = 1;
- if (portp->refcount-- > 1) {
- restore_flags(flags);
- return;
- }
- portp->flags |= ASYNC_CLOSING;
- /*
- * 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 (tty == stli_txcooktty)
- stli_flushchars(tty);
- tty->closing = 1;
- if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
- tty_wait_until_sent(tty, portp->closing_wait);
- portp->flags &= ~ASYNC_INITIALIZED;
- brdp = stli_brds[portp->brdnr];
- stli_rawclose(brdp, portp, 0, 0);
- if (tty->termios->c_cflag & HUPCL) {
- stli_mkasysigs(&portp->asig, 0, 0);
- if (test_bit(ST_CMDING, &portp->state))
- set_bit(ST_DOSIGS, &portp->state);
- else
- stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig,
- sizeof(asysigs_t), 0);
- }
- clear_bit(ST_TXBUSY, &portp->state);
- clear_bit(ST_RXSTOP, &portp->state);
- set_bit(TTY_IO_ERROR, &tty->flags);
- if (tty->ldisc.flush_buffer)
- (tty->ldisc.flush_buffer)(tty);
- set_bit(ST_DOFLUSHRX, &portp->state);
- stli_flushbuffer(tty);
- tty->closing = 0;
- portp->tty = (struct tty_struct *) NULL;
- if (portp->openwaitcnt) {
- if (portp->close_delay)
- msleep_interruptible(jiffies_to_msecs(portp->close_delay));
- wake_up_interruptible(&portp->open_wait);
- }
- portp->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
- wake_up_interruptible(&portp->close_wait);
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * 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(stlibrd_t *brdp, stliport_t *portp)
- {
- struct tty_struct *tty;
- asynotify_t nt;
- asyport_t aport;
- int rc;
- #ifdef DEBUG
- printk("stli_initopen(brdp=%x,portp=%x)\n", (int) brdp, (int) portp);
- #endif
- 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);
- tty = portp->tty;
- if (tty == (struct tty_struct *) NULL)
- return(-ENODEV);
- stli_mkasyport(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(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait)
- {
- volatile cdkhdr_t *hdrp;
- volatile cdkctrl_t *cp;
- volatile unsigned char *bits;
- unsigned long flags;
- int rc;
- #ifdef DEBUG
- printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n",
- (int) brdp, (int) portp, (int) arg, wait);
- #endif
- /*
- * Send a message to the slave to open this port.
- */
- save_flags(flags);
- cli();
- /*
- * 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(portp->raw_wait,
- !test_bit(ST_CLOSING, &portp->state));
- if (signal_pending(current)) {
- restore_flags(flags);
- 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.
- */
- EBRDENABLE(brdp);
- cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
- cp->openarg = arg;
- cp->open = 1;
- hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
- bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
- portp->portidx;
- *bits |= portp->portbit;
- EBRDDISABLE(brdp);
- if (wait == 0) {
- restore_flags(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);
- wait_event_interruptible(portp->raw_wait,
- !test_bit(ST_OPENING, &portp->state));
- if (signal_pending(current))
- rc = -ERESTARTSYS;
- restore_flags(flags);
- 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(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait)
- {
- volatile cdkhdr_t *hdrp;
- volatile cdkctrl_t *cp;
- volatile unsigned char *bits;
- unsigned long flags;
- int rc;
- #ifdef DEBUG
- printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n",
- (int) brdp, (int) portp, (int) arg, wait);
- #endif
- save_flags(flags);
- cli();
- /*
- * Slave is already closing this port. This can happen if a hangup
- * occurs on this port.
- */
- if (wait) {
- wait_event_interruptible(portp->raw_wait,
- !test_bit(ST_CLOSING, &portp->state));
- if (signal_pending(current)) {
- restore_flags(flags);
- return -ERESTARTSYS;
- }
- }
- /*
- * Write the close command into shared memory.
- */
- EBRDENABLE(brdp);
- cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
- cp->closearg = arg;
- cp->close = 1;
- hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
- bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
- portp->portidx;
- *bits |= portp->portbit;
- EBRDDISABLE(brdp);
- set_bit(ST_CLOSING, &portp->state);
- if (wait == 0) {
- restore_flags(flags);
- return(0);
- }
- /*
- * Slave is in action, so now we must wait for the open acknowledgment
- * to come back.
- */
- rc = 0;
- wait_event_interruptible(portp->raw_wait,
- !test_bit(ST_CLOSING, &portp->state));
- if (signal_pending(current))
- rc = -ERESTARTSYS;
- restore_flags(flags);
- 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(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback)
- {
- unsigned long flags;
- #ifdef DEBUG
- printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,"
- "copyback=%d)\n", (int) brdp, (int) portp, (int) cmd,
- (int) arg, size, copyback);
- #endif
- save_flags(flags);
- cli();
- wait_event_interruptible(portp->raw_wait,
- !test_bit(ST_CMDING, &portp->state));
- if (signal_pending(current)) {
- restore_flags(flags);
- 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)) {
- restore_flags(flags);
- return -ERESTARTSYS;
- }
- restore_flags(flags);
- 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(stliport_t *portp)
- {
- stlibrd_t *brdp;
- asyport_t aport;
- #ifdef DEBUG
- printk("stli_setport(portp=%x)\n", (int) portp);
- #endif
- if (portp == (stliport_t *) NULL)
- return(-ENODEV);
- if (portp->tty == (struct tty_struct *) NULL)
- return(-ENODEV);
- if ((portp->brdnr < 0) && (portp->brdnr >= stli_nrbrds))
- return(-ENODEV);
- brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return(-ENODEV);
- stli_mkasyport(portp, &aport, portp->tty->termios);
- return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0));
- }
- /*****************************************************************************/
- /*
- * Possibly need to wait for carrier (DCD signal) to come high. Say
- * maybe because if we are clocal then we don't need to wait...
- */
- static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *filp)
- {
- unsigned long flags;
- int rc, doclocal;
- #ifdef DEBUG
- printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n",
- (int) brdp, (int) portp, (int) filp);
- #endif
- rc = 0;
- doclocal = 0;
- if (portp->tty->termios->c_cflag & CLOCAL)
- doclocal++;
- save_flags(flags);
- cli();
- portp->openwaitcnt++;
- if (! tty_hung_up_p(filp))
- portp->refcount--;
- for (;;) {
- stli_mkasysigs(&portp->asig, 1, 1);
- if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS,
- &portp->asig, sizeof(asysigs_t), 0)) < 0)
- break;
- if (tty_hung_up_p(filp) ||
- ((portp->flags & ASYNC_INITIALIZED) == 0)) {
- if (portp->flags & ASYNC_HUP_NOTIFY)
- rc = -EBUSY;
- else
- rc = -ERESTARTSYS;
- break;
- }
- if (((portp->flags & ASYNC_CLOSING) == 0) &&
- (doclocal || (portp->sigs & TIOCM_CD))) {
- break;
- }
- if (signal_pending(current)) {
- rc = -ERESTARTSYS;
- break;
- }
- interruptible_sleep_on(&portp->open_wait);
- }
- if (! tty_hung_up_p(filp))
- portp->refcount++;
- portp->openwaitcnt--;
- restore_flags(flags);
- return(rc);
- }
- /*****************************************************************************/
- /*
- * 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)
- {
- volatile cdkasy_t *ap;
- volatile cdkhdr_t *hdrp;
- volatile unsigned char *bits;
- unsigned char *shbuf, *chbuf;
- stliport_t *portp;
- stlibrd_t *brdp;
- unsigned int len, stlen, head, tail, size;
- unsigned long flags;
- #ifdef DEBUG
- printk("stli_write(tty=%x,buf=%x,count=%d)\n",
- (int) tty, (int) buf, count);
- #endif
- if ((tty == (struct tty_struct *) NULL) ||
- (stli_tmpwritebuf == (char *) NULL))
- return(0);
- if (tty == stli_txcooktty)
- stli_flushchars(tty);
- portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
- return(0);
- if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
- return(0);
- brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return(0);
- chbuf = (unsigned char *) buf;
- /*
- * All data is now local, shove as much as possible into shared memory.
- */
- save_flags(flags);
- cli();
- EBRDENABLE(brdp);
- ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
- head = (unsigned int) ap->txq.head;
- tail = (unsigned int) ap->txq.tail;
- if (tail != ((unsigned int) ap->txq.tail))
- tail = (unsigned int) 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, count);
- count = 0;
- shbuf = (char *) EBRDGETMEMPTR(brdp, portp->txoffset);
- while (len > 0) {
- stlen = MIN(len, stlen);
- memcpy((shbuf + head), chbuf, stlen);
- chbuf += stlen;
- len -= stlen;
- count += stlen;
- head += stlen;
- if (head >= size) {
- head = 0;
- stlen = tail;
- }
- }
- ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
- ap->txq.head = head;
- if (test_bit(ST_TXBUSY, &portp->state)) {
- if (ap->changed.data & DT_TXEMPTY)
- ap->changed.data &= ~DT_TXEMPTY;
- }
- hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
- bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
- portp->portidx;
- *bits |= portp->portbit;
- set_bit(ST_TXBUSY, &portp->state);
- EBRDDISABLE(brdp);
- restore_flags(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 void stli_putchar(struct tty_struct *tty, unsigned char ch)
- {
- #ifdef DEBUG
- printk("stli_putchar(tty=%x,ch=%x)\n", (int) tty, (int) ch);
- #endif
- if (tty == (struct tty_struct *) NULL)
- return;
- if (tty != stli_txcooktty) {
- if (stli_txcooktty != (struct tty_struct *) NULL)
- stli_flushchars(stli_txcooktty);
- stli_txcooktty = tty;
- }
- stli_txcookbuf[stli_txcooksize++] = ch;
- }
- /*****************************************************************************/
- /*
- * 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)
- {
- volatile cdkhdr_t *hdrp;
- volatile unsigned char *bits;
- volatile cdkasy_t *ap;
- struct tty_struct *cooktty;
- stliport_t *portp;
- stlibrd_t *brdp;
- unsigned int len, stlen, head, tail, size, count, cooksize;
- unsigned char *buf, *shbuf;
- unsigned long flags;
- #ifdef DEBUG
- printk("stli_flushchars(tty=%x)\n", (int) tty);
- #endif
- cooksize = stli_txcooksize;
- cooktty = stli_txcooktty;
- stli_txcooksize = 0;
- stli_txcookrealsize = 0;
- stli_txcooktty = (struct tty_struct *) NULL;
- if (tty == (struct tty_struct *) NULL)
- return;
- if (cooktty == (struct tty_struct *) NULL)
- return;
- if (tty != cooktty)
- tty = cooktty;
- if (cooksize == 0)
- return;
- portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
- return;
- if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
- return;
- brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return;
- save_flags(flags);
- cli();
- EBRDENABLE(brdp);
- ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
- head = (unsigned int) ap->txq.head;
- tail = (unsigned int) ap->txq.tail;
- if (tail != ((unsigned int) ap->txq.tail))
- tail = (unsigned int) 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 = (char *) EBRDGETMEMPTR(brdp, portp->txoffset);
- buf = stli_txcookbuf;
- while (len > 0) {
- stlen = MIN(len, stlen);
- memcpy((shbuf + head), buf, stlen);
- buf += stlen;
- len -= stlen;
- count += stlen;
- head += stlen;
- if (head >= size) {
- head = 0;
- stlen = tail;
- }
- }
- ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
- ap->txq.head = head;
- if (test_bit(ST_TXBUSY, &portp->state)) {
- if (ap->changed.data & DT_TXEMPTY)
- ap->changed.data &= ~DT_TXEMPTY;
- }
- hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
- bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
- portp->portidx;
- *bits |= portp->portbit;
- set_bit(ST_TXBUSY, &portp->state);
- EBRDDISABLE(brdp);
- restore_flags(flags);
- }
- /*****************************************************************************/
- static int stli_writeroom(struct tty_struct *tty)
- {
- volatile cdkasyrq_t *rp;
- stliport_t *portp;
- stlibrd_t *brdp;
- unsigned int head, tail, len;
- unsigned long flags;
- #ifdef DEBUG
- printk("stli_writeroom(tty=%x)\n", (int) tty);
- #endif
- if (tty == (struct tty_struct *) NULL)
- return(0);
- if (tty == stli_txcooktty) {
- if (stli_txcookrealsize != 0) {
- len = stli_txcookrealsize - stli_txcooksize;
- return(len);
- }
- }
- portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
- return(0);
- if ((portp->brdnr <…