📄 rocket.c
字号:
/* * Rocketport device driver for Linux * * Written by Theodore Ts'o, 1995, 1996, 1997. * * Copyright (C) 1995, 1996, 1997 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. *//* * Minor number schema: * * +-------------------------------+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | * +---+-------+-------+-----------+ * | C | Board | AIOP | Port # | * +---+-------+-------+-----------+ * * C=0 implements normal POSIX tty. * C=1 is reserved for the callout device. * * Normally, the user won't have to worry about the AIOP; as far as * the user is concerned, the lower 5 bits of the minor number address * the ports on a particular board (from 0 up to 32). *//* Kernel includes */#include <linux/config.h>#include <linux/version.h>#if (defined(CONFIG_PCI) && (LINUX_VERSION_CODE >= 131072))#define ENABLE_PCI#endif#if (LINUX_VERSION_CODE > 66304)#define NEW_MODULES#ifdef LOCAL_ROCKET_H /* We're building standalone */#define MODULE#endif#endif#ifdef NEW_MODULES#ifdef MODVERSIONS#include <linux/modversions.h>#endif#include <linux/module.h>#else /* !NEW_MODULES */#ifdef MODVERSIONS#define MODULE#endif#include <linux/module.h>#endif /* NEW_MODULES */#include <linux/errno.h>#include <linux/major.h>#include <linux/kernel.h>#include <linux/signal.h>#include <linux/malloc.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/ptrace.h>#include <linux/ioport.h>#ifdef ENABLE_PCI#include <linux/pci.h>#if (LINUX_VERSION_CODE < 0x020163) /* 2.1.99 */#include <linux/bios32.h>#endif#endif#if (LINUX_VERSION_CODE >= 131343) /* 2.1.15 -- XX get correct version */#include <linux/init.h>#else#define __initfunc(x) x#endif #include "rocket_int.h"#ifdef LOCAL_ROCKET_H#include "rocket.h"#include "version.h"#else#include <linux/rocket.h>#define ROCKET_VERSION "1.14c"#define ROCKET_DATE "24-Aug-98"#endif /* LOCAL_ROCKET_H */#define ROCKET_PARANOIA_CHECK#define 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 /* CAUTION!!!!! The TIME_STAT Function relies on the Pentium 64 bit * register. For various reasons related to 1.2.13, the test for this * register is omitted from this driver. If you are going to enable * this option, make sure you are running a Pentium CPU and that a * cat of /proc/cpuinfo shows ability TS Counters as Yes. Warning part * done, don't cry to me if you enable this options and things won't * work. If it gives you any problems, then disable the option. The code * in this function is pretty straight forward, if it breaks on your * CPU, there is probably something funny about your CPU. */#undef TIME_STAT /* For performing timing statistics on driver. */ /* Produces printks, one every TIME_COUNTER loops, eats */ /* some of your CPU time. Good for testing or */ /* other checking, otherwise, leave it undefed */ /* Doug Ledford */#define TIME_STAT_CPU 100 /* This needs to be set to your processor speed */ /* For example, 100Mhz CPU, set this to 100 */#define TIME_COUNTER 180000 /* This is how many iterations to run before */ /* performing the printk statements. */ /* 6000 = 1 minute, 360000 = 1 hour, etc. */ /* Since time_stat is long long, this */ /* Can be really high if you want :) */#undef TIME_STAT_VERBOSE /* Undef this if you want a terse log message. */#define _INLINE_ inline/* * Until we get a formal timer assignment */#ifndef COMTROL_TIMER#define COMTROL_TIMER 26#endif#ifndef NEW_MODULES/* * NB. we must include the kernel idenfication string in to install the module. */#include <linux/version.h>/*static*/ char kernel_version[] = UTS_RELEASE;#endifstatic struct r_port *rp_table[MAX_RP_PORTS];static struct tty_struct *rocket_table[MAX_RP_PORTS];static unsigned int xmit_flags[NUM_BOARDS];static struct termios *rocket_termios[MAX_RP_PORTS];static struct termios *rocket_termios_locked[MAX_RP_PORTS];static void rp_wait_until_sent(struct tty_struct *tty, int timeout);static void rp_flush_buffer(struct tty_struct *tty);static struct tty_driver rocket_driver, callout_driver;static int rocket_refcount = 0;static int rp_num_ports_open = 0;unsigned long board1 = 0;unsigned long board2 = 0;unsigned long board3 = 0;unsigned long board4 = 0;unsigned long controller = 0;unsigned long support_low_speed = 0;int rp_baud_base = 460800;static unsigned long rcktpt_io_addr[NUM_BOARDS];static int max_board;#ifdef TIME_STATstatic unsigned long long time_stat = 0;static unsigned long time_stat_short = 0;static unsigned long time_stat_long = 0;static unsigned long time_counter = 0;#endif#if ((LINUX_VERSION_CODE > 0x020111) && defined(MODULE))MODULE_AUTHOR("Theodore Ts'o");MODULE_DESCRIPTION("Comtrol Rocketport driver");MODULE_PARM(board1, "i");MODULE_PARM_DESC(board1, "I/O port for (ISA) board #1");MODULE_PARM(board2, "i");MODULE_PARM_DESC(board2, "I/O port for (ISA) board #2");MODULE_PARM(board3, "i");MODULE_PARM_DESC(board3, "I/O port for (ISA) board #3");MODULE_PARM(board4, "i");MODULE_PARM_DESC(board4, "I/O port for (ISA) board #4");MODULE_PARM(controller, "i");MODULE_PARM_DESC(controller, "I/O port for (ISA) rocketport controller");MODULE_PARM(support_low_speed, "i");MODULE_PARM_DESC(support_low_speed, "0 means support 50 baud, 1 means support 460400 baud"); #endif/* * Provide backwards compatibility for kernels prior to 2.1.8. */#if (LINUX_VERSION_CODE < 0x20000)typedef dev_t kdev_t;#endif#if (LINUX_VERSION_CODE < 131336)int copy_from_user(void *to, const void *from_user, unsigned long len){ int error; error = verify_area(VERIFY_READ, from_user, len); if (error) return len; memcpy_fromfs(to, from_user, len); return 0;}int copy_to_user(void *to_user, const void *from, unsigned long len){ int error; error = verify_area(VERIFY_WRITE, to_user, len); if (error) return len; memcpy_tofs(to_user, from, len); return 0;}static inline int signal_pending(struct task_struct *p){ return (p->signal & (~p->blocked != 0));}#else#include <asm/uaccess.h>#endif/* * tmp_buf is used as a temporary buffer by rp_write. We need to * lock it in case the memcpy_fromfs blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one * buffer across all the serial ports, since it significantly saves * memory if large numbers of serial ports are open. */static unsigned char *tmp_buf = 0;static struct semaphore tmp_buf_sem = MUTEX;static void rp_start(struct tty_struct *tty);static inline int rocket_paranoia_check(struct r_port *info, kdev_t device, const char *routine){#ifdef ROCKET_PARANOIA_CHECK static const char *badmagic = "Warning: bad magic number for rocketport struct (%d, %d) in %s\n"; if (!info) return 1; if (info->magic != RPORT_MAGIC) { printk(badmagic, MAJOR(device), MINOR(device), routine); return 1; }#endif return 0;}/* * Here begins the interrupt/polling routine for the Rocketport! */static _INLINE_ 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, count; unsigned char *cbuf; char *fbuf; ToRecv= sGetRxCnt(cp); space = 2*TTY_FLIPBUF_SIZE; cbuf = tty->flip.char_buf; fbuf = tty->flip.flag_buf; count = 0;#ifdef ROCKET_DEBUG_INTR printk("rp_do_receive(%d, %d)...", ToRecv, space);#endif if (ToRecv == 0 || (space <= 0)) return; /* * determine how many we can actually read in. If we can't * read any in then we have a software overrun condition. */ if (ToRecv > space) ToRecv = space; /* * if 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("Entering STATMODE...");#endif ChanStatus |= STATMODE; sEnRxStatusMode(cp); } } /* * if we previously entered status mode, then read down the * FIFO one word at a time, pulling apart the character and * the status. Update error counters depending on status */ if (ChanStatus & STATMODE) {#ifdef ROCKET_DEBUG_RECEIVE printk("Ignore %x, read %x...", info->ignore_status_mask, info->read_status_mask);#endif while (ToRecv) { CharNStat= sInW(sGetTxRxDataIO(cp));#ifdef ROCKET_DEBUG_RECEIVE printk("%x...", CharNStat);#endif if (CharNStat & STMBREAKH) CharNStat &= ~(STMFRAMEH | STMPARITYH); if (CharNStat & info->ignore_status_mask) { ToRecv--; continue; } CharNStat &= info->read_status_mask; if (CharNStat & STMBREAKH) { *fbuf++ = TTY_BREAK;#if 0 if (info->flags & ROCKET_SAK) do_SAK(tty);#endif } else if (CharNStat & STMPARITYH) *fbuf++ = TTY_PARITY; else if (CharNStat & STMFRAMEH) *fbuf++ = TTY_FRAME; else if (CharNStat & STMRCVROVRH) *fbuf++ =TTY_OVERRUN; else *fbuf++ = 0; *cbuf++ = CharNStat & 0xff; count++; ToRecv--; } /* * after we've emptied the FIFO in status mode, turn * status mode back off */ if (sGetRxCnt(cp) == 0) {#ifdef ROCKET_DEBUG_RECEIVE printk("Status mode off.\n");#endif sDisRxStatusMode(cp); } } else { /* * we aren't in status mode, so read down the FIFO two * characters at time by doing repeated word IO * transfer. */ wRecv= ToRecv >> 1; if (wRecv) sInStrW(sGetTxRxDataIO(cp), cbuf, wRecv); if (ToRecv & 1) cbuf[ToRecv-1] = sInB(sGetTxRxDataIO(cp)); memset(fbuf, 0, ToRecv); cbuf += ToRecv; fbuf += ToRecv; count += ToRecv; } tty->ldisc.receive_buf(tty, tty->flip.char_buf, tty->flip.flag_buf, count);}/* * This routine is called when a transmit interrupt is found. It's * responsible for pushing data found in the transmit buffer out to * the serial card. */static _INLINE_ void rp_do_transmit(struct r_port *info){ int c; CHANNEL_t *cp = &info->channel; struct tty_struct *tty; #ifdef ROCKET_DEBUG_INTR printk("rp_do_transmit ");#endif if (!info) return; if (!info->tty) { printk("rp: WARNING rp_do_transmit called with info->tty==NULL\n"); xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f)); return; } tty = info->tty; info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); while (1) { if (tty->stopped || tty->hw_stopped) break; c = MIN(info->xmit_fifo_room, MIN(info->xmit_cnt, XMIT_BUF_SIZE - info->xmit_tail)); if (c <= 0 || info->xmit_fifo_room <= 0) break; sOutStrW(sGetTxRxDataIO(cp), 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("tx %d chars...", c);#endif } if (info->xmit_cnt == 0) xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f)); if (info->xmit_cnt < WAKEUP_CHARS) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); }#ifdef ROCKET_DEBUG_INTR printk("(%d,%d,%d,%d)...", info->xmit_cnt, info->xmit_head, info->xmit_tail, info->xmit_fifo_room);#endif}/* * This function is called for each port which has signalled an * interrupt. It checks what interrupts are pending and services * them. */static _INLINE_ void rp_handle_port(struct r_port *info){ CHANNEL_t *cp; struct tty_struct *tty; unsigned int IntMask, ChanStatus; if (!info) return; if ( (info->flags & ROCKET_INITIALIZED) == 0 ) { printk("rp: WARNING: rp_handle_port called with info->flags & NOT_INIT\n"); return; } if (!info->tty) { printk("rp: WARNING: rp_handle_port called with info->tty==NULL\n"); return; } cp = &info->channel; tty = info->tty; IntMask = sGetChanIntID(cp) & info->intmask;#ifdef ROCKET_DEBUG_INTR printk("rp_interrupt %02x...", IntMask);#endif ChanStatus= sGetChanStatus(cp); if (IntMask & RXF_TRIG) { /* Rx FIFO trigger level */ rp_do_receive(info, tty, cp, ChanStatus); }#if 0 if (IntMask & SRC_INT) { /* Special receive condition */ }#endif if (IntMask & DELTA_CD) { /* CD change */#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_INTR) || \ defined(ROCKET_DEBUG_HANGUP)) printk("ttyR%d CD now %s...", info->line, (ChanStatus & CD_ACT) ? "on" : "off");#endif if (!(ChanStatus & CD_ACT) && info->cd_status && !((info->flags & ROCKET_CALLOUT_ACTIVE) && (info->flags & ROCKET_CALLOUT_NOHUP))) {#ifdef ROCKET_DEBUG_HANGUP
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -