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

📄 serial_atmel.c

📁 这是一个SIGMA方案的PMP播放器的UCLINUX程序,可播放DVD,VCD,CD MP3...有很好的参考价值.
💻 C
📖 第 1 页 / 共 4 页
字号:
/* serial port driver for the Atmel AT91 series builtin USARTs * * Copyright (C) 2000, 2001  Erik Andersen <andersen@lineo.com> * * Based on: * drivers/char/68302serial.c * and also based on trioserial.c from Aplio, though this driver * has been extensively changed since then.  No author was  * listed in trioserial.c. *//* Enable this to force this driver to always operate at 57600 */#undef FORCE_57600#include <linux/config.h>#include <linux/version.h>#include <linux/types.h>#include <linux/serial.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/config.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/console.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/arch/irq.h>#include <asm/system.h>#include <asm/segment.h>#include <asm/bitops.h>#include <asm/delay.h>#include <asm/uaccess.h>#include "serial_atmel.h"#define USE_INTS	1#define UART_CLOCK	(ARM_CLK/16)#define SERIAL_XMIT_SIZE	PAGE_SIZE#define RX_SERIAL_SIZE		256/* 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/* Debugging... DEBUG_INTR is bad to use when one of the zs * lines is your console ;( */#undef SERIAL_DEBUG_INTR#undef SERIAL_DEBUG_OPEN#undef SERIAL_DEBUG_FLOW#define RS_ISR_PASS_LIMIT 256#define _INLINE_#ifndef MIN#define MIN(a,b)	((a) < (b) ? (a) : (b))#endifstatic volatile struct atmel_usart_regs *usarts[AT91_USART_CNT] = {	(volatile struct atmel_usart_regs *) AT91_USART0_BASE,	(volatile struct atmel_usart_regs *) AT91_USART1_BASE};static struct atmel_serial atmel_info[AT91_USART_CNT];/* * 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[SERIAL_XMIT_SIZE];	/* This is cheating */DECLARE_MUTEX(tmp_buf_sem);static unsigned char * rx_buf_table[AT91_USART_CNT];static unsigned char rx_buf1[RX_SERIAL_SIZE];static unsigned char rx_buf2[RX_SERIAL_SIZE];/* Console hooks... */struct atmel_serial *atmel_consinfo = 0;DECLARE_TASK_QUEUE(tq_atmel_serial);static struct tq_struct serialpoll;static struct tty_driver serial_driver, callout_driver;static int serial_refcount;static void serpoll(void *data);static void change_speed(struct atmel_serial *info);static struct tty_struct *serial_table[AT91_USART_CNT];static struct termios *serial_termios[AT91_USART_CNT];static struct termios *serial_termios_locked[AT91_USART_CNT];static char prompt0;static void xmit_char(struct atmel_serial *info, char ch);static void xmit_string(struct atmel_serial *info, char *p, int len);static void start_rx(struct atmel_serial *info);static void wait_EOT(volatile struct atmel_usart_regs *);static void uart_init(struct atmel_serial *info);static void uart_speed(struct atmel_serial *info, unsigned cflag);static void tx_enable(volatile struct atmel_usart_regs *uart);static void rx_enable(volatile struct atmel_usart_regs *uart);static void tx_disable(volatile struct atmel_usart_regs *uart);static void rx_disable(volatile struct atmel_usart_regs *uart);static void tx_stop(volatile struct atmel_usart_regs *uart);static void tx_start(volatile struct atmel_usart_regs *uart, int ints);static void rx_stop(volatile struct atmel_usart_regs *uart);static void rx_start(volatile struct atmel_usart_regs *uart, int ints);static void set_ints_mode(int yes, struct atmel_serial *info);static void rs_interrupt(struct atmel_serial *info);extern void show_net_buffers(void);extern void hard_reset_now(void);static void handle_termios_tcsets(struct termios * ptermios, struct atmel_serial * ptty);static int global;static void coucou1(void){	global = 0;}static void coucou2(void){	global = 1;}static void _INLINE_ tx_enable(volatile struct atmel_usart_regs *uart){	uart->ier = US_TXEMPTY;}static void _INLINE_ rx_enable(volatile struct atmel_usart_regs *uart){	uart->ier = US_ENDRX | US_TIMEOUT;}static void _INLINE_ tx_disable(volatile struct atmel_usart_regs *uart){	uart->idr = US_TXEMPTY;}static void _INLINE_ rx_disable(volatile struct atmel_usart_regs *uart){	uart->idr = US_ENDRX | US_TIMEOUT;}static void _INLINE_ tx_stop(volatile struct atmel_usart_regs *uart){	tx_disable(uart);	uart->tcr = 0;	uart->cr = US_TXDIS;}static void _INLINE_ tx_start(volatile struct atmel_usart_regs *uart, int ints){	if (ints) {		tx_enable(uart);	}	uart->cr = US_TXEN;}static void _INLINE_ rx_stop(volatile struct atmel_usart_regs *uart){	rx_disable(uart);	uart->rtor = 0;	uart->rcr = 0;	uart->cr = US_RXDIS;}static void _INLINE_ rx_start(volatile struct atmel_usart_regs *uart, int ints){	uart->cr = US_RXEN | US_STTO;	uart->rtor = 20;	if (ints) {		rx_enable(uart);	}}static void _INLINE_ reset_status(volatile struct atmel_usart_regs *uart){	uart->cr = US_RSTSTA;}static void set_ints_mode(int yes, struct atmel_serial *info){	info->use_ints = yes;// FIXME: check#if 0	(yes) ? unmask_irq(info->irq) : mask_irq(info->irq);#endif}#ifdef US_RTSstatic void atmel_cts_off(struct atmel_serial *info){	volatile struct atmel_usart_regs *uart;	uart = info->usart;	uart->mc &= ~(unsigned long) US_RTS;	info->cts_state = 0;}static void atmel_cts_on(struct atmel_serial *info){	volatile struct atmel_usart_regs *uart;	uart = info->usart;	uart->mc |= US_RTS;	info->cts_state = 1;}/* Sets or clears DTR/RTS on the requested line */static inline void atmel_rtsdtr(struct atmel_serial *ss, int set){        volatile struct atmel_usart_regs *uart;        uart = ss->usart;        if (set) {              uart->mc |= US_DTR | US_RTS;        } else {              uart->mc &= ~(unsigned long) (US_DTR | US_RTS);        }        return;}#endif	/* US_RTS */static inline int serial_paranoia_check(struct atmel_serial *info,					dev_t device, const char *routine){#ifdef SERIAL_PARANOIA_CHECK	static const char *badmagic =		"Warning: bad magic number for serial struct (%d, %d) in %s\n";	static const char *badinfo =		"Warning: null atmel_serial struct for (%d, %d) in %s\n";	if (!info) {		printk(badinfo, MAJOR(device), MINOR(device), routine);		return 1;	}	if (info->magic != SERIAL_MAGIC) {		printk(badmagic, MAJOR(device), MINOR(device), routine);		return 1;	}#endif	return 0;}/* * ------------------------------------------------------------ * 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 atmel_serial *info = (struct atmel_serial *) tty->driver_data;	unsigned long flags;	if (serial_paranoia_check(info, tty->device, "rs_stop"))		return;	save_flags(flags);	cli();	tx_stop(info->usart);	rx_stop(info->usart);	restore_flags(flags);}static void rs_put_char(struct atmel_serial *info, char ch){	int flags = 0;	save_flags(flags);	cli();	xmit_char(info, ch);	wait_EOT(info->usart);	restore_flags(flags);}static void rs_start(struct tty_struct *tty){	struct atmel_serial *info = (struct atmel_serial *) tty->driver_data;	unsigned long flags;	if (serial_paranoia_check(info, tty->device, "rs_start"))		return;	save_flags(flags);	cli();	tx_start(info->usart, info->use_ints);	rx_start(info->usart, info->use_ints);	/* FIXME *///	start_rx(info);	restore_flags(flags);}/* Drop into either the boot monitor or kadb upon receiving a break * from keyboard/console input. */static void batten_down_hatches(void){#if 0	/* If we are doing kadb, we call the debugger	 * else we just drop into the boot monitor.	 * Note that we must flush the user windows	 * first before giving up control.	 */	if ((((unsigned long) linux_dbvec) >= DEBUG_FIRSTVADDR) &&		(((unsigned long) linux_dbvec) <= DEBUG_LASTVADDR))		sp_enter_debugger();	else		panic("atmel_serial: batten_down_hatches");	return;#endif}/* * ---------------------------------------------------------------------- * * 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 assembly 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 atmel_serial *info, int event){	info->event |= 1 << event;	queue_task(&info->tqueue, &tq_atmel_serial);	mark_bh(SERIAL_BH);}extern void breakpoint(void);	/* For the KGDB frame character */static _INLINE_ void receive_chars(struct atmel_serial *info, unsigned long status){//	unsigned char ch; FIXME	int count;	volatile struct atmel_usart_regs *uart = info->usart;#if 0	// hack to receive chars by polling from anywhere	struct atmel_serial *info1 = &atmel_info;	struct tty_struct *tty = info1->tty;	if (!(info->flags & S_INITIALIZED))		return;#else	struct tty_struct *tty = info->tty;	if (!(info->flags & S_INITIALIZED))		return;#endif	count = RX_SERIAL_SIZE - uart->rcr;	// hack to receive chars by polling only BD fields	if (!count) {		return;	}#if 0	ch = info->rx_buf[0];	if (info->is_cons) {		if (status & US_RXBRK) {	/* whee, break received */			batten_down_hatches();			/*rs_recv_clear(info->atmel_channel); */			return;		} else if (ch == 0x10) {	/* ^P */			show_state();			show_free_areas();			show_buffers();			show_net_buffers();			return;		} else if (ch == 0x12) {	/* ^R */			hard_reset_now();			return;		}		/* It is a 'keyboard interrupt' ;-) */		wake_up(&keypress_wait);	}#endif#if 0	/* Look for kgdb 'stop' character, consult the gdb documentation	 * for remote target debugging and arch/sparc/kernel/sparc-stub.c	 * to see how all this works.	 */	if((info->kgdb_channel) && (ch =='\003')) {	    breakpoint();	    goto clear_and_exit;	}#endif	if (!tty)		goto clear_and_exit;	if (tty->flip.count >= TTY_FLIPBUF_SIZE)		queue_task(&tty->flip.tqueue, &tq_timer);	if ((count + tty->flip.count) >= TTY_FLIPBUF_SIZE) {#ifdef US_RTS		atmel_cts_off(info);#endif		serialpoll.data = (void *) info;		queue_task(&serialpoll, &tq_timer);	}	memset(tty->flip.flag_buf_ptr, 0, count);	memcpy(tty->flip.char_buf_ptr, info->rx_buf, count);	tty->flip.char_buf_ptr += count;	if (status & US_PARE)		*(tty->flip.flag_buf_ptr - 1) = TTY_PARITY;	else if (status & US_OVRE)		*(tty->flip.flag_buf_ptr - 1) = TTY_OVERRUN;	else if (status & US_FRAME)		*(tty->flip.flag_buf_ptr - 1) = TTY_FRAME;	tty->flip.count += count;	queue_task(&tty->flip.tqueue, &tq_timer);  clear_and_exit:	start_rx(info);	return;}static _INLINE_ void transmit_chars(struct atmel_serial *info){	if (info->x_char) {		/* Send next char */		xmit_char(info, info->x_char);		info->x_char = 0;		goto clear_and_return;	}	if ((info->xmit_cnt <= 0) || info->tty->stopped) {		/* That's peculiar... */		tx_stop(info->usart);		goto clear_and_return;	}	if (info->xmit_tail + info->xmit_cnt < SERIAL_XMIT_SIZE) {		xmit_string(info, info->xmit_buf + info->xmit_tail,					info->xmit_cnt);		info->xmit_tail =			(info->xmit_tail + info->xmit_cnt) & (SERIAL_XMIT_SIZE - 1);		info->xmit_cnt = 0;	} else {		coucou1();		xmit_string(info, info->xmit_buf + info->xmit_tail,					SERIAL_XMIT_SIZE - info->xmit_tail);		//xmit_string(info, info->xmit_buf, info->xmit_tail + info->xmit_cnt - SERIAL_XMIT_SIZE);		info->xmit_cnt =			info->xmit_cnt - (SERIAL_XMIT_SIZE - info->xmit_tail);		info->xmit_tail = 0;	}#if 0	/* Send char */	xmit_char(info, info->xmit_buf[info->xmit_tail++]);	info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1);	info->xmit_cnt--;#endif	if (info->xmit_cnt < WAKEUP_CHARS)		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);	if (info->xmit_cnt <= 0) {		//tx_stop(info->usart);		goto clear_and_return;	}  clear_and_return:	/* Clear interrupt (should be auto) */	return;}static _INLINE_ void status_handle(struct atmel_serial *info, unsigned long status){#if 0	if (status & DCD) {		if ((info->tty->termios->c_cflag & CRTSCTS) &&			((info->curregs[3] & AUTO_ENAB) == 0)) {			info->curregs[3] |= AUTO_ENAB;			info->pendregs[3] |= AUTO_ENAB;			write_zsreg(info->atmel_channel, 3, info->curregs[3]);		}	} else {		if ((info->curregs[3] & AUTO_ENAB)) {			info->curregs[3] &= ~AUTO_ENAB;			info->pendregs[3] &= ~AUTO_ENAB;			write_zsreg(info->atmel_channel, 3, info->curregs[3]);		}	}#endif	/* Whee, if this is console input and this is a	 * 'break asserted' status change interrupt, call	 * the boot prom.	 */	if ((status & US_RXBRK) && info->break_abort)		batten_down_hatches();	/* XXX Whee, put in a buffer somewhere, the status information	 * XXX whee whee whee... Where does the information go...	 */	reset_status(info->usart);	return;}

⌨️ 快捷键说明

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