aboutsummaryrefslogtreecommitdiff
path: root/drivers/tty/rocket.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2011-02-22 16:14:56 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2011-02-22 16:14:56 -0800
commita6afd9f3e819de4795fcd356e5bfad446e4323f2 (patch)
tree3402b3981867fd4057b4eb33583b6300ae93b9a6 /drivers/tty/rocket.c
parent44ed76b78e158d852f640d533b7acc08b91f2132 (diff)
tty: move a number of tty drivers from drivers/char/ to drivers/tty/
As planned by Arnd Bergmann, this moves the following drivers from drivers/char/ to drivers/tty/ as that's where they really belong: amiserial nozomi synclink rocket cyclades moxa mxser isicom bfin_jtag_comm Cc: Arnd Bergmann <arnd@arndb.de> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: Jiri Slaby <jslaby@suse.cz> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/tty/rocket.c')
-rw-r--r--drivers/tty/rocket.c3199
1 files changed, 3199 insertions, 0 deletions
diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c
new file mode 100644
index 00000000000..3780da8ad12
--- /dev/null
+++ b/drivers/tty/rocket.c
@@ -0,0 +1,3199 @@
+/*
+ * 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 ******/
+#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 ******/
+
+#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/serial.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/mutex.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/wait.h>
+#include <linux/pci.h>
+#include <linux/uaccess.h>
+#include <asm/atomic.h>
+#include <asm/unaligned.h>
+#include <linux/bitops.h>
+#include <linux/spinlock.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 void rp_do_poll(unsigned long dummy);
+
+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 DEFINE_TIMER(rocket_timer, rp_do_poll, 0, 0);
+
+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;
+static const struct tty_port_operations rocket_port_ops;
+
+/*
+ * 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
+};
+
+static Byte_t RData[RDATASIZE] = {
+ 0x00, 0x09, 0xf6, 0x82,
+ 0x02, 0x09, 0x86, 0xfb,
+ 0x04, 0x09, 0x00, 0x0a,
+ 0x06, 0x09, 0x01, 0x0a,
+ 0x08, 0x09, 0x8a, 0x13,
+ 0x0a, 0x09, 0xc5, 0x11,
+ 0x0c, 0x09, 0x86, 0x85,
+ 0x0e, 0x09, 0x20, 0x0a,
+ 0x10, 0x09, 0x21, 0x0a,
+ 0x12, 0x09, 0x41, 0xff,
+ 0x14, 0x09, 0x82, 0x00,
+ 0x16, 0x09, 0x82, 0x7b,
+ 0x18, 0x09, 0x8a, 0x7d,
+ 0x1a, 0x09, 0x88, 0x81,
+ 0x1c, 0x09, 0x86, 0x7a,
+ 0x1e, 0x09, 0x84, 0x81,
+ 0x20, 0x09, 0x82, 0x7c,
+ 0x22, 0x09, 0x0a, 0x0a
+};
+
+static Byte_t RRegData[RREGDATASIZE] = {
+ 0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */
+ 0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */
+ 0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */
+ 0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */
+ 0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */
+ 0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */
+ 0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */
+ 0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */
+ 0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */
+ 0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */
+ 0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */
+ 0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */
+ 0x22, 0x09, 0x0a, 0x0a /* 30: Rx FIFO Enable */
+};
+
+static CONTROLLER_T sController[CTL_SIZE] = {
+ {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0},
+ {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}},
+ {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0},
+ {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}},
+ {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0},
+ {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}},
+ {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0},
+ {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}
+};
+
+static Byte_t sBitMapClrTbl[8] = {
+ 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f
+};
+
+static Byte_t sBitMapSetTbl[8] = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
+};
+
+static int sClockPrescale = 0x14;
+
+/*
+ * 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);
+static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum,
+ int ChanNum);
+static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode);
+static void sFlushRxFIFO(CHANNEL_T * ChP);
+static void sFlushTxFIFO(CHANNEL_T * ChP);
+static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags);
+static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags);
+static void sModemReset(CONTROLLER_T * CtlP, int chan, int on);
+static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on);
+static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data);
+static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum,
+ ByteIO_t * AiopIOList, int AiopIOListSize,
+ WordIO_t ConfigIO, int IRQNum, Byte_t Frequency,
+ int PeriodicOnly, int altChanRingIndicator,
+ int UPCIRingInd);
+static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO,
+ ByteIO_t * AiopIOList, int AiopIOListSize,
+ int IRQNum, Byte_t Frequency, int PeriodicOnly);
+static int sReadAiopID(ByteIO_t io);
+static int sReadAiopNumChan(WordIO_t io);
+
+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,...");
+
+static int rp_init(void);
+static void rp_cleanup_module(void);
+
+module_init(rp_init);
+module_exit(rp_cleanup_module);
+
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*************************************************************************/
+/* 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_WARNING "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;
+ unsigned char *cbuf;
+
+ ToRecv = sGetRxCnt(cp);
+#ifdef ROCKET_DEBUG_INTR
+ printk(KERN_INFO "rp_do_receive(%d)...\n", ToRecv);
+#endif
+ 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...\n");
+#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...\n",
+ info->ignore_status_mask, info->read_status_mask);
+#endif
+ while (ToRecv) {
+ char flag;
+
+ CharNStat = sInW(sGetTxRxDataIO(cp));
+#ifdef ROCKET_DEBUG_RECEIVE
+ printk(KERN_INFO "%x...\n", CharNStat);
+#endif
+ if (CharNStat & STMBREAKH)
+ CharNStat &= ~(STMFRAMEH | STMPARITYH);
+ if (CharNStat & info->ignore_status_mask) {
+ ToRecv--;
+ continue;
+ }
+ CharNStat &= info->read_status_mask;
+ if (CharNStat & STMBREAKH)
+ flag = TTY_BREAK;
+ else if (CharNStat & STMPARITYH)
+ flag = TTY_PARITY;
+ else if (CharNStat & STMFRAMEH)
+ flag = TTY_FRAME;
+ else if (CharNStat & STMRCVROVRH)
+ flag = TTY_OVERRUN;
+ else
+ flag = TTY_NORMAL;
+ tty_insert_flip_char(tty, CharNStat & 0xff, flag);
+ 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.
+ */
+ space = tty_prepare_flip_string(tty, &cbuf, ToRecv);
+ if (space < ToRecv) {
+#ifdef ROCKET_DEBUG_RECEIVE
+ printk(KERN_INFO "rp_do_receive:insufficient space ToRecv=%d space=%d\n", ToRecv, space);
+#endif
+ if (space <= 0)
+ return;
+ ToRecv = space;
+ }
+ wRecv = ToRecv >> 1;
+ if (wRecv)
+ sInStrW(sGetTxRxDataIO(cp), (unsigned short *) cbuf, wRecv);
+ if (ToRecv & 1)
+ cbuf[ToRecv - 1] = sInB(sGetTxRxDataIO(cp));
+ }
+ /* Push the data up to the tty layer */
+ tty_flip_buffer_push(tty);
+}
+
+/*
+ * 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_DEBUG "%s\n", __func__);
+#endif
+ if (!info)
+ return;
+ tty = tty_port_tty_get(&info->port);
+
+ if (tty == NULL) {
+ printk(KERN_WARNING "rp: WARNING %s called with tty==NULL\n", __func__);
+ clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+ return;
+ }
+
+ spin_lock_irqsave(&info->slock, flags);
+ 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, info->xmit_cnt);
+ c = min(c, 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...\n", 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);
+#ifdef ROCKETPORT_HAVE_POLL_WAIT
+ wake_up_interruptible(&tty->poll_wait);
+#endif
+ }
+
+ spin_unlock_irqrestore(&info->slock, flags);
+ tty_kref_put(tty);
+
+#ifdef ROCKET_DEBUG_INTR
+ printk(KERN_DEBUG "(%d,%d,%d,%d)...\n", 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->port.flags & ASYNC_INITIALIZED) == 0) {
+ printk(KERN_WARNING "rp: WARNING: rp_handle_port called with "
+ "info->flags & NOT_INIT\n");
+ return;
+ }
+ tty = tty_port_tty_get(&info->port);
+ if (!tty) {
+ printk(KERN_WARNING "rp: WARNING: rp_handle_port called with "
+ "tty==NULL\n");
+ return;
+ }
+ cp = &info->channel;
+
+ IntMask = sGetChanIntID(cp) & info->intmask;
+#ifdef ROCKET_DEBUG_INTR
+ printk(KERN_INFO "rp_interrupt %02x...\n", 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...\n", 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->port.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
+ tty_kref_put(tty);
+}
+
+/*
+ * 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;
+ unsigned int xmitmask, i;
+ 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 interrupt 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 = kzalloc(sizeof (struct r_port), GFP_KERNEL);
+ if (!info) {
+ printk(KERN_ERR "Couldn't allocate info struct for line #%d\n",
+ line);
+ return;
+ }
+
+ info->magic = RPORT_MAGIC;
+ info->line = line;
+ info->ctlp = ctlp;
+ info->board = board;
+ info->aiop = aiop;
+ info->chan = chan;
+ tty_port_init(&info->port);
+ info->port.ops = &rocket_port_ops;
+ init_completion(&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_ERR "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);
+ mutex_init(&info->write_mtx);
+ rp_table[line] = info;
+ tty_register_device(rocket_driver, line, pci_dev ? &pci_dev->dev :
+ NULL);
+}
+
+/*
+ * 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 tty_struct *tty, struct r_port *info,
+ struct ktermios *old_termios)
+{
+ unsigned cflag;
+ unsigned long flags;
+ unsigned rocketMode;
+ int bits, baud, divisor;
+ CHANNEL_t *cp;
+ struct ktermios *t = tty->termios;
+
+ cp = &info->channel;
+ cflag = t->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(tty);
+ if (!baud)
+ baud = 9600;
+ divisor = ((rp_baud_base[info->board] + (baud >> 1)) / baud) - 1;
+ if ((divisor >= 8192 || divisor < 0) && old_termios) {
+ baud = tty_termios_baud_rate(old_termios);
+ 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);
+
+ /* FIXME: Should really back compute a baud rate from the divisor */
+ tty_encode_baud_rate(tty, baud, baud);
+
+ 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(tty)) {
+ sEnTxSoftFlowCtl(cp);
+ if (I_IXANY(tty)) {
+ sEnIXANY(cp);
+ } else {
+ sDisIXANY(cp);
+ }
+ sSetTxXONChar(cp, START_CHAR(tty));
+ sSetTxXOFFChar(cp, STOP_CHAR(tty));
+ } else {
+ sDisTxSoftFlowCtl(cp);
+ sDisIXANY(cp);
+ sClrTxXOFF(cp);
+ }
+#endif
+
+ /*
+ * Set up ignore/read mask words
+ */
+ info->read_status_mask = STMRCVROVRH | 0xFF;
+ if (I_INPCK(tty))
+ info->read_status_mask |= STMFRAMEH | STMPARITYH;
+ if (I_BRKINT(tty) || I_PARMRK(tty))
+ info->read_status_mask |= STMBREAKH;
+
+ /*
+ * Characters to ignore
+ */
+ info->ignore_status_mask = 0;
+ if (I_IGNPAR(tty))
+ info->ignore_status_mask |= STMFRAMEH | STMPARITYH;
+ if (I_IGNBRK(tty)) {
+ info->ignore_status_mask |= STMBREAKH;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too. (For real raw support).
+ */
+ if (I_IGNPAR(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;
+ }
+ }
+}
+
+static int carrier_raised(struct tty_port *port)
+{
+ struct r_port *info = container_of(port, struct r_port, port);
+ return (sGetChanStatusLo(&info->channel) & CD_ACT) ? 1 : 0;
+}
+
+static void dtr_rts(struct tty_port *port, int on)
+{
+ struct r_port *info = container_of(port, struct r_port, port);
+ if (on) {
+ sSetDTR(&info->channel);
+ sSetRTS(&info->channel);
+ } else {
+ sClrDTR(&info->channel);
+ sClrRTS(&info->channel);
+ }
+}
+
+/*
+ * 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;
+ struct tty_port *port;
+ int line = 0, retval;
+ CHANNEL_t *cp;
+ unsigned long page;
+
+ line = tty->index;
+ if (line < 0 || line >= MAX_RP_PORTS || ((info = rp_table[line]) == NULL))
+ return -ENXIO;
+ port = &info->port;
+
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ if (port->flags & ASYNC_CLOSING) {
+ retval = wait_for_completion_interruptible(&info->close_wait);
+ free_page(page);
+ if (retval)
+ return retval;
+ return ((port->flags & ASYNC_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;
+ tty_port_tty_set(port, tty);
+
+ if (port->count++ == 0) {
+ atomic_inc(&rp_num_ports_open);
+
+#ifdef ROCKET_DEBUG_OPEN
+ printk(KERN_INFO "rocket mod++ = %d...\n",
+ atomic_read(&rp_num_ports_open));
+#endif
+ }
+#ifdef ROCKET_DEBUG_OPEN
+ printk(KERN_INFO "rp_open ttyR%d, count=%d\n", info->line, info->port.count);
+#endif
+
+ /*
+ * Info->count is now 1; so it's safe to sleep now.
+ */
+ if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) {
+ 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);
+
+ set_bit(ASYNCB_INITIALIZED, &info->port.flags);
+
+ /*
+ * Set up the tty->alt_speed kludge
+ */
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
+ tty->alt_speed = 57600;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
+ tty->alt_speed = 115200;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
+ tty->alt_speed = 230400;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
+ tty->alt_speed = 460800;
+
+ configure_r_port(tty, 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 = tty_port_block_til_ready(port, tty, filp);
+ 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->port.count is considered critical.
+ */
+static void rp_close(struct tty_struct *tty, struct file *filp)
+{
+ struct r_port *info = tty->driver_data;
+ struct tty_port *port = &info->port;
+ 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->port.count);
+#endif
+
+ if (tty_port_close_start(port, tty, filp) == 0)
+ return;
+
+ mutex_lock(&port->mutex);
+ cp = &info->channel;
+ /*
+ * 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);
+
+ rp_flush_buffer(tty);
+
+ tty_ldisc_flush(tty);
+
+ clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+
+ /* We can't yet use tty_port_close_end as the buffer handling in this
+ driver is a bit different to the usual */
+
+ if (port->blocked_open) {
+ if (port->close_delay) {
+ msleep_interruptible(jiffies_to_msecs(port->close_delay));
+ }
+ wake_up_interruptible(&port->open_wait);
+ } else {
+ if (info->xmit_buf) {
+ free_page((unsigned long) info->xmit_buf);
+ info->xmit_buf = NULL;
+ }
+ }
+ spin_lock_irq(&port->lock);
+ info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING | ASYNC_NORMAL_ACTIVE);
+ tty->closing = 0;
+ spin_unlock_irq(&port->lock);
+ mutex_unlock(&port->mutex);
+ tty_port_tty_set(port, NULL);
+
+ wake_up_interruptible(&port->close_wait);
+ complete_all(&info->close_wait);
+ atomic_dec(&rp_num_ports_open);
+
+#ifdef ROCKET_DEBUG_OPEN
+ printk(KERN_INFO "rocket mod-- = %d...\n",
+ atomic_read(&rp_num_ports_open));
+ printk(KERN_INFO "rp_close ttyR%d complete shutdown\n", info->line);
+#endif
+
+}
+
+static void rp_set_termios(struct tty_struct *tty,
+ struct ktermios *old_termios)
+{
+ struct r_port *info = tty->driver_data;
+ CHANNEL_t *cp;
+ unsigned cflag;
+
+ if (rocket_paranoia_check(info, "rp_set_termios"))
+ return;
+
+ cflag = tty->termios->c_cflag;
+
+ /*
+ * This driver doesn't support CS5 or CS6
+ */
+ if (((cflag & CSIZE) == CS5) || ((cflag & CSIZE) == CS6))
+ tty->termios->c_cflag =
+ ((cflag & ~CSIZE) | (old_termios->c_cflag & CSIZE));
+ /* Or CMSPAR */
+ tty->termios->c_cflag &= ~CMSPAR;
+
+ configure_r_port(tty, info, old_termios);
+
+ cp = &info->channel;
+
+ /* Handle transition to B0 status */