📄 8250.c
字号:
serial_outp(port, UART_LCR, UART_LCR_DLAB); if (serial_in(port, UART_EFR) == 0) { port->type = PORT_16650; } else { serial_outp(port, UART_LCR, 0xBF); if (serial_in(port, UART_EFR) == 0) autoconfig_startech_uarts(port); } } if (port->type == PORT_16550A) { /* Check for TI 16750 */ serial_outp(port, UART_LCR, save_lcr | UART_LCR_DLAB); serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); scratch = serial_in(port, UART_IIR) >> 5; if (scratch == 7) { /* * If this is a 16750, and not a cheap UART * clone, then it should only go into 64 byte * mode if the UART_FCR7_64BYTE bit was set * while UART_LCR_DLAB was latched. */ serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); serial_outp(port, UART_LCR, 0); serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); scratch = serial_in(port, UART_IIR) >> 5; if (scratch == 6) port->type = PORT_16750; } serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); }#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) /* * Only probe for RSA ports if we got the region. */ if (port->type == PORT_16550A && probeflags & PROBE_RSA) { int i; for (i = 0 ; i < PORT_RSA_MAX ; ++i) { if (!probe_rsa[i] && !force_rsa[i]) break; if (((probe_rsa[i] != port->iobase) || check_region(port->iobase + UART_RSA_BASE, 16)) && (force_rsa[i] != port->iobase)) continue; if (!enable_rsa(port)) continue; port->type = PORT_RSA; port->uartclk = SERIAL_RSA_BAUD_BASE * 16; break; } }#endif serial_outp(port, UART_LCR, save_lcr); if (port->type == PORT_16450) { scratch = serial_in(port, UART_SCR); serial_outp(port, UART_SCR, 0xa5); status1 = serial_in(port, UART_SCR); serial_outp(port, UART_SCR, 0x5a); status2 = serial_in(port, UART_SCR); serial_outp(port, UART_SCR, scratch); if ((status1 != 0xa5) || (status2 != 0x5a)) port->type = PORT_8250; } port->fifosize = uart_config[port->type].dfl_xmit_fifo_size; if (port->type == PORT_UNKNOWN) { restore_flags(flags); return; }#ifdef CONFIG_SERIAL_RSA if (port->iobase && port->type == PORT_RSA) { release_region(port->iobase, 8); request_region(port->iobase + UART_RSA_BASE, 16, "serial_rsa"); }#endif /* * Reset the UART. */#ifdef CONFIG_SERIAL_RSA if (port->type == PORT_RSA) serial_outp(port, UART_RSA_FRR, 0);#endif serial_outp(port, UART_MCR, save_mcr); serial_outp(port, UART_FCR, (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); serial_outp(port, UART_FCR, 0); (void)serial_in(port, UART_RX); serial_outp(port, UART_IER, 0); restore_flags(flags);}static void autoconfig_irq(struct uart_port *port){ unsigned char save_mcr, save_ier; unsigned long irqs; int irq;#ifdef CONFIG_SERIAL_MANY_PORTS unsigned char save_ICP = 0; unsigned short ICP = 0; if (port->flags & ASYNC_FOURPORT) { ICP = (port->iobase & 0xfe0) | 0x1f; save_ICP = inb_p(ICP); outb_p(0x80, ICP); (void) inb_p(ICP); }#endif /* forget possible initially masked and pending IRQ */ probe_irq_off(probe_irq_on()); save_mcr = serial_inp(port, UART_MCR); save_ier = serial_inp(port, UART_IER); serial_outp(port, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); irqs = probe_irq_on(); serial_outp(port, UART_MCR, 0); udelay (10); if (port->flags & ASYNC_FOURPORT) { serial_outp(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); } else { serial_outp(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); } serial_outp(port, UART_IER, 0x0f); /* enable all intrs */ (void)serial_inp(port, UART_LSR); (void)serial_inp(port, UART_RX); (void)serial_inp(port, UART_IIR); (void)serial_inp(port, UART_MSR); serial_outp(port, UART_TX, 0xFF); udelay (20); irq = probe_irq_off(irqs); serial_outp(port, UART_MCR, save_mcr); serial_outp(port, UART_IER, save_ier);#ifdef CONFIG_SERIAL_MANY_PORTS if (port->flags & ASYNC_FOURPORT) outb_p(save_ICP, ICP);#endif port->irq = (irq > 0)? irq : 0;}static void serial8250_stop_tx(struct uart_port *port, u_int from_tty){ if (port->port_ier & UART_IER_THRI) { port->port_ier &= ~UART_IER_THRI; serial_out(port, UART_IER, port->port_ier); } if (port->type == PORT_16C950) { port->port_acr |= UART_ACR_TXDIS; serial_icr_write(port, UART_ACR, port->port_acr); }}static void serial8250_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty){ if (nonempty && !(port->port_ier & UART_IER_THRI)) { port->port_ier |= UART_IER_THRI; serial_out(port, UART_IER, port->port_ier); } /* * We only do this from uart_start */ if (from_tty && port->type == PORT_16C950) { port->port_acr &= ~UART_ACR_TXDIS; serial_icr_write(port, UART_ACR, port->port_acr); }}static void serial8250_stop_rx(struct uart_port *port){ port->port_ier &= ~UART_IER_RLSI; port->read_status_mask &= ~UART_LSR_DR; serial_out(port, UART_IER, port->port_ier);}static void serial8250_enable_ms(struct uart_port *port){ port->port_ier |= UART_IER_MSI; serial_out(port, UART_IER, port->port_ier);}static _INLINE_ voidreceive_chars(struct uart_info *info, int *status, struct pt_regs *regs){ struct tty_struct *tty = info->tty; struct uart_port *port = info->port; unsigned char ch; int max_count = 256; do { if (tty->flip.count >= TTY_FLIPBUF_SIZE) { tty->flip.tqueue.routine((void *)tty); if (tty->flip.count >= TTY_FLIPBUF_SIZE) return; // if TTY_DONT_FLIP is set } ch = serial_inp(port, UART_RX); *tty->flip.char_buf_ptr = ch; *tty->flip.flag_buf_ptr = TTY_NORMAL; port->icount.rx++; if (*status & (UART_LSR_BI | UART_LSR_PE | UART_LSR_FE | UART_LSR_OE)) { /* * For statistics only */ if (*status & UART_LSR_BI) { *status &= ~(UART_LSR_FE | UART_LSR_PE); port->icount.brk++; /* * We do the SysRQ and SAK checking * here because otherwise the break * may get masked by ignore_status_mask * or read_status_mask. */ uart_handle_break(info, &serial8250_console); } else if (*status & UART_LSR_PE) port->icount.parity++; else if (*status & UART_LSR_FE) port->icount.frame++; if (*status & UART_LSR_OE) port->icount.overrun++; /* * Mask off conditions which should be ingored. */ *status &= port->read_status_mask;#ifdef CONFIG_SERIAL_8250_CONSOLE if (port->line == serial8250_console.index) { /* Recover the break flag from console xmit */ *status |= lsr_break_flag; lsr_break_flag = 0; }#endif if (*status & UART_LSR_BI) {#ifdef SERIAL_DEBUG_INTR printk("handling break....");#endif *tty->flip.flag_buf_ptr = TTY_BREAK; } else if (*status & UART_LSR_PE) *tty->flip.flag_buf_ptr = TTY_PARITY; else if (*status & UART_LSR_FE) *tty->flip.flag_buf_ptr = TTY_FRAME; } if (uart_handle_sysrq_char(info, ch, regs)) goto ignore_char; if ((*status & port->ignore_status_mask) == 0) { tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; tty->flip.count++; } if ((*status & UART_LSR_OE) && tty->flip.count < TTY_FLIPBUF_SIZE) { /* * Overrun is special, since it's reported * immediately, and doesn't affect the current * character. */ *tty->flip.flag_buf_ptr = TTY_OVERRUN; tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; tty->flip.count++; } ignore_char: *status = serial_inp(port, UART_LSR); } while ((*status & UART_LSR_DR) && (max_count-- > 0)); tty_flip_buffer_push(tty);}static _INLINE_ void transmit_chars(struct uart_info *info, int *intr_done){ struct uart_port *port = info->port; int count; if (port->x_char) { serial_outp(port, UART_TX, port->x_char); port->icount.tx++; port->x_char = 0; if (intr_done) *intr_done = 0; return; } if (info->xmit.head == info->xmit.tail || info->tty->stopped || info->tty->hw_stopped) { serial8250_stop_tx(port, 0); return; } count = port->fifosize; do { serial_out(port, UART_TX, info->xmit.buf[info->xmit.tail]); info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (info->xmit.head == info->xmit.tail) break; } while (--count > 0); if (CIRC_CNT(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE) < WAKEUP_CHARS) uart_event(info, EVT_WRITE_WAKEUP);#ifdef SERIAL_DEBUG_INTR printk("THRE...");#endif if (intr_done) *intr_done = 0; if (info->xmit.head == info->xmit.tail) serial8250_stop_tx(info->port, 0);}static _INLINE_ void check_modem_status(struct uart_info *info){ struct uart_port *port = info->port; int status; status = serial_in(port, UART_MSR); if (status & UART_MSR_ANY_DELTA) { if (status & UART_MSR_TERI) port->icount.rng++; if (status & UART_MSR_DDSR) port->icount.dsr++; if (status & UART_MSR_DDCD) uart_handle_dcd_change(info, status & UART_MSR_DCD); if (status & UART_MSR_DCTS) uart_handle_cts_change(info, status & UART_MSR_CTS); wake_up_interruptible(&info->delta_msr_wait); }}/* * This handles the interrupt from one port. */static inline voidserial8250_handle_port(struct uart_info *info, struct pt_regs *regs){ int status = serial_inp(info->port, UART_LSR);#ifdef SERIAL_DEBUG_INTR printk("status = %x...", status);#endif if (status & UART_LSR_DR) receive_chars(info, &status, regs); check_modem_status(info); if (status & UART_LSR_THRE) transmit_chars(info, 0);}#ifdef CONFIG_SERIAL_8250_SHARE_IRQ/* * This is the serial driver's generic interrupt routine */static void rs_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct uart_info *info, *end_mark = NULL; int pass_counter = 0;#ifdef CONFIG_SERIAL_8250_MULTIPORT int first_multi = 0; unsigned long port_monitor = rs_multiport[irq].port_monitor;#endif#ifdef SERIAL_DEBUG_INTR printk("rs_interrupt(%d)...", irq);#endif info = *(struct uart_info **)dev_id; if (!info) return;#ifdef CONFIG_SERIAL_8250_MULTIPORT if (port_monitor) first_multi = inb(port_monitor);#endif do { if (!info->tty || (serial_in(info->port, UART_IIR) & UART_IIR_NO_INT)) { if (!end_mark) end_mark = info; goto next; }#ifdef SERIAL_DEBUG_INTR printk("IIR = %x...", serial_in(info->port, UART_IIR));#endif end_mark = NULL; serial8250_handle_port(info, regs); next: info = info->next_info; if (info) continue; info = *(struct uart_info **)dev_id; if (pass_counter++ > RS_ISR_PASS_LIMIT) {#if 0 printk("rs loop break\n");#endif break; /* Prevent infinite loops */ } } while (end_mark != info);#ifdef CONFIG_SERIAL_8250_MULTIPORT if (port_monitor) printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n", info->port->irq, first_multi, inb(port_monitor));#endif#ifdef SERIAL_DEBUG_INTR printk("end.\n");#endif}#endif/* * This is the serial driver's interrupt routine for a single port */static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs *regs){ struct uart_info *info; int pass_counter = 0;#ifdef CONFIG_SERIAL_8250_MULTIPORT int first_multi = 0; unsigned long port_monitor = rs_multiport[irq].port_monitor;#endif#ifdef SERIAL_DEBUG_INTR printk("rs_interrupt_single(%d)...", irq);#endif info = *(struct uart_info **)dev_id; if (!info || !info->tty) return;#ifdef CONFIG_SERIAL_8250_MULTIPORT if (port_monitor) first_multi = inb(port_monitor);#endif do { serial8250_handle_port(info, regs); if (pass_counter++ > RS_ISR_PASS_LIMIT) {#if 0 printk("rs_single loop break.\n");#endif break; }#ifdef SERIAL_DEBUG_INTR printk("IIR = %x...", serial_in(info->port, UART_IIR));#endif } while (!(serial_in(info->port, UART_IIR) & UART_IIR_NO_INT));#ifdef CONFIG_SERIAL_8250_MULTIPORT if (port_monitor) printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", info->port->irq, first_multi, inb(port_monitor));#endif#ifdef SERIAL_DEBUG_INTR printk("end.\n");#endif}#ifdef CONFIG_SERIAL_8250_MULTIPORT/* * This is the serial driver's interrupt routine for multiport boards */static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs *regs){ struct uart_info *info; int pass_counter = 0; struct rs_multiport_struct *multi = &rs_multiport[irq]; int first_multi = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -