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 + -
显示快捷键?