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

📄 serial_amba.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  linux/drivers/char/serial_amba.c * *  Driver for AMBA serial ports * *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. * *  Copyright 1999 ARM Limited *  Copyright (C) 2000 Deep Blue Solutions Ltd. * * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * * * This is a generic driver for ARM AMBA-type serial ports.  They * have a lot of 16550-like features, but are not register compatable. * Note that although they do have CTS, DCD and DSR inputs, they do * not have an RI input, nor do they have DTR or RTS outputs.  If * required, these have to be supplied via some other means (eg, GPIO) * and hooked into this driver. * * This could very easily become a generic serial driver for dumb UARTs * (eg, {82,16x}50, 21285, SA1100). */#include <linux/config.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/sched.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/malloc.h>#include <linux/init.h>#include <linux/circ_buf.h>#include <linux/serial.h>#include <linux/console.h>#include <linux/sysrq.h>#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include <asm/hardware/serial_amba.h>#define SERIAL_AMBA_NAME	"ttyAM"#define SERIAL_AMBA_MAJOR	204#define SERIAL_AMBA_MINOR	16#define SERIAL_AMBA_NR		2#define CALLOUT_AMBA_NAME	"cuaam"#define CALLOUT_AMBA_MAJOR	205#define CALLOUT_AMBA_MINOR	16#define CALLOUT_AMBA_NR		SERIAL_AMBA_NR#ifndef TRUE#define TRUE 1#endif#ifndef FALSE#define FALSE 0#endif#define DEBUG 0#define DEBUG_LEDS 0#if DEBUG_LEDSextern int get_leds(void);extern int set_leds(int);#endif/* * Access routines for the AMBA UARTs */#define UART_GET_INT_STATUS(p)	IO_READ((p)->uart_base + AMBA_UARTIIR)#define UART_GET_FR(p)		IO_READ((p)->uart_base + AMBA_UARTFR)#define UART_GET_CHAR(p)	IO_READ((p)->uart_base + AMBA_UARTDR)#define UART_PUT_CHAR(p, c)	IO_WRITE((p)->uart_base + AMBA_UARTDR, (c))#define UART_GET_RSR(p)		IO_READ((p)->uart_base + AMBA_UARTRSR)#define UART_GET_CR(p)		IO_READ((p)->uart_base + AMBA_UARTCR)#define UART_PUT_CR(p,c)	IO_WRITE((p)->uart_base + AMBA_UARTCR, (c))#define UART_GET_LCRL(p)	IO_READ((p)->uart_base + AMBA_UARTLCR_L)#define UART_PUT_LCRL(p,c)	IO_WRITE((p)->uart_base + AMBA_UARTLCR_L, (c))#define UART_GET_LCRM(p)	IO_READ((p)->uart_base + AMBA_UARTLCR_M)#define UART_PUT_LCRM(p,c)	IO_WRITE((p)->uart_base + AMBA_UARTLCR_M, (c))#define UART_GET_LCRH(p)	IO_READ((p)->uart_base + AMBA_UARTLCR_H)#define UART_PUT_LCRH(p,c)	IO_WRITE((p)->uart_base + AMBA_UARTLCR_H, (c))#define UART_RX_DATA(s)		(((s) & AMBA_UARTFR_RXFE) == 0)#define UART_TX_READY(s)	(((s) & AMBA_UARTFR_TXFF) == 0)#define UART_TX_EMPTY(p)	((UART_GET_FR(p) & AMBA_UARTFR_TMSK) == 0)#define AMBA_UARTRSR_ANY	(AMBA_UARTRSR_OE|AMBA_UARTRSR_BE|AMBA_UARTRSR_PE|AMBA_UARTRSR_FE)#define AMBA_UARTFR_MODEM_ANY	(AMBA_UARTFR_DCD|AMBA_UARTFR_DSR|AMBA_UARTFR_CTS)/* * Things needed by tty driver */static struct tty_driver ambanormal_driver, ambacallout_driver;static int ambauart_refcount;static struct tty_struct *ambauart_table[SERIAL_AMBA_NR];static struct termios *ambauart_termios[SERIAL_AMBA_NR];static struct termios *ambauart_termios_locked[SERIAL_AMBA_NR];#if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif/* * Things needed internally to this driver *//* * 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 u_char *tmp_buf;static DECLARE_MUTEX(tmp_buf_sem);#define HIGH_BITS_OFFSET	((sizeof(long)-sizeof(int))*8)/* number of characters left in xmit buffer before we ask for more */#define WAKEUP_CHARS		256#define AMBA_ISR_PASS_LIMIT	256#define EVT_WRITE_WAKEUP	0struct amba_icount {	__u32	cts;	__u32	dsr;	__u32	rng;	__u32	dcd;	__u32	rx;	__u32	tx;	__u32	frame;	__u32	overrun;	__u32	parity;	__u32	brk;	__u32	buf_overrun;};/* * Static information about the port */struct amba_port {	unsigned int		uart_base;	unsigned int		irq;	unsigned int		uartclk;	unsigned int		fifosize;	unsigned int		tiocm_support;	void (*set_mctrl)(struct amba_port *, u_int mctrl);};	/* * This is the state information which is persistent across opens */struct amba_state {	struct amba_icount	icount;	unsigned int		line;	unsigned int		close_delay;	unsigned int		closing_wait;	unsigned int		custom_divisor;	unsigned int		flags;	struct termios		normal_termios;	struct termios		callout_termios;	int			count;	struct amba_info	*info;};#define AMBA_XMIT_SIZE 1024/* * This is the state information which is only valid when the port is open. */struct amba_info {	struct amba_port	*port;	struct amba_state	*state;	struct tty_struct	*tty;	unsigned char		x_char;	unsigned char		old_status;	unsigned char		read_status_mask;	unsigned char		ignore_status_mask;	struct circ_buf		xmit;	unsigned int		flags;#ifdef SUPPORT_SYSRQ	unsigned long		sysrq;#endif	unsigned int		event;	unsigned int		timeout;	unsigned int		lcr_h;	unsigned int		mctrl;	int			blocked_open;	pid_t			session;	pid_t			pgrp;	struct tasklet_struct	tlet;	wait_queue_head_t	open_wait;	wait_queue_head_t	close_wait;	wait_queue_head_t	delta_msr_wait;};#ifdef CONFIG_SERIAL_AMBA_CONSOLEstatic struct console ambauart_cons;#endifstatic void ambauart_change_speed(struct amba_info *info, struct termios *old_termios);static void ambauart_wait_until_sent(struct tty_struct *tty, int timeout);#if 1 //def CONFIG_SERIAL_INTEGRATORstatic void amba_set_mctrl_null(struct amba_port *port, u_int mctrl){}static struct amba_port amba_ports[SERIAL_AMBA_NR] = {	{		uart_base:	IO_ADDRESS(INTEGRATOR_UART0_BASE),		irq:		IRQ_UARTINT0,		uartclk:	14745600,		fifosize:	8,		set_mctrl:	amba_set_mctrl_null,	},	{		uart_base:	IO_ADDRESS(INTEGRATOR_UART1_BASE),		irq:		IRQ_UARTINT1,		uartclk:	14745600,		fifosize:	8,		set_mctrl:	amba_set_mctrl_null,	}};#endifstatic struct amba_state amba_state[SERIAL_AMBA_NR];static void ambauart_enable_rx_interrupt(struct amba_info *info){	unsigned int cr;	cr = UART_GET_CR(info->port);	cr |= AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE;	UART_PUT_CR(info->port, cr);}static void ambauart_disable_rx_interrupt(struct amba_info *info){	unsigned int cr;	cr = UART_GET_CR(info->port);	cr &= ~(AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE);	UART_PUT_CR(info->port, cr);}static void ambauart_enable_tx_interrupt(struct amba_info *info){	unsigned int cr;	cr = UART_GET_CR(info->port);	cr |= AMBA_UARTCR_TIE;	UART_PUT_CR(info->port, cr);}static void ambauart_disable_tx_interrupt(struct amba_info *info){	unsigned int cr;	cr = UART_GET_CR(info->port);	cr &= ~AMBA_UARTCR_TIE;	UART_PUT_CR(info->port, cr);}static void ambauart_stop(struct tty_struct *tty){	struct amba_info *info = tty->driver_data;	unsigned long flags;	save_flags(flags); cli();	ambauart_disable_tx_interrupt(info);	restore_flags(flags);}static void ambauart_start(struct tty_struct *tty){	struct amba_info *info = tty->driver_data;	unsigned long flags;	save_flags(flags); cli();	if (info->xmit.head != info->xmit.tail	    && info->xmit.buf)		ambauart_enable_tx_interrupt(info);	restore_flags(flags);}/* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */static void ambauart_event(struct amba_info *info, int event){	info->event |= 1 << event;	tasklet_schedule(&info->tlet);}static void#ifdef SUPPORT_SYSRQambauart_rx_chars(struct amba_info *info, struct pt_regs *regs)#elseambauart_rx_chars(struct amba_info *info)#endif{	struct tty_struct *tty = info->tty;	unsigned int status, ch, rsr, flg, ignored = 0;	struct amba_icount *icount = &info->state->icount;	struct amba_port *port = info->port;	status = UART_GET_FR(port);	while (UART_RX_DATA(status)) {		ch = UART_GET_CHAR(port);		if (tty->flip.count >= TTY_FLIPBUF_SIZE)			goto ignore_char;		icount->rx++;		flg = TTY_NORMAL;		/*		 * Note that the error handling code is		 * out of the main execution path		 */		rsr = UART_GET_RSR(port);		if (rsr & AMBA_UARTRSR_ANY)			goto handle_error;#ifdef SUPPORT_SYSRQ		if (info->sysrq) {			if (ch && time_before(jiffies, info->sysrq)) {				handle_sysrq(ch, regs, NULL, NULL);				info->sysrq = 0;				goto ignore_char;			}			info->sysrq = 0;		}#endif	error_return:		*tty->flip.flag_buf_ptr++ = flg;		*tty->flip.char_buf_ptr++ = ch;		tty->flip.count++;	ignore_char:		status = UART_GET_FR(port);	}out:	tty_flip_buffer_push(tty);	return;handle_error:	if (rsr & AMBA_UARTRSR_BE) {		rsr &= ~(AMBA_UARTRSR_FE | AMBA_UARTRSR_PE);		icount->brk++;#ifdef SUPPORT_SYSRQ		if (info->state->line == ambauart_cons.index) {			if (!info->sysrq) {				info->sysrq = jiffies + HZ*5;				goto ignore_char;			}		}#endif	} else if (rsr & AMBA_UARTRSR_PE)		icount->parity++;	else if (rsr & AMBA_UARTRSR_FE)		icount->frame++;	if (rsr & AMBA_UARTRSR_OE)		icount->overrun++;	if (rsr & info->ignore_status_mask) {		if (++ignored > 100)			goto out;		goto ignore_char;	}	rsr &= info->read_status_mask;	if (rsr & AMBA_UARTRSR_BE)		flg = TTY_BREAK;	else if (rsr & AMBA_UARTRSR_PE)		flg = TTY_PARITY;	else if (rsr & AMBA_UARTRSR_FE)		flg = TTY_FRAME;	if (rsr & AMBA_UARTRSR_OE) {		/*		 * CHECK: does overrun affect the current character?		 * ASSUMPTION: it does not.		 */		*tty->flip.flag_buf_ptr++ = flg;		*tty->flip.char_buf_ptr++ = ch;		tty->flip.count++;		if (tty->flip.count >= TTY_FLIPBUF_SIZE)			goto ignore_char;		ch = 0;		flg = TTY_OVERRUN;	}#ifdef SUPPORT_SYSRQ	info->sysrq = 0;#endif	goto error_return;}static void ambauart_tx_chars(struct amba_info *info){	struct amba_port *port = info->port;	int count;	if (info->x_char) {		UART_PUT_CHAR(port, info->x_char);		info->state->icount.tx++;		info->x_char = 0;		return;	}	if (info->xmit.head == info->xmit.tail	    || info->tty->stopped	    || info->tty->hw_stopped) {		ambauart_disable_tx_interrupt(info);		return;	}	count = port->fifosize;	do {		UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]);		info->xmit.tail = (info->xmit.tail + 1) & (AMBA_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,		     AMBA_XMIT_SIZE) < WAKEUP_CHARS)		ambauart_event(info, EVT_WRITE_WAKEUP);	if (info->xmit.head == info->xmit.tail) {		ambauart_disable_tx_interrupt(info);	}}static void ambauart_modem_status(struct amba_info *info){	unsigned int status, delta;	struct amba_icount *icount = &info->state->icount;	status = UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY;	delta = status ^ info->old_status;	info->old_status = status;	if (!delta)		return;	if (delta & AMBA_UARTFR_DCD) {		icount->dcd++;#ifdef CONFIG_HARD_PPS		if ((info->flags & ASYNC_HARDPPS_CD) &&		    (status & AMBA_UARTFR_DCD)			hardpps();#endif		if (info->flags & ASYNC_CHECK_CD) {			if (status & AMBA_UARTFR_DCD)				wake_up_interruptible(&info->open_wait);			else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&				   (info->flags & ASYNC_CALLOUT_NOHUP))) {				if (info->tty)					tty_hangup(info->tty);			}		}	}	if (delta & AMBA_UARTFR_DSR)		icount->dsr++;	if (delta & AMBA_UARTFR_CTS) {		icount->cts++;		if (info->flags & ASYNC_CTS_FLOW) {			status &= AMBA_UARTFR_CTS;			if (info->tty->hw_stopped) {				if (status) {

⌨️ 快捷键说明

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