specialix.c

来自「linux 内核源代码」· C语言 代码 · 共 2,562 行 · 第 1/5 页

C
2,562
字号
	func_exit();}/* * 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;	unsigned long flags;	func_enter();	if (!(tty = port->tty) || !tty->termios) {		func_exit();		return;	}	port->IER  = 0;	port->COR2 = 0;	/* Select port on the board */	spin_lock_irqsave(&bp->lock, flags);	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);	spin_unlock_irqrestore(&bp->lock, flags);	dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);	baud = tty_get_baud_rate(tty);	if (baud == 38400) {		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)			baud = 57600;		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)			baud = 115200;	}	if (!baud) {		/* Drop DTR & exit */		dprintk (SX_DEBUG_TERMIOS, "Dropping DTR...  Hmm....\n");		if (!SX_CRTSCTS (tty)) {			port -> MSVR &= ~ MSVR_DTR;			spin_lock_irqsave(&bp->lock, flags);			sx_out(bp, CD186x_MSVR, port->MSVR );			spin_unlock_irqrestore(&bp->lock, flags);		}		else			dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");		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/2) / 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);		}	}	spin_lock_irqsave(&bp->lock, flags);	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);	spin_unlock_irqrestore(&bp->lock, flags);	if (port->custom_divisor)		baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor;	baud = (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;	spin_lock_irqsave(&bp->lock, flags);	sx_out(bp, CD186x_RTPR, tmp);	spin_unlock_irqrestore(&bp->lock, flags);	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;		spin_lock_irqsave(&bp->lock, flags);		tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));		spin_unlock_irqrestore(&bp->lock, flags);#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;		spin_lock_irqsave(&bp->lock, flags);		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));		spin_unlock_irqrestore(&bp->lock, flags);	}	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;	/* Set input FIFO size (1-8 bytes) */	cor3 |= sx_rxfifo;	/* Setting up CD186x channel registers */	spin_lock_irqsave(&bp->lock, flags);	sx_out(bp, CD186x_COR1, cor1);	sx_out(bp, CD186x_COR2, port->COR2);	sx_out(bp, CD186x_COR3, cor3);	spin_unlock_irqrestore(&bp->lock, flags);	/* Make CD186x know about registers change */	sx_wait_CCR(bp);	spin_lock_irqsave(&bp->lock, flags);	sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);	/* Setting up modem option registers */	dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);	sx_out(bp, CD186x_MCOR1, mcor1);	sx_out(bp, CD186x_MCOR2, mcor2);	spin_unlock_irqrestore(&bp->lock, flags);	/* Enable CD186x transmitter & receiver */	sx_wait_CCR(bp);	spin_lock_irqsave(&bp->lock, flags);	sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);	/* Enable interrupts */	sx_out(bp, CD186x_IER, port->IER);	/* And finally set the modem lines... */	sx_out(bp, CD186x_MSVR, port->MSVR);	spin_unlock_irqrestore(&bp->lock, flags);	func_exit();}/* Must be called with interrupts enabled */static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port){	unsigned long flags;	func_enter();	if (port->flags & ASYNC_INITIALIZED) {		func_exit();		return 0;	}	if (!port->xmit_buf) {		/* We may sleep in get_zeroed_page() */		unsigned long tmp;		if (!(tmp = get_zeroed_page(GFP_KERNEL))) {			func_exit();			return -ENOMEM;		}		if (port->xmit_buf) {			free_page(tmp);			func_exit();			return -ERESTARTSYS;		}		port->xmit_buf = (unsigned char *) tmp;	}	spin_lock_irqsave(&port->lock, flags);	if (port->tty)		clear_bit(TTY_IO_ERROR, &port->tty->flags);	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;	sx_change_speed(bp, port);	port->flags |= ASYNC_INITIALIZED;	spin_unlock_irqrestore(&port->lock, flags);	func_exit();	return 0;}/* Must be called with interrupts disabled */static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port){	struct tty_struct *tty;	int i;	unsigned long flags;	func_enter();	if (!(port->flags & ASYNC_INITIALIZED)) {		func_exit();		return;	}	if (sx_debug & SX_DEBUG_FIFO) {		dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ",			board_No(bp), port_No(port), port->overrun);		for (i = 0; i < 10; i++) {			dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);		}		dprintk(SX_DEBUG_FIFO, "].\n");	}	if (port->xmit_buf) {		free_page((unsigned long) port->xmit_buf);		port->xmit_buf = NULL;	}	/* Select port */	spin_lock_irqsave(&bp->lock, flags);	sx_out(bp, CD186x_CAR, port_No(port));	if (!(tty = port->tty) || C_HUPCL(tty)) {		/* Drop DTR */		sx_out(bp, CD186x_MSVDTR, 0);	}	spin_unlock_irqrestore(&bp->lock, flags);	/* Reset port */	sx_wait_CCR(bp);	spin_lock_irqsave(&bp->lock, flags);	sx_out(bp, CD186x_CCR, CCR_SOFTRESET);	/* Disable all interrupts from this port */	port->IER = 0;	sx_out(bp, CD186x_IER, port->IER);	spin_unlock_irqrestore(&bp->lock, flags);	if (tty)		set_bit(TTY_IO_ERROR, &tty->flags);	port->flags &= ~ASYNC_INITIALIZED;	if (!bp->count)		sx_shutdown_board(bp);	func_exit();}static int block_til_ready(struct tty_struct *tty, struct file * filp,                           struct specialix_port *port){	DECLARE_WAITQUEUE(wait,  current);	struct specialix_board *bp = port_Board(port);	int    retval;	int    do_clocal = 0;	int    CD;	unsigned long flags;	func_enter();	/*	 * If the device is in the middle of being closed, then block	 * until it's done, and then try again.	 */	if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {		interruptible_sleep_on(&port->close_wait);		if (port->flags & ASYNC_HUP_NOTIFY) {			func_exit();			return -EAGAIN;		} else {			func_exit();			return -ERESTARTSYS;		}	}	/*	 * If non-blocking mode is set, or the port is not enabled,	 * then make the check up front and then exit.	 */	if ((filp->f_flags & O_NONBLOCK) ||	    (tty->flags & (1 << TTY_IO_ERROR))) {		port->flags |= ASYNC_NORMAL_ACTIVE;		func_exit();		return 0;	}	if (C_CLOCAL(tty))		do_clocal = 1;	/*	 * Block waiting for the carrier detect and the line to become	 * free (i.e., not in use by the callout).  While we are in	 * this loop, info->count is dropped by one, so that	 * rs_close() knows when to free things.  We restore it upon	 * exit, either normal or abnormal.	 */	retval = 0;	add_wait_queue(&port->open_wait, &wait);	spin_lock_irqsave(&port->lock, flags);	if (!tty_hung_up_p(filp)) {		port->count--;	}	spin_unlock_irqrestore(&port->lock, flags);	port->blocked_open++;	while (1) {		spin_lock_irqsave(&bp->lock, flags);		sx_out(bp, CD186x_CAR, port_No(port));		CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;		if (SX_CRTSCTS (tty)) {			/* Activate RTS */			port->MSVR |= MSVR_DTR;		/* WTF? */			sx_out (bp, CD186x_MSVR, port->MSVR);		} else {			/* Activate DTR */			port->MSVR |= MSVR_DTR;			sx_out (bp, CD186x_MSVR, port->MSVR);		}		spin_unlock_irqrestore(&bp->lock, flags);		set_current_state(TASK_INTERRUPTIBLE);		if (tty_hung_up_p(filp) ||		    !(port->flags & ASYNC_INITIALIZED)) {			if (port->flags & ASYNC_HUP_NOTIFY)				retval = -EAGAIN;			else				retval = -ERESTARTSYS;			break;		}		if (!(port->flags & ASYNC_CLOSING) &&		    (do_clocal || CD))			break;		if (signal_pending(current)) {			retval = -ERESTARTSYS;			break;		}		schedule();	}	set_current_state(TASK_RUNNING);	remove_wait_queue(&port->open_wait, &wait);	spin_lock_irqsave(&port->lock, flags);	if (!tty_hung_up_p(filp)) {		port->count++;	}	port->blocked_open--;	spin_unlock_irqrestore(&port->lock, flags);	if (retval) {		func_exit();		return retval;	}	port->flags |= ASYNC_NORMAL_ACTIVE;	func_exit();	return 0;}static int sx_open(struct tty_struct * tty, struct file * filp){	int board;	int error;	struct specialix_port * port;	struct specialix_board * bp;	int i;	unsigned long flags;	func_enter();	board = SX_BOARD(tty->index);	if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {		func_exit();		return -ENODEV;	}	bp = &sx_board[board];	port = sx_port + board * SX_NPORT + SX_PORT(tty->index);	port->overrun = 0;	for (i = 0; i < 10; i++)		port->hits[i]=0;	dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n",	        board, bp, port, SX_PORT(tty->index));	if (sx_paranoia_check(port, tty->name, "sx_open")) {		func_enter();		return -ENODEV;	}	if ((error = sx_setup_board(bp))) {		func_exit();		return error;	}	spin_lock_irqsave(&bp->lock, flags);	port->count++;	bp->count++;	tty->driver_data = port;	port->tty = tty;	spin_unlock_irqrestore(&bp->lock, flags);	if ((error = sx_setup_port(bp, port))) {		func_enter();		return error;	}	if ((error = block_til_ready(tty, filp, port))) {		func_enter();		return error;	}	func_exit();	return 0;}static void sx_close(struct tty_struct * tty, struct file * filp){	struct specialix_port *port = (struct specialix_port *) tty->driver_data;	struct specialix_board *bp;	unsigned long flags;	unsigned long timeout;	func_enter();	if (!port || sx_paranoia_check(port, tty->name, "close")) {		func_exit();		return;	}	spin_lock_irqsave(&port->lock, flags);	if (tty_hung_up_p(filp)) {		spin_unlock_irqrestore(&port->lock, flags);

⌨️ 快捷键说明

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