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

📄 sgiserial.c

📁 移植到2410开发板上的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
 */void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs){	struct sgi_serial * info = (struct sgi_serial *) dev_id;	unsigned char zs_intreg;	zs_intreg = read_zsreg(info->zs_next->zs_channel, 3);	/* NOTE: The read register 3, which holds the irq status,	 *       does so for both channels on each chip.  Although	 *       the status value itself must be read from the A	 *       channel and is only valid when read from channel A.	 *       Yes... broken hardware...	 */#define CHAN_A_IRQMASK (CHARxIP | CHATxIP | CHAEXT)#define CHAN_B_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT)	/* *** Chip 1 *** */	/* Channel B -- /dev/ttyb, could be the console */	if(zs_intreg & CHAN_B_IRQMASK) {		if (zs_intreg & CHBRxIP)			receive_chars(info, regs);		if (zs_intreg & CHBTxIP)			transmit_chars(info);		if (zs_intreg & CHBEXT)			status_handle(info);	}	info=info->zs_next;	/* Channel A -- /dev/ttya, could be the console */	if(zs_intreg & CHAN_A_IRQMASK) {		if (zs_intreg & CHARxIP)			receive_chars(info, regs);		if (zs_intreg & CHATxIP)			transmit_chars(info);		if (zs_intreg & CHAEXT)			status_handle(info);	}}/* * ------------------------------------------------------------------- * Here ends the serial 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_){	struct sgi_serial	*info = (struct sgi_serial *) private_;	struct tty_struct	*tty;		tty = info->tty;	if (!tty)		return;	if (test_and_clear_bit(RS_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_){	struct sgi_serial	*info = (struct sgi_serial *) private_;	struct tty_struct	*tty;		tty = info->tty;	if (!tty)		return;	tty_hangup(tty);}static int startup(struct sgi_serial * info){	volatile unsigned char junk;	unsigned long flags;	if (info->flags & ZILOG_INITIALIZED)		return 0;	if (!info->xmit_buf) {		info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);		if (!info->xmit_buf)			return -ENOMEM;	}	save_flags(flags); cli();#ifdef SERIAL_DEBUG_OPEN	printk("starting up ttys%d (irq %d)...\n", info->line, info->irq);#endif	/*	 * Clear the FIFO buffers and disable them	 * (they will be reenabled in change_speed())	 */	ZS_CLEARFIFO(info->zs_channel);	info->xmit_fifo_size = 1;	/*	 * Clear the interrupt registers.	 */	udelay(2);	info->zs_channel->control = ERR_RES;	junk = ioc_icontrol->istat0;	udelay(2);	info->zs_channel->control = RES_H_IUS;	junk = ioc_icontrol->istat0;	/*	 * Now, initialize the Zilog	 */	zs_rtsdtr(info, 1);	/*	 * Finally, enable sequencing and interrupts	 */	info->curregs[1] |= (info->curregs[1] & ~0x18) | (EXT_INT_ENAB|INT_ALL_Rx);	info->pendregs[1] = info->curregs[1];	info->curregs[3] |= (RxENABLE | Rx8);	info->pendregs[3] = info->curregs[3];	/* We enable Tx interrupts as needed. */	info->curregs[5] |= (TxENAB | Tx8);	info->pendregs[5] = info->curregs[5];	info->curregs[9] |= (NV | MIE);	info->pendregs[9] = info->curregs[9];	write_zsreg(info->zs_channel, 3, info->curregs[3]);	write_zsreg(info->zs_channel, 5, info->curregs[5]);	write_zsreg(info->zs_channel, 9, info->curregs[9]);		/*	 * And clear the interrupt registers again for luck.	 */	udelay(2);	info->zs_channel->control = ERR_RES;	junk = ioc_icontrol->istat0;	udelay(2);	info->zs_channel->control = RES_H_IUS;	junk = ioc_icontrol->istat0;	if (info->tty)		clear_bit(TTY_IO_ERROR, &info->tty->flags);	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;	/*	 * and set the speed of the serial port	 */	change_speed(info);	info->flags |= ZILOG_INITIALIZED;	restore_flags(flags);	return 0;}/* * 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 sgi_serial * info){	unsigned long	flags;	if (!(info->flags & ZILOG_INITIALIZED))		return;#ifdef SERIAL_DEBUG_OPEN	printk("Shutting down serial port %d (irq %d)....", info->line,	       info->irq);#endif		save_flags(flags); cli(); /* Disable interrupts */		if (info->xmit_buf) {		free_page((unsigned long) info->xmit_buf);		info->xmit_buf = 0;	}	if (info->tty)		set_bit(TTY_IO_ERROR, &info->tty->flags);		info->flags &= ~ZILOG_INITIALIZED;	restore_flags(flags);}/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static void change_speed(struct sgi_serial *info){	unsigned short port;	unsigned cflag;	int	i;	int	brg;	if (!info->tty || !info->tty->termios)		return;	cflag = info->tty->termios->c_cflag;	if (!(port = info->port))		return;	i = cflag & CBAUD;	if (i & CBAUDEX) {		/* XXX CBAUDEX is not obeyed.		 * It is impossible at a 32bits SPARC.		 * But we have to report this to user ... someday.		 */		i = B9600;	}	if (i == 0) {		/* XXX B0, hangup the line. */		do_serial_hangup(info);	} else if (baud_table[i]) {		info->zs_baud = baud_table[i];		info->clk_divisor = 16;		info->curregs[4] = X16CLK;		info->curregs[11] = TCBR | RCBR;		brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);		info->curregs[12] = (brg & 255);		info->curregs[13] = ((brg >> 8) & 255);		info->curregs[14] = BRENABL;	}	/* byte size and parity */	switch (cflag & CSIZE) {	case CS5:		info->curregs[3] &= ~(0xc0);		info->curregs[3] |= Rx5;		info->pendregs[3] = info->curregs[3];		info->curregs[5] &= ~(0xe0);		info->curregs[5] |= Tx5;		info->pendregs[5] = info->curregs[5];		break;	case CS6:		info->curregs[3] &= ~(0xc0);		info->curregs[3] |= Rx6;		info->pendregs[3] = info->curregs[3];		info->curregs[5] &= ~(0xe0);		info->curregs[5] |= Tx6;		info->pendregs[5] = info->curregs[5];		break;	case CS7:		info->curregs[3] &= ~(0xc0);		info->curregs[3] |= Rx7;		info->pendregs[3] = info->curregs[3];		info->curregs[5] &= ~(0xe0);		info->curregs[5] |= Tx7;		info->pendregs[5] = info->curregs[5];		break;	case CS8:	default: /* defaults to 8 bits */		info->curregs[3] &= ~(0xc0);		info->curregs[3] |= Rx8;		info->pendregs[3] = info->curregs[3];		info->curregs[5] &= ~(0xe0);		info->curregs[5] |= Tx8;		info->pendregs[5] = info->curregs[5];		break;	}	info->curregs[4] &= ~(0x0c);	if (cflag & CSTOPB) {		info->curregs[4] |= SB2;	} else {		info->curregs[4] |= SB1;	}	info->pendregs[4] = info->curregs[4];	if (cflag & PARENB) {		info->curregs[4] |= PAR_ENA;		info->pendregs[4] |= PAR_ENA;	} else {		info->curregs[4] &= ~PAR_ENA;		info->pendregs[4] &= ~PAR_ENA;	}	if (!(cflag & PARODD)) {		info->curregs[4] |= PAR_EVEN;		info->pendregs[4] |= PAR_EVEN;	} else {		info->curregs[4] &= ~PAR_EVEN;		info->pendregs[4] &= ~PAR_EVEN;	}	/* Load up the new values */	load_zsregs(info->zs_channel, info->curregs);	return;}/* This is for console output over ttya/ttyb */static void zs_cons_put_char(char ch){	struct sgi_zschannel *chan = zs_conschan;	volatile unsigned char junk;	int flags, loops = 0;	save_flags(flags); cli();	while(((junk = chan->control) & Tx_BUF_EMP)==0 && loops < 10000) {		loops++;		udelay(2);	}	udelay(2);	chan->data = ch;	junk = ioc_icontrol->istat0;	restore_flags(flags);}/*  * This is the more generic put_char function for the driver. * In earlier versions of this driver, "rs_put_char" was the * name of the console-specific fucntion, now called zs_cons_put_char */static void rs_put_char(struct tty_struct *tty, char ch){	struct sgi_zschannel *chan = 		((struct sgi_serial *)tty->driver_data)->zs_channel;	volatile unsigned char junk;	int flags, loops = 0;	save_flags(flags); cli();	while(((junk = chan->control) & Tx_BUF_EMP)==0 && loops < 10000) {		loops++;		udelay(2);	}	udelay(2);	chan->data = ch;	junk = ioc_icontrol->istat0;	restore_flags(flags);}/* These are for receiving and sending characters under the kgdb * source level kernel debugger. */int putDebugChar(char kgdb_char){	struct sgi_zschannel *chan = zs_kgdbchan;	volatile unsigned char junk;	unsigned long flags;	save_flags(flags); cli();	udelay(2);	while((chan->control & Tx_BUF_EMP)==0)		udelay(2);	udelay(2);	chan->data = kgdb_char;	junk = ioc_icontrol->istat0;	restore_flags(flags);	return 1;}char getDebugChar(void){	struct sgi_zschannel *chan = zs_kgdbchan;	unsigned char junk;	while((chan->control & Rx_CH_AV)==0)		udelay(2);	junk = ioc_icontrol->istat0;	udelay(2);	return chan->data;}/* * Fair output driver allows a process to speak. */static void rs_fair_output(void){	int left;		/* Output no more than that */	unsigned long flags;	struct sgi_serial *info = zs_consinfo;	volatile unsigned char junk;	char c;	if (info == 0) return;	if (info->xmit_buf == 0) return;	save_flags(flags);  cli();	left = info->xmit_cnt;	while (left != 0) {		c = info->xmit_buf[info->xmit_tail];		info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);		info->xmit_cnt--;		restore_flags(flags);		zs_cons_put_char(c);		save_flags(flags);  cli();		left = MIN(info->xmit_cnt, left-1);	}	/* Last character is being transmitted now (hopefully). */	udelay(2);	zs_conschan->control = RES_Tx_P;	junk = ioc_icontrol->istat0;	restore_flags(flags);	return;}static int rs_write(struct tty_struct * tty, int from_user,		    const unsigned char *buf, int count){	int	c, total = 0;	struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;	unsigned long flags;	if (serial_paranoia_check(info, tty->device, "rs_write"))		return 0;	if (!tty || !info->xmit_buf)		return 0;	save_flags(flags);	while (1) {		cli();				c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,				   SERIAL_XMIT_SIZE - info->xmit_head));		if (c <= 0)			break;		if (from_user) {			down(&tmp_buf_sem);			copy_from_user(tmp_buf, buf, c);			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,				       SERIAL_XMIT_SIZE - info->xmit_head));			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);			up(&tmp_buf_sem);		} else			memcpy(info->xmit_buf + info->xmit_head, buf, c);		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);		info->xmit_cnt += c;		restore_flags(flags);		buf += c;		count -= c;		total += c;	}	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {	/*	 * The above test used to include the condition 	 * "&& !(info->curregs[5] & TxENAB)", but there	 * is reason to suspect that it is never statisfied	 * when the port is running.  The problem may in fact	 * have been masked by the fact that, if O_POST is set,	 * there is always a rs_flush_xx operation following the	 * rs_write, and the flush ignores that condition when	 * it kicks off the transmit.	 */		/* Enable transmitter */		info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;		info->pendregs[1] |= TxINT_ENAB|EXT_INT_ENAB;		write_zsreg(info->zs_channel, 1, info->curregs[1]);		info->curregs[5] |= TxENAB;		info->pendregs[5] |= TxENAB;		write_zsreg(info->zs_channel, 5, info->curregs[5]);	/*	 * The following code is imported from the 2.3.6 Sun sbus zs.c	 * driver, of which an earlier version served as the basis	 * for sgiserial.c.  Perhaps due to changes over time in	 * the line discipline code, ns_write()s with from_user	 * set would not otherwise actually kick-off output in	 * Linux 2.2.x or later.  Maybe it never really worked.	 */		rs_put_char(tty, info->xmit_buf[info->xmit_tail++]);                info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);                info->xmit_cnt--;	}	restore_flags(flags);	return total;}static int rs_write_room(struct tty_struct *tty){	struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;	int	ret;					if (serial_paranoia_check(info, tty->device, "rs_write_room"))		return 0;	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;	if (ret < 0)		ret = 0;	return ret;}static int rs_chars_in_buffer(struct tty_struct *tty){	struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;					if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))		return 0;	return info->xmit_cnt;}static void rs_flush_buffer(struct tty_struct *tty){	struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;					if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))		return;	cli();	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;	sti();	wake_up_interruptible(&tty->write_wait);	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&	    tty->ldisc.write_wakeup)		(tty->ldisc.write_wakeup)(tty);}static void rs_flush_chars(struct tty_struct *tty){	struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;	unsigned long flags;	if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))		return;	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||	    !info->xmit_buf)		return;	/* Enable transmitter */	save_flags(flags); cli();	info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;	info->pendregs[1] |= TxINT_ENAB|EXT_INT_ENAB;	write_zsreg(info->zs_channel, 1, info->curregs[1]);	info->curregs[5] |= TxENAB;	info->pendregs[5] |= TxENAB;	write_zsreg(info->zs_channel, 5, info->curregs[5]);	/*	 * Send a first (bootstrapping) character. A best solution is	 * to call transmit_chars() here which handles output in a	 * generic way. Current transmit_chars() not only transmits,	 * but resets interrupts also what we do not desire here.	 * XXX Discuss with David.	 */	if (info->zs_channel->control & Tx_BUF_EMP) {

⌨️ 快捷键说明

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