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

📄 zs.c

📁 自己根据lkd和情境分析
💻 C
📖 第 1 页 / 共 5 页
字号:
/* $Id: zs.c,v 1.1.1.1 2004/02/04 12:56:50 laputa Exp $ * zs.c: Zilog serial port driver for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be) * Fixes by Pete A. Zaitcev <zaitcev@yahoo.com>. * * Fixed to use tty_get_baud_rate(). *   Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12 * * /proc/tty/driver/serial now exists and is readable. *   Alex Buell <alex.buell@tahallah.demon.co.uk>, 2001-12-23 * */#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/keyboard.h>#include <linux/console.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/bootmem.h>#include <linux/sysrq.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/oplib.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include <asm/kdebug.h>#include <asm/page.h>#include <asm/pgtable.h>#include <asm/sbus.h>#ifdef __sparc_v9__#include <asm/fhc.h>#endif#ifdef CONFIG_PCI#include <linux/pci.h>#endif#include "sunserial.h"#include "zs.h"#include "sunkbd.h"#include "sunmouse.h"static int num_serial = 2; /* sun4/sun4c/sun4m - Two chips on board. */#define NUM_SERIAL num_serial#define NUM_CHANNELS (NUM_SERIAL * 2)#define KEYBOARD_LINE 0x2#define MOUSE_LINE    0x3/* On 32-bit sparcs we need to delay after register accesses * to accomodate sun4 systems, but we do not need to flush writes. * On 64-bit sparc we only need to flush single writes to ensure * completion. */#ifndef __sparc_v9__#define ZSDELAY()		udelay(5)#define ZSDELAY_LONG()		udelay(20)#define ZS_WSYNC(channel)	do { } while(0)#else#define ZSDELAY()#define ZSDELAY_LONG()#define ZS_WSYNC(__channel) \	sbus_readb(&((__channel)->control))#endifstruct sun_zslayout **zs_chips;struct sun_zschannel **zs_channels;struct sun_zschannel *zs_mousechan;struct sun_zschannel *zs_kbdchan;struct sun_zschannel *zs_kgdbchan;int *zs_nodes;struct sun_serial *zs_soft;struct sun_serial *zs_chain;  /* IRQ servicing chain */int zilog_irq;struct tty_struct *zs_ttys;/* Console hooks... */#ifdef CONFIG_SERIAL_CONSOLEstatic struct console zs_console;static int zs_console_init(void);/* * Define this to get the zs_fair_output() functionality. */#undef SERIAL_CONSOLE_FAIR_OUTPUT#endif /* CONFIG_SERIAL_CONSOLE */static unsigned char kgdb_regs[16] = {	0, 0, 0,                     /* write 0, 1, 2 */	(Rx8 | RxENAB),              /* write 3 */	(X16CLK | SB1 | PAR_EVEN),   /* write 4 */	(DTR | Tx8 | TxENAB),        /* write 5 */	0, 0, 0,                     /* write 6, 7, 8 */	(NV),                        /* write 9 */	(NRZ),                       /* write 10 */	(TCBR | RCBR),               /* write 11 */	0, 0,                        /* BRG time constant, write 12 + 13 */	(BRSRC | BRENAB),            /* write 14 */	(DCDIE)                      /* write 15 */};static unsigned char zscons_regs[16] = {	0,                           /* write 0 */	(EXT_INT_ENAB | INT_ALL_Rx), /* write 1 */	0,                           /* write 2 */	(Rx8 | RxENAB),              /* write 3 */	(X16CLK),                    /* write 4 */	(DTR | Tx8 | TxENAB),        /* write 5 */	0, 0, 0,                     /* write 6, 7, 8 */	(NV | MIE),                  /* write 9 */	(NRZ),                       /* write 10 */	(TCBR | RCBR),               /* write 11 */	0, 0,                        /* BRG time constant, write 12 + 13 */	(BRSRC | BRENAB),            /* write 14 */	(DCDIE | CTSIE | TxUIE | BRKIE) /* write 15 */};#define ZS_CLOCK         4915200   /* Zilog input clock rate */DECLARE_TASK_QUEUE(tq_serial);static 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#define SERIAL_DO_RESTART/* 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_STROBE_TIME 10#define RS_ISR_PASS_LIMIT 256#define _INLINE_ inlineint zs_init(void);static void zs_kgdb_hook(int);static void change_speed(struct sun_serial *info);static struct tty_struct **serial_table;static struct termios **serial_termios;static struct termios **serial_termios_locked;#ifndef MIN#define MIN(a,b)	((a) < (b) ? (a) : (b))#endif#undef ZS_LOG#ifdef ZS_LOGstruct zs_logent {	u8 reg, val;	u8 write, __pad;#define REGIRQ	0xff#define REGDATA	0xfe#define REGCTRL	0xfd};struct zs_logent zslog[32];int zs_curlog;#define ZSLOG(__reg, __val, __write) \do{	int index = zs_curlog; \	zslog[index].reg = (__reg); \	zslog[index].val = (__val); \	zslog[index].write = (__write); \	zs_curlog = (index + 1) & (32 - 1); \}while(0)int zs_dumplog(char *buffer){	int len = 0;	int i;	for (i = 0; i < 32; i++) {		u8 reg, val, write;		reg = zslog[i].reg;		val = zslog[i].val;		write = zslog[i].write;		len += sprintf(buffer + len,			       "ZSLOG[%2d]: reg %2x val %2x %s\n",			       i, reg, val, write ? "write" : "read");	}	len += sprintf(buffer + len, "ZS current log index %d\n",		       zs_curlog);	return len;}#else#define ZSLOG(x,y,z)	do { } while (0)#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 = 0;static DECLARE_MUTEX(tmp_buf_sem);static inline int serial_paranoia_check(struct sun_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 sun_serial 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;}/* Reading and writing Zilog8530 registers.  The delays are to make this * driver work on the Sun4 which needs a settling delay after each chip * register access, other machines handle this in hardware via auxiliary * flip-flops which implement the settle time we do in software. */static unsigned char read_zsreg(struct sun_zschannel *channel,				unsigned char reg){	unsigned char retval;	sbus_writeb(reg, &channel->control);	ZSDELAY();	retval = sbus_readb(&channel->control);	ZSDELAY();	ZSLOG(reg, retval, 0);	return retval;}static void write_zsreg(struct sun_zschannel *channel,			unsigned char reg, unsigned char value){	ZSLOG(reg, value, 1);	sbus_writeb(reg, &channel->control);	ZSDELAY();	sbus_writeb(value, &channel->control);	ZSDELAY();}static void load_zsregs(struct sun_serial *info, unsigned char *regs){	struct sun_zschannel *channel = info->zs_channel;	unsigned long flags;	unsigned char stat;	int i;	for (i = 0; i < 1000; i++) {		stat = read_zsreg(channel, R1);		if (stat & ALL_SNT)			break;		udelay(100);	}	write_zsreg(channel, R3, 0);	ZS_CLEARSTAT(channel);	ZS_CLEARERR(channel);	ZS_CLEARFIFO(channel);	/* Load 'em up */	save_flags(flags); cli();	if (info->channelA)		write_zsreg(channel, R9, CHRA);	else		write_zsreg(channel, R9, CHRB);	ZSDELAY_LONG();	write_zsreg(channel, R4, regs[R4]);	write_zsreg(channel, R3, regs[R3] & ~RxENAB);	write_zsreg(channel, R5, regs[R5] & ~TxENAB);	write_zsreg(channel, R9, regs[R9] & ~MIE);	write_zsreg(channel, R10, regs[R10]);	write_zsreg(channel, R11, regs[R11]);	write_zsreg(channel, R12, regs[R12]);	write_zsreg(channel, R13, regs[R13]);	write_zsreg(channel, R14, regs[R14] & ~BRENAB);	write_zsreg(channel, R14, regs[R14]);	write_zsreg(channel, R14, (regs[R14] & ~SNRZI) | BRENAB);	write_zsreg(channel, R3, regs[R3]);	write_zsreg(channel, R5, regs[R5]);	write_zsreg(channel, R15, regs[R15]);	write_zsreg(channel, R0, RES_EXT_INT);	write_zsreg(channel, R0, ERR_RES);	write_zsreg(channel, R1, regs[R1]);	write_zsreg(channel, R9, regs[R9]);	restore_flags(flags);}#define ZS_PUT_CHAR_MAX_DELAY	2000	/* 10 ms */static void zs_put_char(struct sun_zschannel *channel, char ch){	int loops = ZS_PUT_CHAR_MAX_DELAY;	/* Do not change this to use ZSDELAY as this is	 * a timed polling loop and on sparc64 ZSDELAY	 * is a nop.  -DaveM	 */	do {		u8 val = sbus_readb(&channel->control);		ZSLOG(REGCTRL, val, 0);		if (val & Tx_BUF_EMP)			break;		udelay(5);	} while (--loops);	sbus_writeb(ch, &channel->data);	ZSDELAY();	ZS_WSYNC(channel);	ZSLOG(REGDATA, ch, 1);}/* Sets or clears DTR/RTS on the requested line */static void zs_rtsdtr(struct sun_serial *ss, int set){	unsigned long flags;	save_flags(flags); cli();	if(set) {		ss->curregs[5] |= (RTS | DTR);		write_zsreg(ss->zs_channel, 5, ss->curregs[5]);	} else {		ss->curregs[5] &= ~(RTS | DTR);		write_zsreg(ss->zs_channel, 5, ss->curregs[5]);	}	restore_flags(flags);	return;}static void kgdb_chaninit(struct sun_serial *ss, int intson, int bps){	int brg;	if(intson) {		kgdb_regs[R1] = INT_ALL_Rx;		kgdb_regs[R9] |= MIE;	} else {		kgdb_regs[R1] = 0;		kgdb_regs[R9] &= ~MIE;	}	brg = BPS_TO_BRG(bps, ZS_CLOCK/16);	kgdb_regs[R12] = (brg & 255);	kgdb_regs[R13] = ((brg >> 8) & 255);	load_zsregs(ss, kgdb_regs);}/* * ------------------------------------------------------------ * zs_stop() and zs_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. * ------------------------------------------------------------ */static void zs_stop(struct tty_struct *tty){	struct sun_serial *info = (struct sun_serial *) tty->driver_data;	unsigned long flags;	if (serial_paranoia_check(info, tty->device, "zs_stop"))		return;		save_flags(flags); cli();	if (info->curregs[5] & TxENAB) {		info->curregs[5] &= ~TxENAB;		write_zsreg(info->zs_channel, 5, info->curregs[5]);	}	restore_flags(flags);}static void zs_start(struct tty_struct *tty){	struct sun_serial *info = (struct sun_serial *) tty->driver_data;	unsigned long flags;		if (serial_paranoia_check(info, tty->device, "zs_start"))		return;		save_flags(flags); cli();	if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {		info->curregs[5] |= TxENAB;		write_zsreg(info->zs_channel, 5, info->curregs[5]);	}	restore_flags(flags);}/* Drop into either the boot monitor or kadb upon receiving a break * from keyboard/console input. */void batten_down_hatches(void){	if (!stop_a_enabled)		return;	/* 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.	 */	printk("\n");	flush_user_windows();#ifndef __sparc_v9__	if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) &&	   (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR))		sp_enter_debugger();	else#endif		prom_cmdline();	/* XXX We want to notify the keyboard driver that all	 * XXX keys are in the up state or else weird things	 * XXX happen...	 */	return;}/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines.  All of the following * subroutines are declared as inline and are folded into * zs_interrupt().  They were separated out for readability's sake. * * Note: zs_interrupt() is a "fast" interrupt, which means that it * runs with interrupts turned off.  People who may want to modify * zs_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 void zs_sched_event(struct sun_serial *info, int event){	info->event |= 1 << event;	queue_task(&info->tqueue, &tq_serial);	mark_bh(SERIAL_BH);}#ifndef __sparc_v9__extern void breakpoint(void);  /* For the KGDB frame character */#endifstatic void receive_chars(struct sun_serial *info, struct pt_regs *regs){	struct tty_struct *tty = info->tty;	int do_queue_task = 0;	while (1) {		unsigned char ch, r1;		r1 = read_zsreg(info->zs_channel, R1);		if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {			sbus_writeb(ERR_RES, &info->zs_channel->control);			ZSDELAY();			ZS_WSYNC(info->zs_channel);			ZSLOG(REGCTRL, ERR_RES, 1);		}		ch = sbus_readb(&info->zs_channel->data);

⌨️ 快捷键说明

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