diff options
Diffstat (limited to 'drivers/tty/rocket.c')
-rw-r--r-- | drivers/tty/rocket.c | 3127 |
1 files changed, 0 insertions, 3127 deletions
diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c deleted file mode 100644 index 2540b2e4c8e8..000000000000 --- a/drivers/tty/rocket.c +++ /dev/null @@ -1,3127 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) -/* - * 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. - */ - -/* - * 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 <linux/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(struct timer_list *unused); - -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); - -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 bool 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 -}; - -#ifdef CONFIG_PCI -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 -}; -#endif - -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 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 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_hw(board1, ulong, ioport, 0); -MODULE_PARM_DESC(board1, "I/O port for (ISA) board #1"); -module_param_hw(board2, ulong, ioport, 0); -MODULE_PARM_DESC(board2, "I/O port for (ISA) board #2"); -module_param_hw(board3, ulong, ioport, 0); -MODULE_PARM_DESC(board3, "I/O port for (ISA) board #3"); -module_param_hw(board4, ulong, ioport, 0); -MODULE_PARM_DESC(board4, "I/O port for (ISA) board #4"); -module_param_hw(controller, ulong, ioport, 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 __init 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, 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(&info->port, 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(&info->port, &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(&info->port); -} - -/* - * 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) - 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; - unsigned int IntMask, ChanStatus; - - if (!info) - return; - - if (!tty_port_initialized(&info->port)) { - printk(KERN_WARNING "rp: WARNING: rp_handle_port called with " - "info->flags & NOT_INIT\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, 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_port_tty_hangup(&info->port, false); - } - 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 -} - -/* - * The top level polling routine. Repeats every 1/100 HZ (10ms). - */ -static void rp_do_poll(struct timer_list *unused) -{ - 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 -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; - info->flags &= ~ROCKET_MODE_MASK; - if (board < ARRAY_SIZE(pc104) && line < ARRAY_SIZE(pc104_1)) - 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; - } - else - info->flags |= ROCKET_MODE_RS232; - - 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); - tty_port_destroy(&info->port); - 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_port_register_device(&info->port, 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 retval; - CHANNEL_t *cp; - unsigned long page; - - info = rp_table[tty->index]; - if (info == NULL) - return -ENXIO; - port = &info->port; - - page = __get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - /* - * 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 (!tty_port_initialized(port)) { - 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); - - tty_port_set_initialized(&info->port, 1); - - configure_r_port(tty, info, NULL); - if (C_BAUD(tty)) { - 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); - tty->closing = 0; - spin_unlock_irq(&port->lock); - tty_port_set_initialized(port, 0); - tty_port_set_active(port, 0); - mutex_unlock(&port->mutex); - tty_port_tty_set(port, NULL); - - 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 */ - if ((old_termios->c_cflag & CBAUD) && !C_BAUD(tty)) { - sClrDTR(cp); - sClrRTS(cp); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && C_BAUD(tty)) { - sSetRTS(cp); - sSetDTR(cp); - } - - if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) - rp_start(tty); -} - -static int rp_break(struct tty_struct *tty, int break_state) -{ - struct r_port *info = tty->driver_data; - unsigned long flags; - - if (rocket_paranoia_check(info, "rp_break")) - return -EINVAL; - - spin_lock_irqsave(&info->slock, flags); - if (break_state == -1) - sSendBreak(&info->channel); - else - sClrBreak(&info->channel); - spin_unlock_irqrestore(&info->slock, flags); - return 0; -} - -/* - * sGetChanRI used to be a macro in rocket_int.h. When the functionality for - * the UPCI boards was added, it was decided to make this a function because - * the macro was getting too complicated. All cases except the first one - * (UPCIRingInd) are taken directly from the original macro. - */ -static int sGetChanRI(CHANNEL_T * ChP) -{ - CONTROLLER_t *CtlP = ChP->CtlP; - int ChanNum = ChP->ChanNum; - int RingInd = 0; - - if (CtlP->UPCIRingInd) - RingInd = !(sInB(CtlP->UPCIRingInd) & sBitMapSetTbl[ChanNum]); - else if (CtlP->AltChanRingIndicator) - RingInd = sInB((ByteIO_t) (ChP->ChanStat + 8)) & DSR_ACT; - else if (CtlP->boardType == ROCKET_TYPE_PC104) - RingInd = !(sInB(CtlP->AiopIO[3]) & sBitMapSetTbl[ChanNum]); - - return RingInd; -} - -/********************************************************************************************/ -/* Here are the routines used by rp_ioctl. These are all called from exception handlers. */ - -/* - * Returns the state of the serial modem control lines. These next 2 functions - * are the way kernel versions > 2.5 handle modem control lines rather than IOCTLs. - */ -static int rp_tiocmget(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - unsigned int control, result, ChanStatus; - - ChanStatus = sGetChanStatusLo(&info->channel); - control = info->channel.TxControl[3]; - result = ((control & SET_RTS) ? TIOCM_RTS : 0) | - ((control & SET_DTR) ? TIOCM_DTR : 0) | - ((ChanStatus & CD_ACT) ? TIOCM_CAR : 0) | - (sGetChanRI(&info->channel) ? TIOCM_RNG : 0) | - ((ChanStatus & DSR_ACT) ? TIOCM_DSR : 0) | - ((ChanStatus & CTS_ACT) ? TIOCM_CTS : 0); - - return result; -} - -/* - * Sets the modem control lines - */ -static int rp_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct r_port *info = tty->driver_data; - - if (set & TIOCM_RTS) - info->channel.TxControl[3] |= SET_RTS; - if (set & TIOCM_DTR) - info->channel.TxControl[3] |= SET_DTR; - if (clear & TIOCM_RTS) - info->channel.TxControl[3] &= ~SET_RTS; - if (clear & TIOCM_DTR) - info->channel.TxControl[3] &= ~SET_DTR; - - out32(info->channel.IndexAddr, info->channel.TxControl); - return 0; -} - -static int get_config(struct r_port *info, struct rocket_config __user *retinfo) -{ - struct rocket_config tmp; - - memset(&tmp, 0, sizeof (tmp)); - mutex_lock(&info->port.mutex); - tmp.line = info->line; - tmp.flags = info->flags; - tmp.close_delay = info->port.close_delay; - tmp.closing_wait = info->port.closing_wait; - tmp.port = rcktpt_io_addr[(info->line >> 5) & 3]; - mutex_unlock(&info->port.mutex); - - if (copy_to_user(retinfo, &tmp, sizeof (*retinfo))) - return -EFAULT; - return 0; -} - -static int set_config(struct tty_struct *tty, struct r_port *info, - struct rocket_config __user *new_info) -{ - struct rocket_config new_serial; - - if (copy_from_user(&new_serial, new_info, sizeof (new_serial))) - return -EFAULT; - - mutex_lock(&info->port.mutex); - if (!capable(CAP_SYS_ADMIN)) - { - if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK)) { - mutex_unlock(&info->port.mutex); - return -EPERM; - } - info->flags = ((info->flags & ~ROCKET_USR_MASK) | (new_serial.flags & ROCKET_USR_MASK)); - mutex_unlock(&info->port.mutex); - return 0; - } - - if ((new_serial.flags ^ info->flags) & ROCKET_SPD_MASK) { - /* warn about deprecation, unless clearing */ - if (new_serial.flags & ROCKET_SPD_MASK) - dev_warn_ratelimited(tty->dev, "use of SPD flags is deprecated\n"); - } - - info->flags = ((info->flags & ~ROCKET_FLAGS) | (new_serial.flags & ROCKET_FLAGS)); - info->port.close_delay = new_serial.close_delay; - info->port.closing_wait = new_serial.closing_wait; - - mutex_unlock(&info->port.mutex); - - configure_r_port(tty, info, NULL); - return 0; -} - -/* - * This function fills in a rocket_ports struct with information - * about what boards/ports are in the system. This info is passed - * to user space. See setrocket.c where the info is used to create - * the /dev/ttyRx ports. - */ -static int get_ports(struct r_port *info, struct rocket_ports __user *retports) -{ - struct rocket_ports *tmp; - int board, ret = 0; - - tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); - if (!tmp) - return -ENOMEM; - - tmp->tty_major = rocket_driver->major; - - for (board = 0; board < 4; board++) { - tmp->rocketModel[board].model = rocketModel[board].model; - strcpy(tmp->rocketModel[board].modelString, - rocketModel[board].modelString); - tmp->rocketModel[board].numPorts = rocketModel[board].numPorts; - tmp->rocketModel[board].loadrm2 = rocketModel[board].loadrm2; - tmp->rocketModel[board].startingPortNumber = - rocketModel[board].startingPortNumber; - } - if (copy_to_user(retports, tmp, sizeof(*retports))) - ret = -EFAULT; - kfree(tmp); - return ret; -} - -static int reset_rm2(struct r_port *info, void __user *arg) -{ - int reset; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (copy_from_user(&reset, arg, sizeof (int))) - return -EFAULT; - if (reset) - reset = 1; - - if (rcktpt_type[info->board] != ROCKET_TYPE_MODEMII && - rcktpt_type[info->board] != ROCKET_TYPE_MODEMIII) - return -EINVAL; - - if (info->ctlp->BusType == isISA) - sModemReset(info->ctlp, info->chan, reset); - else - sPCIModemReset(info->ctlp, info->chan, reset); - - return 0; -} - -static int get_version(struct r_port *info, struct rocket_version __user *retvers) -{ - if (copy_to_user(retvers, &driver_version, sizeof (*retvers))) - return -EFAULT; - return 0; -} - -/* IOCTL call handler into the driver */ -static int rp_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct r_port *info = tty->driver_data; - void __user *argp = (void __user *)arg; - int ret = 0; - - if (cmd != RCKP_GET_PORTS && rocket_paranoia_check(info, "rp_ioctl")) - return -ENXIO; - - switch (cmd) { - case RCKP_GET_CONFIG: - dev_warn_ratelimited(tty->dev, - "RCKP_GET_CONFIG option is deprecated\n"); - ret = get_config(info, argp); - break; - case RCKP_SET_CONFIG: - dev_warn_ratelimited(tty->dev, - "RCKP_SET_CONFIG option is deprecated\n"); - ret = set_config(tty, info, argp); - break; - case RCKP_GET_PORTS: - dev_warn_ratelimited(tty->dev, - "RCKP_GET_PORTS option is deprecated\n"); - ret = get_ports(info, argp); - break; - case RCKP_RESET_RM2: - dev_warn_ratelimited(tty->dev, - "RCKP_RESET_RM2 option is deprecated\n"); - ret = reset_rm2(info, argp); - break; - case RCKP_GET_VERSION: - dev_warn_ratelimited(tty->dev, - "RCKP_GET_VERSION option is deprecated\n"); - ret = get_version(info, argp); - break; - default: - ret = -ENOIOCTLCMD; - } - return ret; -} - -static void rp_send_xchar(struct tty_struct *tty, char ch) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - - if (rocket_paranoia_check(info, "rp_send_xchar")) - return; - - cp = &info->channel; - if (sGetTxCnt(cp)) - sWriteTxPrioByte(cp, ch); - else - sWriteTxByte(sGetTxRxDataIO(cp), ch); -} - -static void rp_throttle(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - -#ifdef ROCKET_DEBUG_THROTTLE - printk(KERN_INFO "throttle %s ....\n", tty->name); -#endif - - if (rocket_paranoia_check(info, "rp_throttle")) - return; - - if (I_IXOFF(tty)) - rp_send_xchar(tty, STOP_CHAR(tty)); - - sClrRTS(&info->channel); -} - -static void rp_unthrottle(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; -#ifdef ROCKET_DEBUG_THROTTLE - printk(KERN_INFO "unthrottle %s ....\n", tty->name); -#endif - - if (rocket_paranoia_check(info, "rp_unthrottle")) - return; - - if (I_IXOFF(tty)) - rp_send_xchar(tty, START_CHAR(tty)); - - sSetRTS(&info->channel); -} - -/* - * ------------------------------------------------------------ - * rp_stop() and rp_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. - * ------------------------------------------------------------ - */ -static void rp_stop(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - -#ifdef ROCKET_DEBUG_FLOW - printk(KERN_INFO "stop %s: %d %d....\n", tty->name, - info->xmit_cnt, info->xmit_fifo_room); -#endif - - if (rocket_paranoia_check(info, "rp_stop")) - return; - - if (sGetTxCnt(&info->channel)) - sDisTransmit(&info->channel); -} - -static void rp_start(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - -#ifdef ROCKET_DEBUG_FLOW - printk(KERN_INFO "start %s: %d %d....\n", tty->name, - info->xmit_cnt, info->xmit_fifo_room); -#endif - - if (rocket_paranoia_check(info, "rp_stop")) - return; - - sEnTransmit(&info->channel); - set_bit((info->aiop * 8) + info->chan, - (void *) &xmit_flags[info->board]); -} - -/* - * rp_wait_until_sent() --- wait until the transmitter is empty - */ -static void rp_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - unsigned long orig_jiffies; - int check_time, exit_time; - int txcnt; - - if (rocket_paranoia_check(info, "rp_wait_until_sent")) - return; - - cp = &info->channel; - - orig_jiffies = jiffies; -#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT - printk(KERN_INFO "In %s(%d) (jiff=%lu)...\n", __func__, timeout, - jiffies); - printk(KERN_INFO "cps=%d...\n", info->cps); -#endif - while (1) { - txcnt = sGetTxCnt(cp); - if (!txcnt) { - if (sGetChanStatusLo(cp) & TXSHRMT) - break; - check_time = (HZ / info->cps) / 5; - } else { - check_time = HZ * txcnt / info->cps; - } - if (timeout) { - exit_time = orig_jiffies + timeout - jiffies; - if (exit_time <= 0) - break; - if (exit_time < check_time) - check_time = exit_time; - } - if (check_time == 0) - check_time = 1; -#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT - printk(KERN_INFO "txcnt = %d (jiff=%lu,check=%d)...\n", txcnt, - jiffies, check_time); -#endif - msleep_interruptible(jiffies_to_msecs(check_time)); - if (signal_pending(current)) - break; - } - __set_current_state(TASK_RUNNING); -#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT - printk(KERN_INFO "txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies); -#endif -} - -/* - * rp_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -static void rp_hangup(struct tty_struct *tty) -{ - CHANNEL_t *cp; - struct r_port *info = tty->driver_data; - unsigned long flags; - - if (rocket_paranoia_check(info, "rp_hangup")) - return; - -#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_HANGUP)) - printk(KERN_INFO "rp_hangup of ttyR%d...\n", info->line); -#endif - rp_flush_buffer(tty); - spin_lock_irqsave(&info->port.lock, flags); - if (info->port.count) - atomic_dec(&rp_num_ports_open); - clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - spin_unlock_irqrestore(&info->port.lock, flags); - - tty_port_hangup(&info->port); - - cp = &info->channel; - sDisRxFIFO(cp); - sDisTransmit(cp); - sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); - sDisCTSFlowCtl(cp); - sDisTxSoftFlowCtl(cp); - sClrTxXOFF(cp); - tty_port_set_initialized(&info->port, 0); - - wake_up_interruptible(&info->port.open_wait); -} - -/* - * Exception handler - write char routine. The RocketPort driver uses a - * double-buffering strategy, with the twist that if the in-memory CPU - * buffer is empty, and there's space in the transmit FIFO, the - * writing routines will write directly to transmit FIFO. - * Write buffer and counters protected by spinlocks - */ -static int rp_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - unsigned long flags; - - if (rocket_paranoia_check(info, "rp_put_char")) - return 0; - - /* - * Grab the port write mutex, locking out other processes that try to - * write to this port - */ - mutex_lock(&info->write_mtx); - -#ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_put_char %c...\n", ch); -#endif - - spin_lock_irqsave(&info->slock, flags); - cp = &info->channel; - - if (!tty->stopped && info->xmit_fifo_room == 0) - info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); - - if (tty->stopped || info->xmit_fifo_room == 0 || info->xmit_cnt != 0) { - info->xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= XMIT_BUF_SIZE - 1; - info->xmit_cnt++; - set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - } else { - sOutB(sGetTxRxDataIO(cp), ch); - info->xmit_fifo_room--; - } - spin_unlock_irqrestore(&info->slock, flags); - mutex_unlock(&info->write_mtx); - return 1; -} - -/* - * Exception handler - write routine, called when user app writes to the device. - * A per port write mutex is used to protect from another process writing to - * this port at the same time. This other process could be running on the other CPU - * or get control of the CPU if the copy_from_user() blocks due to a page fault (swapped out). - * Spinlocks protect the info xmit members. - */ -static int rp_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - const unsigned char *b; - int c, retval = 0; - unsigned long flags; - - if (count <= 0 || rocket_paranoia_check(info, "rp_write")) - return 0; - - if (mutex_lock_interruptible(&info->write_mtx)) - return -ERESTARTSYS; - -#ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_write %d chars...\n", count); -#endif - cp = &info->channel; - - if (!tty->stopped && info->xmit_fifo_room < count) - info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); - - /* - * If the write queue for the port is empty, and there is FIFO space, stuff bytes - * into FIFO. Use the write queue for temp storage. - */ - if (!tty->stopped && info->xmit_cnt == 0 && info->xmit_fifo_room > 0) { - c = min(count, info->xmit_fifo_room); - b = buf; - - /* Push data into FIFO, 2 bytes at a time */ - sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) b, c / 2); - - /* If there is a byte remaining, write it */ - if (c & 1) - sOutB(sGetTxRxDataIO(cp), b[c - 1]); - - retval += c; - buf += c; - count -= c; - - spin_lock_irqsave(&info->slock, flags); - info->xmit_fifo_room -= c; - spin_unlock_irqrestore(&info->slock, flags); - } - - /* If count is zero, we wrote it all and are done */ - if (!count) - goto end; - - /* Write remaining data into the port's xmit_buf */ - while (1) { - /* Hung up ? */ - if (!tty_port_active(&info->port)) - goto end; - c = min(count, XMIT_BUF_SIZE - info->xmit_cnt - 1); - c = min(c, XMIT_BUF_SIZE - info->xmit_head); - if (c <= 0) - break; - - b = buf; - memcpy(info->xmit_buf + info->xmit_head, b, c); - - spin_lock_irqsave(&info->slock, flags); - info->xmit_head = - (info->xmit_head + c) & (XMIT_BUF_SIZE - 1); - info->xmit_cnt += c; - spin_unlock_irqrestore(&info->slock, flags); - - buf += c; - count -= c; - retval += c; - } - - if ((retval > 0) && !tty->stopped) - set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); - -end: - if (info->xmit_cnt < WAKEUP_CHARS) { - tty_wakeup(tty); -#ifdef ROCKETPORT_HAVE_POLL_WAIT - wake_up_interruptible(&tty->poll_wait); -#endif - } - mutex_unlock(&info->write_mtx); - return retval; -} - -/* - * Return the number of characters that can be sent. We estimate - * only using the in-memory transmit buffer only, and ignore the - * potential space in the transmit FIFO. - */ -static int rp_write_room(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - int ret; - - if (rocket_paranoia_check(info, "rp_write_room")) - return 0; - - ret = XMIT_BUF_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; -#ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_write_room returns %d...\n", ret); -#endif - return ret; -} - -/* - * Return the number of characters in the buffer. Again, this only - * counts those characters in the in-memory transmit buffer. - */ -static int rp_chars_in_buffer(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - - if (rocket_paranoia_check(info, "rp_chars_in_buffer")) - return 0; - -#ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_chars_in_buffer returns %d...\n", info->xmit_cnt); -#endif - return info->xmit_cnt; -} - -/* - * Flushes the TX fifo for a port, deletes data in the xmit_buf stored in the - * r_port struct for the port. Note that spinlock are used to protect info members, - * do not call this function if the spinlock is already held. - */ -static void rp_flush_buffer(struct tty_struct *tty) -{ - struct r_port *info = tty->driver_data; - CHANNEL_t *cp; - unsigned long flags; - - if (rocket_paranoia_check(info, "rp_flush_buffer")) - return; - - spin_lock_irqsave(&info->slock, flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - spin_unlock_irqrestore(&info->slock, flags); - -#ifdef ROCKETPORT_HAVE_POLL_WAIT - wake_up_interruptible(&tty->poll_wait); -#endif - tty_wakeup(tty); - - cp = &info->channel; - sFlushTxFIFO(cp); -} - -#ifdef CONFIG_PCI - -static const struct pci_device_id rocket_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP4QUAD) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8OCTA) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_URP8OCTA) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_URP8INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8J) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP4J) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8SNI) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP16SNI) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP16INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_URP16INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_CRP16INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP32INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_URP32INTF) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RPP4) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RPP8) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP2_232) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP2_422) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP6M) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP4M) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_UPCI_RM3_8PORT) }, - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_UPCI_RM3_4PORT) }, - { } -}; -MODULE_DEVICE_TABLE(pci, rocket_pci_ids); - -/* Resets the speaker controller on RocketModem II and III devices */ -static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model) -{ - ByteIO_t addr; - - /* RocketModem II speaker control is at the 8th port location of offset 0x40 */ - if ((model == MODEL_RP4M) || (model == MODEL_RP6M)) { - addr = CtlP->AiopIO[0] + 0x4F; - sOutB(addr, 0); - } - - /* RocketModem III speaker control is at the 1st port location of offset 0x80 */ - if ((model == MODEL_UPCI_RM3_8PORT) - || (model == MODEL_UPCI_RM3_4PORT)) { - addr = CtlP->AiopIO[0] + 0x88; - sOutB(addr, 0); - } -} - -/*************************************************************************** -Function: sPCIInitController -Purpose: Initialization of controller global registers and controller - structure. -Call: sPCIInitController(CtlP,CtlNum,AiopIOList,AiopIOListSize, - IRQNum,Frequency,PeriodicOnly) - CONTROLLER_T *CtlP; Ptr to controller structure - int CtlNum; Controller number - ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. - This list must be in the order the AIOPs will be found on the - controller. Once an AIOP in the list is not found, it is - assumed that there are no more AIOPs on the controller. - int AiopIOListSize; Number of addresses in AiopIOList - int IRQNum; Interrupt Request number. Can be any of the following: - 0: Disable global interrupts - 3: IRQ 3 - 4: IRQ 4 - 5: IRQ 5 - 9: IRQ 9 - 10: IRQ 10 - 11: IRQ 11 - 12: IRQ 12 - 15: IRQ 15 - Byte_t Frequency: A flag identifying the frequency - of the periodic interrupt, can be any one of the following: - FREQ_DIS - periodic interrupt disabled - FREQ_137HZ - 137 Hertz - FREQ_69HZ - 69 Hertz - FREQ_34HZ - 34 Hertz - FREQ_17HZ - 17 Hertz - FREQ_9HZ - 9 Hertz - FREQ_4HZ - 4 Hertz - If IRQNum is set to 0 the Frequency parameter is - overidden, it is forced to a value of FREQ_DIS. - int PeriodicOnly: 1 if all interrupts except the periodic - interrupt are to be blocked. - 0 is both the periodic interrupt and - other channel interrupts are allowed. - If IRQNum is set to 0 the PeriodicOnly parameter is - overidden, it is forced to a value of 0. -Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller - initialization failed. - -Comments: - If periodic interrupts are to be disabled but AIOP interrupts - are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. - - If interrupts are to be completely disabled set IRQNum to 0. - - Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an - invalid combination. - - This function performs initialization of global interrupt modes, - but it does not actually enable global interrupts. To enable - and disable global interrupts use functions sEnGlobalInt() and - sDisGlobalInt(). Enabling of global interrupts is normally not - done until all other initializations are complete. - - Even if interrupts are globally enabled, they must also be - individually enabled for each channel that is to generate - interrupts. - -Warnings: No range checking on any of the parameters is done. - - No context switches are allowed while executing this function. - - After this function all AIOPs on the controller are disabled, - they can be enabled with sEnAiop(). -*/ -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) -{ - int i; - ByteIO_t io; - - CtlP->AltChanRingIndicator = altChanRingIndicator; - CtlP->UPCIRingInd = UPCIRingInd; - CtlP->CtlNum = CtlNum; - CtlP->CtlID = CTLID_0001; /* controller release 1 */ - CtlP->BusType = isPCI; /* controller release 1 */ - - if (ConfigIO) { - CtlP->isUPCI = 1; - CtlP->PCIIO = ConfigIO + _PCI_9030_INT_CTRL; - CtlP->PCIIO2 = ConfigIO + _PCI_9030_GPIO_CTRL; - CtlP->AiopIntrBits = upci_aiop_intr_bits; - } else { - CtlP->isUPCI = 0; - CtlP->PCIIO = - (WordIO_t) ((ByteIO_t) AiopIOList[0] + _PCI_INT_FUNC); - CtlP->AiopIntrBits = aiop_intr_bits; - } - - sPCIControllerEOI(CtlP); /* clear EOI if warm init */ - /* Init AIOPs */ - CtlP->NumAiop = 0; - for (i = 0; i < AiopIOListSize; i++) { - io = AiopIOList[i]; - CtlP->AiopIO[i] = (WordIO_t) io; - CtlP->AiopIntChanIO[i] = io + _INT_CHAN; - - CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ - if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ - break; /* done looking for AIOPs */ - - CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ - sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ - sOutB(io + _INDX_DATA, sClockPrescale); - CtlP->NumAiop++; /* bump count of AIOPs */ - } - - if (CtlP->NumAiop == 0) - return (-1); - else - return (CtlP->NumAiop); -} - -/* - * Called when a PCI card is found. Retrieves and stores model information, - * init's aiopic and serial port hardware. - * Inputs: i is the board number (0-n) - */ -static __init int register_PCI(int i, struct pci_dev *dev) -{ - int num_aiops, aiop, max_num_aiops, chan; - unsigned int aiopio[MAX_AIOPS_PER_BOARD]; - CONTROLLER_t *ctlp; - - int fast_clock = 0; - int altChanRingIndicator = 0; - int ports_per_aiop = 8; - WordIO_t ConfigIO = 0; - ByteIO_t UPCIRingInd = 0; - - if (!dev || !pci_match_id(rocket_pci_ids, dev) || - pci_enable_device(dev) || i >= NUM_BOARDS) - return 0; - - rcktpt_io_addr[i] = pci_resource_start(dev, 0); - - rcktpt_type[i] = ROCKET_TYPE_NORMAL; - rocketModel[i].loadrm2 = 0; - rocketModel[i].startingPortNumber = nextLineNumber; - - /* Depending on the model, set up some config variables */ - switch (dev->device) { - case PCI_DEVICE_ID_RP4QUAD: - max_num_aiops = 1; - ports_per_aiop = 4; - rocketModel[i].model = MODEL_RP4QUAD; - strcpy(rocketModel[i].modelString, "RocketPort 4 port w/quad cable"); - rocketModel[i].numPorts = 4; - break; - case PCI_DEVICE_ID_RP8OCTA: - max_num_aiops = 1; - rocketModel[i].model = MODEL_RP8OCTA; - strcpy(rocketModel[i].modelString, "RocketPort 8 port w/octa cable"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_URP8OCTA: - max_num_aiops = 1; - rocketModel[i].model = MODEL_UPCI_RP8OCTA; - strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/octa cable"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP8INTF: - max_num_aiops = 1; - rocketModel[i].model = MODEL_RP8INTF; - strcpy(rocketModel[i].modelString, "RocketPort 8 port w/external I/F"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_URP8INTF: - max_num_aiops = 1; - rocketModel[i].model = MODEL_UPCI_RP8INTF; - strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/external I/F"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP8J: - max_num_aiops = 1; - rocketModel[i].model = MODEL_RP8J; - strcpy(rocketModel[i].modelString, "RocketPort 8 port w/RJ11 connectors"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP4J: - max_num_aiops = 1; - ports_per_aiop = 4; - rocketModel[i].model = MODEL_RP4J; - strcpy(rocketModel[i].modelString, "RocketPort 4 port w/RJ45 connectors"); - rocketModel[i].numPorts = 4; - break; - case PCI_DEVICE_ID_RP8SNI: - max_num_aiops = 1; - rocketModel[i].model = MODEL_RP8SNI; - strcpy(rocketModel[i].modelString, "RocketPort 8 port w/ custom DB78"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP16SNI: - max_num_aiops = 2; - rocketModel[i].model = MODEL_RP16SNI; - strcpy(rocketModel[i].modelString, "RocketPort 16 port w/ custom DB78"); - rocketModel[i].numPorts = 16; - break; - case PCI_DEVICE_ID_RP16INTF: - max_num_aiops = 2; - rocketModel[i].model = MODEL_RP16INTF; - strcpy(rocketModel[i].modelString, "RocketPort 16 port w/external I/F"); - rocketModel[i].numPorts = 16; - break; - case PCI_DEVICE_ID_URP16INTF: - max_num_aiops = 2; - rocketModel[i].model = MODEL_UPCI_RP16INTF; - strcpy(rocketModel[i].modelString, "RocketPort UPCI 16 port w/external I/F"); - rocketModel[i].numPorts = 16; - break; - case PCI_DEVICE_ID_CRP16INTF: - max_num_aiops = 2; - rocketModel[i].model = MODEL_CPCI_RP16INTF; - strcpy(rocketModel[i].modelString, "RocketPort Compact PCI 16 port w/external I/F"); - rocketModel[i].numPorts = 16; - break; - case PCI_DEVICE_ID_RP32INTF: - max_num_aiops = 4; - rocketModel[i].model = MODEL_RP32INTF; - strcpy(rocketModel[i].modelString, "RocketPort 32 port w/external I/F"); - rocketModel[i].numPorts = 32; - break; - case PCI_DEVICE_ID_URP32INTF: - max_num_aiops = 4; - rocketModel[i].model = MODEL_UPCI_RP32INTF; - strcpy(rocketModel[i].modelString, "RocketPort UPCI 32 port w/external I/F"); - rocketModel[i].numPorts = 32; - break; - case PCI_DEVICE_ID_RPP4: - max_num_aiops = 1; - ports_per_aiop = 4; - altChanRingIndicator++; - fast_clock++; - rocketModel[i].model = MODEL_RPP4; - strcpy(rocketModel[i].modelString, "RocketPort Plus 4 port"); - rocketModel[i].numPorts = 4; - break; - case PCI_DEVICE_ID_RPP8: - max_num_aiops = 2; - ports_per_aiop = 4; - altChanRingIndicator++; - fast_clock++; - rocketModel[i].model = MODEL_RPP8; - strcpy(rocketModel[i].modelString, "RocketPort Plus 8 port"); - rocketModel[i].numPorts = 8; - break; - case PCI_DEVICE_ID_RP2_232: - max_num_aiops = 1; - ports_per_aiop = 2; - altChanRingIndicator++; - fast_clock++; - rocketModel[i].model = MODEL_RP2_232; - strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS232"); - rocketModel[i].numPorts = 2; - break; - case PCI_DEVICE_ID_RP2_422: - max_num_aiops = 1; - ports_per_aiop = 2; - altChanRingIndicator++; - fast_clock++; - rocketModel[i].model = MODEL_RP2_422; - strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS422"); - rocketModel[i].numPorts = 2; - break; - case PCI_DEVICE_ID_RP6M: - - max_num_aiops = 1; - ports_per_aiop = 6; - - /* If revision is 1, the rocketmodem flash must be loaded. - * If it is 2 it is a "socketed" version. */ - if (dev->revision == 1) { - rcktpt_type[i] = ROCKET_TYPE_MODEMII; - rocketModel[i].loadrm2 = 1; - } else { - rcktpt_type[i] = ROCKET_TYPE_MODEM; - } - - rocketModel[i].model = MODEL_RP6M; - strcpy(rocketModel[i].modelString, "RocketModem 6 port"); - rocketModel[i].numPorts = 6; - break; - case PCI_DEVICE_ID_RP4M: - max_num_aiops = 1; - ports_per_aiop = 4; - if (dev->revision == 1) { - rcktpt_type[i] = ROCKET_TYPE_MODEMII; - rocketModel[i].loadrm2 = 1; - } else { - rcktpt_type[i] = ROCKET_TYPE_MODEM; - } - - rocketModel[i].model = MODEL_RP4M; - strcpy(rocketModel[i].modelString, "RocketModem 4 port"); - rocketModel[i].numPorts = 4; - break; - default: - max_num_aiops = 0; - break; - } - - /* - * Check for UPCI boards. - */ - - switch (dev->device) { - case PCI_DEVICE_ID_URP32INTF: - case PCI_DEVICE_ID_URP8INTF: - case PCI_DEVICE_ID_URP16INTF: - case PCI_DEVICE_ID_CRP16INTF: - case PCI_DEVICE_ID_URP8OCTA: - rcktpt_io_addr[i] = pci_resource_start(dev, 2); - ConfigIO = pci_resource_start(dev, 1); - if (dev->device == PCI_DEVICE_ID_URP8OCTA) { - UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; - - /* - * Check for octa or quad cable. - */ - if (! - (sInW(ConfigIO + _PCI_9030_GPIO_CTRL) & - PCI_GPIO_CTRL_8PORT)) { - ports_per_aiop = 4; - rocketModel[i].numPorts = 4; - } - } - break; - case PCI_DEVICE_ID_UPCI_RM3_8PORT: - max_num_aiops = 1; - rocketModel[i].model = MODEL_UPCI_RM3_8PORT; - strcpy(rocketModel[i].modelString, "RocketModem III 8 port"); - rocketModel[i].numPorts = 8; - rcktpt_io_addr[i] = pci_resource_start(dev, 2); - UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; - ConfigIO = pci_resource_start(dev, 1); - rcktpt_type[i] = ROCKET_TYPE_MODEMIII; - break; - case PCI_DEVICE_ID_UPCI_RM3_4PORT: - max_num_aiops = 1; - rocketModel[i].model = MODEL_UPCI_RM3_4PORT; - strcpy(rocketModel[i].modelString, "RocketModem III 4 port"); - rocketModel[i].numPorts = 4; - rcktpt_io_addr[i] = pci_resource_start(dev, 2); - UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; - ConfigIO = pci_resource_start(dev, 1); - rcktpt_type[i] = ROCKET_TYPE_MODEMIII; - break; - default: - break; - } - - if (fast_clock) { - sClockPrescale = 0x12; /* mod 2 (divide by 3) */ - rp_baud_base[i] = 921600; - } else { - /* - * If support_low_speed is set, use the slow clock - * prescale, which supports 50 bps - */ - if (support_low_speed) { - /* mod 9 (divide by 10) prescale */ - sClockPrescale = 0x19; - rp_baud_base[i] = 230400; - } else { - /* mod 4 (divide by 5) prescale */ - sClockPrescale = 0x14; - rp_baud_base[i] = 460800; - } - } - - for (aiop = 0; aiop < max_num_aiops; aiop++) - aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x40); - ctlp = sCtlNumToCtlPtr(i); - num_aiops = sPCIInitController(ctlp, i, aiopio, max_num_aiops, ConfigIO, 0, FREQ_DIS, 0, altChanRingIndicator, UPCIRingInd); - for (aiop = 0; aiop < max_num_aiops; aiop++) - ctlp->AiopNumChan[aiop] = ports_per_aiop; - - dev_info(&dev->dev, "comtrol PCI controller #%d found at " - "address %04lx, %d AIOP(s) (%s), creating ttyR%d - %ld\n", - i, rcktpt_io_addr[i], num_aiops, rocketModel[i].modelString, - rocketModel[i].startingPortNumber, - rocketModel[i].startingPortNumber + rocketModel[i].numPorts-1); - - if (num_aiops <= 0) { - rcktpt_io_addr[i] = 0; - return (0); - } - is_PCI[i] = 1; - - /* Reset the AIOPIC, init the serial ports */ - for (aiop = 0; aiop < num_aiops; aiop++) { - sResetAiopByNum(ctlp, aiop); - for (chan = 0; chan < ports_per_aiop; chan++) - init_r_port(i, aiop, chan, dev); - } - - /* Rocket modems must be reset */ - if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) || - (rcktpt_type[i] == ROCKET_TYPE_MODEMII) || - (rcktpt_type[i] == ROCKET_TYPE_MODEMIII)) { - for (chan = 0; chan < ports_per_aiop; chan++) - sPCIModemReset(ctlp, chan, 1); - msleep(500); - for (chan = 0; chan < ports_per_aiop; chan++) - sPCIModemReset(ctlp, chan, 0); - msleep(500); - rmSpeakerReset(ctlp, rocketModel[i].model); - } - return (1); -} - -/* - * Probes for PCI cards, inits them if found - * Input: board_found = number of ISA boards already found, or the - * starting board number - * Returns: Number of PCI boards found - */ -static int __init init_PCI(int boards_found) -{ - struct pci_dev *dev = NULL; - int count = 0; - - /* Work through the PCI device list, pulling out ours */ - while ((dev = pci_get_device(PCI_VENDOR_ID_RP, PCI_ANY_ID, dev))) { - if (register_PCI(count + boards_found, dev)) - count++; - } - return (count); -} - -#endif /* CONFIG_PCI */ - -/* - * Probes for ISA cards - * Input: i = the board number to look for - * Returns: 1 if board found, 0 else - */ -static int __init init_ISA(int i) -{ - int num_aiops, num_chan = 0, total_num_chan = 0; - int aiop, chan; - unsigned int aiopio[MAX_AIOPS_PER_BOARD]; - CONTROLLER_t *ctlp; - char *type_string; - - /* If io_addr is zero, no board configured */ - if (rcktpt_io_addr[i] == 0) - return (0); - - /* Reserve the IO region */ - if (!request_region(rcktpt_io_addr[i], 64, "Comtrol RocketPort")) { - printk(KERN_ERR "Unable to reserve IO region for configured " - "ISA RocketPort at address 0x%lx, board not " - "installed...\n", rcktpt_io_addr[i]); - rcktpt_io_addr[i] = 0; - return (0); - } - - ctlp = sCtlNumToCtlPtr(i); - - ctlp->boardType = rcktpt_type[i]; - - switch (rcktpt_type[i]) { - case ROCKET_TYPE_PC104: - type_string = "(PC104)"; - break; - case ROCKET_TYPE_MODEM: - type_string = "(RocketModem)"; - break; - case ROCKET_TYPE_MODEMII: - type_string = "(RocketModem II)"; - break; - default: - type_string = ""; - break; - } - - /* - * If support_low_speed is set, use the slow clock prescale, - * which supports 50 bps - */ - if (support_low_speed) { - sClockPrescale = 0x19; /* mod 9 (divide by 10) prescale */ - rp_baud_base[i] = 230400; - } else { - sClockPrescale = 0x14; /* mod 4 (divide by 5) prescale */ - rp_baud_base[i] = 460800; - } - - for (aiop = 0; aiop < MAX_AIOPS_PER_BOARD; aiop++) - aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x400); - - num_aiops = sInitController(ctlp, i, controller + (i * 0x400), aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); - - if (ctlp->boardType == ROCKET_TYPE_PC104) { - sEnAiop(ctlp, 2); /* only one AIOPIC, but these */ - sEnAiop(ctlp, 3); /* CSels used for other stuff */ - } - - /* If something went wrong initing the AIOP's release the ISA IO memory */ - if (num_aiops <= 0) { - release_region(rcktpt_io_addr[i], 64); - rcktpt_io_addr[i] = 0; - return (0); - } - - rocketModel[i].startingPortNumber = nextLineNumber; - - for (aiop = 0; aiop < num_aiops; aiop++) { - sResetAiopByNum(ctlp, aiop); - sEnAiop(ctlp, aiop); - num_chan = sGetAiopNumChan(ctlp, aiop); - total_num_chan += num_chan; - for (chan = 0; chan < num_chan; chan++) - init_r_port(i, aiop, chan, NULL); - } - is_PCI[i] = 0; - if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) || (rcktpt_type[i] == ROCKET_TYPE_MODEMII)) { - num_chan = sGetAiopNumChan(ctlp, 0); - total_num_chan = num_chan; - for (chan = 0; chan < num_chan; chan++) - sModemReset(ctlp, chan, 1); - msleep(500); - for (chan = 0; chan < num_chan; chan++) - sModemReset(ctlp, chan, 0); - msleep(500); - strcpy(rocketModel[i].modelString, "RocketModem ISA"); - } else { - strcpy(rocketModel[i].modelString, "RocketPort ISA"); - } - rocketModel[i].numPorts = total_num_chan; - rocketModel[i].model = MODEL_ISA; - - printk(KERN_INFO "RocketPort ISA card #%d found at 0x%lx - %d AIOPs %s\n", - i, rcktpt_io_addr[i], num_aiops, type_string); - - printk(KERN_INFO "Installing %s, creating /dev/ttyR%d - %ld\n", - rocketModel[i].modelString, - rocketModel[i].startingPortNumber, - rocketModel[i].startingPortNumber + - rocketModel[i].numPorts - 1); - - return (1); -} - -static const struct tty_operations rocket_ops = { - .open = rp_open, - .close = rp_close, - .write = rp_write, - .put_char = rp_put_char, - .write_room = rp_write_room, - .chars_in_buffer = rp_chars_in_buffer, - .flush_buffer = rp_flush_buffer, - .ioctl = rp_ioctl, - .throttle = rp_throttle, - .unthrottle = rp_unthrottle, - .set_termios = rp_set_termios, - .stop = rp_stop, - .start = rp_start, - .hangup = rp_hangup, - .break_ctl = rp_break, - .send_xchar = rp_send_xchar, - .wait_until_sent = rp_wait_until_sent, - .tiocmget = rp_tiocmget, - .tiocmset = rp_tiocmset, -}; - -static const struct tty_port_operations rocket_port_ops = { - .carrier_raised = carrier_raised, - .dtr_rts = dtr_rts, -}; - -/* - * The module "startup" routine; it's run when the module is loaded. - */ -static int __init rp_init(void) -{ - int ret = -ENOMEM, pci_boards_found, isa_boards_found, i; - - printk(KERN_INFO "RocketPort device driver module, version %s, %s\n", - ROCKET_VERSION, ROCKET_DATE); - - rocket_driver = alloc_tty_driver(MAX_RP_PORTS); - if (!rocket_driver) - goto err; - - /* - * If board 1 is non-zero, there is at least one ISA configured. If controller is - * zero, use the default controller IO address of board1 + 0x40. - */ - if (board1) { - if (controller == 0) - controller = board1 + 0x40; - } else { - controller = 0; /* Used as a flag, meaning no ISA boards */ - } - - /* If an ISA card is configured, reserve the 4 byte IO space for the Mudbac controller */ - if (controller && (!request_region(controller, 4, "Comtrol RocketPort"))) { - printk(KERN_ERR "Unable to reserve IO region for first " - "configured ISA RocketPort controller 0x%lx. " - "Driver exiting\n", controller); - ret = -EBUSY; - goto err_tty; - } - - /* Store ISA variable retrieved from command line or .conf file. */ - rcktpt_io_addr[0] = board1; - rcktpt_io_addr[1] = board2; - rcktpt_io_addr[2] = board3; - rcktpt_io_addr[3] = board4; - - rcktpt_type[0] = modem1 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; - rcktpt_type[0] = pc104_1[0] ? ROCKET_TYPE_PC104 : rcktpt_type[0]; - rcktpt_type[1] = modem2 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; - rcktpt_type[1] = pc104_2[0] ? ROCKET_TYPE_PC104 : rcktpt_type[1]; - rcktpt_type[2] = modem3 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; - rcktpt_type[2] = pc104_3[0] ? ROCKET_TYPE_PC104 : rcktpt_type[2]; - rcktpt_type[3] = modem4 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; - rcktpt_type[3] = pc104_4[0] ? ROCKET_TYPE_PC104 : rcktpt_type[3]; - - /* - * Set up the tty driver structure and then register this - * driver with the tty layer. - */ - - rocket_driver->flags = TTY_DRIVER_DYNAMIC_DEV; - rocket_driver->name = "ttyR"; - rocket_driver->driver_name = "Comtrol RocketPort"; - rocket_driver->major = TTY_ROCKET_MAJOR; - rocket_driver->minor_start = 0; - rocket_driver->type = TTY_DRIVER_TYPE_SERIAL; - rocket_driver->subtype = SERIAL_TYPE_NORMAL; - rocket_driver->init_termios = tty_std_termios; - rocket_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - rocket_driver->init_termios.c_ispeed = 9600; - rocket_driver->init_termios.c_ospeed = 9600; -#ifdef ROCKET_SOFT_FLOW - rocket_driver->flags |= TTY_DRIVER_REAL_RAW; -#endif - tty_set_operations(rocket_driver, &rocket_ops); - - ret = tty_register_driver(rocket_driver); - if (ret < 0) { - printk(KERN_ERR "Couldn't install tty RocketPort driver\n"); - goto err_controller; - } - -#ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "RocketPort driver is major %d\n", rocket_driver.major); -#endif - - /* - * OK, let's probe each of the controllers looking for boards. Any boards found - * will be initialized here. - */ - isa_boards_found = 0; - pci_boards_found = 0; - - for (i = 0; i < NUM_BOARDS; i++) { - if (init_ISA(i)) - isa_boards_found++; - } - -#ifdef CONFIG_PCI - if (isa_boards_found < NUM_BOARDS) - pci_boards_found = init_PCI(isa_boards_found); -#endif - - max_board = pci_boards_found + isa_boards_found; - - if (max_board == 0) { - printk(KERN_ERR "No rocketport ports found; unloading driver\n"); - ret = -ENXIO; - goto err_ttyu; - } - - return 0; -err_ttyu: - tty_unregister_driver(rocket_driver); -err_controller: - if (controller) - release_region(controller, 4); -err_tty: - put_tty_driver(rocket_driver); -err: - return ret; -} - - -static void rp_cleanup_module(void) -{ - int retval; - int i; - - del_timer_sync(&rocket_timer); - - retval = tty_unregister_driver(rocket_driver); - if (retval) - printk(KERN_ERR "Error %d while trying to unregister " - "rocketport driver\n", -retval); - - for (i = 0; i < MAX_RP_PORTS; i++) - if (rp_table[i]) { - tty_unregister_device(rocket_driver, i); - tty_port_destroy(&rp_table[i]->port); - kfree(rp_table[i]); - } - - put_tty_driver(rocket_driver); - - for (i = 0; i < NUM_BOARDS; i++) { - if (rcktpt_io_addr[i] <= 0 || is_PCI[i]) - continue; - release_region(rcktpt_io_addr[i], 64); - } - if (controller) - release_region(controller, 4); -} - -/*************************************************************************** -Function: sInitController -Purpose: Initialization of controller global registers and controller - structure. -Call: sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize, - IRQNum,Frequency,PeriodicOnly) - CONTROLLER_T *CtlP; Ptr to controller structure - int CtlNum; Controller number - ByteIO_t MudbacIO; Mudbac base I/O address. - ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. - This list must be in the order the AIOPs will be found on the - controller. Once an AIOP in the list is not found, it is - assumed that there are no more AIOPs on the controller. - int AiopIOListSize; Number of addresses in AiopIOList - int IRQNum; Interrupt Request number. Can be any of the following: - 0: Disable global interrupts - 3: IRQ 3 - 4: IRQ 4 - 5: IRQ 5 - 9: IRQ 9 - 10: IRQ 10 - 11: IRQ 11 - 12: IRQ 12 - 15: IRQ 15 - Byte_t Frequency: A flag identifying the frequency - of the periodic interrupt, can be any one of the following: - FREQ_DIS - periodic interrupt disabled - FREQ_137HZ - 137 Hertz - FREQ_69HZ - 69 Hertz - FREQ_34HZ - 34 Hertz - FREQ_17HZ - 17 Hertz - FREQ_9HZ - 9 Hertz - FREQ_4HZ - 4 Hertz - If IRQNum is set to 0 the Frequency parameter is - overidden, it is forced to a value of FREQ_DIS. - int PeriodicOnly: 1 if all interrupts except the periodic - interrupt are to be blocked. - 0 is both the periodic interrupt and - other channel interrupts are allowed. - If IRQNum is set to 0 the PeriodicOnly parameter is - overidden, it is forced to a value of 0. -Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller - initialization failed. - -Comments: - If periodic interrupts are to be disabled but AIOP interrupts - are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. - - If interrupts are to be completely disabled set IRQNum to 0. - - Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an - invalid combination. - - This function performs initialization of global interrupt modes, - but it does not actually enable global interrupts. To enable - and disable global interrupts use functions sEnGlobalInt() and - sDisGlobalInt(). Enabling of global interrupts is normally not - done until all other initializations are complete. - - Even if interrupts are globally enabled, they must also be - individually enabled for each channel that is to generate - interrupts. - -Warnings: No range checking on any of the parameters is done. - - No context switches are allowed while executing this function. - - After this function all AIOPs on the controller are disabled, - they can be enabled with sEnAiop(). -*/ -static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, - ByteIO_t * AiopIOList, int AiopIOListSize, - int IRQNum, Byte_t Frequency, int PeriodicOnly) -{ - int i; - ByteIO_t io; - int done; - - CtlP->AiopIntrBits = aiop_intr_bits; - CtlP->AltChanRingIndicator = 0; - CtlP->CtlNum = CtlNum; - CtlP->CtlID = CTLID_0001; /* controller release 1 */ - CtlP->BusType = isISA; - CtlP->MBaseIO = MudbacIO; - CtlP->MReg1IO = MudbacIO + 1; - CtlP->MReg2IO = MudbacIO + 2; - CtlP->MReg3IO = MudbacIO + 3; -#if 1 - CtlP->MReg2 = 0; /* interrupt disable */ - CtlP->MReg3 = 0; /* no periodic interrupts */ -#else - if (sIRQMap[IRQNum] == 0) { /* interrupts globally disabled */ - CtlP->MReg2 = 0; /* interrupt disable */ - CtlP->MReg3 = 0; /* no periodic interrupts */ - } else { - CtlP->MReg2 = sIRQMap[IRQNum]; /* set IRQ number */ - CtlP->MReg3 = Frequency; /* set frequency */ - if (PeriodicOnly) { /* periodic interrupt only */ - CtlP->MReg3 |= PERIODIC_ONLY; - } - } -#endif - sOutB(CtlP->MReg2IO, CtlP->MReg2); - sOutB(CtlP->MReg3IO, CtlP->MReg3); - sControllerEOI(CtlP); /* clear EOI if warm init */ - /* Init AIOPs */ - CtlP->NumAiop = 0; - for (i = done = 0; i < AiopIOListSize; i++) { - io = AiopIOList[i]; - CtlP->AiopIO[i] = (WordIO_t) io; - CtlP->AiopIntChanIO[i] = io + _INT_CHAN; - sOutB(CtlP->MReg2IO, CtlP->MReg2 | (i & 0x03)); /* AIOP index */ - sOutB(MudbacIO, (Byte_t) (io >> 6)); /* set up AIOP I/O in MUDBAC */ - if (done) - continue; - sEnAiop(CtlP, i); /* enable the AIOP */ - CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ - if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ - done = 1; /* done looking for AIOPs */ - else { - CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ - sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ - sOutB(io + _INDX_DATA, sClockPrescale); - CtlP->NumAiop++; /* bump count of AIOPs */ - } - sDisAiop(CtlP, i); /* disable AIOP */ - } - - if (CtlP->NumAiop == 0) - return (-1); - else - return (CtlP->NumAiop); -} - -/*************************************************************************** -Function: sReadAiopID -Purpose: Read the AIOP idenfication number directly from an AIOP. -Call: sReadAiopID(io) - ByteIO_t io: AIOP base I/O address -Return: int: Flag AIOPID_XXXX if a valid AIOP is found, where X - is replace by an identifying number. - Flag AIOPID_NULL if no valid AIOP is found -Warnings: No context switches are allowed while executing this function. - -*/ -static int sReadAiopID(ByteIO_t io) -{ - Byte_t AiopID; /* ID byte from AIOP */ - - sOutB(io + _CMD_REG, RESET_ALL); /* reset AIOP */ - sOutB(io + _CMD_REG, 0x0); - AiopID = sInW(io + _CHN_STAT0) & 0x07; - if (AiopID == 0x06) - return (1); - else /* AIOP does not exist */ - return (-1); -} - -/*************************************************************************** -Function: sReadAiopNumChan -Purpose: Read the number of channels available in an AIOP directly from - an AIOP. -Call: sReadAiopNumChan(io) - WordIO_t io: AIOP base I/O address -Return: int: The number of channels available -Comments: The number of channels is determined by write/reads from identical - offsets within the SRAM address spaces for channels 0 and 4. - If the channel 4 space is mirrored to channel 0 it is a 4 channel - AIOP, otherwise it is an 8 channel. -Warnings: No context switches are allowed while executing this function. -*/ -static int sReadAiopNumChan(WordIO_t io) -{ - Word_t x; - static Byte_t R[4] = { 0x00, 0x00, 0x34, 0x12 }; - - /* write to chan 0 SRAM */ - out32((DWordIO_t) io + _INDX_ADDR, R); - sOutW(io + _INDX_ADDR, 0); /* read from SRAM, chan 0 */ - x = sInW(io + _INDX_DATA); - sOutW(io + _INDX_ADDR, 0x4000); /* read from SRAM, chan 4 */ - if (x != sInW(io + _INDX_DATA)) /* if different must be 8 chan */ - return (8); - else - return (4); -} - -/*************************************************************************** -Function: sInitChan -Purpose: Initialization of a channel and channel structure -Call: sInitChan(CtlP,ChP,AiopNum,ChanNum) - CONTROLLER_T *CtlP; Ptr to controller structure - CHANNEL_T *ChP; Ptr to channel structure - int AiopNum; AIOP number within controller - int ChanNum; Channel number within AIOP -Return: int: 1 if initialization succeeded, 0 if it fails because channel - number exceeds number of channels available in AIOP. -Comments: This function must be called before a channel can be used. -Warnings: No range checking on any of the parameters is done. - - No context switches are allowed while executing this function. -*/ -static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum, - int ChanNum) -{ - int i; - WordIO_t AiopIO; - WordIO_t ChIOOff; - Byte_t *ChR; - Word_t ChOff; - static Byte_t R[4]; - int brd9600; - - if (ChanNum >= CtlP->AiopNumChan[AiopNum]) - return 0; /* exceeds num chans in AIOP */ - - /* Channel, AIOP, and controller identifiers */ - ChP->CtlP = CtlP; - ChP->ChanID = CtlP->AiopID[AiopNum]; - ChP->AiopNum = AiopNum; - ChP->ChanNum = ChanNum; - - /* Global direct addresses */ - AiopIO = CtlP->AiopIO[AiopNum]; - ChP->Cmd = (ByteIO_t) AiopIO + _CMD_REG; - ChP->IntChan = (ByteIO_t) AiopIO + _INT_CHAN; - ChP->IntMask = (ByteIO_t) AiopIO + _INT_MASK; - ChP->IndexAddr = (DWordIO_t) AiopIO + _INDX_ADDR; - ChP->IndexData = AiopIO + _INDX_DATA; - - /* Channel direct addresses */ - ChIOOff = AiopIO + ChP->ChanNum * 2; - ChP->TxRxData = ChIOOff + _TD0; - ChP->ChanStat = ChIOOff + _CHN_STAT0; - ChP->TxRxCount = ChIOOff + _FIFO_CNT0; - ChP->IntID = (ByteIO_t) AiopIO + ChP->ChanNum + _INT_ID0; - - /* Initialize the channel from the RData array */ - for (i = 0; i < RDATASIZE; i += 4) { - R[0] = RData[i]; - R[1] = RData[i + 1] + 0x10 * ChanNum; - R[2] = RData[i + 2]; - R[3] = RData[i + 3]; - out32(ChP->IndexAddr, R); - } - - ChR = ChP->R; - for (i = 0; i < RREGDATASIZE; i += 4) { - ChR[i] = RRegData[i]; - ChR[i + 1] = RRegData[i + 1] + 0x10 * ChanNum; - ChR[i + 2] = RRegData[i + 2]; - ChR[i + 3] = RRegData[i + 3]; - } - - /* Indexed registers */ - ChOff = (Word_t) ChanNum *0x1000; - - if (sClockPrescale == 0x14) - brd9600 = 47; - else - brd9600 = 23; - - ChP->BaudDiv[0] = (Byte_t) (ChOff + _BAUD); - ChP->BaudDiv[1] = (Byte_t) ((ChOff + _BAUD) >> 8); - ChP->BaudDiv[2] = (Byte_t) brd9600; - ChP->BaudDiv[3] = (Byte_t) (brd9600 >> 8); - out32(ChP->IndexAddr, ChP->BaudDiv); - - ChP->TxControl[0] = (Byte_t) (ChOff + _TX_CTRL); - ChP->TxControl[1] = (Byte_t) ((ChOff + _TX_CTRL) >> 8); - ChP->TxControl[2] = 0; - ChP->TxControl[3] = 0; - out32(ChP->IndexAddr, ChP->TxControl); - - ChP->RxControl[0] = (Byte_t) (ChOff + _RX_CTRL); - ChP->RxControl[1] = (Byte_t) ((ChOff + _RX_CTRL) >> 8); - ChP->RxControl[2] = 0; - ChP->RxControl[3] = 0; - out32(ChP->IndexAddr, ChP->RxControl); - - ChP->TxEnables[0] = (Byte_t) (ChOff + _TX_ENBLS); - ChP->TxEnables[1] = (Byte_t) ((ChOff + _TX_ENBLS) >> 8); - ChP->TxEnables[2] = 0; - ChP->TxEnables[3] = 0; - out32(ChP->IndexAddr, ChP->TxEnables); - - ChP->TxCompare[0] = (Byte_t) (ChOff + _TXCMP1); - ChP->TxCompare[1] = (Byte_t) ((ChOff + _TXCMP1) >> 8); - ChP->TxCompare[2] = 0; - ChP->TxCompare[3] = 0; - out32(ChP->IndexAddr, ChP->TxCompare); - - ChP->TxReplace1[0] = (Byte_t) (ChOff + _TXREP1B1); - ChP->TxReplace1[1] = (Byte_t) ((ChOff + _TXREP1B1) >> 8); - ChP->TxReplace1[2] = 0; - ChP->TxReplace1[3] = 0; - out32(ChP->IndexAddr, ChP->TxReplace1); - - ChP->TxReplace2[0] = (Byte_t) (ChOff + _TXREP2); - ChP->TxReplace2[1] = (Byte_t) ((ChOff + _TXREP2) >> 8); - ChP->TxReplace2[2] = 0; - ChP->TxReplace2[3] = 0; - out32(ChP->IndexAddr, ChP->TxReplace2); - - ChP->TxFIFOPtrs = ChOff + _TXF_OUTP; - ChP->TxFIFO = ChOff + _TX_FIFO; - - sOutB(ChP->Cmd, (Byte_t) ChanNum | RESTXFCNT); /* apply reset Tx FIFO count */ - sOutB(ChP->Cmd, (Byte_t) ChanNum); /* remove reset Tx FIFO count */ - sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ - sOutW(ChP->IndexData, 0); - ChP->RxFIFOPtrs = ChOff + _RXF_OUTP; - ChP->RxFIFO = ChOff + _RX_FIFO; - - sOutB(ChP->Cmd, (Byte_t) ChanNum | RESRXFCNT); /* apply reset Rx FIFO count */ - sOutB(ChP->Cmd, (Byte_t) ChanNum); /* remove reset Rx FIFO count */ - sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs); /* clear Rx out ptr */ - sOutW(ChP->IndexData, 0); - sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ - sOutW(ChP->IndexData, 0); - ChP->TxPrioCnt = ChOff + _TXP_CNT; - sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioCnt); - sOutB(ChP->IndexData, 0); - ChP->TxPrioPtr = ChOff + _TXP_PNTR; - sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioPtr); - sOutB(ChP->IndexData, 0); - ChP->TxPrioBuf = ChOff + _TXP_BUF; - sEnRxProcessor(ChP); /* start the Rx processor */ - - return 1; -} - -/*************************************************************************** -Function: sStopRxProcessor -Purpose: Stop the receive processor from processing a channel. -Call: sStopRxProcessor(ChP) - CHANNEL_T *ChP; Ptr to channel structure - -Comments: The receive processor can be started again with sStartRxProcessor(). - This function causes the receive processor to skip over the - stopped channel. It does not stop it from processing other channels. - -Warnings: No context switches are allowed while executing this function. - - Do not leave the receive processor stopped for more than one - character time. - - After calling this function a delay of 4 uS is required to ensure - that the receive processor is no longer processing this channel. -*/ -static void sStopRxProcessor(CHANNEL_T * ChP) -{ - Byte_t R[4]; - - R[0] = ChP->R[0]; - R[1] = ChP->R[1]; - R[2] = 0x0a; - R[3] = ChP->R[3]; - out32(ChP->IndexAddr, R); -} - -/*************************************************************************** -Function: sFlushRxFIFO -Purpose: Flush the Rx FIFO -Call: sFlushRxFIFO(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: void -Comments: To prevent data from being enqueued or dequeued in the Tx FIFO - while it is being flushed the receive processor is stopped - and the transmitter is disabled. After these operations a - 4 uS delay is done before clearing the pointers to allow - the receive processor to stop. These items are handled inside - this function. -Warnings: No context switches are allowed while executing this function. -*/ -static void sFlushRxFIFO(CHANNEL_T * ChP) -{ - int i; - Byte_t Ch; /* channel number within AIOP */ - int RxFIFOEnabled; /* 1 if Rx FIFO enabled */ - - if (sGetRxCnt(ChP) == 0) /* Rx FIFO empty */ - return; /* don't need to flush */ - - RxFIFOEnabled = 0; - if (ChP->R[0x32] == 0x08) { /* Rx FIFO is enabled */ - RxFIFOEnabled = 1; - sDisRxFIFO(ChP); /* disable it */ - for (i = 0; i < 2000 / 200; i++) /* delay 2 uS to allow proc to disable FIFO */ - sInB(ChP->IntChan); /* depends on bus i/o timing */ - } - sGetChanStatus(ChP); /* clear any pending Rx errors in chan stat */ - Ch = (Byte_t) sGetChanNum(ChP); - sOutB(ChP->Cmd, Ch | RESRXFCNT); /* apply reset Rx FIFO count */ - sOutB(ChP->Cmd, Ch); /* remove reset Rx FIFO count */ - sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs); /* clear Rx out ptr */ - sOutW(ChP->IndexData, 0); - sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ - sOutW(ChP->IndexData, 0); - if (RxFIFOEnabled) - sEnRxFIFO(ChP); /* enable Rx FIFO */ -} - -/*************************************************************************** -Function: sFlushTxFIFO -Purpose: Flush the Tx FIFO -Call: sFlushTxFIFO(ChP) - CHANNEL_T *ChP; Ptr to channel structure -Return: void -Comments: To prevent data from being enqueued or dequeued in the Tx FIFO - while it is being flushed the receive processor is stopped - and the transmitter is disabled. After these operations a - 4 uS delay is done before clearing the pointers to allow - the receive processor to stop. These items are handled inside - this function. -Warnings: No context switches are allowed while executing this function. -*/ -static void sFlushTxFIFO(CHANNEL_T * ChP) -{ - int i; - Byte_t Ch; /* channel number within AIOP */ - int TxEnabled; /* 1 if transmitter enabled */ - - if (sGetTxCnt(ChP) == 0) /* Tx FIFO empty */ - return; /* don't need to flush */ - - TxEnabled = 0; - if (ChP->TxControl[3] & TX_ENABLE) { - TxEnabled = 1; - sDisTransmit(ChP); /* disable transmitter */ - } - sStopRxProcessor(ChP); /* stop Rx processor */ - for (i = 0; i < 4000 / 200; i++) /* delay 4 uS to allow proc to stop */ - sInB(ChP->IntChan); /* depends on bus i/o timing */ - Ch = (Byte_t) sGetChanNum(ChP); - sOutB(ChP->Cmd, Ch | RESTXFCNT); /* apply reset Tx FIFO count */ - sOutB(ChP->Cmd, Ch); /* remove reset Tx FIFO count */ - sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ - sOutW(ChP->IndexData, 0); - if (TxEnabled) - sEnTransmit(ChP); /* enable transmitter */ - sStartRxProcessor(ChP); /* restart Rx processor */ -} - -/*************************************************************************** -Function: sWriteTxPrioByte -Purpose: Write a byte of priority transmit data to a channel -Call: sWriteTxPrioByte(ChP,Data) - CHANNEL_T *ChP; Ptr to channel structure - Byte_t Data; The transmit data byte - -Return: int: 1 if the bytes is successfully written, otherwise 0. - -Comments: The priority byte is transmitted before any data in the Tx FIFO. - -Warnings: No context switches are allowed while executing this function. -*/ -static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data) -{ - Byte_t DWBuf[4]; /* buffer for double word writes */ - Word_t *WordPtr; /* must be far because Win SS != DS */ - register DWordIO_t IndexAddr; - - if (sGetTxCnt(ChP) > 1) { /* write it to Tx priority buffer */ - IndexAddr = ChP->IndexAddr; - sOutW((WordIO_t) IndexAddr, ChP->TxPrioCnt); /* get priority buffer status */ - if (sInB((ByteIO_t) ChP->IndexData) & PRI_PEND) /* priority buffer busy */ - return (0); /* nothing sent */ - - WordPtr = (Word_t *) (&DWBuf[0]); - *WordPtr = ChP->TxPrioBuf; /* data byte address */ - - DWBuf[2] = Data; /* data byte value */ - out32(IndexAddr, DWBuf); /* write it out */ - - *WordPtr = ChP->TxPrioCnt; /* Tx priority count address */ - - DWBuf[2] = PRI_PEND + 1; /* indicate 1 byte pending */ - DWBuf[3] = 0; /* priority buffer pointer */ - out32(IndexAddr, DWBuf); /* write it out */ - } else { /* write it to Tx FIFO */ - - sWriteTxByte(sGetTxRxDataIO(ChP), Data); - } - return (1); /* 1 byte sent */ -} - -/*************************************************************************** -Function: sEnInterrupts -Purpose: Enable one or more interrupts for a channel -Call: sEnInterrupts(ChP,Flags) - CHANNEL_T *ChP; Ptr to channel structure - Word_t Flags: Interrupt enable flags, can be any combination - of the following flags: - TXINT_EN: Interrupt on Tx FIFO empty - RXINT_EN: Interrupt on Rx FIFO at trigger level (see - sSetRxTrigger()) - SRCINT_EN: Interrupt on SRC (Special Rx Condition) - MCINT_EN: Interrupt on modem input change - CHANINT_EN: Allow channel interrupt signal to the AIOP's - Interrupt Channel Register. -Return: void -Comments: If an interrupt enable flag is set in Flags, that interrupt will be - enabled. If an interrupt enable flag is not set in Flags, that - interrupt will not be changed. Interrupts can be disabled with - function sDisInterrupts(). - - This function sets the appropriate bit for the channel in the AIOP's - Interrupt Mask Register if the CHANINT_EN flag is set. This allows - this channel's bit to be set in the AIOP's Interrupt Channel Register. - - Interrupts must also be globally enabled before channel interrupts - will be passed on to the host. This is done with function - sEnGlobalInt(). - - In some cases it may be desirable to disable interrupts globally but - enable channel interrupts. This would allow the global interrupt - status register to be used to determine which AIOPs need service. -*/ -static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags) -{ - Byte_t Mask; /* Interrupt Mask Register */ - - ChP->RxControl[2] |= - ((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); - - out32(ChP->IndexAddr, ChP->RxControl); - - ChP->TxControl[2] |= ((Byte_t) Flags & TXINT_EN); - - out32(ChP->IndexAddr, ChP->TxControl); - - if (Flags & CHANINT_EN) { - Mask = sInB(ChP->IntMask) | sBitMapSetTbl[ChP->ChanNum]; - sOutB(ChP->IntMask, Mask); - } -} - -/*************************************************************************** -Function: sDisInterrupts -Purpose: Disable one or more interrupts for a channel -Call: sDisInterrupts(ChP,Flags) - CHANNEL_T *ChP; Ptr to channel structure - Word_t Flags: Interrupt flags, can be any combination - of the following flags: - TXINT_EN: Interrupt on Tx FIFO empty - RXINT_EN: Interrupt on Rx FIFO at trigger level (see - sSetRxTrigger()) - SRCINT_EN: Interrupt on SRC (Special Rx Condition) - MCINT_EN: Interrupt on modem input change - CHANINT_EN: Disable channel interrupt signal to the - AIOP's Interrupt Channel Register. -Return: void -Comments: If an interrupt flag is set in Flags, that interrupt will be - disabled. If an interrupt flag is not set in Flags, that - interrupt will not be changed. Interrupts can be enabled with - function sEnInterrupts(). - - This function clears the appropriate bit for the channel in the AIOP's - Interrupt Mask Register if the CHANINT_EN flag is set. This blocks - this channel's bit from being set in the AIOP's Interrupt Channel - Register. -*/ -static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags) -{ - Byte_t Mask; /* Interrupt Mask Register */ - - ChP->RxControl[2] &= - ~((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); - out32(ChP->IndexAddr, ChP->RxControl); - ChP->TxControl[2] &= ~((Byte_t) Flags & TXINT_EN); - out32(ChP->IndexAddr, ChP->TxControl); - - if (Flags & CHANINT_EN) { - Mask = sInB(ChP->IntMask) & sBitMapClrTbl[ChP->ChanNum]; - sOutB(ChP->IntMask, Mask); - } -} - -static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode) -{ - sOutB(ChP->CtlP->AiopIO[2], (mode & 0x18) | ChP->ChanNum); -} - -/* - * Not an official SSCI function, but how to reset RocketModems. - * ISA bus version - */ -static void sModemReset(CONTROLLER_T * CtlP, int chan, int on) -{ - ByteIO_t addr; - Byte_t val; - - addr = CtlP->AiopIO[0] + 0x400; - val = sInB(CtlP->MReg3IO); - /* if AIOP[1] is not enabled, enable it */ - if ((val & 2) == 0) { - val = sInB(CtlP->MReg2IO); - sOutB(CtlP->MReg2IO, (val & 0xfc) | (1 & 0x03)); - sOutB(CtlP->MBaseIO, (unsigned char) (addr >> 6)); - } - - sEnAiop(CtlP, 1); - if (!on) - addr += 8; - sOutB(addr + chan, 0); /* apply or remove reset */ - sDisAiop(CtlP, 1); -} - -/* - * Not an official SSCI function, but how to reset RocketModems. - * PCI bus version - */ -static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on) -{ - ByteIO_t addr; - - addr = CtlP->AiopIO[0] + 0x40; /* 2nd AIOP */ - if (!on) - addr += 8; - sOutB(addr + chan, 0); /* apply or remove reset */ -} - -/* Returns the line number given the controller (board), aiop and channel number */ -static unsigned char GetLineNumber(int ctrl, int aiop, int ch) -{ - return lineNumbers[(ctrl << 5) | (aiop << 3) | ch]; -} - -/* - * Stores the line number associated with a given controller (board), aiop - * and channel number. - * Returns: The line number assigned - */ -static unsigned char SetLineNumber(int ctrl, int aiop, int ch) -{ - lineNumbers[(ctrl << 5) | (aiop << 3) | ch] = nextLineNumber++; - return (nextLineNumber - 1); -} |