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

📄 serial.c

📁 arm平台上的uclinux系统全部源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  linux/arch/arm/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. * * This module exports the following rs232 io functions: * *	int rs_init(void); * 	int rs_open(struct tty_struct * tty, struct file * filp) * *  Slight modifications for ARM Copyright (C) 1995, 1996 Russell King */#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/config.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 <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/segment.h>#include <asm/bitops.h>#include <asm/serial.h>static char *serial_name = "Serial driver";static char *serial_version = "4.13";DECLARE_TASK_QUEUE(tq_serial);struct tty_driver serial_driver, callout_driver;static int serial_refcount;/* serial subtype definitions */#define SERIAL_TYPE_NORMAL	1#define SERIAL_TYPE_CALLOUT	2/* number of characters left in xmit buffer before we ask for more */#define WAKEUP_CHARS 256/* * Serial driver configuration section.  Here are the various options: * * CONFIG_HUB6 *		Enables support for the venerable Bell Technologies *		HUB6 card. * * SERIAL_PARANOIA_CHECK * 		Check the magic number for the async_structure where * 		ever possible. */#define SERIAL_PARANOIA_CHECK#define CONFIG_SERIAL_NOPAUSE_IO#define SERIAL_DO_RESTART#undef SERIAL_DEBUG_INTR#undef SERIAL_DEBUG_OPEN#undef SERIAL_DEBUG_FLOW#define RS_STROBE_TIME (10*HZ)#define RS_ISR_PASS_LIMIT 256#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)#define _INLINE_ 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  /* * 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 struct rs_multiport_struct rs_multiport[NR_IRQS];static int IRQ_timeout[NR_IRQS];static volatile int rs_irq_triggered;static volatile int rs_triggered;static int rs_wild_int_mask;static void autoconfig(struct async_struct * info);static void change_speed(struct async_struct *info);struct async_struct rs_table[] = {	RS_UARTS};#define NR_PORTS	(sizeof(rs_table)/sizeof(struct async_struct))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/* * tmp_buf is used as a temporary buffer by serial_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;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;}/* * This is used to figure out the divisor speeds and the timeouts */static int baud_table[] = {	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,	9600, 19200, 38400, 57600, 115200, 0 };static inline unsigned int serial_in(struct async_struct *info, int offset){#ifdef CONFIG_HUB6    if (info->hub6) {	outb(info->hub6 - 1 + offset, info->port);	return inb(info->port+1);    } else#endif	return inb(info->port + offset);}static inline unsigned int serial_inp(struct async_struct *info, int offset){#ifdef CONFIG_HUB6    if (info->hub6) {	outb(info->hub6 - 1 + offset, info->port);	return inb_p(info->port+1);    } else#endif#ifdef CONFIG_SERIAL_NOPAUSE_IO	return 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) {	outb(info->hub6 - 1 + offset, info->port);	outb(value, info->port+1);    } else#endif	outb(value, info->port+offset);}static inline void serial_outp(struct async_struct *info, int offset,			       int value){#ifdef CONFIG_HUB6    if (info->hub6) {	outb(info->hub6 - 1 + offset, info->port);	outb_p(value, info->port+1);    } else#endif#ifdef CONFIG_SERIAL_NOPAUSE_IO	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_cli (flags);	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_cli (flags);	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 is the serial driver's interrupt routine while we are probing * for submarines. */static void rs_probe(int irq, void *dev_id, struct pt_regs * regs){	rs_irq_triggered = irq;	rs_triggered |= 1 << irq;	return;}/* * 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_irq_off(&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;	do {		ch = serial_inp(info, UART_RX);		if (*status & info->ignore_status_mask) {			if (++ignored > 100)				break;			goto ignore_char;		}		if (tty->flip.count >= TTY_FLIPBUF_SIZE)			break;		tty->flip.count++;		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;		else if (*status & UART_LSR_OE) 			*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;		else			*tty->flip.flag_buf_ptr++ = 0;		*tty->flip.char_buf_ptr++ = ch;	ignore_char:		*status = serial_inp(info, UART_LSR) & info->read_status_mask;	} while (*status & UART_LSR_DR);	queue_task_irq_off(&tty->flip.tqueue, &tq_timer);#ifdef SERIAL_DEBUG_INTR	printk("DR...");#endif}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->x_char = 0;		if (intr_done)			*intr_done = 0;		return;	}	if ((info->xmit_cnt <= 0) || 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 & (SERIAL_XMIT_SIZE-1);		if (--info->xmit_cnt <= 0)			break;	} while (--count > 0);		if (info->xmit_cnt < 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_cnt <= 0) {		info->IER &= ~UART_IER_THRI;		serial_out(info, UART_IER, info->IER);	}}static _INLINE_ void check_modem_status(struct async_struct *info){	int	status;		status = serial_in(info, UART_MSR);	if (status & UART_MSR_ANY_DELTA) {		/* update input line counters */		if (status & UART_MSR_TERI)			info->icount.rng++;		if (status & UART_MSR_DDSR)			info->icount.dsr++;		if (status & UART_MSR_DDCD)			info->icount.dcd++;		if (status & UART_MSR_DCTS)			info->icount.cts++;		wake_up_interruptible(&info->delta_msr_wait);	}	if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))		printk("ttys%d CD now %s...", info->line,		       (status & UART_MSR_DCD) ? "on" : "off");#endif				if (status & UART_MSR_DCD)			wake_up_interruptible(&info->open_wait);		else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&			   (info->flags & ASYNC_CALLOUT_NOHUP))) {#ifdef SERIAL_DEBUG_OPEN			printk("scheduling hangup...");#endif			queue_task_irq_off(&info->tqueue_hangup,					   &tq_scheduler);		}	}	if (info->flags & ASYNC_CTS_FLOW) {		if (info->tty->hw_stopped) {			if (status & UART_MSR_CTS) {#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))				printk("CTS tx start...");#endif				info->tty->hw_stopped = 0;				info->IER |= UART_IER_THRI;				serial_out(info, UART_IER, info->IER);				rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);				return;			}		} else {			if (!(status & UART_MSR_CTS)) {#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))				printk("CTS tx stop...");#endif				info->tty->hw_stopped = 1;				info->IER &= ~UART_IER_THRI;				serial_out(info, UART_IER, info->IER);			}		}	}}/* * This is the serial driver's generic interrupt routine */static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs){	int status;	struct async_struct * info;	int pass_counter = 0;	struct async_struct *end_mark = 0;	int first_multi = 0;	struct rs_multiport_struct *multi;#ifdef SERIAL_DEBUG_INTR	printk("rs_interrupt(%d)...", irq);#endif		info = IRQ_ports[irq];	if (!info)		return;		multi = &rs_multiport[irq];	if (multi->port_monitor)		first_multi = inb(multi->port_monitor);	do {		if (!info->tty ||		    (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) {			if (!end_mark)				end_mark = info;			goto next;		}		end_mark = 0;		info->last_active = jiffies;		status = serial_inp(info, UART_LSR) & info->read_status_mask;#ifdef SERIAL_DEBUG_INTR		printk("status = %x...", status);#endif		if (status & UART_LSR_DR)			receive_chars(info, &status);		check_modem_status(info);		if (status & UART_LSR_THRE)			transmit_chars(info, 0);	next:		info = info->next_port;		if (!info) {			info = IRQ_ports[irq];			if (pass_counter++ > RS_ISR_PASS_LIMIT) {#if 0				printk("rs loop break\n");#endif				break; 	/* Prevent infinite loops */

⌨️ 快捷键说明

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