mxser_new.c

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

C
2,540
字号
	if (i == BAUD_TABLE_NO) {		quot = info->baud_base % info->speed;		quot *= 8;		if ((quot % info->speed) > (info->speed / 2)) {			quot /= info->speed;			quot++;		} else {			quot /= info->speed;		}		SET_MOXA_MUST_ENUM_VALUE(info->ioaddr, quot);	} else		SET_MOXA_MUST_ENUM_VALUE(info->ioaddr, 0);	return ret;}/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static int mxser_change_speed(struct mxser_port *info,		struct ktermios *old_termios){	unsigned cflag, cval, fcr;	int ret = 0;	unsigned char status;	long baud;	if (!info->tty || !info->tty->termios)		return ret;	cflag = info->tty->termios->c_cflag;	if (!(info->ioaddr))		return ret;	if (mxser_set_baud_method[info->tty->index] == 0) {		if ((cflag & CBAUD) == B_SPEC)			baud = info->speed;		else			baud = tty_get_baud_rate(info->tty);		mxser_set_baud(info, baud);	}	/* byte size and parity */	switch (cflag & CSIZE) {	case CS5:		cval = 0x00;		break;	case CS6:		cval = 0x01;		break;	case CS7:		cval = 0x02;		break;	case CS8:		cval = 0x03;		break;	default:		cval = 0x00;		break;		/* too keep GCC shut... */	}	if (cflag & CSTOPB)		cval |= 0x04;	if (cflag & PARENB)		cval |= UART_LCR_PARITY;	if (!(cflag & PARODD))		cval |= UART_LCR_EPAR;	if (cflag & CMSPAR)		cval |= UART_LCR_SPAR;	if ((info->type == PORT_8250) || (info->type == PORT_16450)) {		if (info->board->chip_flag) {			fcr = UART_FCR_ENABLE_FIFO;			fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE;			SET_MOXA_MUST_FIFO_VALUE(info);		} else			fcr = 0;	} else {		fcr = UART_FCR_ENABLE_FIFO;		if (info->board->chip_flag) {			fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE;			SET_MOXA_MUST_FIFO_VALUE(info);		} else {			switch (info->rx_trigger) {			case 1:				fcr |= UART_FCR_TRIGGER_1;				break;			case 4:				fcr |= UART_FCR_TRIGGER_4;				break;			case 8:				fcr |= UART_FCR_TRIGGER_8;				break;			default:				fcr |= UART_FCR_TRIGGER_14;				break;			}		}	}	/* CTS flow control flag and modem status interrupts */	info->IER &= ~UART_IER_MSI;	info->MCR &= ~UART_MCR_AFE;	if (cflag & CRTSCTS) {		info->flags |= ASYNC_CTS_FLOW;		info->IER |= UART_IER_MSI;		if ((info->type == PORT_16550A) || (info->board->chip_flag)) {			info->MCR |= UART_MCR_AFE;		} else {			status = inb(info->ioaddr + UART_MSR);			if (info->tty->hw_stopped) {				if (status & UART_MSR_CTS) {					info->tty->hw_stopped = 0;					if (info->type != PORT_16550A &&							!info->board->chip_flag) {						outb(info->IER & ~UART_IER_THRI,							info->ioaddr +							UART_IER);						info->IER |= UART_IER_THRI;						outb(info->IER, info->ioaddr +								UART_IER);					}					tty_wakeup(info->tty);				}			} else {				if (!(status & UART_MSR_CTS)) {					info->tty->hw_stopped = 1;					if ((info->type != PORT_16550A) &&							(!info->board->chip_flag)) {						info->IER &= ~UART_IER_THRI;						outb(info->IER, info->ioaddr +								UART_IER);					}				}			}		}	} else {		info->flags &= ~ASYNC_CTS_FLOW;	}	outb(info->MCR, info->ioaddr + UART_MCR);	if (cflag & CLOCAL) {		info->flags &= ~ASYNC_CHECK_CD;	} else {		info->flags |= ASYNC_CHECK_CD;		info->IER |= UART_IER_MSI;	}	outb(info->IER, info->ioaddr + UART_IER);	/*	 * Set up parity check flag	 */	info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;	if (I_INPCK(info->tty))		info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))		info->read_status_mask |= UART_LSR_BI;	info->ignore_status_mask = 0;	if (I_IGNBRK(info->tty)) {		info->ignore_status_mask |= UART_LSR_BI;		info->read_status_mask |= UART_LSR_BI;		/*		 * If we're ignore parity and break indicators, ignore		 * overruns too.  (For real raw support).		 */		if (I_IGNPAR(info->tty)) {			info->ignore_status_mask |=						UART_LSR_OE |						UART_LSR_PE |						UART_LSR_FE;			info->read_status_mask |=						UART_LSR_OE |						UART_LSR_PE |						UART_LSR_FE;		}	}	if (info->board->chip_flag) {		SET_MOXA_MUST_XON1_VALUE(info->ioaddr, START_CHAR(info->tty));		SET_MOXA_MUST_XOFF1_VALUE(info->ioaddr, STOP_CHAR(info->tty));		if (I_IXON(info->tty)) {			ENABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->ioaddr);		} else {			DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->ioaddr);		}		if (I_IXOFF(info->tty)) {			ENABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(info->ioaddr);		} else {			DISABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(info->ioaddr);		}	}	outb(fcr, info->ioaddr + UART_FCR);	/* set fcr */	outb(cval, info->ioaddr + UART_LCR);	return ret;}static void mxser_check_modem_status(struct mxser_port *port, int status){	/* update input line counters */	if (status & UART_MSR_TERI)		port->icount.rng++;	if (status & UART_MSR_DDSR)		port->icount.dsr++;	if (status & UART_MSR_DDCD)		port->icount.dcd++;	if (status & UART_MSR_DCTS)		port->icount.cts++;	port->mon_data.modem_status = status;	wake_up_interruptible(&port->delta_msr_wait);	if ((port->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {		if (status & UART_MSR_DCD)			wake_up_interruptible(&port->open_wait);	}	if (port->flags & ASYNC_CTS_FLOW) {		if (port->tty->hw_stopped) {			if (status & UART_MSR_CTS) {				port->tty->hw_stopped = 0;				if ((port->type != PORT_16550A) &&						(!port->board->chip_flag)) {					outb(port->IER & ~UART_IER_THRI,						port->ioaddr + UART_IER);					port->IER |= UART_IER_THRI;					outb(port->IER, port->ioaddr +							UART_IER);				}				tty_wakeup(port->tty);			}		} else {			if (!(status & UART_MSR_CTS)) {				port->tty->hw_stopped = 1;				if (port->type != PORT_16550A &&						!port->board->chip_flag) {					port->IER &= ~UART_IER_THRI;					outb(port->IER, port->ioaddr +							UART_IER);				}			}		}	}}static int mxser_startup(struct mxser_port *info){	unsigned long page;	unsigned long flags;	page = __get_free_page(GFP_KERNEL);	if (!page)		return -ENOMEM;	spin_lock_irqsave(&info->slock, flags);	if (info->flags & ASYNC_INITIALIZED) {		free_page(page);		spin_unlock_irqrestore(&info->slock, flags);		return 0;	}	if (!info->ioaddr || !info->type) {		if (info->tty)			set_bit(TTY_IO_ERROR, &info->tty->flags);		free_page(page);		spin_unlock_irqrestore(&info->slock, flags);		return 0;	}	if (info->xmit_buf)		free_page(page);	else		info->xmit_buf = (unsigned char *) page;	/*	 * Clear the FIFO buffers and disable them	 * (they will be reenabled in mxser_change_speed())	 */	if (info->board->chip_flag)		outb((UART_FCR_CLEAR_RCVR |			UART_FCR_CLEAR_XMIT |			MOXA_MUST_FCR_GDA_MODE_ENABLE), info->ioaddr + UART_FCR);	else		outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT),			info->ioaddr + UART_FCR);	/*	 * At this point there's no way the LSR could still be 0xFF;	 * if it is, then bail out, because there's likely no UART	 * here.	 */	if (inb(info->ioaddr + UART_LSR) == 0xff) {		spin_unlock_irqrestore(&info->slock, flags);		if (capable(CAP_SYS_ADMIN)) {			if (info->tty)				set_bit(TTY_IO_ERROR, &info->tty->flags);			return 0;		} else			return -ENODEV;	}	/*	 * Clear the interrupt registers.	 */	(void) inb(info->ioaddr + UART_LSR);	(void) inb(info->ioaddr + UART_RX);	(void) inb(info->ioaddr + UART_IIR);	(void) inb(info->ioaddr + UART_MSR);	/*	 * Now, initialize the UART	 */	outb(UART_LCR_WLEN8, info->ioaddr + UART_LCR);	/* reset DLAB */	info->MCR = UART_MCR_DTR | UART_MCR_RTS;	outb(info->MCR, info->ioaddr + UART_MCR);	/*	 * Finally, enable interrupts	 */	info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;	if (info->board->chip_flag)		info->IER |= MOXA_MUST_IER_EGDAI;	outb(info->IER, info->ioaddr + UART_IER);	/* enable interrupts */	/*	 * And clear the interrupt registers again for luck.	 */	(void) inb(info->ioaddr + UART_LSR);	(void) inb(info->ioaddr + UART_RX);	(void) inb(info->ioaddr + UART_IIR);	(void) inb(info->ioaddr + UART_MSR);	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	 */	mxser_change_speed(info, NULL);	info->flags |= ASYNC_INITIALIZED;	spin_unlock_irqrestore(&info->slock, flags);	return 0;}/* * This routine will shutdown a serial port; interrupts maybe disabled, and * DTR is dropped if the hangup on close termio flag is on. */static void mxser_shutdown(struct mxser_port *info){	unsigned long flags;	if (!(info->flags & ASYNC_INITIALIZED))		return;	spin_lock_irqsave(&info->slock, flags);	/*	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq	 * here so the queue might never be waken up	 */	wake_up_interruptible(&info->delta_msr_wait);	/*	 * Free the IRQ, if necessary	 */	if (info->xmit_buf) {		free_page((unsigned long) info->xmit_buf);		info->xmit_buf = NULL;	}	info->IER = 0;	outb(0x00, info->ioaddr + UART_IER);	if (!info->tty || (info->tty->termios->c_cflag & HUPCL))		info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS);	outb(info->MCR, info->ioaddr + UART_MCR);	/* clear Rx/Tx FIFO's */	if (info->board->chip_flag)		outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT |				MOXA_MUST_FCR_GDA_MODE_ENABLE,				info->ioaddr + UART_FCR);	else		outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,			info->ioaddr + UART_FCR);	/* read data port to reset things */	(void) inb(info->ioaddr + UART_RX);	if (info->tty)		set_bit(TTY_IO_ERROR, &info->tty->flags);	info->flags &= ~ASYNC_INITIALIZED;	if (info->board->chip_flag)		SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(info->ioaddr);	spin_unlock_irqrestore(&info->slock, flags);}/* * This routine is called whenever a serial port is opened.  It * enables interrupts for a serial port, linking in its async structure into * the IRQ chain.   It also performs the serial-specific * initialization for the tty structure. */static int mxser_open(struct tty_struct *tty, struct file *filp){	struct mxser_port *info;	unsigned long flags;	int retval, line;	line = tty->index;	if (line == MXSER_PORTS)		return 0;	if (line < 0 || line > MXSER_PORTS)		return -ENODEV;	info = &mxser_boards[line / MXSER_PORTS_PER_BOARD].ports[line % MXSER_PORTS_PER_BOARD];	if (!info->ioaddr)		return -ENODEV;	tty->driver_data = info;	info->tty = tty;	/*	 * Start up serial port	 */	spin_lock_irqsave(&info->slock, flags);	info->count++;	spin_unlock_irqrestore(&info->slock, flags);	retval = mxser_startup(info);	if (retval)		return retval;	retval = mxser_block_til_ready(tty, filp, info);	if (retval)		return retval;	/* unmark here for very high baud rate (ex. 921600 bps) used */	tty->low_latency = 1;	return 0;}/* * This routine is called when the serial port gets closed.  First, we * wait for the last remaining data to be sent.  Then, we unlink its * async structure from the interrupt chain if necessary, and we free * that IRQ if nothing is left in the chain. */static void mxser_close(struct tty_struct *tty, struct file *filp){	struct mxser_port *info = tty->driver_data;	unsigned long timeout;	unsigned long flags;	if (tty->index == MXSER_PORTS)		return;	if (!info)		return;	spin_lock_irqsave(&info->slock, flags);	if (tty_hung_up_p(filp)) {		spin_unlock_irqrestore(&info->slock, flags);		return;	}	if ((tty->count == 1) && (info->count != 1)) {		/*		 * Uh, oh.  tty->count is 1, which means that the tty		 * structure will be freed.  Info->count should always		 * be one in these conditions.  If it's greater than		 * one, we've got real problems, since it means the		 * serial port won't be shutdown.		 */		printk(KERN_ERR "mxser_close: bad serial port count; "			"tty->count is 1, info->count is %d\n", info->count);		info->count = 1;	}	if (--info->count < 0) {		printk(KERN_ERR "mxser_close: bad serial port count for "			"ttys%d: %d\n", tty->index, info->count);		info->count = 0;	}	if (info->count) {		spin_unlock_irqrestore(&info->slock, flags);		return;	}	info->flags |= ASYNC_CLOSING;	spin_unlock_irqrestore(&info->slock, flags);	/*	 * Save the termios structure, since this port may have	 * separate termios for callout and dialin.	 */	if (info->flags & ASYNC_NORMAL_ACTIVE)		info->normal_termios = *tty->termios;	/*	 * 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 (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)		tty_wait_until_sent(tty, info->closing_wait);	/*

⌨️ 快捷键说明

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