📄 8250.c
字号:
#ifdef SERIAL_DEBUG_INTR printk("rs_interrupt_multi(%d)...", irq);#endif info = *(struct uart_info **)dev_id; if (!info) return; if (!multi->port1) { /* should never happen */ printk("rs_interrupt_multi: port1 NULL!\n"); return; } if (multi->port_monitor) first_multi = inb(multi->port_monitor); while (1) { if (!info->tty || (serial_in(info->port, UART_IIR) & UART_IIR_NO_INT)) goto next; serial8250_handle_port(info, regs); next: info = info->next; if (info) continue; info = *(struct uart_info **)dev_id; /* * The user was a bonehead, and misconfigured their * multiport info. Rather than lock up the kernel * in an infinite loop, if we loop too many times, * print a message and break out of the loop. */ if (pass_counter++ > RS_ISR_PASS_LIMIT) { printk("Misconfigured multiport serial info " "for irq %d. Breaking out irq loop\n", irq); break; } if (multi->port_monitor) printk("rs port monitor irq %d: 0x%x, 0x%x\n", info->port->irq, first_multi, inb(multi->port_monitor)); if ((inb(multi->port1) & multi->mask1) != multi->match1) continue; if (!multi->port2) break; if ((inb(multi->port2) & multi->mask2) != multi->match2) continue; if (!multi->port3) break; if ((inb(multi->port3) & multi->mask3) != multi->match3) continue; if (!multi->port4) break; if ((inb(multi->port4) & multi->mask4) != multi->match4) continue; break; }#ifdef SERIAL_DEBUG_INTR printk("end.\n");#endif}#endifstatic u_int serial8250_tx_empty(struct uart_port *port){ return serial_in(port, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;}static u_int serial8250_get_mctrl(struct uart_port *port){ unsigned long flags; unsigned char status; unsigned int ret; save_flags(flags); cli(); status = serial_in(port, UART_MSR); restore_flags(flags); ret = 0; if (status & UART_MSR_DCD) ret |= TIOCM_CAR; if (status & UART_MSR_RI) ret |= TIOCM_RNG; if (status & UART_MSR_DSR) ret |= TIOCM_DSR; if (status & UART_MSR_CTS) ret |= TIOCM_CTS; return ret;}static void serial8250_set_mctrl(struct uart_port *port, u_int mctrl){ unsigned char mcr = 0; if (mctrl & TIOCM_RTS) mcr |= UART_MCR_RTS; if (mctrl & TIOCM_DTR) mcr |= UART_MCR_DTR; if (mctrl & TIOCM_OUT1) mcr |= UART_MCR_OUT1; if (mctrl & TIOCM_OUT2) mcr |= UART_MCR_OUT2; if (mctrl & TIOCM_LOOP) mcr |= UART_MCR_LOOP; serial_out(port, UART_MCR, mcr);}static void serial8250_break_ctl(struct uart_port *port, int break_state){ if (break_state == -1) port->port_lcr |= UART_LCR_SBC; else port->port_lcr &= ~UART_LCR_SBC; serial_out(port, UART_LCR, port->port_lcr);}static int serial8250_startup(struct uart_port *port, struct uart_info *info){ void (*handler)(int, void *, struct pt_regs *); int retval; if (port->type == PORT_16C950) { /* Wake up and initialize UART */ port->port_acr = 0; serial_outp(port, UART_LCR, 0xBF); serial_outp(port, UART_EFR, UART_EFR_ECB); serial_outp(port, UART_IER, 0); serial_outp(port, UART_LCR, 0); serial_icr_write(port, UART_CSR, 0); /* Reset the UART */ serial_outp(port, UART_LCR, 0xBF); serial_outp(port, UART_EFR, UART_EFR_ECB); serial_outp(port, UART_LCR, 0); }#ifdef CONFIG_SERIAL_RSA /* * If this is an RSA port, see if we can kick it up to the * higher speed clock. */ if (port->type == PORT_RSA) { if (port->uartclk != SERIAL_RSA_BAUD_BASE * 16 && enable_rsa(port)) port->uartclk = SERIAL_RSA_BAUD_BASE * 16; if (port->uartclk == SERIAL_RSA_BAUD_BASE * 16) serial_outp(port, UART_RSA_FRR, 0); }#endif /* * Clear the FIFO buffers and disable them. * (they will be reeanbled in change_speed()) */ if (uart_config[port->type].flags & UART_CLEAR_FIFO) { serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_outp(port, UART_FCR, 0); } /* * Clear the interrupt registers. */ (void) serial_inp(port, UART_LSR); (void) serial_inp(port, UART_RX); (void) serial_inp(port, UART_IIR); (void) serial_inp(port, UART_MSR); /* * 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 (!(port->flags & ASYNC_BUGGY_UART) && (serial_inp(port, UART_LSR) == 0xff)) { printk("ttyS%d: LSR safety check engaged!\n", port->line); return -ENODEV; } /* * Allocate the IRQ if necessary */ if (port->irq && (!IRQ_ports[port->irq] || !IRQ_ports[port->irq]->next_info)) { handler = rs_interrupt_single; if (IRQ_ports[port->irq]) {#ifdef CONFIG_SERIAL_8250_SHARE_IRQ handler = rs_interrupt; free_irq(port->irq, &IRQ_ports[port->irq]);#ifdef CONFIG_SERIAL_8250_MULTIPORT if (rs_multiport[port->irq].port1) handler = serial8250_interrupt_multi;#endif#else return -EBUSY;#endif /* CONFIG_SERIAL_8250_SHARE_IRQ */ } retval = request_irq(port->irq, handler, SA_SHIRQ, "serial", &IRQ_ports[port->irq]); if (retval) return retval; } /* * Insert serial port into IRQ chain. */ info->next_info = IRQ_ports[port->irq]; IRQ_ports[port->irq] = info; /* * Now, initialize the UART */ serial_outp(port, UART_LCR, UART_LCR_WLEN8);#ifdef CONFIG_SERIAL_MANY_PORTS if (port->flags & ASYNC_FOURPORT) { if (port->irq == 0) info->mctrl |= TIOCM_OUT1; } else#endif /* * Most PC uarts need OUT2 raised to enable interrupts. */ if (port->irq != 0) info->mctrl |= TIOCM_OUT2; /* FIXME: ALPHA_KLUDGE_MCR; */ serial8250_set_mctrl(port, info->mctrl); /* * Finally, enable interrupts. Note: Modem status interrupts * are set via change_speed(), which will be occuring imminently * anyway, so we don't enable them here. */ port->port_ier = UART_IER_RLSI | UART_IER_RDI; serial_outp(port, UART_IER, port->port_ier);#ifdef CONFIG_SERIAL_MANY_PORTS if (port->flags & ASYNC_FOURPORT) { unsigned int ICP; /* * Enable interrupts on the AST Fourport board */ ICP = (port->iobase & 0xfe0) | 0x01f; outb_p(0x80, ICP); (void) inb_p(ICP); }#endif /* * And clear the interrupt registers again for luck. */ (void) serial_inp(port, UART_LSR); (void) serial_inp(port, UART_RX); (void) serial_inp(port, UART_IIR); (void) serial_inp(port, UART_MSR); return 0;}static void serial8250_shutdown(struct uart_port *port, struct uart_info *info){ struct uart_info **infop; int retval; /* * First, disable all intrs from the port. */ port->port_ier = 0; serial_outp(port, UART_IER, 0); synchronize_irq(); /* * unlink the serial port from the IRQ chain... */ for (infop = &IRQ_ports[port->irq]; *infop; infop = &(*infop)->next_info) if (*infop == info) break; if (*infop == info) *infop = info->next_info; /* * Free the IRQ, if necessary */ if (port->irq && (!IRQ_ports[port->irq] || !IRQ_ports[port->irq]->next_info)) { free_irq(port->irq, &IRQ_ports[port->irq]); if (IRQ_ports[port->irq]) { retval = request_irq(port->irq, rs_interrupt_single, SA_SHIRQ, "serial", &IRQ_ports[port->irq]); if (retval) printk("serial shutdown: request_irq: error %d" " couldn't reacquire IRQ.\n", retval); } }#ifdef CONFIG_SERIAL_MANY_PORTS if (port->flags & ASYNC_FOURPORT) { /* reset interrupts on the AST Fourport board */ inb((port->iobase & 0xfe0) | 0x1f); info->mctrl |= TIOCM_OUT1; } else#endif info->mctrl &= ~TIOCM_OUT2; /* FIXME: ALPHA_KLUDGE_MCR; */ serial8250_set_mctrl(port, info->mctrl); /* * Disable break condition and FIFOs */ serial_out(port, UART_LCR, serial_inp(port, UART_LCR) & ~UART_LCR_SBC); serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_outp(port, UART_FCR, 0);#ifdef CONFIG_SERIAL_RSA /* * Reset the RSA board back to 115kbps compat mode. */ if (port->type == PORT_RSA && port->uartclk == SERIAL_RSA_BAUD_BASE * 16 && disable_rsa(port)) port->uartclk = SERIAL_RSA_BAUD_BASE_LO * 16;#endif /* * Read data port to reset things */ (void) serial_in(port, UART_RX);}static void serial8250_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot){ unsigned char cval, fcr = 0; unsigned long flags; switch (cflag & CSIZE) { case CS5: cval = 0x00; break; case CS6: cval = 0x01; break; case CS7: cval = 0x02; break; default: case CS8: cval = 0x03; break; } if (cflag & CSTOPB) cval |= 0x04; if (cflag & PARENB) cval |= UART_LCR_PARITY; if (!(cflag & PARODD)) cval |= UART_LCR_EPAR;#ifdef CMSPAR if (cflag & CMSPAR) cval |= UART_LCR_SPAR;#endif /* * Work around a bug in the Oxford Semiconductor 952 rev B * chip which causes it to seriously miscalculate baud rates * when DLL is 0. */ if ((quot & 0xff) == 0 && port->type == PORT_16C950 && port->port_rev == 0x5201) quot ++; if (uart_config[port->type].flags & UART_USE_FIFO) { if ((port->uartclk / quot) < (2400 * 16)) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;#ifdef CONFIG_SERIAL_RSA else if (port->type == PORT_RSA) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14;#endif else fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; } if (port->type == PORT_16750) fcr |= UART_FCR7_64BYTE; port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (iflag & IGNPAR) port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; if (iflag & (BRKINT | PARMRK)) port->read_status_mask |= UART_LSR_BI; /* * Characteres to ignore */ port->ignore_status_mask = 0; if (iflag & IGNPAR) port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; if (iflag & IGNBRK) { port->ignore_status_mask |= UART_LSR_BI; /* * If we're ignoring parity and break indicators, * ignore overruns too (for real raw support). */ if (iflag & IGNPAR) port->ignore_status_mask |= UART_LSR_OE; } /* * ignore all characters if CREAD is not set */ if ((cflag & CREAD) == 0) port->ignore_status_mask |= UART_LSR_DR; /* * CTS flow control flag and modem status interrupts */ port->port_ier &= ~UART_IER_MSI; if (port->flags & ASYNC_HARDPPS_CD || cflag & CRTSCTS || !(cflag & CLOCAL)) port->port_ier |= UART_IER_MSI; /* * Ok, we're now changing the port state. Do it with * interrupts disabled. */ save_flags(flags); cli(); serial_out(port, UART_IER, port->port_ier); if (uart_config[port->type].flags & UART_STARTECH) { serial_outp(port, UART_LCR, 0xBF); serial_outp(port, UART_EFR, cflag & CRTSCTS ? UART_EFR_CTS :0); } serial_outp(port, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ serial_outp(port, UART_DLL, quot & 0xff); /* LS of divisor */ serial_outp(port, UART_DLM, quot >> 8); /* MS of divisor */ if (port->type == PORT_16750) serial_outp(port, UART_FCR, fcr); /* set fcr */ serial_outp(port, UART_LCR, cval); /* reset DLAB */ port->port_lcr = cval; /* Save LCR */ if (port->type != PORT_16750) { if (fcr & UART_FCR_ENABLE_FIFO) { /* emulated UARTs (Lucent Venus 167x) need two steps */ serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); } serial_outp(port, UART_FCR, fcr); /* set fcr */ } restore_flags(flags);}static void serial8250_pm(struct uart_port *port, u_int state, u_int oldstate){ if (state) { /* sleep */ if (uart_config[port->type].flags & UART_STARTECH) { /* Arrange to enter sleep mode */ serial_outp(port, UART_LCR, 0xBF); serial_outp(port, UART_EFR, UART_EFR_ECB); serial_outp(port, UART_LCR, 0); serial_outp(port, UART_IER, UART_IERX_SLEEP); serial_outp(port, UART_LCR, 0xBF); serial_outp(port, UART_EFR, 0); serial_outp(port, UART_LCR, 0); } if (port->type == PORT_16750) { /* Arrange to enter sleep mode */ serial_outp(port, UART_IER, UART_IERX_SLEEP); } } else { /* wake */ if (uart_config[port->type].flags & UART_STARTECH) { /* Wake up UART */ serial_outp(port, UART_LCR, 0xBF); serial_outp(port, UART_EFR, UART_EFR_ECB); /* * Turn off LCR == 0xBF so we actually set the IER * register on the XR16C850 */ serial_outp(port, UART_LCR, 0); serial_outp(port, UART_IER, 0); /* * Now reset LCR so we can turn off the ECB bit
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -