/* * RocketPort device driver for Linux * * Written by Theodore Ts'o, 1995, 1996, 1997, 1998, 1999, 2000. * * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2003 by Comtrol, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Kernel Synchronization: * * This driver has 2 kernel control paths - exception handlers (calls into the driver * from user mode) and the timer bottom half (tasklet). This is a polled driver, interrupts * are not used. * * Critical data: * - rp_table[], accessed through passed "info" pointers, is a global (static) array of * serial port state information and the xmit_buf circular buffer. Protected by * a per port spinlock. * - xmit_flags[], an array of ints indexed by line (port) number, indicating that there * is data to be transmitted. Protected by atomic bit operations. * - rp_num_ports, int indicating number of open ports, protected by atomic operations. * * rp_write() and rp_write_char() functions use a per port semaphore to protect against * simultaneous access to the same port by more than one process. */ /****** Defines ******/ #define ROCKET_PARANOIA_CHECK #define ROCKET_DISABLE_SIMUSAGE #undef ROCKET_SOFT_FLOW #undef ROCKET_DEBUG_OPEN #undef ROCKET_DEBUG_INTR #undef ROCKET_DEBUG_WRITE #undef ROCKET_DEBUG_FLOW #undef ROCKET_DEBUG_THROTTLE #undef ROCKET_DEBUG_WAIT_UNTIL_SENT #undef ROCKET_DEBUG_RECEIVE #undef ROCKET_DEBUG_HANGUP #undef REV_PCI_ORDER #undef ROCKET_DEBUG_IO #define POLL_PERIOD HZ/100 /* Polling period .01 seconds (10ms) */ /****** Kernel includes ******/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /****** RocketPort includes ******/ #include "rocket_int.h" #include "rocket.h" #define ROCKET_VERSION "2.09" #define ROCKET_DATE "12-June-2003" /****** RocketPort Local Variables ******/ static void rp_do_poll(unsigned long dummy); static struct tty_driver *rocket_driver; static struct rocket_version driver_version = { ROCKET_VERSION, ROCKET_DATE }; static struct r_port *rp_table[MAX_RP_PORTS]; /* The main repository of serial port state information. */ static unsigned int xmit_flags[NUM_BOARDS]; /* Bit significant, indicates port had data to transmit. */ /* eg. Bit 0 indicates port 0 has xmit data, ... */ static atomic_t rp_num_ports_open; /* Number of serial ports open */ static DEFINE_TIMER(rocket_timer, rp_do_poll, 0, 0); static unsigned long board1; /* ISA addresses, retrieved from rocketport.conf */ static unsigned long board2; static unsigned long board3; static unsigned long board4; static unsigned long controller; static int support_low_speed; static unsigned long modem1; static unsigned long modem2; static unsigned long modem3; static unsigned long modem4; static unsigned long pc104_1[8]; static unsigned long pc104_2[8]; static unsigned long pc104_3[8]; static unsigned long pc104_4[8]; static unsigned long *pc104[4] = { pc104_1, pc104_2, pc104_3, pc104_4 }; static int rp_baud_base[NUM_BOARDS]; /* Board config info (Someday make a per-board structure) */ static unsigned long rcktpt_io_addr[NUM_BOARDS]; static int rcktpt_type[NUM_BOARDS]; static int is_PCI[NUM_BOARDS]; static rocketModel_t rocketModel[NUM_BOARDS]; static int max_board; static const struct tty_port_operations rocket_port_ops; /* * The following arrays define the interrupt bits corresponding to each AIOP. * These bits are different between the ISA and regular PCI boards and the * Universal PCI boards. */ static Word_t aiop_intr_bits[AIOP_CTL_SIZE] = { AIOP_INTR_BIT_0, AIOP_INTR_BIT_1, AIOP_INTR_BIT_2, AIOP_INTR_BIT_3 }; static Word_t upci_aiop_intr_bits[AIOP_CTL_SIZE] = { UPCI_AIOP_INTR_BIT_0, UPCI_AIOP_INTR_BIT_1, UPCI_AIOP_INTR_BIT_2, UPCI_AIOP_INTR_BIT_3 }; static Byte_t RData[RDATASIZE] = { 0x00, 0x09, 0xf6, 0x82, 0x02, 0x09, 0x86, 0xfb, 0x04, 0x09, 0x00, 0x0a, 0x06, 0x09, 0x01, 0x0a, 0x08, 0x09, 0x8a, 0x13, 0x0a, 0x09, 0xc5, 0x11, 0x0c, 0x09, 0x86, 0x85, 0x0e, 0x09, 0x20, 0x0a, 0x10, 0x09, 0x21, 0x0a, 0x12, 0x09, 0x41, 0xff, 0x14, 0x09, 0x82, 0x00, 0x16, 0x09, 0x82, 0x7b, 0x18, 0x09, 0x8a, 0x7d, 0x1a, 0x09, 0x88, 0x81, 0x1c, 0x09, 0x86, 0x7a, 0x1e, 0x09, 0x84, 0x81, 0x20, 0x09, 0x82, 0x7c, 0x22, 0x09, 0x0a, 0x0a }; static Byte_t RRegData[RREGDATASIZE] = { 0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */ 0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */ 0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */ 0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */ 0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */ 0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */ 0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */ 0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */ 0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */ 0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */ 0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */ 0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */ 0x22, 0x09, 0x0a, 0x0a /* 30: Rx FIFO Enable */ }; static CONTROLLER_T sController[CTL_SIZE] = { {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}} }; static Byte_t sBitMapClrTbl[8] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f }; static Byte_t sBitMapSetTbl[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; static int sClockPrescale = 0x14; /* * Line number is the ttySIx number (x), the Minor number. We * assign them sequentially, starting at zero. The following * array keeps track of the line number assigned to a given board/aiop/channel. */ static unsigned char lineNumbers[MAX_RP_PORTS]; static unsigned long nextLineNumber; /***** RocketPort Static Prototypes *********/ static int __init init_ISA(int i); static void rp_wait_until_sent(struct tty_struct *tty, int timeout); static void rp_flush_buffer(struct tty_struct *tty); static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model); static unsigned char GetLineNumber(int ctrl, int aiop, int ch); static unsigned char SetLineNumber(int ctrl, int aiop, int ch); static void rp_start(struct tty_struct *tty); static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum, int ChanNum); static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode); static void sFlushRxFIFO(CHANNEL_T * ChP); static void sFlushTxFIFO(CHANNEL_T * ChP); static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags); static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags); static void sModemReset(CONTROLLER_T * CtlP, int chan, int on); static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on); static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data); static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t * AiopIOList, int AiopIOListSize, WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, int PeriodicOnly, int altChanRingIndicator, int UPCIRingInd); static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, ByteIO_t * AiopIOList, int AiopIOListSize, int IRQNum, Byte_t Frequency, int PeriodicOnly); static int sReadAiopID(ByteIO_t io); static int sReadAiopNumChan(WordIO_t io); MODULE_AUTHOR("Theodore Ts'o"); MODULE_DESCRIPTION("Comtrol RocketPort driver"); module_param(board1, ulong, 0); MODULE_PARM_DESC(board1, "I/O port for (ISA) board #1"); module_param(board2, ulong, 0); MODULE_PARM_DESC(board2, "I/O port for (ISA) board #2"); module_param(board3, ulong, 0); MODULE_PARM_DESC(board3, "I/O port for (ISA) board #3"); module_param(board4, ulong, 0); MODULE_PARM_DESC(board4, "I/O port for (ISA) board #4"); module_param(controller, ulong, 0); MODULE_PARM_DESC(controller, "I/O port for (ISA) rocketport controller"); module_param(support_low_speed, bool, 0); MODULE_PARM_DESC(support_low_speed, "1 means support 50 baud, 0 means support 460400 baud"); module_param(modem1, ulong, 0); MODULE_PARM_DESC(modem1, "1 means (ISA) board #1 is a RocketModem"); module_param(modem2, ulong, 0); MODULE_PARM_DESC(modem2, "1 means (ISA) board #2 is a RocketModem"); module_param(modem3, ulong, 0); MODULE_PARM_DESC(modem3, "1 means (ISA) board #3 is a RocketModem"); module_param(modem4, ulong, 0); MODULE_PARM_DESC(modem4, "1 means (ISA) board #4 is a RocketModem"); module_param_array(pc104_1, ulong, NULL, 0); MODULE_PARM_DESC(pc104_1, "set interface types for ISA(PC104) board #1 (e.g. pc104_1=232,232,485,485,..."); module_param_array(pc104_2, ulong, NULL, 0); MODULE_PARM_DESC(pc104_2, "set interface types for ISA(PC104) board #2 (e.g. pc104_2=232,232,485,485,..."); module_param_array(pc104_3, ulong, NULL, 0); MODULE_PARM_DESC(pc104_3, "set interface types for ISA(PC104) board #3 (e.g. pc104_3=232,232,485,485,..."); module_param_array(pc104_4, ulong, NULL, 0); MODULE_PARM_DESC(pc104_4, "set interface types for ISA(PC104) board #4 (e.g. pc104_4=232,232,485,485,..."); static int rp_init(void); static void rp_cleanup_module(void); module_init(rp_init); module_exit(rp_cleanup_module); MODULE_LICENSE("Dual BSD/GPL"); /*************************************************************************/ /* Module code starts here */ static inline int rocket_paranoia_check(struct r_port *info, const char *routine) { #ifdef ROCKET_PARANOIA_CHECK if (!info) return 1; if (info->magic != RPORT_MAGIC) { printk(KERN_WARNING "Warning: bad magic number for rocketport " "struct in %s\n", routine); return 1; } #endif return 0; } /* Serial port receive data function. Called (from timer poll) when an AIOPIC signals * that receive data is present on a serial port. Pulls data from FIFO, moves it into the * tty layer. */ static void rp_do_receive(struct r_port *info, struct tty_struct *tty, CHANNEL_t * cp, unsigned int ChanStatus) { unsigned int CharNStat; int ToRecv, wRecv, space; unsigned char *cbuf; ToRecv = sGetRxCnt(cp); #ifdef ROCKET_DEBUG_INTR printk(KERN_INFO "rp_do_receive(%d)...\n", ToRecv); #endif if (ToRecv == 0) return; /* * if status indicates there are errored characters in the * FIFO, then enter status mode (a word in FIFO holds * character and status). */ if (ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) { if (!(ChanStatus & STATMODE)) { #ifdef ROCKET_DEBUG_RECEIVE printk(KERN_INFO "Entering STATMODE...\n"); #endif ChanStatus |= STATMODE; sEnRxStatusMode(cp); } } /* * if we previously entered status mode, then read down the * FIFO one word at a time, pulling apart the character and * the status. Update error counters depending on status */ if (ChanStatus & STATMODE) { #ifdef ROCKET_DEBUG_RECEIVE printk(KERN_INFO "Ignore %x, read %x...\n", info->ignore_status_mask, info->read_status_mask); #endif while (ToRecv) { char flag; CharNStat = sInW(sGetTxRxDataIO(cp)); #ifdef ROCKET_DEBUG_RECEIVE printk(KERN_INFO "%x...\n", CharNStat); #endif if (CharNStat & STMBREAKH) CharNStat &= ~(STMFRAMEH | STMPARITYH); if (CharNStat & info->ignore_status_mask) { ToRecv--; continue; } CharNStat &= info->read_status_mask; if (CharNStat & STMBREAKH) flag = TTY_BREAK; else if (CharNStat & STMPARITYH) flag = TTY_PARITY; else if (CharNStat & STMFRAMEH) flag = TTY_FRAME; else if (CharNStat & STMRCVROVRH) flag = TTY_OVERRUN; else flag = TTY_NORMAL; tty_insert_flip_char(tty, CharNStat & 0xff, flag); ToRecv--; } /* * after we've emptied the FIFO in status mode, turn * status mode back off */ if (sGetRxCnt(cp) == 0) { #ifdef ROCKET_DEBUG_RECEIVE printk(KERN_INFO "Status mode off.\n"); #endif sDisRxStatusMode(cp); } } else { /* * we aren't in status mode, so read down the FIFO two * characters at time by doing repeated word IO * transfer. */ space = tty_prepare_flip_string(tty, &cbuf, ToRecv); if (space < ToRecv) { #ifdef ROCKET_DEBUG_RECEIVE printk(KERN_INFO "rp_do_receive:insufficient space ToRecv=%d space=%d\n", ToRecv, space); #endif if (space <= 0) return; ToRecv = space; } wRecv = ToRecv >> 1; if (wRecv) sInStrW(sGetTxRxDataIO(cp), (unsigned short *) cbuf, wRecv); if (ToRecv & 1) cbuf[ToRecv - 1] = sInB(sGetTxRxDataIO(cp)); } /* Push the data up to the tty layer */ tty_flip_buffer_push(tty); } /* * Serial port transmit data function. Called from the timer polling loop as a * result of a bit set in xmit_flags[], indicating data (from the tty layer) is ready * to be sent out the serial port. Data is buffered in rp_table[line].xmit_buf, it is * moved to the port's xmit FIFO. *info is critical data, protected by spinlocks. */ static void rp_do_transmit(struct r_port *info) { int c; CHANNEL_t *cp = &info->channel; struct tty_struct *tty; unsigned long flags; #ifdef ROCKET_DEBUG_INTR printk(KERN_DEBUG "%s\n", __func__); #endif if (!info) return; tty = tty_port_tty_get(&info->port); if (tty == NULL) { printk(KERN_WARNING "rp: WARNING %s called with tty==NULL\n", __func__); clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); return; } spin_lock_irqsave(&info->slock, flags); info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); /* Loop sending data to FIFO until done or FIFO full */ while (1) { if (tty->stopped || tty->hw_stopped) break; c = min(info->xmit_fifo_room, info->xmit_cnt); c = min(c, XMIT_BUF_SIZE - info->xmit_tail); if (c <= 0 || info->xmit_fifo_room <= 0) break; sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) (info->xmit_buf + info->xmit_tail), c / 2); if (c & 1) sOutB(sGetTxRxDataIO(cp), info->xmit_buf[info->xmit_tail + c - 1]); info->xmit_tail += c; info->xmit_tail &= XMIT_BUF_SIZE - 1; info->xmit_cnt -= c; info->xmit_fifo_room -= c; #ifdef ROCKET_DEBUG_INTR printk(KERN_INFO "tx %d chars...\n", c); #endif } if (info->xmit_cnt == 0) clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); if (info->xmit_cnt < WAKEUP_CHARS) { tty_wakeup(tty); #ifdef ROCKETPORT_HAVE_POLL_WAIT wake_up_interruptible(&tty->poll_wait); #endif } spin_unlock_irqrestore(&info->slock, flags); tty_kref_put(tty); #ifdef ROCKET_DEBUG_INTR printk(KERN_DEBUG "(%d,%d,%d,%d)...\n", info->xmit_cnt, info->xmit_head, info->xmit_tail, info->xmit_fifo_room); #endif } /* * Called when a serial port signals it has read data in it's RX FIFO. * It checks what interrupts are pending and services them, including * receiving serial data. */ static void rp_handle_port(struct r_port *info) { CHANNEL_t *cp; struct tty_struct *tty; unsigned int IntMask, ChanStatus; if (!info) return; if ((info->port.flags & ASYNC_INITIALIZED) == 0) { printk(KERN_WARNING "rp: WARNING: rp_handle_port called with " "info->flags & NOT_INIT\n"); return; } tty = tty_port_tty_get(&info->port); if (!tty) { printk(KERN_WARNING "rp: WARNING: rp_handle_port called with " "tty==NULL\n"); return; } cp = &info->channel; IntMask = sGetChanIntID(cp) & info->intmask; #ifdef ROCKET_DEBUG_INTR printk(KERN_INFO "rp_interrupt %02x...\n", IntMask); #endif ChanStatus = sGetChanStatus(cp); if (IntMask & RXF_TRIG) { /* Rx FIFO trigger level */ rp_do_receive(info, tty, cp, ChanStatus); } if (IntMask & DELTA_CD) { /* CD change */ #if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_INTR) || defined(ROCKET_DEBUG_HANGUP)) printk(KERN_INFO "ttyR%d CD now %s...\n", info->line, (ChanStatus & CD_ACT) ? "on" : "off"); #endif if (!(ChanStatus & CD_ACT) && info->cd_status) { #ifdef ROCKET_DEBUG_HANGUP printk(KERN_INFO "CD drop, calling hangup.\n"); #endif tty_hangup(tty); } info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0; wake_up_interruptible(&info->port.open_wait); } #ifdef ROCKET_DEBUG_INTR if (IntMask & DELTA_CTS) { /* CTS change */ printk(KERN_INFO "CTS change...\n"); } if (IntMask & DELTA_DSR) { /* DSR change */ printk(KERN_INFO "DSR change...\n"); } #endif tty_kref_put(tty); } /* * The top level polling routine. Repeats every 1/100 HZ (10ms). */ static void rp_do_poll(unsigned long dummy) { CONTROLLER_t *ctlp; int ctrl, aiop, ch, line; unsigned int xmitmask, i; unsigned int CtlMask; unsigned char AiopMask; Word_t bit; /* Walk through all the boards (ctrl's) */ for (ctrl = 0; ctrl < max_board; ctrl++) { if (rcktpt_io_addr[ctrl] <= 0) continue; /* Get a ptr to the board's control struct */ ctlp = sCtlNumToCtlPtr(ctrl); /* Get the interrupt status from the board */ #ifdef CONFIG_PCI if (ctlp->BusType == isPCI) CtlMask = sPCIGetControllerIntStatus(ctlp); else #endif CtlMask = sGetControllerIntStatus(ctlp); /* Check if any AIOP read bits are set */ for (aiop = 0; CtlMask; aiop++) { bit = ctlp->AiopIntrBits[aiop]; if (CtlMask & bit) { CtlMask &= ~bit; AiopMask = sGetAiopIntStatus(ctlp, aiop); /* Check if any port read bits are set */ for (ch = 0; AiopMask; AiopMask >>= 1, ch++) { if (AiopMask & 1) { /* Get the line number (/dev/ttyRx number). */ /* Read the data from the port. */ line = GetLineNumber(ctrl, aiop, ch); rp_handle_port(rp_table[line]); } } } } xmitmask = xmit_flags[ctrl]; /* * xmit_flags contains bit-significant flags, indicating there is data * to xmit on the port. Bit 0 is port 0 on this board, bit 1 is port * 1, ... (32 total possible). The variable i has the aiop and ch * numbers encoded in it (port 0-7 are aiop0, 8-15 are aiop1, etc). */ if (xmitmask) { for (i = 0; i < rocketModel[ctrl].numPorts; i++) { if (xmitmask & (1 << i)) { aiop = (i & 0x18) >> 3; ch = i & 0x07; line = GetLineNumber(ctrl, aiop, ch); rp_do_transmit(rp_table[line]); } } } } /* * Reset the timer so we get called at the next clock tick (10ms). */ if (atomic_read(&rp_num_ports_open)) mod_timer(&rocket_timer, jiffies + POLL_PERIOD); } /* * Initializes the r_port structure for a port, as well as enabling the port on * the board. * Inputs: board, aiop, chan numbers */ static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) { unsigned rocketMode; struct r_port *info; int line; CONTROLLER_T *ctlp; /* Get the next available line number */ line = SetLineNumber(board, aiop, chan); ctlp = sCtlNumToCtlPtr(board); /* Get a r_port struct for the port, fill it in and save it globally, indexed by line number */ info = kzalloc(sizeof (struct r_port), GFP_KERNEL); if (!info) { printk(KERN_ERR "Couldn't allocate info struct for line #%d\n", line); return; } info->magic = RPORT_MAGIC; info->line = line; info->ctlp = ctlp; info->board = board; info->aiop = aiop; info->chan = chan; tty_port_init(&info->port); info->port.ops = &rocket_port_ops; init_completion(&info->close_wait); info->flags &= ~ROCKET_MODE_MASK; switch (pc104[board][line]) { case 422: info->flags |= ROCKET_MODE_RS422; break; case 485: info->flags |= ROCKET_MODE_RS485; break; case 232: default: info->flags |= ROCKET_MODE_RS232; break; } info->intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; if (sInitChan(ctlp, &info->channel, aiop, chan) == 0) { printk(KERN_ERR "RocketPort sInitChan(%d, %d, %d) failed!\n", board, aiop, chan); kfree(info); return; } rocketMode = info->flags & ROCKET_MODE_MASK; if ((info->flags & ROCKET_RTS_TOGGLE) || (rocketMode == ROCKET_MODE_RS485)) sEnRTSToggle(&info->channel); else sDisRTSToggle(&info->channel); if (ctlp->boardType == ROCKET_TYPE_PC104) { switch (rocketMode) { case ROCKET_MODE_RS485: sSetInterfaceMode(&info->channel, InterfaceModeRS485); break; case ROCKET_MODE_RS422: sSetInterfaceMode(&info->channel, InterfaceModeRS422); break; case ROCKET_MODE_RS232: default: if (info->flags & ROCKET_RTS_TOGGLE) sSetInterfaceMode(&info->channel, InterfaceModeRS232T); else sSetInterfaceMode(&info->channel, InterfaceModeRS232); break; } } spin_lock_init(&info->slock); mutex_init(&info->write_mtx); rp_table[line] = info; tty_register_device(rocket_driver, line, pci_dev ? &pci_dev->dev : NULL); } /* * Configures a rocketport port according to its termio settings. Called from * user mode into the driver (exception handler). *info CD manipulation is spinlock protected. */ static void configure_r_port(struct tty_struct *tty, struct r_port *info, struct ktermios *old_termios) { unsigned cflag; unsigned long flags; unsigned rocketMode; int bits, baud, divisor; CHANNEL_t *cp; struct ktermios *t = tty->termios; cp = &info->channel; cflag = t->c_cflag; /* Byte size and parity */ if ((cflag & CSIZE) == CS8) { sSetData8(cp); bits = 10; } else { sSetData7(cp); bits = 9; } if (cflag & CSTOPB) { sSetStop2(cp); bits++; } else { sSetStop1(cp); } if (cflag & PARENB) { sEnParity(cp); bits++; if (cflag & PARODD) { sSetOddParity(cp); } else { sSetEvenParity(cp); } } else { sDisParity(cp); } /* baud rate */ baud = tty_get_baud_rate(tty); if (!baud) baud = 9600; divisor = ((rp_baud_base[info->board] + (baud >> 1)) / baud) - 1; if ((divisor >= 8192 || divisor < 0) && old_termios) { baud = tty_termios_baud_rate(old_termios); if (!baud) baud = 9600; divisor = (rp_baud_base[info->board] / baud) - 1; } if (divisor >= 8192 || divisor < 0) { baud = 9600; divisor = (rp_baud_base[info->board] / baud) - 1; } info->cps = baud / bits; sSetBaud(cp, divisor); /* FIXME: Should really back compute a baud rate from the divisor */ tty_encode_baud_rate(tty, baud, baud); if (cflag & CRTSCTS) { info->intmask |= DELTA_CTS; sEnCTSFlowCtl(cp); } else { info->intmask &= ~DELTA_CTS; sDisCTSFlowCtl(cp); } if (cflag & CLOCAL) { info->intmask &= ~DELTA_CD; } else { spin_lock_irqsave(&info->slock, flags); if (sGetChanStatus(cp) & CD_ACT) info->cd_status = 1; else info->cd_status = 0; info->intmask |= DELTA_CD; spin_unlock_irqrestore(&info->slock, flags); } /* * Handle software flow control in the board */ #ifdef ROCKET_SOFT_FLOW if (I_IXON(tty)) { sEnTxSoftFlowCtl(cp); if (I_IXANY(tty)) { sEnIXANY(cp); } else { sDisIXANY(cp); } sSetTxXONChar(cp, START_CHAR(tty)); sSetTxXOFFChar(cp, STOP_CHAR(tty)); } else { sDisTxSoftFlowCtl(cp); sDisIXANY(cp); sClrTxXOFF(cp); } #endif /* * Set up ignore/read mask words */ info->read_status_mask = STMRCVROVRH | 0xFF; if (I_INPCK(tty)) info->read_status_mask |= STMFRAMEH | STMPARITYH; if (I_BRKINT(tty) || I_PARMRK(tty)) info->read_status_mask |= STMBREAKH; /* * Characters to ignore */ info->ignore_status_mask = 0; if (I_IGNPAR(tty)) info->ignore_status_mask |= STMFRAMEH | STMPARITYH; if (I_IGNBRK(tty)) { info->ignore_status_mask |= STMBREAKH; /* * If we're ignoring parity and break indicators, * ignore overruns too. (For real raw support). */ if (I_IGNPAR(tty)) info->ignore_status_mask |= STMRCVROVRH; } rocketMode = info->flags & ROCKET_MODE_MASK; if ((info->flags & ROCKET_RTS_TOGGLE) || (rocketMode == ROCKET_MODE_RS485)) sEnRTSToggle(cp); else sDisRTSToggle(cp); sSetRTS(&info->channel); if (cp->CtlP->boardType == ROCKET_TYPE_PC104) { switch (rocketMode) { case ROCKET_MODE_RS485: sSetInterfaceMode(cp, InterfaceModeRS485); break; case ROCKET_MODE_RS422: sSetInterfaceMode(cp, InterfaceModeRS422); break; case ROCKET_MODE_RS232: default: if (info->flags & ROCKET_RTS_TOGGLE) sSetInterfaceMode(cp, InterfaceModeRS232T); else sSetInterfaceMode(cp, InterfaceModeRS232); break; } } } static int carrier_raised(struct tty_port *port) { struct r_port *info = container_of(port, struct r_port, port); return (sGetChanStatusLo(&info->channel) & CD_ACT) ? 1 : 0; } static void dtr_rts(struct tty_port *port, int on) { struct r_port *info = container_of(port, struct r_port, port); if (on) { sSetDTR(&info->channel); sSetRTS(&info->channel); } else { sClrDTR(&info->channel); sClrRTS(&info->channel); } } /* * Exception handler that opens a serial port. Creates xmit_buf storage, fills in * port's r_port struct. Initializes the port hardware. */ static int rp_open(struct tty_struct *tty, struct file *filp) { struct r_port *info; struct tty_port *port; int line = 0, retval; CHANNEL_t *cp; unsigned long page; line = tty->index; if (line < 0 || line >= MAX_RP_PORTS || ((info = rp_table[line]) == NULL)) return -ENXIO; port = &info->port; page = __get_free_page(GFP_KERNEL); if (!page) return -ENOMEM; if (port->flags & ASYNC_CLOSING) { retval = wait_for_completion_interruptible(&info->close_wait); free_page(page); if (retval) return retval; return ((port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); } /* * We must not sleep from here until the port is marked fully in use. */ if (info->xmit_buf) free_page(page); else info->xmit_buf = (unsigned char *) page; tty->driver_data = info; tty_port_tty_set(port, tty); if (port->count++ == 0) { atomic_inc(&rp_num_ports_open); #ifdef ROCKET_DEBUG_OPEN printk(KERN_INFO "rocket mod++ = %d...\n", atomic_read(&rp_num_ports_open)); #endif } #ifdef ROCKET_DEBUG_OPEN printk(KERN_INFO "rp_open ttyR%d, count=%d\n", info->line, info->port.count); #endif /* * Info->count is now 1; so it's safe to sleep now. */ if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) { cp = &info->channel; sSetRxTrigger(cp, TRIG_1); if (sGetChanStatus(cp) & CD_ACT) info->cd_status = 1; else info->cd_status = 0; sDisRxStatusMode(cp); sFlushRxFIFO(cp); sFlushTxFIFO(cp); sEnInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); sSetRxTrigger(cp, TRIG_1); sGetChanStatus(cp); sDisRxStatusMode(cp); sClrTxXOFF(cp); sDisCTSFlowCtl(cp); sDisTxSoftFlowCtl(cp); sEnRxFIFO(cp); sEnTransmit(cp); set_bit(ASYNCB_INITIALIZED, &info->port.flags); /* * Set up the tty->alt_speed kludge */ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) tty->alt_speed = 57600; if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI) tty->alt_speed = 115200; if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI) tty->alt_speed = 230400; if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP) tty->alt_speed = 460800; configure_r_port(tty, info, NULL); if (tty->termios->c_cflag & CBAUD) { sSetDTR(cp); sSetRTS(cp); } } /* Starts (or resets) the maint polling loop */ mod_timer(&rocket_timer, jiffies + POLL_PERIOD); retval = tty_port_block_til_ready(port, tty, filp); if (retval) { #ifdef ROCKET_DEBUG_OPEN printk(KERN_INFO "rp_open returning after block_til_ready with %d\n", retval); #endif return retval; } return 0; } /* * Exception handler that closes a serial port. info->port.count is considered critical. */ static void rp_close(struct tty_struct *tty, struct file *filp) { struct r_port *info = tty->driver_data; struct tty_port *port = &info->port; int timeout; CHANNEL_t *cp; if (rocket_paranoia_check(info, "rp_close")) return; #ifdef ROCKET_DEBUG_OPEN printk(KERN_INFO "rp_close ttyR%d, count = %d\n", info->line, info->port.count); #endif if (tty_port_close_start(port, tty, filp) == 0) return; mutex_lock(&port->mutex); cp = &info->channel; /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ timeout = (sGetTxCnt(cp) + 1) * HZ / info->cps; if (timeout == 0) timeout = 1; rp_wait_until_sent(tty, timeout); clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); sDisTransmit(cp); sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); sDisCTSFlowCtl(cp); sDisTxSoftFlowCtl(cp); sClrTxXOFF(cp); sFlushRxFIFO(cp); sFlushTxFIFO(cp); sClrRTS(cp); if (C_HUPCL(tty)) sClrDTR(cp); rp_flush_buffer(tty); tty_ldisc_flush(tty); clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); /* We can't yet use tty_port_close_end as the buffer handling in this driver is a bit different to the usual */ if (port->blocked_open) { if (port->close_delay) { msleep_interruptible(jiffies_to_msecs(port->close_delay)); } wake_up_interruptible(&port->open_wait); } else { if (info->xmit_buf) { free_page((unsigned long) info->xmit_buf); info->xmit_buf = NULL; } } spin_lock_irq(&port->lock); info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING | ASYNC_NORMAL_ACTIVE); tty->closing = 0; spin_unlock_irq(&port->lock); mutex_unlock(&port->mutex); tty_port_tty_set(port, NULL); wake_up_interruptible(&port->close_wait); complete_all(&info->close_wait); atomic_dec(&rp_num_ports_open); #ifdef ROCKET_DEBUG_OPEN printk(KERN_INFO "rocket mod-- = %d...\n", atomic_read(&rp_num_ports_open)); printk(KERN_INFO "rp_close ttyR%d complete shutdown\n", info->line); #endif } static void rp_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct r_port *info = tty->driver_data; CHANNEL_t *cp; unsigned cflag; if (rocket_paranoia_check(info, "rp_set_termios")) return; cflag = tty->termios->c_cflag; /* * This driver doesn't support CS5 or CS6 */ if (((cflag & CSIZE) == CS5) || ((cflag & CSIZE) == CS6)) tty->termios->c_cflag = ((cflag & ~CSIZE) | (old_termios->c_cflag & CSIZE)); /* Or CMSPAR */ tty->termios->c_cflag &= ~CMSPAR; configure_r_port(tty, info, old_termios); cp = &info->channel; /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(tty->termios->c_cflag & CBAUD)) { sClrDTR(cp); sClrRTS(cp); } /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (tty->termios->c_cflag & CBAUD)) { if (!tty->hw_stopped || !(tty->termios->c_cflag & CRTSCTS)) sSetRTS(cp); sSetDTR(cp); } if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; 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; if (!retinfo) return -EFAULT; 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)); configure_r_port(tty, info, NULL); mutex_unlock(&info->port.mutex); return 0; } 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; if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) tty->alt_speed = 57600; if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI) tty->alt_speed = 115200; if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI) tty->alt_speed = 230400; if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP) tty->alt_speed = 460800; 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; if (!retports) return -EFAULT; memset(&tmp, 0, sizeof (tmp)); 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))) return -EFAULT; return 0; } 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_STRUCT: if (copy_to_user(argp, info, sizeof (struct r_port))) ret = -EFAULT; break; case RCKP_GET_CONFIG: ret = get_config(info, argp); break; case RCKP_SET_CONFIG: ret = set_config(tty, info, argp); break; case RCKP_GET_PORTS: ret = get_ports(info, argp); break; case RCKP_RESET_RM2: ret = reset_rm2(info, argp); break; case RCKP_GET_VERSION: 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: %d....\n", tty->name, tty->ldisc.chars_in_buffer(tty)); #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: %d....\n", tty->name, tty->ldisc.chars_in_buffer(tty)); #endif if (rocket_paranoia_check(info, "rp_throttle")) 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 RP_wait_until_sent(%d) (jiff=%lu)...\n", 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.flags & ASYNC_CLOSING) { spin_unlock_irqrestore(&info->port.lock, flags); return; } 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); clear_bit(ASYNCB_INITIALIZED, &info->port.flags); 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 && !tty->hw_stopped && info->xmit_fifo_room == 0) info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); if (tty->stopped || tty->hw_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 && !tty->hw_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 && !tty->hw_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 (!test_bit(ASYNCB_NORMAL_ACTIVE, &info->port.flags)) 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 && !tty->hw_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 struct pci_device_id __devinitdata __used rocket_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_ANY_ID) }, { } }; MODULE_DEVICE_TABLE(pci, rocket_pci_ids); /* * 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, num_chan, 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_enable_device(dev)) 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); num_chan = ports_per_aiop; for (chan = 0; chan < num_chan; 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)) { num_chan = ports_per_aiop; for (chan = 0; chan < num_chan; chan++) sPCIModemReset(ctlp, chan, 1); msleep(500); for (chan = 0; chan < num_chan; 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->owner = THIS_MODULE; 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 lookin