specialix.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,265 行 · 第 1/4 页

C
2,265
字号
		channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;	if (channel < CD186x_NCH) {		port = &sx_port[board_No(bp) * SX_NPORT + channel];		if (port->flags & ASYNC_INITIALIZED) {			return port;		}	}	printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", 	       board_No(bp), what, channel);	return NULL;}static inline void sx_receive_exc(struct specialix_board * bp){	struct specialix_port *port;	struct tty_struct *tty;	unsigned char status;	unsigned char ch;	if (!(port = sx_get_port(bp, "Receive")))		return;	tty = port->tty;	if (tty->flip.count >= TTY_FLIPBUF_SIZE) {		printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n",		       board_No(bp), port_No(port));		return;	}	#ifdef SX_REPORT_OVERRUN		status = sx_in(bp, CD186x_RCSR);	if (status & RCSR_OE) {		port->overrun++;#if SPECIALIX_DEBUG 		printk(KERN_DEBUG "sx%d: port %d: Overrun. Total %ld overruns.\n", 		       board_No(bp), port_No(port), port->overrun);#endif			}	status &= port->mark_mask;#else		status = sx_in(bp, CD186x_RCSR) & port->mark_mask;#endif		ch = sx_in(bp, CD186x_RDR);	if (!status) {		return;	}	if (status & RCSR_TOUT) {		printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n", 		       board_No(bp), port_No(port));		return;			} else if (status & RCSR_BREAK) {#ifdef SPECIALIX_DEBUG		printk(KERN_DEBUG "sx%d: port %d: Handling break...\n",		       board_No(bp), port_No(port));#endif		*tty->flip.flag_buf_ptr++ = TTY_BREAK;		if (port->flags & ASYNC_SAK)			do_SAK(tty);			} else if (status & RCSR_PE) 		*tty->flip.flag_buf_ptr++ = TTY_PARITY;		else if (status & RCSR_FE) 		*tty->flip.flag_buf_ptr++ = TTY_FRAME;		else if (status & RCSR_OE)		*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;		else		*tty->flip.flag_buf_ptr++ = 0;		*tty->flip.char_buf_ptr++ = ch;	tty->flip.count++;	schedule_delayed_work(&tty->flip.work, 1);}static inline void sx_receive(struct specialix_board * bp){	struct specialix_port *port;	struct tty_struct *tty;	unsigned char count;		if (!(port = sx_get_port(bp, "Receive")))		return;		tty = port->tty;		count = sx_in(bp, CD186x_RDCR);	#ifdef SX_REPORT_FIFO	port->hits[count > 8 ? 9 : count]++;#endif			while (count--) {		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {			printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n",			       board_No(bp), port_No(port));			break;		}		*tty->flip.char_buf_ptr++ = sx_in(bp, CD186x_RDR);		*tty->flip.flag_buf_ptr++ = 0;		tty->flip.count++;	}	schedule_delayed_work(&tty->flip.work, 1);}static inline void sx_transmit(struct specialix_board * bp){	struct specialix_port *port;	struct tty_struct *tty;	unsigned char count;			if (!(port = sx_get_port(bp, "Transmit")))		return;		tty = port->tty;		if (port->IER & IER_TXEMPTY) {		/* FIFO drained */		sx_out(bp, CD186x_CAR, port_No(port));		port->IER &= ~IER_TXEMPTY;		sx_out(bp, CD186x_IER, port->IER);		return;	}		if ((port->xmit_cnt <= 0 && !port->break_length)	    || tty->stopped || tty->hw_stopped) {		sx_out(bp, CD186x_CAR, port_No(port));		port->IER &= ~IER_TXRDY;		sx_out(bp, CD186x_IER, port->IER);		return;	}		if (port->break_length) {		if (port->break_length > 0) {			if (port->COR2 & COR2_ETC) {				sx_out(bp, CD186x_TDR, CD186x_C_ESC);				sx_out(bp, CD186x_TDR, CD186x_C_SBRK);				port->COR2 &= ~COR2_ETC;			}			count = min_t(int, port->break_length, 0xff);			sx_out(bp, CD186x_TDR, CD186x_C_ESC);			sx_out(bp, CD186x_TDR, CD186x_C_DELAY);			sx_out(bp, CD186x_TDR, count);			if (!(port->break_length -= count))				port->break_length--;		} else {			sx_out(bp, CD186x_TDR, CD186x_C_ESC);			sx_out(bp, CD186x_TDR, CD186x_C_EBRK);			sx_out(bp, CD186x_COR2, port->COR2);			sx_wait_CCR(bp);			sx_out(bp, CD186x_CCR, CCR_CORCHG2);			port->break_length = 0;		}		return;	}		count = CD186x_NFIFO;	do {		sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);		port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);		if (--port->xmit_cnt <= 0)			break;	} while (--count > 0);		if (port->xmit_cnt <= 0) {		sx_out(bp, CD186x_CAR, port_No(port));		port->IER &= ~IER_TXRDY;		sx_out(bp, CD186x_IER, port->IER);	}	if (port->xmit_cnt <= port->wakeup_chars)		sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);}static inline void sx_check_modem(struct specialix_board * bp){	struct specialix_port *port;	struct tty_struct *tty;	unsigned char mcr;#ifdef SPECIALIX_DEBUG	printk (KERN_DEBUG "Modem intr. ");#endif	if (!(port = sx_get_port(bp, "Modem")))		return;		tty = port->tty;		mcr = sx_in(bp, CD186x_MCR);	printk ("mcr = %02x.\n", mcr);	if ((mcr & MCR_CDCHG)) {#ifdef SPECIALIX_DEBUG 		printk (KERN_DEBUG "CD just changed... ");#endif		if (sx_in(bp, CD186x_MSVR) & MSVR_CD) {#ifdef SPECIALIX_DEBUG			printk ( "Waking up guys in open.\n");#endif			wake_up_interruptible(&port->open_wait);		} else {#ifdef SPECIALIX_DEBUG			printk ( "Sending HUP.\n");#endif			schedule_work(&port->tqueue_hangup);		}	}	#ifdef SPECIALIX_BRAIN_DAMAGED_CTS	if (mcr & MCR_CTSCHG) {		if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {			tty->hw_stopped = 0;			port->IER |= IER_TXRDY;			if (port->xmit_cnt <= port->wakeup_chars)				sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);		} else {			tty->hw_stopped = 1;			port->IER &= ~IER_TXRDY;		}		sx_out(bp, CD186x_IER, port->IER);	}	if (mcr & MCR_DSSXHG) {		if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {			tty->hw_stopped = 0;			port->IER |= IER_TXRDY;			if (port->xmit_cnt <= port->wakeup_chars)				sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);		} else {			tty->hw_stopped = 1;			port->IER &= ~IER_TXRDY;		}		sx_out(bp, CD186x_IER, port->IER);	}#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */		/* Clear change bits */	sx_out(bp, CD186x_MCR, 0);}/* The main interrupt processing routine */static irqreturn_t sx_interrupt(int irq, void *dev_id, struct pt_regs *regs){	unsigned char status;	unsigned char ack;	struct specialix_board *bp;	unsigned long loop = 0;	int saved_reg;	bp = dev_id;		if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {#ifdef SPECIALIX_DEBUG 		printk (KERN_DEBUG "sx: False interrupt. irq %d.\n", irq);#endif		return IRQ_NONE;	}	saved_reg = bp->reg;	while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &	                                   (SRSR_RREQint |		                            SRSR_TREQint |	                                    SRSR_MREQint)))) {			if (status & SRSR_RREQint) {			ack = sx_in(bp, CD186x_RRAR);			if (ack == (SX_ID | GIVR_IT_RCV))				sx_receive(bp);			else if (ack == (SX_ID | GIVR_IT_REXC))				sx_receive_exc(bp);			else				printk(KERN_ERR "sx%d: Bad receive ack 0x%02x.\n",				       board_No(bp), ack);				} else if (status & SRSR_TREQint) {			ack = sx_in(bp, CD186x_TRAR);			if (ack == (SX_ID | GIVR_IT_TX))				sx_transmit(bp);			else				printk(KERN_ERR "sx%d: Bad transmit ack 0x%02x.\n",				       board_No(bp), ack);		} else if (status & SRSR_MREQint) {			ack = sx_in(bp, CD186x_MRAR);			if (ack == (SX_ID | GIVR_IT_MODEM)) 				sx_check_modem(bp);			else				printk(KERN_ERR "sx%d: Bad modem ack 0x%02x.\n",				       board_No(bp), ack);				} 		sx_out(bp, CD186x_EOIR, 0);   /* Mark end of interrupt */	}	bp->reg = saved_reg;	outb (bp->reg, bp->base + SX_ADDR_REG);	return IRQ_HANDLED;}/* *  Routines for open & close processing. */void turn_ints_off (struct specialix_board *bp){	if (bp->flags & SX_BOARD_IS_PCI) {		/* This was intended for enabeling the interrupt on the		 * PCI card. However it seems that it's already enabled		 * and as PCI interrupts can be shared, there is no real		 * reason to have to turn it off. */	}	(void) sx_in_off (bp, 0); /* Turn off interrupts. */}void turn_ints_on (struct specialix_board *bp){	if (bp->flags & SX_BOARD_IS_PCI) {		/* play with the PCI chip. See comment above. */	}	(void) sx_in (bp, 0); /* Turn ON interrupts. */}/* Called with disabled interrupts */static inline int sx_setup_board(struct specialix_board * bp){	int error;	if (bp->flags & SX_BOARD_ACTIVE) 		return 0;	if (bp->flags & SX_BOARD_IS_PCI)		error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT | SA_SHIRQ, "specialix IO8+", bp);	else		error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", bp);	if (error) 		return error;	turn_ints_on (bp);	bp->flags |= SX_BOARD_ACTIVE;	return 0;}/* Called with disabled interrupts */static inline void sx_shutdown_board(struct specialix_board *bp){	if (!(bp->flags & SX_BOARD_ACTIVE))		return;		bp->flags &= ~SX_BOARD_ACTIVE;	#if SPECIALIX_DEBUG > 2	printk ("Freeing IRQ%d for board %d.\n", bp->irq, board_No (bp));#endif	free_irq(bp->irq, bp);	turn_ints_off (bp);}/* * Setting up port characteristics.  * Must be called with disabled interrupts */static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port){	struct tty_struct *tty;	unsigned long baud;	long tmp;	unsigned char cor1 = 0, cor3 = 0;	unsigned char mcor1 = 0, mcor2 = 0;	static unsigned long again;		if (!(tty = port->tty) || !tty->termios)		return;	port->IER  = 0;	port->COR2 = 0;	/* Select port on the board */	sx_out(bp, CD186x_CAR, port_No(port));	/* The Specialix board doens't implement the RTS lines.	   They are used to set the IRQ level. Don't touch them. */	if (SX_CRTSCTS(tty))		port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);	else		port->MSVR =  (sx_in(bp, CD186x_MSVR) & MSVR_RTS);#ifdef DEBUG_SPECIALIX	printk (KERN_DEBUG "sx: got MSVR=%02x.\n", port->MSVR);#endif	baud = C_BAUD(tty);		if (baud & CBAUDEX) {		baud &= ~CBAUDEX;		if (baud < 1 || baud > 2) 			port->tty->termios->c_cflag &= ~CBAUDEX;		else			baud += 15;	}	if (baud == 15) {		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)			baud ++;		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)			baud += 2;	}			if (!baud_table[baud]) {		/* Drop DTR & exit */#ifdef SPECIALIX_DEBUG		printk (KERN_DEBUG "Dropping DTR...  Hmm....\n");#endif		if (!SX_CRTSCTS (tty)) {			port -> MSVR &= ~ MSVR_DTR;			sx_out(bp, CD186x_MSVR, port->MSVR );		} #ifdef DEBUG_SPECIALIX		else			printk (KERN_DEBUG "Can't drop DTR: no DTR.\n");#endif		return;	} else {		/* Set DTR on */		if (!SX_CRTSCTS (tty)) {			port ->MSVR |= MSVR_DTR;		}	}		/*	 * Now we must calculate some speed depended things 	 */	/* Set baud rate for port */	tmp = port->custom_divisor ;	if ( tmp )		printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n"		                  "This is an untested option, please be carefull.\n",		                  port_No (port), tmp);	else		tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] +		         CD186x_TPC/2) / CD186x_TPC);	if ((tmp < 0x10) && time_before(again, jiffies)) { 		again = jiffies + HZ * 60;		/* Page 48 of version 2.0 of the CL-CD1865 databook */		if (tmp >= 12) {			printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"			        "Performance degradation is possible.\n"			        "Read specialix.txt for more info.\n",			        port_No (port), tmp);		} else {			printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"			        "Warning: overstressing Cirrus chip. "			        "This might not work.\n"			        "Read specialix.txt for more info.\n", 			        port_No (port), tmp);		}	}	sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff); 	sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff); 	sx_out(bp, CD186x_RBPRL, tmp & 0xff); 	sx_out(bp, CD186x_TBPRL, tmp & 0xff);	if (port->custom_divisor) {		baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor;		baud = ( baud + 5 ) / 10;	} else 		baud = (baud_table[baud] + 5) / 10;   /* Estimated CPS */	/* Two timer ticks seems enough to wakeup something like SLIP driver */	tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;			port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?					      SERIAL_XMIT_SIZE - 1 : tmp);		/* Receiver timeout will be transmission time for 1.5 chars */	tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;	tmp = (tmp > 0xff) ? 0xff : tmp;	sx_out(bp, CD186x_RTPR, tmp);		switch (C_CSIZE(tty)) {	 case CS5:		cor1 |= COR1_5BITS;		break;	 case CS6:		cor1 |= COR1_6BITS;		break;	 case CS7:		cor1 |= COR1_7BITS;		break;	 case CS8:		cor1 |= COR1_8BITS;		break;	}		if (C_CSTOPB(tty)) 		cor1 |= COR1_2SB;		cor1 |= COR1_IGNORE;	if (C_PARENB(tty)) {		cor1 |= COR1_NORMPAR;		if (C_PARODD(tty)) 			cor1 |= COR1_ODDP;		if (I_INPCK(tty)) 			cor1 &= ~COR1_IGNORE;	}	/* Set marking of some errors */	port->mark_mask = RCSR_OE | RCSR_TOUT;	if (I_INPCK(tty)) 		port->mark_mask |= RCSR_FE | RCSR_PE;	if (I_BRKINT(tty) || I_PARMRK(tty)) 		port->mark_mask |= RCSR_BREAK;	if (I_IGNPAR(tty)) 		port->mark_mask &= ~(RCSR_FE | RCSR_PE);	if (I_IGNBRK(tty)) {		port->mark_mask &= ~RCSR_BREAK;		if (I_IGNPAR(tty)) 			/* Real raw mode. Ignore all */			port->mark_mask &= ~RCSR_OE;	}	/* Enable Hardware Flow Control */	if (C_CRTSCTS(tty)) {#ifdef SPECIALIX_BRAIN_DAMAGED_CTS		port->IER |= IER_DSR | IER_CTS;		mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;		mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;		tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));#else		port->COR2 |= COR2_CTSAE; #endif	}	/* Enable Software Flow Control. FIXME: I'm not sure about this */	/* Some people reported that it works, but I still doubt it */	if (I_IXON(tty)) {		port->COR2 |= COR2_TXIBE;		cor3 |= (COR3_FCT | COR3_SCDE);		if (I_IXANY(tty))			port->COR2 |= COR2_IXM;		sx_out(bp, CD186x_SCHR1, START_CHAR(tty));		sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));		sx_out(bp, CD186x_SCHR3, START_CHAR(tty));		sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));	}	if (!C_CLOCAL(tty)) {		/* Enable CD check */		port->IER |= IER_CD;		mcor1 |= MCOR1_CDZD;		mcor2 |= MCOR2_CDOD;	}		if (C_CREAD(tty)) 		/* Enable receiver */		port->IER |= IER_RXD;

⌨️ 快捷键说明

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