specialix.c

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

C
2,265
字号
		/* Set input FIFO size (1-8 bytes) */	cor3 |= SPECIALIX_RXFIFO; 	/* Setting up CD186x channel registers */	sx_out(bp, CD186x_COR1, cor1);	sx_out(bp, CD186x_COR2, port->COR2);	sx_out(bp, CD186x_COR3, cor3);	/* Make CD186x know about registers change */	sx_wait_CCR(bp);	sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);	/* Setting up modem option registers */#ifdef DEBUG_SPECIALIX	printk ("Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);#endif	sx_out(bp, CD186x_MCOR1, mcor1);	sx_out(bp, CD186x_MCOR2, mcor2);	/* Enable CD186x transmitter & receiver */	sx_wait_CCR(bp);	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);}/* Must be called with interrupts enabled */static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port){	unsigned long flags;		if (port->flags & ASYNC_INITIALIZED)		return 0;		if (!port->xmit_buf) {		/* We may sleep in get_zeroed_page() */		unsigned long tmp;				if (!(tmp = get_zeroed_page(GFP_KERNEL)))			return -ENOMEM;		if (port->xmit_buf) {			free_page(tmp);			return -ERESTARTSYS;		}		port->xmit_buf = (unsigned char *) tmp;	}			save_flags(flags); cli();			if (port->tty) 		clear_bit(TTY_IO_ERROR, &port->tty->flags);			if (port->count == 1) 		bp->count++;			port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;	sx_change_speed(bp, port);	port->flags |= ASYNC_INITIALIZED;			restore_flags(flags);	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;		if (!(port->flags & ASYNC_INITIALIZED)) 		return;	#ifdef SX_REPORT_OVERRUN	printk(KERN_INFO "sx%d: port %d: Total %ld overruns were detected.\n",	       board_No(bp), port_No(port), port->overrun);#endif	#ifdef SX_REPORT_FIFO	{		int i;				printk(KERN_INFO "sx%d: port %d: FIFO hits [ ",		       board_No(bp), port_No(port));		for (i = 0; i < 10; i++) {			printk("%ld ", port->hits[i]);		}		printk("].\n");	}#endif		if (port->xmit_buf) {		free_page((unsigned long) port->xmit_buf);		port->xmit_buf = NULL;	}	/* Select port */	sx_out(bp, CD186x_CAR, port_No(port));	if (!(tty = port->tty) || C_HUPCL(tty)) {		/* Drop DTR */		sx_out(bp, CD186x_MSVDTR, 0);	}		/* Reset port */	sx_wait_CCR(bp);	sx_out(bp, CD186x_CCR, CCR_SOFTRESET);	/* Disable all interrupts from this port */	port->IER = 0;	sx_out(bp, CD186x_IER, port->IER);		if (tty)		set_bit(TTY_IO_ERROR, &tty->flags);	port->flags &= ~ASYNC_INITIALIZED;		if (--bp->count < 0) {		printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d\n",		       board_No(bp), bp->count);		bp->count = 0;	}		/*	 * If this is the last opened port on the board	 * shutdown whole board	 */	if (!bp->count) 		sx_shutdown_board(bp);}	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;	/*	 * 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)			return -EAGAIN;		else			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;		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);	cli();	if (!tty_hung_up_p(filp))		port->count--;	sti();	port->blocked_open++;	while (1) {		cli();		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);		} 		sti();		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();	}	current->state = TASK_RUNNING;	remove_wait_queue(&port->open_wait, &wait);	if (!tty_hung_up_p(filp))		port->count++;	port->blocked_open--;	if (retval)		return retval;		port->flags |= ASYNC_NORMAL_ACTIVE;	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;		board = SX_BOARD(tty->index);	if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT))		return -ENODEV;		bp = &sx_board[board];	port = sx_port + board * SX_NPORT + SX_PORT(tty->index);#ifdef DEBUG_SPECIALIX	printk (KERN_DEBUG "Board = %d, bp = %p, port = %p, portno = %d.\n", 	        board, bp, port, SX_PORT(tty->index));#endif	if (sx_paranoia_check(port, tty->name, "sx_open"))		return -ENODEV;	if ((error = sx_setup_board(bp)))		return error;	port->count++;	tty->driver_data = port;	port->tty = tty;	if ((error = sx_setup_port(bp, port))) 		return error;		if ((error = block_til_ready(tty, filp, port)))		return error;	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;		if (!port || sx_paranoia_check(port, tty->name, "close"))		return;		save_flags(flags); cli();	if (tty_hung_up_p(filp)) {		restore_flags(flags);		return;	}		bp = port_Board(port);	if ((tty->count == 1) && (port->count != 1)) {		printk(KERN_ERR "sx%d: sx_close: bad port count;"		       " tty->count is 1, port count is %d\n",		       board_No(bp), port->count);		port->count = 1;	}	if (--port->count < 0) {		printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",		       board_No(bp), port_No(port), port->count);		port->count = 0;	}	if (port->count) {		restore_flags(flags);		return;	}	port->flags |= ASYNC_CLOSING;	/*	 * Now we wait for the transmit buffer to clear; and we notify 	 * the line discipline to only process XON/XOFF characters.	 */	tty->closing = 1;	if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)		tty_wait_until_sent(tty, port->closing_wait);	/*	 * At this point we stop accepting input.  To do this, we	 * disable the receive line status interrupts, and tell the	 * interrupt driver to stop checking the data ready bit in the	 * line status register.	 */	port->IER &= ~IER_RXD;	if (port->flags & ASYNC_INITIALIZED) {		port->IER &= ~IER_TXRDY;		port->IER |= IER_TXEMPTY;		sx_out(bp, CD186x_CAR, port_No(port));		sx_out(bp, CD186x_IER, port->IER);		/*		 * Before we drop DTR, make sure the UART transmitter		 * has completely drained; this is especially		 * important if there is a transmit FIFO!		 */		timeout = jiffies+HZ;		while(port->IER & IER_TXEMPTY) {			current->state = TASK_INTERRUPTIBLE; 			schedule_timeout(port->timeout);			if (time_after(jiffies, timeout)) {				printk (KERN_INFO "Timeout waiting for close\n");				break;			}		}	}	sx_shutdown_port(bp, port);	if (tty->driver->flush_buffer)		tty->driver->flush_buffer(tty);	tty_ldisc_flush(tty);	tty->closing = 0;	port->event = 0;	port->tty = NULL;	if (port->blocked_open) {		if (port->close_delay) {			current->state = TASK_INTERRUPTIBLE;			schedule_timeout(port->close_delay);		}		wake_up_interruptible(&port->open_wait);	}	port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);	wake_up_interruptible(&port->close_wait);	restore_flags(flags);}static int sx_write(struct tty_struct * tty, int from_user,                     const unsigned char *buf, int count){	struct specialix_port *port = (struct specialix_port *)tty->driver_data;	struct specialix_board *bp;	int c, total = 0;	unsigned long flags;					if (sx_paranoia_check(port, tty->name, "sx_write"))		return 0;		bp = port_Board(port);	if (!tty || !port->xmit_buf || !tmp_buf)		return 0;	save_flags(flags);	if (from_user) {		down(&tmp_buf_sem);		while (1) {			c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,					   SERIAL_XMIT_SIZE - port->xmit_head));			if (c <= 0)				break;			c -= copy_from_user(tmp_buf, buf, c);			if (!c) {				if (!total)					total = -EFAULT;				break;			}			cli();			c = min_t(int, c, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,				       SERIAL_XMIT_SIZE - port->xmit_head));			memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c);			port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);			port->xmit_cnt += c;			restore_flags(flags);			buf += c;			count -= c;			total += c;		}		up(&tmp_buf_sem);	} else {		while (1) {			cli();			c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,					   SERIAL_XMIT_SIZE - port->xmit_head));			if (c <= 0) {				restore_flags(flags);				break;			}			memcpy(port->xmit_buf + port->xmit_head, buf, c);			port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);			port->xmit_cnt += c;			restore_flags(flags);			buf += c;			count -= c;			total += c;		}	}	cli();	if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&	    !(port->IER & IER_TXRDY)) {		port->IER |= IER_TXRDY;		sx_out(bp, CD186x_CAR, port_No(port));		sx_out(bp, CD186x_IER, port->IER);	}	restore_flags(flags);	return total;}static void sx_put_char(struct tty_struct * tty, unsigned char ch){	struct specialix_port *port = (struct specialix_port *)tty->driver_data;	unsigned long flags;	if (sx_paranoia_check(port, tty->name, "sx_put_char"))		return;	if (!tty || !port->xmit_buf)		return;	save_flags(flags); cli();		if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {		restore_flags(flags);		return;	}	port->xmit_buf[port->xmit_head++] = ch;	port->xmit_head &= SERIAL_XMIT_SIZE - 1;	port->xmit_cnt++;	restore_flags(flags);}static void sx_flush_chars(struct tty_struct * tty){	struct specialix_port *port = (struct specialix_port *)tty->driver_data;	unsigned long flags;					if (sx_paranoia_check(port, tty->name, "sx_flush_chars"))		return;		if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||	    !port->xmit_buf)		return;	save_flags(flags); cli();	port->IER |= IER_TXRDY;	sx_out(port_Board(port), CD186x_CAR, port_No(port));	sx_out(port_Board(port), CD186x_IER, port->IER);	restore_flags(flags);}static int sx_write_room(struct tty_struct * tty){	struct specialix_port *port = (struct specialix_port *)tty->driver_data;	int	ret;					if (sx_paranoia_check(port, tty->name, "sx_write_room"))		return 0;	ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;	if (ret < 0)		ret = 0;	return ret;}static int sx_chars_in_buffer(struct tty_struct *tty){	struct specialix_port *port = (struct specialix_port *)tty->driver_data;					if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer"))		return 0;		return port->xmit_cnt;}static void sx_flush_buffer(struct tty_struct *tty){	struct specialix_port *port = (struct specialix_port *)tty->driver_data;	unsigned long flags;					if (sx_paranoia_check(port, tty->name, "sx_flush_buffer"))		return;	save_flags(flags); cli();	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;	restore_flags(flags);		tty_wakeup(tty);	wake_up_interruptible(&tty->write_wait);}static int sx_tiocmget(struct tty_struct *tty, struct file *file){	struct specialix_port *port = (struct specialix_port *)tty->driver_data;	struct specialix_board * bp;	unsigned char status;	unsigned int result;	unsigned long flags;	if (sx_paranoia_check(port, tty->name, __FUNCTION__))		return -ENODEV;	bp = port_Board(port);	save_flags(flags); cli();	sx_out(bp, CD186x_CAR, port_No(port));	status = sx_in(bp, CD186x_MSVR);	restore_flags(flags);#ifdef DEBUG_SPECIALIX	printk (KERN_DEBUG "Got msvr[%d] = %02x, car = %d.\n", 		port_No(port), status, sx_in (bp, CD186x_CAR));	printk (KERN_DEBUG "sx_port = %p, port = %p\n", sx_port, port);#endif	if (SX_CRTSCTS(port->tty)) {		result  = /*   (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */ 		          |   ((status & MSVR_DTR) ? TIOCM_RTS : 0)		          |   ((status & MSVR_CD)  ? TIOCM_CAR : 0)		          |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */		          |   ((status & MSVR_CTS) ? TIOCM_CTS : 0);	} else {		result  = /*   (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */ 		          |   ((status & MSVR_DTR) ? TIOCM_DTR : 0)		          |   ((status & MSVR_CD)  ? TIOCM_CAR : 0)		          |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */		          |   ((status & MSVR_CTS) ? TIOCM_CTS : 0);	}	return result;}static int sx_tiocmset(struct tty_struct *tty, struct file *file,		       unsigned int set, unsigned int clear){	struct specialix_port *port = (struct specialix_port *)tty->driver_data;	unsigned long flags;	struct specialix_board *bp;	if (sx_paranoia_check(port, tty->name, __FUNCTION__))		return -ENODEV;	bp = port_Board(port);	save_flags(flags); cli();   /*	if (set & TIOCM_RTS)

⌨️ 快捷键说明

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