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

📄 serial.c

📁 上传linux-jx2410的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * * BRIEF MODULE DESCRIPTION *	Au1000 serial port driver. * * Copyright 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. *         	ppopov@mvista.com or source@mvista.com * *  Derived almost entirely from drivers/char/serial.c: * *  Copyright (C) 1991, 1992  Linus Torvalds *  Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997,  * 		1998, 1999  Theodore Ts'o * *  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  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT, *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *  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. */static char *serial_version = "1.01";static char *serial_revdate = "2001-02-08";#include <linux/config.h>#include <linux/version.h>#undef SERIAL_PARANOIA_CHECK#define CONFIG_SERIAL_NOPAUSE_IO#define SERIAL_DO_RESTART/* Set of debugging defines */#undef SERIAL_DEBUG_INTR#undef SERIAL_DEBUG_OPEN#undef SERIAL_DEBUG_FLOW#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT#undef SERIAL_DEBUG_PCI#undef SERIAL_DEBUG_AUTOCONF#ifdef MODULE#undef CONFIG_AU1000_SERIAL_CONSOLE#endif#define CONFIG_SERIAL_RSA#define RS_STROBE_TIME (10*HZ)#define RS_ISR_PASS_LIMIT 256  /* * End of serial driver configuration section. */#include <linux/module.h>#include <linux/types.h>#ifdef LOCAL_HEADERS#include "serial_local.h"#else#include <linux/serial.h>#include <linux/serialP.h>#include <asm/au1000.h>#include <asm/serial.h>#define LOCAL_VERSTRING ""#endif#include <linux/errno.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/init.h>#include <asm/uaccess.h>#include <linux/delay.h>#ifdef CONFIG_AU1000_SERIAL_CONSOLE#include <linux/console.h>#endif#ifdef CONFIG_MAGIC_SYSRQ#include <linux/sysrq.h>#endif#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/bitops.h>#ifdef CONFIG_MAC_SERIAL#define SERIAL_DEV_OFFSET	2#else#define SERIAL_DEV_OFFSET	0#endif#ifdef SERIAL_INLINE#define _INLINE_ inline#else#define _INLINE_#endifstatic char *serial_name = "Serial driver";static DECLARE_TASK_QUEUE(tq_serial);static struct tty_driver serial_driver, callout_driver;static int serial_refcount;static struct timer_list serial_timer;extern unsigned long get_au1000_uart_baud(void);/* serial subtype definitions */#ifndef SERIAL_TYPE_NORMAL#define SERIAL_TYPE_NORMAL	1#define SERIAL_TYPE_CALLOUT	2#endif/* number of characters left in xmit buffer before we ask for more */#define WAKEUP_CHARS 256/* * IRQ_timeout		- How long the timeout should be for each IRQ * 				should be after the IRQ has been active. */static struct async_struct *IRQ_ports[NR_IRQS];static int IRQ_timeout[NR_IRQS];#ifdef CONFIG_AU1000_SERIAL_CONSOLEstatic struct console sercons;static int lsr_break_flag;#endif#if defined(CONFIG_AU1000_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)static unsigned long break_pressed; /* break, really ... */#endifstatic void autoconfig(struct serial_state * state);static void change_speed(struct async_struct *info, struct termios *old);static void rs_wait_until_sent(struct tty_struct *tty, int timeout);/* * Here we define the default xmit fifo size used for each type of * UART */static struct serial_uart_config uart_config[] = {	{ "unknown", 1, 0 }, 	{ "8250", 1, 0 }, 	{ "16450", 1, 0 }, 	{ "16550", 1, 0 }, 	{ 0, 0}};static struct serial_state rs_table[RS_TABLE_SIZE] = {	SERIAL_PORT_DFNS	/* Defined in serial.h */};#define NR_PORTS	(sizeof(rs_table)/sizeof(struct serial_state))#ifndef PREPARE_FUNC#define PREPARE_FUNC(dev)  (dev->prepare)#define ACTIVATE_FUNC(dev)  (dev->activate)#define DEACTIVATE_FUNC(dev)  (dev->deactivate)#endif#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)static struct tty_struct *serial_table[NR_PORTS];static struct termios *serial_termios[NR_PORTS];static struct termios *serial_termios_locked[NR_PORTS];#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s)#else#define DBG_CNT(s)#endif/* * tmp_buf is used as a temporary buffer by serial_write.  We need to * lock it in case the copy_from_user 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;#ifdef DECLARE_MUTEXstatic DECLARE_MUTEX(tmp_buf_sem);#elsestatic struct semaphore tmp_buf_sem = MUTEX;#endifstatic inline int serial_paranoia_check(struct async_struct *info,					kdev_t device, const char *routine){#ifdef SERIAL_PARANOIA_CHECK	static const char *badmagic =		"Warning: bad magic number for serial struct (%s) in %s\n";	static const char *badinfo =		"Warning: null async_struct for (%s) in %s\n";	if (!info) {		printk(badinfo, kdevname(device), routine);		return 1;	}	if (info->magic != SERIAL_MAGIC) {		printk(badmagic, kdevname(device), routine);		return 1;	}#endif	return 0;}static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset){	return (inl(info->port+offset) & 0xff);}static _INLINE_ void serial_out(struct async_struct *info, int offset, int value){	outl(value & 0xff, info->port+offset);}/* * We used to support using pause I/O for certain machines.  We * haven't supported this for a while, but just in case it's badly * needed for certain old 386 machines, I've left these #define's * in.... */#define serial_inp(info, offset)		serial_in(info, offset)#define serial_outp(info, offset, value)	serial_out(info, offset, value)/* * ------------------------------------------------------------ * rs_stop() and rs_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. * ------------------------------------------------------------ */static void rs_stop(struct tty_struct *tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;	unsigned long flags;	if (serial_paranoia_check(info, tty->device, "rs_stop"))		return;		save_flags(flags); cli();	if (info->IER & UART_IER_THRI) {		info->IER &= ~UART_IER_THRI;		serial_out(info, UART_IER, info->IER);	}	restore_flags(flags);}static void rs_start(struct tty_struct *tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;	unsigned long flags;		if (serial_paranoia_check(info, tty->device, "rs_start"))		return;		save_flags(flags); cli();	if (info->xmit.head != info->xmit.tail	    && info->xmit.buf	    && !(info->IER & UART_IER_THRI)) {		info->IER |= UART_IER_THRI;		serial_out(info, UART_IER, info->IER);	}	restore_flags(flags);}/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines.  All of the following * subroutines are declared as inline and are folded into * rs_interrupt().  They were separated out for readability's sake. * * Note: rs_interrupt() is a "fast" interrupt, which means that it * runs with interrupts turned off.  People who may want to modify * rs_interrupt() should try to keep the interrupt handler as fast as * possible.  After you are done making modifications, it is not a bad * idea to do: *  * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c * * and look at the resulting assemble code in serial.s. * * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93 * ----------------------------------------------------------------------- *//* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */static _INLINE_ void rs_sched_event(struct async_struct *info,				  int event){	info->event |= 1 << event;	queue_task(&info->tqueue, &tq_serial);	mark_bh(SERIAL_BH);}static _INLINE_ void receive_chars(struct async_struct *info,				 int *status, struct pt_regs * regs){	struct tty_struct *tty = info->tty;	unsigned char ch;	int ignored = 0;	struct	async_icount *icount;	icount = &info->state->icount;	do {		ch = serial_inp(info, UART_RX);		if (tty->flip.count >= TTY_FLIPBUF_SIZE)			goto ignore_char;		*tty->flip.char_buf_ptr = ch;		icount->rx++;		#ifdef SERIAL_DEBUG_INTR		printk("DR%02x:%02x...", ch, *status);#endif		*tty->flip.flag_buf_ptr = 0;		if (*status & (UART_LSR_BI | UART_LSR_PE |			       UART_LSR_FE | UART_LSR_OE)) {			/*			 * For statistics only			 */			if (*status & UART_LSR_BI) {				*status &= ~(UART_LSR_FE | UART_LSR_PE);				icount->brk++;				/*				 * We do the SysRQ and SAK checking				 * here because otherwise the break				 * may get masked by ignore_status_mask				 * or read_status_mask.				 */#if defined(CONFIG_AU1000_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)				if (info->line == sercons.index) {					if (!break_pressed) {						break_pressed = jiffies;						goto ignore_char;					}					break_pressed = 0;				}#endif				if (info->flags & ASYNC_SAK)					do_SAK(tty);			} else if (*status & UART_LSR_PE)				icount->parity++;			else if (*status & UART_LSR_FE)				icount->frame++;			if (*status & UART_LSR_OE)				icount->overrun++;			/*			 * Now check to see if character should be			 * ignored, and mask off conditions which			 * should be ignored.			 */			if (*status & info->ignore_status_mask) {				if (++ignored > 100)					break;				goto ignore_char;			}			*status &= info->read_status_mask;#ifdef CONFIG_AU1000_SERIAL_CONSOLE			if (info->line == sercons.index) {				/* Recover the break flag from console xmit */				*status |= lsr_break_flag;				lsr_break_flag = 0;			}#endif			if (*status & (UART_LSR_BI)) {#ifdef SERIAL_DEBUG_INTR				printk("handling break....");#endif				*tty->flip.flag_buf_ptr = TTY_BREAK;			} else if (*status & UART_LSR_PE)				*tty->flip.flag_buf_ptr = TTY_PARITY;			else if (*status & UART_LSR_FE)				*tty->flip.flag_buf_ptr = TTY_FRAME;			if (*status & UART_LSR_OE) {				/*				 * Overrun is special, since it's				 * reported immediately, and doesn't				 * affect the current character				 */				tty->flip.count++;				tty->flip.flag_buf_ptr++;				tty->flip.char_buf_ptr++;				*tty->flip.flag_buf_ptr = TTY_OVERRUN;				if (tty->flip.count >= TTY_FLIPBUF_SIZE)					goto ignore_char;			}		}#if defined(CONFIG_AU1000_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)		if (break_pressed && info->line == sercons.index) {			if (ch != 0 &&			    time_before(jiffies, break_pressed + HZ*5)) {				handle_sysrq(ch, regs, NULL, NULL);				break_pressed = 0;				goto ignore_char;			}			break_pressed = 0;		}#endif		tty->flip.flag_buf_ptr++;		tty->flip.char_buf_ptr++;		tty->flip.count++;	ignore_char:		*status = serial_inp(info, UART_LSR);	} while (*status & UART_LSR_DR);	tty_flip_buffer_push(tty);}static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done){	int count;	if (info->x_char) {		serial_outp(info, UART_TX, info->x_char);		info->state->icount.tx++;		info->x_char = 0;		if (intr_done)			*intr_done = 0;		return;	}	if (info->xmit.head == info->xmit.tail	    || info->tty->stopped	    || info->tty->hw_stopped) {		info->IER &= ~UART_IER_THRI;		serial_out(info, UART_IER, info->IER);		return;	}		count = info->xmit_fifo_size;	do {		serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]);		info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);		info->state->icount.tx++;		if (info->xmit.head == info->xmit.tail)			break;	} while (--count > 0);		if (CIRC_CNT(info->xmit.head,		     info->xmit.tail,		     SERIAL_XMIT_SIZE) < WAKEUP_CHARS)		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);#ifdef SERIAL_DEBUG_INTR	printk("THRE...");#endif	if (intr_done)		*intr_done = 0;	if (info->xmit.head == info->xmit.tail) {		info->IER &= ~UART_IER_THRI;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -