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

📄 duart_serial.c

📁 powerpc内核mpc8241linux系统下char驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  linux/drivers/char/serial.c * *  Copyright (C) 1991, 1992  Linus Torvalds * *  Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92.  Now *  much more extensible to support other serial cards based on the *  16450/16550A UART's.  Added support for the AST FourPort and the *  Accent Async board.   * *  set_serial_info fixed to set the flags, custom divisor, and uart * 	type fields.  Fix suggested by Michael K. Johnson 12/12/92. * *  11/95: TIOCMIWAIT, TIOCGICOUNT by Angelo Haritsis <ah@doc.ic.ac.uk> * *  03/96: Modularised by Angelo Haritsis <ah@doc.ic.ac.uk> * *  rs_set_termios fixed to look also for changes of the input *      flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK. *                                            Bernd Anh鋟pl 05/17/96. * *  1/97:  Extended dumb serial ports are a config option now.   *         Saves 4k.   Michael A. Griffith <grif@acm.org> *  *  8/97: Fix bug in rs_set_termios with RTS *        Stanislav V. Voronyi <stas@uanet.kharkov.ua> * *  3/98: Change the IRQ detection, use of probe_irq_o*(), *	  supress TIOCSERGWILD and TIOCSERSWILD *	  Etienne Lorrain <etienne.lorrain@ibm.net> * *  4/98: Added changes to support the ARM architecture proposed by * 	  Russell King * * This module exports the following rs232 io functions: * *	int rs_init(void); *//* * Serial driver configuration section.  Here are the various options: * * CONFIG_HUB6 *		Enables support for the venerable Bell Technologies *		HUB6 card. * * CONFIG_SERIAL_MANY_PORTS * 		Enables support for ports beyond the standard, stupid * 		COM 1/2/3/4. * * CONFIG_SERIAL_MULTIPORT * 		Enables support for special multiport board support. * * CONFIG_SERIAL_SHARE_IRQ * 		Enables support for multiple serial ports on one IRQ * * CONFIG_SERIAL_DETECT_IRQ *		Enable the autodetection of IRQ on standart ports * * SERIAL_PARANOIA_CHECK * 		Check the magic number for the async_structure where * 		ever possible. */#undef SERIAL_PARANOIA_CHECK#define CONFIG_SERIAL_NOPAUSE_IO#define SERIAL_DO_RESTART#if 0/* Normally these defines are controlled by the autoconf.h */#define CONFIG_SERIAL_MANY_PORTS#define CONFIG_SERIAL_SHARE_IRQ#define CONFIG_SERIAL_DETECT_IRQ#define CONFIG_SERIAL_MULTIPORT#define CONFIG_HUB6#endif/* Sanity checks */#ifdef CONFIG_SERIAL_MULTIPORT#ifndef CONFIG_SERIAL_SHARE_IRQ#define CONFIG_SERIAL_SHARE_IRQ#endif#endif#ifdef CONFIG_HUB6#ifndef CONFIG_SERIAL_MANY_PORTS#define CONFIG_SERIAL_MANY_PORTS#endif#ifndef CONFIG_SERIAL_SHARE_IRQ#define CONFIG_SERIAL_SHARE_IRQ#endif#endif/* Set of debugging defines */#undef SERIAL_DEBUG_INTR#undef SERIAL_DEBUG_OPEN#undef SERIAL_DEBUG_FLOW#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT/*ebony*/#define RS_STROBE_TIME (0*HZ)#define RS_ISR_PASS_LIMIT 256#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)#define SERIAL_INLINE  #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#define CONFIG_ADDR                 0xfec00000#define CONFIG_DATA                 0xfee00000#define BAUD_CLKIN                  (GetFreq()*1000*1000) /* 100MHz or 66MHz *//* * End of serial driver configuration section. */#include <linux/config.h>#include <linux/module.h>#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/serial.h>#include <linux/serial_reg.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/malloc.h>#include <linux/init.h>#include <linux/delay.h>#ifdef CONFIG_SERIAL_CONSOLE#include <linux/console.h>#endif#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include <asm/serial.h>#ifdef SERIAL_INLINE#define _INLINE_ inline#endif	static char *serial_name = "Serial driver";static char *serial_version = "4.27";static DECLARE_TASK_QUEUE(tq_serial);static struct tty_driver serial_driver, callout_driver;static int serial_refcount;/* 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];#ifdef CONFIG_SERIAL_MULTIPORTstatic struct rs_multiport_struct rs_multiport[NR_IRQS];#endifstatic int IRQ_timeout[NR_IRQS];#ifdef CONFIG_SERIAL_CONSOLEstatic struct console sercons;#endifstatic unsigned detect_uart_irq (struct serial_state * state);static void autoconfig(struct serial_state * info);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 }, 	{ "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, 	{ "cirrus", 1, 0 }, 	{ "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, 	{ "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO |		  UART_STARTECH }, 	{ "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},	{ 0, 0}};static struct serial_state rs_table[] = {	SERIAL_PORT_DFNS	/* Defined in serial.h */};#define NR_PORTS	(sizeof(rs_table)/sizeof(struct serial_state))static struct tty_struct *serial_table[NR_PORTS];static struct termios *serial_termios[NR_PORTS];static struct termios *serial_termios_locked[NR_PORTS];#ifndef MIN#define MIN(a,b)	((a) < (b) ? (a) : (b))#endif/********************* Read PCI Configuration, ebony*********/   unsigned int ReadPCIConfig(int Devnum, int Reg){        unsigned int *pInt;        unsigned int Lval;        pInt = (unsigned int *) CONFIG_ADDR;        *pInt = ( Reg<<24 ) | (Devnum<<19) | 0x80; /* Reversed endian */        pInt = (unsigned int *) CONFIG_DATA;        Lval = *pInt;        return Lval;}/*   GetFreq(), to get the memory clock for divisor --ebony */int GetFreq(){  unsigned int pll;  int freq,val_pll;  pll=ReadPCIConfig(0,0xe2);  val_pll=(pll&0x0000ff00)>>8;  switch ( val_pll )  {    case 0x40:  freq=33;  break;    case 0x20:  freq=66;  break;    case 0x60:  freq=66;  break;    case 0x70:  freq=66;  break;    case 0xa0:  freq=66;  break;    case 0xb0:  freq=66;  break;    case 0xc0:  freq=83;  break;    case 0x80:  freq=100; break; }  return freq;}/* * 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;static struct semaphore tmp_buf_sem = MUTEX;static 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){#ifdef CONFIG_HUB6    if (info->hub6) {	eumb_outb(info->hub6 - 1 + offset, info->port);	return eumb_inb(info->port+1);    } else#endif	return eumb_inb(info->port + offset);}static inline unsigned int serial_inp(struct async_struct *info, int offset){#ifdef CONFIG_HUB6    if (info->hub6) {	eumb_outb(info->hub6 - 1 + offset, info->port);	return inb_p(info->port+1);    } else#endif#ifdef CONFIG_SERIAL_NOPAUSE_IO	return eumb_inb(info->port + offset);#else	return inb_p(info->port + offset);#endif}static inline void serial_out(struct async_struct *info, int offset, int value){#ifdef CONFIG_HUB6    if (info->hub6) {	eumb_outb(info->hub6 - 1 + offset, info->port);	eumb_outb(value, info->port+1);    } else#endif	eumb_outb(value, info->port+offset);}static inline void serial_outp(struct async_struct *info, int offset,			       int value){#ifdef CONFIG_HUB6    if (info->hub6) {	eumb_outb(info->hub6 - 1 + offset, info->port);	outb_p(value, info->port+1);    } else#endif#ifdef CONFIG_SERIAL_NOPAUSE_IO	eumb_outb(value, info->port+offset);#else    	outb_p(value, info->port+offset);#endif}/* * ------------------------------------------------------------ * 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_cnt && 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 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)			break;		*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++;			} 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;					if (*status & (UART_LSR_BI)) {#ifdef SERIAL_DEBUG_INTR				printk("handling break....");#endif				*tty->flip.flag_buf_ptr = TTY_BREAK;				if (info->flags & ASYNC_SAK)					do_SAK(tty);			} 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				 */				if (tty->flip.count < TTY_FLIPBUF_SIZE) {					tty->flip.count++;

⌨️ 快捷键说明

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