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

📄 dz.c

📁 linux和2410结合开发 用他可以生成2410所需的zImage文件
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * dz.c: Serial port driver for DECStations equiped  *       with the DZ chipset. * * Copyright (C) 1998 Olivier A. D. Lebaillif  *              * Email: olivier.lebaillif@ifrsys.com * * [31-AUG-98] triemer * Changed IRQ to use Harald's dec internals interrupts.h * removed base_addr code - moving address assignment to setup.c * Changed name of dz_init to rs_init to be consistent with tc code * [13-NOV-98] triemer fixed code to receive characters *    after patches by harald to irq code.   * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout *            field from "current" - somewhere between 2.1.121 and 2.1.131Qua Jun 27 15:02:26 BRT 2001 * [27-JUN-2001] Arnaldo Carvalho de Melo <acme@conectiva.com.br> - cleanups *   * Parts (C) 1999 David Airlie, airlied@linux.ie  * [07-SEP-99] Bugfixes  *//* #define DEBUG_DZ 1 */#include <linux/version.h>#include <linux/module.h>#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/init.h> #include <linux/slab.h>#include <linux/mm.h>#include <linux/major.h>#include <linux/param.h>#include <linux/tqueue.h>#include <linux/interrupt.h>#include <linux/serial.h>#include <linux/serialP.h>#include <asm-mips/wbflush.h>#include <asm/dec/interrupts.h>			/* for definition of SERIAL *//* for definition of struct console */#ifdef CONFIG_SERIAL_CONSOLE#define CONSOLE_LINE (3)#endif /* ifdef CONFIG_SERIAL_CONSOLE */#if defined(CONFIG_SERIAL_CONSOLE) || defined(DEBUG_DZ)#include <linux/console.h>#endif /* if defined(CONFIG_SERIAL_CONSOLE) || defined(DEBUG_DZ) */#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/serial.h>#include <asm/uaccess.h>#include <asm/irq.h>#include <asm/dec/machtype.h>#include <asm/dec/kn01.h>#include <asm/dec/kn02.h>#ifdef DEBUG_DZ#include <linux/ptrace.h>#include <linux/fs.h>#include <asm/bootinfo.h>extern int (*prom_printf) (char *,...);#endif#include "dz.h"#define DZ_INTR_DEBUG 1DECLARE_TASK_QUEUE(tq_serial);extern wait_queue_head_t keypress_wait; static struct dz_serial *lines[4];static unsigned char tmp_buffer[256];#ifdef DEBUG_DZ/* * debugging code to send out chars via prom  */static void debug_console( const char *s,int count){	unsigned i;	for (i = 0; i < count; i++) {		if (*s == 10)			prom_printf("%c", 13);		prom_printf("%c", *s++);	}}#endif/* * ------------------------------------------------------------ * dz_in () and dz_out () * * These routines are used to access the registers of the DZ  * chip, hiding relocation differences between implementation. * ------------------------------------------------------------ */static inline unsigned short dz_in (struct dz_serial *info, unsigned offset){	volatile u16 *addr = (volatile u16 *)(info->port + offset);	return *addr;}static inline void dz_out (struct dz_serial *info, unsigned offset,                           unsigned short value){	volatile u16 *addr = (volatile u16 *)(info->port + offset);	*addr = value;}/* * ------------------------------------------------------------ * rs_stop () and rs_start () * * These routines are called before setting or resetting  * tty->stopped. They enable or disable transmitter interrupts,  * as necessary. * ------------------------------------------------------------ */static void dz_stop (struct tty_struct *tty){	struct dz_serial *info; 	unsigned short mask, tmp;	if (!tty) 		return;  	info = (struct dz_serial *)tty->driver_data; 	mask = 1 << info->line;	tmp = dz_in (info, DZ_TCR);       /* read the TX flag */	tmp &= ~mask;                   /* clear the TX flag */	dz_out (info, DZ_TCR, tmp);}static void dz_start (struct tty_struct *tty){	struct dz_serial *info = (struct dz_serial *)tty->driver_data;	unsigned short mask, tmp;	mask = 1 << info->line;	tmp = dz_in (info, DZ_TCR);      /* read the TX flag */	tmp |= mask;                   /* set the TX flag */	dz_out (info, DZ_TCR, tmp);}/* * ------------------------------------------------------------ * Here starts the interrupt handling routines.  All of the  * following subroutines are declared as inline and are folded  * into dz_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 dz.c * * and look at the resulting assemble code in serial.s. * * ------------------------------------------------------------ *//* * ------------------------------------------------------------ * dz_sched_event () * * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. * ------------------------------------------------------------ */static inline void dz_sched_event (struct dz_serial *info, int event){	info->event |= 1 << event;	queue_task(&info->tqueue, &tq_serial);	mark_bh(SERIAL_BH);}/* * ------------------------------------------------------------ * receive_char () * * This routine deals with inputs from any lines. * ------------------------------------------------------------ */static inline void receive_chars (struct dz_serial *info_in){	struct dz_serial *info;	struct tty_struct *tty = 0;	struct async_icount *icount;	int ignore = 0;	unsigned short status, tmp;	unsigned char ch;	/*	 * This code is going to be a problem...  the call to tty_flip_buffer	 * is going to need to be rethought...	 */	do {		status = dz_in (info_in, DZ_RBUF);		info = lines[LINE(status)];		/* punt so we don't get duplicate characters */		if (!(status & DZ_DVAL))			goto ignore_char;		ch = UCHAR(status);			/* grab the char */#if 0		if (info->is_console) {			if (ch == 0)				return;			/* it's a break ... */			wake_up (&keypress_wait);       /* It is a 'keyboard interrupt' ;-) */		}#endif		tty = info->tty;	/* now tty points to the proper dev */		icount = &info->icount;		if (!tty)			break;		if (tty->flip.count >= TTY_FLIPBUF_SIZE) break;		*tty->flip.char_buf_ptr = ch;		*tty->flip.flag_buf_ptr = 0;		icount->rx++;		/* keep track of the statistics */		if (status & (DZ_OERR | DZ_FERR | DZ_PERR)) {			if (status & DZ_PERR)		/* parity error */				icount->parity++;			else if (status & DZ_FERR)	/* frame error */				icount->frame++;			if (status & DZ_OERR)		/* overrun error */				icount->overrun++;			/*			 * Check to see if we should ignore the character and			 * mask off conditions that should be ignored			 */			if (status & info->ignore_status_mask) {				if (++ignore > 100)					break;				goto ignore_char;			}			/* mask off the error conditions we want to ignore */			tmp = status & info->read_status_mask;			if (tmp & DZ_PERR) {				*tty->flip.flag_buf_ptr = TTY_PARITY;#ifdef DEBUG_DZ				debug_console("PERR\n",5);#endif /* DEBUG_DZ */			} else if (tmp & DZ_FERR) {				*tty->flip.flag_buf_ptr = TTY_FRAME;#ifdef DEBUG_DZ				debug_console("FERR\n",5);#endif /* DEBUG_DZ */			} if (tmp & DZ_OERR) { #ifdef DEBUG_DZ				debug_console("OERR\n",5);#endif /* DEBUG_DZ */				if (tty->flip.count < TTY_FLIPBUF_SIZE) {					tty->flip.count++;					tty->flip.flag_buf_ptr++;					tty->flip.char_buf_ptr++;					*tty->flip.flag_buf_ptr = TTY_OVERRUN;				}			}		}	tty->flip.flag_buf_ptr++;	tty->flip.char_buf_ptr++;	tty->flip.count++;ignore_char:	;	} while (status & DZ_DVAL);	if (tty)		tty_flip_buffer_push(tty);}/* * ------------------------------------------------------------ * transmit_char () * * This routine deals with outputs to any lines. * ------------------------------------------------------------ */static inline void transmit_chars (struct dz_serial *info){	unsigned char tmp;	if (info->x_char) {           /* XON/XOFF chars */		dz_out(info, DZ_TDR, info->x_char);		info->icount.tx++;		info->x_char = 0;		return;	}	/* if nothing to do or stopped or hardware stopped */	if ((info->xmit_cnt <= 0) || info->tty->stopped ||	    info->tty->hw_stopped) {		dz_stop(info->tty);		return;	}	/*	 * If something to do ... (rember the dz has no output fifo so we go	 * one char at a time :-<	 */	tmp = (unsigned short) info->xmit_buf[info->xmit_tail++];	dz_out(info, DZ_TDR, tmp);	info->xmit_tail = info->xmit_tail & (DZ_XMIT_SIZE - 1);	info->icount.tx++;	if (--info->xmit_cnt < WAKEUP_CHARS)	dz_sched_event(info, DZ_EVENT_WRITE_WAKEUP);	/* Are we done */	if (info->xmit_cnt <= 0)		dz_stop(info->tty);}/* * ------------------------------------------------------------ * check_modem_status () * * Only valid for the MODEM line duh ! * ------------------------------------------------------------ */static inline void check_modem_status (struct dz_serial *info){	unsigned short status;	/* if not ne modem line just return */	if (info->line != DZ_MODEM)		return;	status = dz_in(info, DZ_MSR);  	/* it's easy, since DSR2 is the only bit in the register */	if (status)		info->icount.dsr++;}/* * ------------------------------------------------------------ * dz_interrupt () * * this is the main interrupt routine for the DZ chip. * It deals with the multiple ports. * ------------------------------------------------------------ */static void dz_interrupt (int irq, void *dev, struct pt_regs *regs){	struct dz_serial *info;	unsigned short status;	 /* get the reason why we just got an irq */	status = dz_in((struct dz_serial *)dev, DZ_CSR);	info = lines[LINE(status)];     /* re-arrange info the proper port */	if (status & DZ_RDONE) 		receive_chars(info);	/* the receive function */	if (status & DZ_TRDY) 		transmit_chars (info);}/* * ------------------------------------------------------------------- * Here ends the DZ interrupt routines. * ------------------------------------------------------------------- *//* * This routine is used to handle the "bottom half" processing for the * serial driver, known also the "software interrupt" processing. * This processing is done at the kernel interrupt level, after the * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This * is where time-consuming activities which can not be done in the * interrupt driver proper are done; the interrupt driver schedules * them using rs_sched_event(), and they get done here. */static void do_serial_bh (void){	run_task_queue (&tq_serial);}static void do_softint (void *private_data){	struct dz_serial *info = (struct dz_serial *) private_data;	struct tty_struct *tty = info->tty;	if (!tty)		return;	if (test_and_clear_bit(DZ_EVENT_WRITE_WAKEUP, &info->event)) {		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&		    tty->ldisc.write_wakeup)			(tty->ldisc.write_wakeup) (tty);		wake_up_interruptible (&tty->write_wait);	}}/* * ------------------------------------------------------------------- * This routine is called from the scheduler tqueue when the interrupt * routine has signalled that a hangup has occurred.  The path of * hangup processing is: * *      serial interrupt routine -> (scheduler tqueue) -> *      do_serial_hangup() -> tty->hangup() -> rs_hangup() * -------------------------------------------------------------------  */static void do_serial_hangup (void *private_data){	struct dz_serial *info = (struct dz_serial *) private_data;	struct tty_struct *tty = info->tty;;        	if (!tty)		return;	tty_hangup(tty);}/* * ------------------------------------------------------------------- * startup () * * various initialization tasks * -------------------------------------------------------------------  */static int startup (struct dz_serial *info){	unsigned long page, flags;	unsigned short tmp;	if (info->is_initialized)		return 0;  	save_and_cli(flags);	if (!info->port) {		if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);		restore_flags(flags);		return -ENODEV;	}	if (!info->xmit_buf) {		page = get_free_page(GFP_KERNEL);		if (!page) {			restore_flags (flags);		return -ENOMEM;		}		info->xmit_buf = (unsigned char *)page;	}	if (info->tty)		clear_bit(TTY_IO_ERROR, &info->tty->flags);	/* enable the interrupt and the scanning */	tmp = dz_in(info, DZ_CSR);	tmp |= (DZ_RIE | DZ_TIE | DZ_MSE);	dz_out(info, DZ_CSR, tmp);	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;	change_speed(info);			/* set up the speed */	/*	 * Clear the line transmitter buffer I can't figure out why I need to	 * do this - but its necessary - in order for the console portion and	 * the interrupt portion to live happily side by side.	 */	info->is_initialized = 1;	restore_flags(flags);	return 0;}/*  * ------------------------------------------------------------------- * shutdown () * * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. * -------------------------------------------------------------------  */static void shutdown (struct dz_serial *info){	unsigned long flags;	unsigned short tmp;	if (!info->is_initialized)		return;	save_and_cli(flags);	dz_stop (info->tty);	info->cflags &= ~DZ_CREAD;	/* turn off receive enable flag */	dz_out(info, DZ_LPR, info->cflags);	if (info->xmit_buf) {               /* free Tx buffer */		free_page((unsigned long)info->xmit_buf);		info->xmit_buf = 0;	}	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {		tmp = dz_in(info, DZ_TCR);		if (tmp & DZ_MODEM_DTR) {			tmp &= ~DZ_MODEM_DTR;			dz_out(info, DZ_TCR, tmp);		}	}	if (info->tty)		set_bit (TTY_IO_ERROR, &info->tty->flags);	info->is_initialized = 0;

⌨️ 快捷键说明

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