aboutsummaryrefslogtreecommitdiff
path: root/drivers/char/rocket.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/rocket.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/rocket.c')
-rw-r--r--drivers/char/rocket.c3299
1 files changed, 3299 insertions, 0 deletions
diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c
new file mode 100644
index 00000000000..5bcbeb0cb9a
--- /dev/null
+++ b/drivers/char/rocket.c
@@ -0,0 +1,3299 @@
+/*
+ * RocketPort device driver for Linux
+ *
+ * Written by Theodore Ts'o, 1995, 1996, 1997, 1998, 1999, 2000.
+ *
+ * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2003 by Comtrol, Inc.
+ *
+ * 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.
+ */
+
+/*
+ * Kernel Synchronization:
+ *
+ * This driver has 2 kernel control paths - exception handlers (calls into the driver
+ * from user mode) and the timer bottom half (tasklet). This is a polled driver, interrupts
+ * are not used.
+ *
+ * Critical data:
+ * - rp_table[], accessed through passed "info" pointers, is a global (static) array of
+ * serial port state information and the xmit_buf circular buffer. Protected by
+ * a per port spinlock.
+ * - xmit_flags[], an array of ints indexed by line (port) number, indicating that there
+ * is data to be transmitted. Protected by atomic bit operations.
+ * - rp_num_ports, int indicating number of open ports, protected by atomic operations.
+ *
+ * rp_write() and rp_write_char() functions use a per port semaphore to protect against
+ * simultaneous access to the same port by more than one process.
+ */
+
+/****** Defines ******/
+#ifdef PCI_NUM_RESOURCES
+#define PCI_BASE_ADDRESS(dev, r) ((dev)->resource[r].start)
+#else
+#define PCI_BASE_ADDRESS(dev, r) ((dev)->base_address[r])
+#endif
+
+#define ROCKET_PARANOIA_CHECK
+#define ROCKET_DISABLE_SIMUSAGE
+
+#undef ROCKET_SOFT_FLOW
+#undef ROCKET_DEBUG_OPEN
+#undef ROCKET_DEBUG_INTR
+#undef ROCKET_DEBUG_WRITE
+#undef ROCKET_DEBUG_FLOW
+#undef ROCKET_DEBUG_THROTTLE
+#undef ROCKET_DEBUG_WAIT_UNTIL_SENT
+#undef ROCKET_DEBUG_RECEIVE
+#undef ROCKET_DEBUG_HANGUP
+#undef REV_PCI_ORDER
+#undef ROCKET_DEBUG_IO
+
+#define POLL_PERIOD HZ/100 /* Polling period .01 seconds (10ms) */
+
+/****** Kernel includes ******/
+
+#ifdef MODVERSIONS
+#include <config/modversions.h>
+#endif
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/major.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/pci.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/bitops.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+#include <linux/init.h>
+
+/****** RocketPort includes ******/
+
+#include "rocket_int.h"
+#include "rocket.h"
+
+#define ROCKET_VERSION "2.09"
+#define ROCKET_DATE "12-June-2003"
+
+/****** RocketPort Local Variables ******/
+
+static struct tty_driver *rocket_driver;
+
+static struct rocket_version driver_version = {
+ ROCKET_VERSION, ROCKET_DATE
+};
+
+static struct r_port *rp_table[MAX_RP_PORTS]; /* The main repository of serial port state information. */
+static unsigned int xmit_flags[NUM_BOARDS]; /* Bit significant, indicates port had data to transmit. */
+ /* eg. Bit 0 indicates port 0 has xmit data, ... */
+static atomic_t rp_num_ports_open; /* Number of serial ports open */
+static struct timer_list rocket_timer;
+
+static unsigned long board1; /* ISA addresses, retrieved from rocketport.conf */
+static unsigned long board2;
+static unsigned long board3;
+static unsigned long board4;
+static unsigned long controller;
+static int support_low_speed;
+static unsigned long modem1;
+static unsigned long modem2;
+static unsigned long modem3;
+static unsigned long modem4;
+static unsigned long pc104_1[8];
+static unsigned long pc104_2[8];
+static unsigned long pc104_3[8];
+static unsigned long pc104_4[8];
+static unsigned long *pc104[4] = { pc104_1, pc104_2, pc104_3, pc104_4 };
+
+static int rp_baud_base[NUM_BOARDS]; /* Board config info (Someday make a per-board structure) */
+static unsigned long rcktpt_io_addr[NUM_BOARDS];
+static int rcktpt_type[NUM_BOARDS];
+static int is_PCI[NUM_BOARDS];
+static rocketModel_t rocketModel[NUM_BOARDS];
+static int max_board;
+
+/*
+ * The following arrays define the interrupt bits corresponding to each AIOP.
+ * These bits are different between the ISA and regular PCI boards and the
+ * Universal PCI boards.
+ */
+
+static Word_t aiop_intr_bits[AIOP_CTL_SIZE] = {
+ AIOP_INTR_BIT_0,
+ AIOP_INTR_BIT_1,
+ AIOP_INTR_BIT_2,
+ AIOP_INTR_BIT_3
+};
+
+static Word_t upci_aiop_intr_bits[AIOP_CTL_SIZE] = {
+ UPCI_AIOP_INTR_BIT_0,
+ UPCI_AIOP_INTR_BIT_1,
+ UPCI_AIOP_INTR_BIT_2,
+ UPCI_AIOP_INTR_BIT_3
+};
+
+/*
+ * Line number is the ttySIx number (x), the Minor number. We
+ * assign them sequentially, starting at zero. The following
+ * array keeps track of the line number assigned to a given board/aiop/channel.
+ */
+static unsigned char lineNumbers[MAX_RP_PORTS];
+static unsigned long nextLineNumber;
+
+/***** RocketPort Static Prototypes *********/
+static int __init init_ISA(int i);
+static void rp_wait_until_sent(struct tty_struct *tty, int timeout);
+static void rp_flush_buffer(struct tty_struct *tty);
+static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model);
+static unsigned char GetLineNumber(int ctrl, int aiop, int ch);
+static unsigned char SetLineNumber(int ctrl, int aiop, int ch);
+static void rp_start(struct tty_struct *tty);
+
+#ifdef MODULE
+MODULE_AUTHOR("Theodore Ts'o");
+MODULE_DESCRIPTION("Comtrol RocketPort driver");
+module_param(board1, ulong, 0);
+MODULE_PARM_DESC(board1, "I/O port for (ISA) board #1");
+module_param(board2, ulong, 0);
+MODULE_PARM_DESC(board2, "I/O port for (ISA) board #2");
+module_param(board3, ulong, 0);
+MODULE_PARM_DESC(board3, "I/O port for (ISA) board #3");
+module_param(board4, ulong, 0);
+MODULE_PARM_DESC(board4, "I/O port for (ISA) board #4");
+module_param(controller, ulong, 0);
+MODULE_PARM_DESC(controller, "I/O port for (ISA) rocketport controller");
+module_param(support_low_speed, bool, 0);
+MODULE_PARM_DESC(support_low_speed, "1 means support 50 baud, 0 means support 460400 baud");
+module_param(modem1, ulong, 0);
+MODULE_PARM_DESC(modem1, "1 means (ISA) board #1 is a RocketModem");
+module_param(modem2, ulong, 0);
+MODULE_PARM_DESC(modem2, "1 means (ISA) board #2 is a RocketModem");
+module_param(modem3, ulong, 0);
+MODULE_PARM_DESC(modem3, "1 means (ISA) board #3 is a RocketModem");
+module_param(modem4, ulong, 0);
+MODULE_PARM_DESC(modem4, "1 means (ISA) board #4 is a RocketModem");
+module_param_array(pc104_1, ulong, NULL, 0);
+MODULE_PARM_DESC(pc104_1, "set interface types for ISA(PC104) board #1 (e.g. pc104_1=232,232,485,485,...");
+module_param_array(pc104_2, ulong, NULL, 0);
+MODULE_PARM_DESC(pc104_2, "set interface types for ISA(PC104) board #2 (e.g. pc104_2=232,232,485,485,...");
+module_param_array(pc104_3, ulong, NULL, 0);
+MODULE_PARM_DESC(pc104_3, "set interface types for ISA(PC104) board #3 (e.g. pc104_3=232,232,485,485,...");
+module_param_array(pc104_4, ulong, NULL, 0);
+MODULE_PARM_DESC(pc104_4, "set interface types for ISA(PC104) board #4 (e.g. pc104_4=232,232,485,485,...");
+
+int rp_init(void);
+static void rp_cleanup_module(void);
+
+module_init(rp_init);
+module_exit(rp_cleanup_module);
+
+#endif
+
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+/*************************************************************************/
+/* Module code starts here */
+
+static inline int rocket_paranoia_check(struct r_port *info,
+ const char *routine)
+{
+#ifdef ROCKET_PARANOIA_CHECK
+ if (!info)
+ return 1;
+ if (info->magic != RPORT_MAGIC) {
+ printk(KERN_INFO "Warning: bad magic number for rocketport struct in %s\n",
+ routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+
+/* Serial port receive data function. Called (from timer poll) when an AIOPIC signals
+ * that receive data is present on a serial port. Pulls data from FIFO, moves it into the
+ * tty layer.
+ */
+static void rp_do_receive(struct r_port *info,
+ struct tty_struct *tty,
+ CHANNEL_t * cp, unsigned int ChanStatus)
+{
+ unsigned int CharNStat;
+ int ToRecv, wRecv, space = 0, count;
+ unsigned char *cbuf;
+ char *fbuf;
+ struct tty_ldisc *ld;
+
+ ld = tty_ldisc_ref(tty);
+
+ ToRecv = sGetRxCnt(cp);
+ if (ld)
+ space = ld->receive_room(tty);
+ if (space > 2 * TTY_FLIPBUF_SIZE)
+ space = 2 * TTY_FLIPBUF_SIZE;
+ cbuf = tty->flip.char_buf;
+ fbuf = tty->flip.flag_buf;
+ count = 0;
+#ifdef ROCKET_DEBUG_INTR
+ printk(KERN_INFO "rp_do_receive(%d, %d)...", ToRecv, space);
+#endif
+
+ /*
+ * determine how many we can actually read in. If we can't
+ * read any in then we have a software overrun condition.
+ */
+ if (ToRecv > space)
+ ToRecv = space;
+
+ if (ToRecv <= 0)
+ return;
+
+ /*
+ * if status indicates there are errored characters in the
+ * FIFO, then enter status mode (a word in FIFO holds
+ * character and status).
+ */
+ if (ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) {
+ if (!(ChanStatus & STATMODE)) {
+#ifdef ROCKET_DEBUG_RECEIVE
+ printk(KERN_INFO "Entering STATMODE...");
+#endif
+ ChanStatus |= STATMODE;
+ sEnRxStatusMode(cp);
+ }
+ }
+
+ /*
+ * if we previously entered status mode, then read down the
+ * FIFO one word at a time, pulling apart the character and
+ * the status. Update error counters depending on status
+ */
+ if (ChanStatus & STATMODE) {
+#ifdef ROCKET_DEBUG_RECEIVE
+ printk(KERN_INFO "Ignore %x, read %x...", info->ignore_status_mask,
+ info->read_status_mask);
+#endif
+ while (ToRecv) {
+ CharNStat = sInW(sGetTxRxDataIO(cp));
+#ifdef ROCKET_DEBUG_RECEIVE
+ printk(KERN_INFO "%x...", CharNStat);
+#endif
+ if (CharNStat & STMBREAKH)
+ CharNStat &= ~(STMFRAMEH | STMPARITYH);
+ if (CharNStat & info->ignore_status_mask) {
+ ToRecv--;
+ continue;
+ }
+ CharNStat &= info->read_status_mask;
+ if (CharNStat & STMBREAKH)
+ *fbuf++ = TTY_BREAK;
+ else if (CharNStat & STMPARITYH)
+ *fbuf++ = TTY_PARITY;
+ else if (CharNStat & STMFRAMEH)
+ *fbuf++ = TTY_FRAME;
+ else if (CharNStat & STMRCVROVRH)
+ *fbuf++ = TTY_OVERRUN;
+ else
+ *fbuf++ = 0;
+ *cbuf++ = CharNStat & 0xff;
+ count++;
+ ToRecv--;
+ }
+
+ /*
+ * after we've emptied the FIFO in status mode, turn
+ * status mode back off
+ */
+ if (sGetRxCnt(cp) == 0) {
+#ifdef ROCKET_DEBUG_RECEIVE
+ printk(KERN_INFO "Status mode off.\n");
+#endif
+ sDisRxStatusMode(cp);
+ }
+ } else {
+ /*
+ * we aren't in status mode, so read down the FIFO two
+ * characters at time by doing repeated word IO
+ * transfer.
+ */
+ wRecv = ToRecv >> 1;
+ if (wRecv)
+ sInStrW(sGetTxRxDataIO(cp), (unsigned short *) cbuf, wRecv);
+ if (ToRecv & 1)
+ cbuf[ToRecv - 1] = sInB(sGetTxRxDataIO(cp));
+ memset(fbuf, 0, ToRecv);
+ cbuf += ToRecv;
+ fbuf += ToRecv;
+ count += ToRecv;
+ }
+ /* Push the data up to the tty layer */
+ ld->receive_buf(tty, tty->flip.char_buf, tty->flip.flag_buf, count);
+ tty_ldisc_deref(ld);
+}
+
+/*
+ * Serial port transmit data function. Called from the timer polling loop as a
+ * result of a bit set in xmit_flags[], indicating data (from the tty layer) is ready
+ * to be sent out the serial port. Data is buffered in rp_table[line].xmit_buf, it is
+ * moved to the port's xmit FIFO. *info is critical data, protected by spinlocks.
+ */
+static void rp_do_transmit(struct r_port *info)
+{
+ int c;
+ CHANNEL_t *cp = &info->channel;
+ struct tty_struct *tty;
+ unsigned long flags;
+
+#ifdef ROCKET_DEBUG_INTR
+ printk(KERN_INFO "rp_do_transmit ");
+#endif
+ if (!info)
+ return;
+ if (!info->tty) {
+ printk(KERN_INFO "rp: WARNING rp_do_transmit called with info->tty==NULL\n");
+ clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+ return;
+ }
+
+ spin_lock_irqsave(&info->slock, flags);
+ tty = info->tty;
+ info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp);
+
+ /* Loop sending data to FIFO until done or FIFO full */
+ while (1) {
+ if (tty->stopped || tty->hw_stopped)
+ break;
+ c = min(info->xmit_fifo_room, min(info->xmit_cnt, XMIT_BUF_SIZE - info->xmit_tail));
+ if (c <= 0 || info->xmit_fifo_room <= 0)
+ break;
+ sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) (info->xmit_buf + info->xmit_tail), c / 2);
+ if (c & 1)
+ sOutB(sGetTxRxDataIO(cp), info->xmit_buf[info->xmit_tail + c - 1]);
+ info->xmit_tail += c;
+ info->xmit_tail &= XMIT_BUF_SIZE - 1;
+ info->xmit_cnt -= c;
+ info->xmit_fifo_room -= c;
+#ifdef ROCKET_DEBUG_INTR
+ printk(KERN_INFO "tx %d chars...", c);
+#endif
+ }
+
+ if (info->xmit_cnt == 0)
+ clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+
+ if (info->xmit_cnt < WAKEUP_CHARS) {
+ tty_wakeup(tty);
+ wake_up_interruptible(&tty->write_wait);
+#ifdef ROCKETPORT_HAVE_POLL_WAIT
+ wake_up_interruptible(&tty->poll_wait);
+#endif
+ }
+
+ spin_unlock_irqrestore(&info->slock, flags);
+
+#ifdef ROCKET_DEBUG_INTR
+ printk(KERN_INFO "(%d,%d,%d,%d)...", info->xmit_cnt, info->xmit_head,
+ info->xmit_tail, info->xmit_fifo_room);
+#endif
+}
+
+/*
+ * Called when a serial port signals it has read data in it's RX FIFO.
+ * It checks what interrupts are pending and services them, including
+ * receiving serial data.
+ */
+static void rp_handle_port(struct r_port *info)
+{
+ CHANNEL_t *cp;
+ struct tty_struct *tty;
+ unsigned int IntMask, ChanStatus;
+
+ if (!info)
+ return;
+
+ if ((info->flags & ROCKET_INITIALIZED) == 0) {
+ printk(KERN_INFO "rp: WARNING: rp_handle_port called with info->flags & NOT_INIT\n");
+ return;
+ }
+ if (!info->tty) {
+ printk(KERN_INFO "rp: WARNING: rp_handle_port called with info->tty==NULL\n");
+ return;
+ }
+ cp = &info->channel;
+ tty = info->tty;
+
+ IntMask = sGetChanIntID(cp) & info->intmask;
+#ifdef ROCKET_DEBUG_INTR
+ printk(KERN_INFO "rp_interrupt %02x...", IntMask);
+#endif
+ ChanStatus = sGetChanStatus(cp);
+ if (IntMask & RXF_TRIG) { /* Rx FIFO trigger level */
+ rp_do_receive(info, tty, cp, ChanStatus);
+ }
+ if (IntMask & DELTA_CD) { /* CD change */
+#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_INTR) || defined(ROCKET_DEBUG_HANGUP))
+ printk(KERN_INFO "ttyR%d CD now %s...", info->line,
+ (ChanStatus & CD_ACT) ? "on" : "off");
+#endif
+ if (!(ChanStatus & CD_ACT) && info->cd_status) {
+#ifdef ROCKET_DEBUG_HANGUP
+ printk(KERN_INFO "CD drop, calling hangup.\n");
+#endif
+ tty_hangup(tty);
+ }
+ info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0;
+ wake_up_interruptible(&info->open_wait);
+ }
+#ifdef ROCKET_DEBUG_INTR
+ if (IntMask & DELTA_CTS) { /* CTS change */
+ printk(KERN_INFO "CTS change...\n");
+ }
+ if (IntMask & DELTA_DSR) { /* DSR change */
+ printk(KERN_INFO "DSR change...\n");
+ }
+#endif
+}
+
+/*
+ * The top level polling routine. Repeats every 1/100 HZ (10ms).
+ */
+static void rp_do_poll(unsigned long dummy)
+{
+ CONTROLLER_t *ctlp;
+ int ctrl, aiop, ch, line, i;
+ unsigned int xmitmask;
+ unsigned int CtlMask;
+ unsigned char AiopMask;
+ Word_t bit;
+
+ /* Walk through all the boards (ctrl's) */
+ for (ctrl = 0; ctrl < max_board; ctrl++) {
+ if (rcktpt_io_addr[ctrl] <= 0)
+ continue;
+
+ /* Get a ptr to the board's control struct */
+ ctlp = sCtlNumToCtlPtr(ctrl);
+
+ /* Get the interupt status from the board */
+#ifdef CONFIG_PCI
+ if (ctlp->BusType == isPCI)
+ CtlMask = sPCIGetControllerIntStatus(ctlp);
+ else
+#endif
+ CtlMask = sGetControllerIntStatus(ctlp);
+
+ /* Check if any AIOP read bits are set */
+ for (aiop = 0; CtlMask; aiop++) {
+ bit = ctlp->AiopIntrBits[aiop];
+ if (CtlMask & bit) {
+ CtlMask &= ~bit;
+ AiopMask = sGetAiopIntStatus(ctlp, aiop);
+
+ /* Check if any port read bits are set */
+ for (ch = 0; AiopMask; AiopMask >>= 1, ch++) {
+ if (AiopMask & 1) {
+
+ /* Get the line number (/dev/ttyRx number). */
+ /* Read the data from the port. */
+ line = GetLineNumber(ctrl, aiop, ch);
+ rp_handle_port(rp_table[line]);
+ }
+ }
+ }
+ }
+
+ xmitmask = xmit_flags[ctrl];
+
+ /*
+ * xmit_flags contains bit-significant flags, indicating there is data
+ * to xmit on the port. Bit 0 is port 0 on this board, bit 1 is port
+ * 1, ... (32 total possible). The variable i has the aiop and ch
+ * numbers encoded in it (port 0-7 are aiop0, 8-15 are aiop1, etc).
+ */
+ if (xmitmask) {
+ for (i = 0; i < rocketModel[ctrl].numPorts; i++) {
+ if (xmitmask & (1 << i)) {
+ aiop = (i & 0x18) >> 3;
+ ch = i & 0x07;
+ line = GetLineNumber(ctrl, aiop, ch);
+ rp_do_transmit(rp_table[line]);
+ }
+ }
+ }
+ }
+
+ /*
+ * Reset the timer so we get called at the next clock tick (10ms).
+ */
+ if (atomic_read(&rp_num_ports_open))
+ mod_timer(&rocket_timer, jiffies + POLL_PERIOD);
+}
+
+/*
+ * Initializes the r_port structure for a port, as well as enabling the port on
+ * the board.
+ * Inputs: board, aiop, chan numbers
+ */
+static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev)
+{
+ unsigned rocketMode;
+ struct r_port *info;
+ int line;
+ CONTROLLER_T *ctlp;
+
+ /* Get the next available line number */
+ line = SetLineNumber(board, aiop, chan);
+
+ ctlp = sCtlNumToCtlPtr(board);
+
+ /* Get a r_port struct for the port, fill it in and save it globally, indexed by line number */
+ info = kmalloc(sizeof (struct r_port), GFP_KERNEL);
+ if (!info) {
+ printk(KERN_INFO "Couldn't allocate info struct for line #%d\n", line);
+ return;
+ }
+ memset(info, 0, sizeof (struct r_port));
+
+ info->magic = RPORT_MAGIC;
+ info->line = line;
+ info->ctlp = ctlp;
+ info->board = board;
+ info->aiop = aiop;
+ info->chan = chan;
+ info->closing_wait = 3000;
+ info->close_delay = 50;
+ init_waitqueue_head(&info->open_wait);
+ init_waitqueue_head(&info->close_wait);
+ info->flags &= ~ROCKET_MODE_MASK;
+ switch (pc104[board][line]) {
+ case 422:
+ info->flags |= ROCKET_MODE_RS422;
+ break;
+ case 485:
+ info->flags |= ROCKET_MODE_RS485;
+ break;
+ case 232:
+ default:
+ info->flags |= ROCKET_MODE_RS232;
+ break;
+ }
+
+ info->intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR;
+ if (sInitChan(ctlp, &info->channel, aiop, chan) == 0) {
+ printk(KERN_INFO "RocketPort sInitChan(%d, %d, %d) failed!\n", board, aiop, chan);
+ kfree(info);
+ return;
+ }
+
+ rocketMode = info->flags & ROCKET_MODE_MASK;
+
+ if ((info->flags & ROCKET_RTS_TOGGLE) || (rocketMode == ROCKET_MODE_RS485))
+ sEnRTSToggle(&info->channel);
+ else
+ sDisRTSToggle(&info->channel);
+
+ if (ctlp->boardType == ROCKET_TYPE_PC104) {
+ switch (rocketMode) {
+ case ROCKET_MODE_RS485:
+ sSetInterfaceMode(&info->channel, InterfaceModeRS485);
+ break;
+ case ROCKET_MODE_RS422:
+ sSetInterfaceMode(&info->channel, InterfaceModeRS422);
+ break;
+ case ROCKET_MODE_RS232:
+ default:
+ if (info->flags & ROCKET_RTS_TOGGLE)
+ sSetInterfaceMode(&info->channel, InterfaceModeRS232T);
+ else
+ sSetInterfaceMode(&info->channel, InterfaceModeRS232);
+ break;
+ }
+ }
+ spin_lock_init(&info->slock);
+ sema_init(&info->write_sem, 1);
+ rp_table[line] = info;
+ if (pci_dev)
+ tty_register_device(rocket_driver, line, &pci_dev->dev);
+}
+
+/*
+ * Configures a rocketport port according to its termio settings. Called from
+ * user mode into the driver (exception handler). *info CD manipulation is spinlock protected.
+ */
+static void configure_r_port(struct r_port *info,
+ struct termios *old_termios)
+{
+ unsigned cflag;
+ unsigned long flags;
+ unsigned rocketMode;
+ int bits, baud, divisor;
+ CHANNEL_t *cp;
+
+ if (!info->tty || !info->tty->termios)
+ return;
+ cp = &info->channel;
+ cflag = info->tty->termios->c_cflag;
+
+ /* Byte size and parity */
+ if ((cflag & CSIZE) == CS8) {
+ sSetData8(cp);
+ bits = 10;
+ } else {
+ sSetData7(cp);
+ bits = 9;
+ }
+ if (cflag & CSTOPB) {
+ sSetStop2(cp);
+ bits++;
+ } else {
+ sSetStop1(cp);
+ }
+
+ if (cflag & PARENB) {
+ sEnParity(cp);
+ bits++;
+ if (cflag & PARODD) {
+ sSetOddParity(cp);
+ } else {
+ sSetEvenParity(cp);
+ }
+ } else {
+ sDisParity(cp);
+ }
+
+ /* baud rate */
+ baud = tty_get_baud_rate(info->tty);
+ if (!baud)
+ baud = 9600;
+ divisor = ((rp_baud_base[info->board] + (baud >> 1)) / baud) - 1;
+ if ((divisor >= 8192 || divisor < 0) && old_termios) {
+ info->tty->termios->c_cflag &= ~CBAUD;
+ info->tty->termios->c_cflag |=
+ (old_termios->c_cflag & CBAUD);
+ baud = tty_get_baud_rate(info->tty);
+ if (!baud)
+ baud = 9600;
+ divisor = (rp_baud_base[info->board] / baud) - 1;
+ }
+ if (divisor >= 8192 || divisor < 0) {
+ baud = 9600;
+ divisor = (rp_baud_base[info->board] / baud) - 1;
+ }
+ info->cps = baud / bits;
+ sSetBaud(cp, divisor);
+
+ if (cflag & CRTSCTS) {
+ info->intmask |= DELTA_CTS;
+ sEnCTSFlowCtl(cp);
+ } else {
+ info->intmask &= ~DELTA_CTS;
+ sDisCTSFlowCtl(cp);
+ }
+ if (cflag & CLOCAL) {
+ info->intmask &= ~DELTA_CD;
+ } else {
+ spin_lock_irqsave(&info->slock, flags);
+ if (sGetChanStatus(cp) & CD_ACT)
+ info->cd_status = 1;
+ else
+ info->cd_status = 0;
+ info->intmask |= DELTA_CD;
+ spin_unlock_irqrestore(&info->slock, flags);
+ }
+
+ /*
+ * Handle software flow control in the board
+ */
+#ifdef ROCKET_SOFT_FLOW
+ if (I_IXON(info->tty)) {
+ sEnTxSoftFlowCtl(cp);
+ if (I_IXANY(info->tty)) {
+ sEnIXANY(cp);
+ } else {
+ sDisIXANY(cp);
+ }
+ sSetTxXONChar(cp, START_CHAR(info->tty));
+ sSetTxXOFFChar(cp, STOP_CHAR(info->tty));
+ } else {
+ sDisTxSoftFlowCtl(cp);
+ sDisIXANY(cp);
+ sClrTxXOFF(cp);
+ }
+#endif
+
+ /*
+ * Set up ignore/read mask words
+ */
+ info->read_status_mask = STMRCVROVRH | 0xFF;
+ if (I_INPCK(info->tty))
+ info->read_status_mask |= STMFRAMEH | STMPARITYH;
+ if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+ info->read_status_mask |= STMBREAKH;
+
+ /*
+ * Characters to ignore
+ */
+ info->ignore_status_mask = 0;
+ if (I_IGNPAR(info->tty))
+ info->ignore_status_mask |= STMFRAMEH | STMPARITYH;
+ if (I_IGNBRK(info->tty)) {
+ info->ignore_status_mask |= STMBREAKH;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too. (For real raw support).
+ */
+ if (I_IGNPAR(info->tty))
+ info->ignore_status_mask |= STMRCVROVRH;
+ }
+
+ rocketMode = info->flags & ROCKET_MODE_MASK;
+
+ if ((info->flags & ROCKET_RTS_TOGGLE)
+ || (rocketMode == ROCKET_MODE_RS485))
+ sEnRTSToggle(cp);
+ else
+ sDisRTSToggle(cp);
+
+ sSetRTS(&info->channel);
+
+ if (cp->CtlP->boardType == ROCKET_TYPE_PC104) {
+ switch (rocketMode) {
+ case ROCKET_MODE_RS485:
+ sSetInterfaceMode(cp, InterfaceModeRS485);
+ break;
+ case ROCKET_MODE_RS422:
+ sSetInterfaceMode(cp, InterfaceModeRS422);
+ break;
+ case ROCKET_MODE_RS232:
+ default:
+ if (info->flags & ROCKET_RTS_TOGGLE)
+ sSetInterfaceMode(cp, InterfaceModeRS232T);
+ else
+ sSetInterfaceMode(cp, InterfaceModeRS232);
+ break;
+ }
+ }
+}
+
+/* info->count is considered critical, protected by spinlocks. */
+static int block_til_ready(struct tty_struct *tty, struct file *filp,
+ struct r_port *info)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int retval;
+ int do_clocal = 0, extra_count = 0;
+ unsigned long flags;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (tty_hung_up_p(filp))
+ return ((info->flags & ROCKET_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS);
+ if (info->flags & ROCKET_CLOSING) {
+ interruptible_sleep_on(&info->close_wait);
+ return ((info->flags & ROCKET_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS);
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) {
+ info->flags |= ROCKET_NORMAL_ACTIVE;
+ return 0;
+ }
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+
+ /*
+ * Block waiting for the carrier detect and the line to become free. While we are in
+ * this loop, info->count is dropped by one, so that rp_close() knows when to free things.
+ * We restore it upon exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef ROCKET_DEBUG_OPEN
+ printk(KERN_INFO "block_til_ready before block: ttyR%d, count = %d\n", info->line, info->count);
+#endif
+ spin_lock_irqsave(&info->slock, flags);
+
+#ifdef ROCKET_DISABLE_SIMUSAGE
+ info->flags |= ROCKET_NORMAL_ACTIVE;
+#else
+ if (!tty_hung_up_p(filp)) {
+ extra_count = 1;
+ info->count--;
+ }
+#endif
+ info->blocked_open++;
+
+ spin_unlock_irqrestore(&info->slock, flags);
+
+ while (1) {
+ if (tty->termios->c_cflag & CBAUD) {
+ sSetDTR(&info->channel);
+ sSetRTS(&info->channel);
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (tty_hung_up_p(filp) || !(info->flags & ROCKET_INITIALIZED)) {
+ if (info->flags & ROCKET_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+ break;
+ }
+ if (!(info->flags & ROCKET_CLOSING) && (do_clocal || (sGetChanStatusLo(&info->channel) & CD_ACT)))
+ break;
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef ROCKET_DEBUG_OPEN
+ printk(KERN_INFO "block_til_ready blocking: ttyR%d, count = %d, flags=0x%0x\n",
+ info->line, info->count, info->flags);
+#endif
+ schedule(); /* Don't hold spinlock here, will hang PC */
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+
+ spin_lock_irqsave(&info->slock, flags);
+
+ if (extra_count)
+ info->count++;
+ info->blocked_open--;
+
+ spin_unlock_irqrestore(&info->slock, flags);
+
+#ifdef ROCKET_DEBUG_OPEN
+ printk(KERN_INFO "block_til_ready after blocking: ttyR%d, count = %d\n",
+ info->line, info->count);
+#endif
+ if (retval)
+ return retval;
+ info->flags |= ROCKET_NORMAL_ACTIVE;
+ return 0;
+}
+
+/*
+ * Exception handler that opens a serial port. Creates xmit_buf storage, fills in
+ * port's r_port struct. Initializes the port hardware.
+ */
+static int rp_open(struct tty_struct *tty, struct file *filp)
+{
+ struct r_port *info;
+ int line = 0, retval;
+ CHANNEL_t *cp;
+ unsigned long page;
+
+ line = TTY_GET_LINE(tty);
+ if ((line < 0) || (line >= MAX_RP_PORTS) || ((info = rp_table[line]) == NULL))
+ return -ENXIO;
+
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ if (info->flags & ROCKET_CLOSING) {
+ interruptible_sleep_on(&info->close_wait);
+ free_page(page);
+ return ((info->flags & ROCKET_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS);
+ }
+
+ /*
+ * We must not sleep from here until the port is marked fully in use.
+ */
+ if (info->xmit_buf)
+ free_page(page);
+ else
+ info->xmit_buf = (unsigned char *) page;
+
+ tty->driver_data = info;
+ info->tty = tty;
+
+ if (info->count++ == 0) {
+ atomic_inc(&rp_num_ports_open);
+
+#ifdef ROCKET_DEBUG_OPEN
+ printk(KERN_INFO "rocket mod++ = %d...", atomic_read(&rp_num_ports_open));
+#endif
+ }
+#ifdef ROCKET_DEBUG_OPEN
+ printk(KERN_INFO "rp_open ttyR%d, count=%d\n", info->line, info->count);
+#endif
+
+ /*
+ * Info->count is now 1; so it's safe to sleep now.
+ */
+ info->session = current->signal->session;
+ info->pgrp = process_group(current);
+
+ if ((info->flags & ROCKET_INITIALIZED) == 0) {
+ cp = &info->channel;
+ sSetRxTrigger(cp, TRIG_1);
+ if (sGetChanStatus(cp) & CD_ACT)
+ info->cd_status = 1;
+ else
+ info->cd_status = 0;
+ sDisRxStatusMode(cp);
+ sFlushRxFIFO(cp);
+ sFlushTxFIFO(cp);
+
+ sEnInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN));
+ sSetRxTrigger(cp, TRIG_1);
+
+ sGetChanStatus(cp);
+ sDisRxStatusMode(cp);
+ sClrTxXOFF(cp);
+
+ sDisCTSFlowCtl(cp);
+ sDisTxSoftFlowCtl(cp);
+
+ sEnRxFIFO(cp);
+ sEnTransmit(cp);
+
+ info->flags |= ROCKET_INITIALIZED;
+
+ /*
+ * Set up the tty->alt_speed kludge
+ */
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
+ info->tty->alt_speed = 460800;
+
+ configure_r_port(info, NULL);
+ if (tty->termios->c_cflag & CBAUD) {
+ sSetDTR(cp);
+ sSetRTS(cp);
+ }
+ }
+ /* Starts (or resets) the maint polling loop */
+ mod_timer(&rocket_timer, jiffies + POLL_PERIOD);
+
+ retval = block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef ROCKET_DEBUG_OPEN
+ printk(KERN_INFO "rp_open returning after block_til_ready with %d\n", retval);
+#endif
+ return retval;
+ }
+ return 0;
+}
+
+/*
+ * Exception handler that closes a serial port. info->count is considered critical.
+ */
+static void rp_close(struct tty_struct *tty, struct file *filp)
+{
+ struct r_port *info = (struct r_port *) tty->driver_data;
+ unsigned long flags;
+ int timeout;
+ CHANNEL_t *cp;
+
+ if (rocket_paranoia_check(info, "rp_close"))
+ return;
+
+#ifdef ROCKET_DEBUG_OPEN
+ printk(KERN_INFO "rp_close ttyR%d, count = %d\n", info->line, info->count);
+#endif
+
+ if (tty_hung_up_p(filp))
+ return;
+ spin_lock_irqsave(&info->slock, flags);
+
+ if ((tty->count == 1) && (info->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. Info->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk(KERN_INFO "rp_close: bad serial port count; tty->count is 1, "
+ "info->count is %d\n", info->count);
+ info->count = 1;
+ }
+ if (--info->count < 0) {
+ printk(KERN_INFO "rp_close: bad serial port count for ttyR%d: %d\n",
+ info->line, info->count);
+ info->count = 0;
+ }
+ if (info->count) {
+ spin_unlock_irqrestore(&info->slock, flags);
+ return;
+ }
+ info->flags |= ROCKET_CLOSING;
+ spin_unlock_irqrestore(&info->slock, flags);
+
+ cp = &info->channel;
+
+ /*
+ * Notify the line discpline to only process XON/XOFF characters
+ */
+ tty->closing = 1;
+
+ /*
+ * If transmission was throttled by the application request,
+ * just flush the xmit buffer.
+ */
+ if (tty->flow_stopped)
+ rp_flush_buffer(tty);
+
+ /*
+ * Wait for the transmit buffer to clear
+ */
+ if (info->closing_wait != ROCKET_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, info->closing_wait);
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ timeout = (sGetTxCnt(cp) + 1) * HZ / info->cps;
+ if (timeout == 0)
+ timeout = 1;
+ rp_wait_until_sent(tty, timeout);
+ clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+
+ sDisTransmit(cp);
+ sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN));
+ sDisCTSFlowCtl(cp);
+ sDisTxSoftFlowCtl(cp);
+ sClrTxXOFF(cp);
+ sFlushRxFIFO(cp);
+ sFlushTxFIFO(cp);
+ sClrRTS(cp);
+ if (C_HUPCL(tty))
+ sClrDTR(cp);
+
+ if (TTY_DRIVER_FLUSH_BUFFER_EXISTS(tty))
+ TTY_DRIVER_FLUSH_BUFFER(tty);
+
+ tty_ldisc_flush(tty);
+
+ clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+
+ if (info->blocked_open) {
+ if (info->close_delay) {
+ msleep_interruptible(jiffies_to_msecs(info->close_delay));
+ }
+ wake_up_interruptible(&info->open_wait);
+ } else {
+ if (info->xmit_buf) {
+ free_page((unsigned long) info->xmit_buf);
<