⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rocket.c

📁 powerpc内核mpc8241linux系统下char驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * 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 + -