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

📄 sab82532.c

📁 讲述linux的初始化过程
💻 C
📖 第 1 页 / 共 5 页
字号:
/* $Id: sab82532.c,v 1.54 2000/12/07 04:35:39 anton Exp $ * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. * * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be) * */#include <linux/config.h>#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/serialP.h>#include <linux/serial_reg.h>#include <linux/console.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/delay.h>#include <asm/sab82532.h>#include <asm/uaccess.h>#include <asm/ebus.h>#include <asm/irq.h>#include "sunserial.h"static DECLARE_TASK_QUEUE(tq_serial);/* This is (one of many) a special gross hack to allow SU and * SAB serials to co-exist on the same machine. -DaveM */#undef SERIAL_BH#define SERIAL_BH	AURORA_BHstatic struct tty_driver serial_driver, callout_driver;static int sab82532_refcount;/* number of characters left in xmit buffer before we ask for more */#define WAKEUP_CHARS 256#define SERIAL_PARANOIA_CHECK#define SERIAL_DO_RESTART/* Set of debugging defines */#undef SERIAL_DEBUG_OPEN#undef SERIAL_DEBUG_FLOW#undef SERIAL_DEBUG_MODEM#undef SERIAL_DEBUG_WAIT_UNTIL_SENT#undef SERIAL_DEBUG_SEND_BREAK#undef SERIAL_DEBUG_INTR#define SERIAL_DEBUG_OVERFLOW 1/* Trace things on serial device, useful for console debugging: */#undef SERIAL_LOG_DEVICE#ifdef SERIAL_LOG_DEVICEstatic void dprint_init(int tty);#endifstatic void change_speed(struct sab82532 *info);static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout);/* * This assumes you have a 29.4912 MHz clock for your UART. */#define BASE_BAUD ( 29491200 / 16 )static struct sab82532 *sab82532_chain = 0;static struct tty_struct *sab82532_table[NR_PORTS];static struct termios *sab82532_termios[NR_PORTS];static struct termios *sab82532_termios_locked[NR_PORTS];#ifdef MODULE#undef CONFIG_SERIAL_CONSOLE#endif#ifdef CONFIG_SERIAL_CONSOLEextern int serial_console;static struct console sab82532_console;static int sab82532_console_init(void);static void batten_down_hatches(struct sab82532 *info);#endif#ifndef MIN#define MIN(a,b)	((a) < (b) ? (a) : (b))#endifstatic char *sab82532_version[16] = {	"V1.0", "V2.0", "V3.2", "V(0x03)",	"V(0x04)", "V(0x05)", "V(0x06)", "V(0x07)",	"V(0x08)", "V(0x09)", "V(0x0a)", "V(0x0b)",	"V(0x0c)", "V(0x0d)", "V(0x0e)", "V(0x0f)"};static char serial_version[16];/* * tmp_buf is used as a temporary buffer by sab82532_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 unsigned char *tmp_buf = 0;static DECLARE_MUTEX(tmp_buf_sem);static inline int serial_paranoia_check(struct sab82532 *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 sab82532 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. * * The formula is:    Baud = BASE_BAUD / ((N + 1) * (1 << M)), * * with               0 <= N < 64 and 0 <= M < 16 * * XXX:	Speeds with M = 0 might not work properly for XTAL frequencies *      above 10 MHz. */struct ebrg_struct {	int	baud;	int	n;	int	m;};static struct ebrg_struct ebrg_table[] = {	{       0,	0,	 0 },	{      50,	35,	10 },	{      75,	47,	 9 },	{     110,	32,	 9 },	{     134,	53,	 8 },	{     150,	47,	 8 },	{     200,	35,	 8 },	{     300,	47,	 7 },	{     600,	47,	 6 },	{    1200,	47,	 5 },	{    1800,	31,	 5 },	{    2400,	47,	 4 },	{    4800,	47,	 3 },	{    9600,	47,	 2 },	{   19200,	47,	 1 },	{   38400,	23,	 1 },	{   57600,	15,	 1 },	{  115200,	 7,	 1 },	{  230400,	 3,	 1 },	{  460800,	 1,	 1 },	{   76800,	11,	 1 },	{  153600,	 5,	 1 },	{  307200,	 3,	 1 },	{  614400,	 3,	 0 },	{  921600,	 0,	 1 },};#define NR_EBRG_VALUES	(sizeof(ebrg_table)/sizeof(struct ebrg_struct))#define SAB82532_MAX_TEC_TIMEOUT 200000	/* 1 character time (at 50 baud) */#define SAB82532_MAX_CEC_TIMEOUT  50000	/* 2.5 TX CLKs (at 50 baud) */static __inline__ void sab82532_tec_wait(struct sab82532 *info){	int timeout = info->tec_timeout;	while ((readb(&info->regs->r.star) & SAB82532_STAR_TEC) && --timeout)		udelay(1);}static __inline__ void sab82532_cec_wait(struct sab82532 *info){	int timeout = info->cec_timeout;	while ((readb(&info->regs->r.star) & SAB82532_STAR_CEC) && --timeout)		udelay(1);}static __inline__ void sab82532_start_tx(struct sab82532 *info){	unsigned long flags;	int i;	save_flags(flags); cli();	if (info->xmit_cnt <= 0)		goto out;	if (!(readb(&info->regs->r.star) & SAB82532_STAR_XFW))		goto out;	info->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);	writeb(info->interrupt_mask1, &info->regs->w.imr1);	info->all_sent = 0;	for (i = 0; i < info->xmit_fifo_size; i++) {		u8 val = info->xmit_buf[info->xmit_tail++];		writeb(val, &info->regs->w.xfifo[i]);		info->xmit_tail &= (SERIAL_XMIT_SIZE - 1);		info->icount.tx++;		if (--info->xmit_cnt <= 0)			break;	}	/* Issue a Transmit Frame command. */	sab82532_cec_wait(info);	writeb(SAB82532_CMDR_XF, &info->regs->w.cmdr);out:	restore_flags(flags);}/* * ------------------------------------------------------------ * sab82532_stop() and sab82532_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. * ------------------------------------------------------------ */static void sab82532_stop(struct tty_struct *tty){	struct sab82532 *info = (struct sab82532 *)tty->driver_data;	unsigned long flags;	if (serial_paranoia_check(info, tty->device, "sab82532_stop"))		return;	save_flags(flags); cli();	info->interrupt_mask1 |= SAB82532_IMR1_XPR;	writeb(info->interrupt_mask1, &info->regs->w.imr1);	restore_flags(flags);}static void sab82532_start(struct tty_struct *tty){	struct sab82532 *info = (struct sab82532 *)tty->driver_data;	unsigned long flags;		if (serial_paranoia_check(info, tty->device, "sab82532_start"))		return;	save_flags(flags); cli();	info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);	writeb(info->interrupt_mask1, &info->regs->w.imr1);	sab82532_start_tx(info);	restore_flags(flags);}/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines.  All of the following * subroutines are declared as inline and are folded into * sab82532_interrupt().  They were separated out for readability's sake. * * Note: sab82532_interrupt() is a "fast" interrupt, which means that it * runs with interrupts turned off.  People who may want to modify * sab82532_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 inline void sab82532_sched_event(struct sab82532 *info, int event){	info->event |= 1 << event;	queue_task(&info->tqueue, &tq_serial);	mark_bh(SERIAL_BH);}static inline void receive_chars(struct sab82532 *info,				 union sab82532_irq_status *stat){	struct tty_struct *tty = info->tty;	unsigned char buf[32];	unsigned char status;	int free_fifo = 0;	int i, count = 0;	/* Read number of BYTES (Character + Status) available. */	if (stat->sreg.isr0 & SAB82532_ISR0_RPF) {		count = info->recv_fifo_size;		free_fifo++;	}	if (stat->sreg.isr0 & SAB82532_ISR0_TCD) {		count = readb(&info->regs->r.rbcl) & (info->recv_fifo_size - 1);		free_fifo++;	}	/* Issue a FIFO read command in case we where idle. */	if (stat->sreg.isr0 & SAB82532_ISR0_TIME) {		sab82532_cec_wait(info);		writeb(SAB82532_CMDR_RFRD, &info->regs->w.cmdr);		return;	}	if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {#ifdef SERIAL_DEBUG_OVERFLOW		printk("sab82532: receive_chars: RFO");#endif		free_fifo++;	}	/* Read the FIFO. */	for (i = 0; i < count; i++)		buf[i] = readb(&info->regs->r.rfifo[i]);	/* Issue Receive Message Complete command. */	if (free_fifo) {		sab82532_cec_wait(info);		writeb(SAB82532_CMDR_RMC, &info->regs->w.cmdr);	}#ifdef CONFIG_SERIAL_CONSOLE	if (info->is_console)		wake_up(&keypress_wait);#endif	if (!tty)		return;	for (i = 0; i < count; ) {		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {#ifdef SERIAL_DEBUG_OVERFLOW			printk("sab82532: receive_chars: tty overrun\n");#endif			info->icount.buf_overrun++;			break;		}		tty->flip.count++;		*tty->flip.char_buf_ptr++ = buf[i++];		status = buf[i++];		info->icount.rx++;#ifdef SERIAL_DEBUG_INTR                printk("DR%02x:%02x...", (unsigned char)*(tty->flip.char_buf_ptr - 1), status);#endif		if (status & SAB82532_RSTAT_PE) {			*tty->flip.flag_buf_ptr++ = TTY_PARITY;			info->icount.parity++;		} else if (status & SAB82532_RSTAT_FE) {			*tty->flip.flag_buf_ptr++ = TTY_FRAME;			info->icount.frame++;		}		else			*tty->flip.flag_buf_ptr++ = TTY_NORMAL;	}	queue_task(&tty->flip.tqueue, &tq_timer);}static inline void transmit_chars(struct sab82532 *info,				  union sab82532_irq_status *stat){	int i;	if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) {		info->interrupt_mask1 |= SAB82532_IMR1_ALLS;		writeb(info->interrupt_mask1, &info->regs->w.imr1);		info->all_sent = 1;	}	if (!(stat->sreg.isr1 & SAB82532_ISR1_XPR))		return;	if (!info->tty) {		info->interrupt_mask1 |= SAB82532_IMR1_XPR;		writeb(info->interrupt_mask1, &info->regs->w.imr1);		return;	}	if ((info->xmit_cnt <= 0) || info->tty->stopped ||	    info->tty->hw_stopped) {		info->interrupt_mask1 |= SAB82532_IMR1_XPR;		writeb(info->interrupt_mask1, &info->regs->w.imr1);		return;	}	info->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);	writeb(info->interrupt_mask1, &info->regs->w.imr1);	info->all_sent = 0;	/* Stuff 32 bytes into Transmit FIFO. */	for (i = 0; i < info->xmit_fifo_size; i++) {		u8 val = info->xmit_buf[info->xmit_tail++];		writeb(val, &info->regs->w.xfifo[i]);		info->xmit_tail &= (SERIAL_XMIT_SIZE - 1);		info->icount.tx++;		if (--info->xmit_cnt <= 0)			break;	}	/* Issue a Transmit Frame command. */	sab82532_cec_wait(info);	writeb(SAB82532_CMDR_XF, &info->regs->w.cmdr);	if (info->xmit_cnt < WAKEUP_CHARS)		sab82532_sched_event(info, RS_EVENT_WRITE_WAKEUP);#ifdef SERIAL_DEBUG_INTR	printk("THRE...");#endif	if (info->xmit_cnt <= 0) {		info->interrupt_mask1 |= SAB82532_IMR1_XPR;		writeb(info->interrupt_mask1, &info->regs->w.imr1);	}}static inline void check_status(struct sab82532 *info,				union sab82532_irq_status *stat){	struct tty_struct *tty = info->tty;	int modem_change = 0;	if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {#ifdef CONFIG_SERIAL_CONSOLE		if (info->is_console) {			batten_down_hatches(info);			return;		}#endif		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {			info->icount.buf_overrun++;			goto check_modem;		}		tty->flip.count++;		*tty->flip.flag_buf_ptr++ = TTY_PARITY;		*tty->flip.char_buf_ptr++ = 0;		info->icount.brk++;	}	if (!tty)		return;	if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {			info->icount.buf_overrun++;			goto check_modem;		}		tty->flip.count++;		*tty->flip.flag_buf_ptr++ = TTY_PARITY;		*tty->flip.char_buf_ptr++ = 0;		info->icount.overrun++;	}check_modem:	if (stat->sreg.isr0 & SAB82532_ISR0_CDSC) {		info->dcd = (readb(&info->regs->r.vstr) & SAB82532_VSTR_CD) ? 0 : 1;		info->icount.dcd++;		modem_change++;#ifdef SERIAL_DEBUG_MODEM		printk("DCD change: %d\n", info->icount.dcd);

⌨️ 快捷键说明

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