📄 serial.c
字号:
info->last_active = jiffies; status = serial_inp(info, 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); next: info = info->next_port; if (info) continue; info = IRQ_ports[irq]; /* * 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->state->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}#endif/* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. * ------------------------------------------------------------------- *//* * This routine is used to handle the "bottom half" processing for the * serial driver, known also the "software interrupt" processing. * This processing is done at the kernel interrupt level, after the * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This * is where time-consuming activities which can not be done in the * interrupt driver proper are done; the interrupt driver schedules * them using rs_sched_event(), and they get done here. */static void do_serial_bh(void){ run_task_queue(&tq_serial);}static void do_softint(void *private_){ struct async_struct *info = (struct async_struct *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait);#ifdef SERIAL_HAVE_POLL_WAIT wake_up_interruptible(&tty->poll_wait);#endif }}/* * This subroutine is called when the RS_TIMER goes off. It is used * by the serial driver to handle ports that do not have an interrupt * (irq=0). This doesn't work very well for 16450's, but gives barely * passable results for a 16550A. (Although at the expense of much * CPU overhead). */static void rs_timer(unsigned long dummy){ static unsigned long last_strobe; struct async_struct *info; unsigned int i; unsigned long flags; if ((jiffies - last_strobe) >= RS_STROBE_TIME) { for (i=0; i < NR_IRQS; i++) { info = IRQ_ports[i]; if (!info) continue; save_flags(flags); cli();#ifdef CONFIG_SERIAL_SHARE_IRQ if (info->next_port) { do { serial_out(info, UART_IER, 0); info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); info = info->next_port; } while (info);#ifdef CONFIG_SERIAL_MULTIPORT if (rs_multiport[i].port1) rs_interrupt_multi(i, NULL, NULL); else#endif rs_interrupt(i, NULL, NULL); } else#endif /* CONFIG_SERIAL_SHARE_IRQ */ rs_interrupt_single(i, NULL, NULL); restore_flags(flags); } } last_strobe = jiffies; mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); if (IRQ_ports[0]) { save_flags(flags); cli();#ifdef CONFIG_SERIAL_SHARE_IRQ rs_interrupt(0, NULL, NULL);#else rs_interrupt_single(0, NULL, NULL);#endif restore_flags(flags); mod_timer(&serial_timer, jiffies + IRQ_timeout[0]); }}/* * --------------------------------------------------------------- * Low level utility subroutines for the serial driver: routines to * figure out the appropriate timeout for an interrupt chain, routines * to initialize and startup a serial port, and routines to shutdown a * serial port. Useful stuff like that. * --------------------------------------------------------------- *//* * This routine figures out the correct timeout for a particular IRQ. * It uses the smallest timeout of all of the serial ports in a * particular interrupt chain. Now only used for IRQ 0.... */static void figure_IRQ_timeout(int irq){ struct async_struct *info; int timeout = 60*HZ; /* 60 seconds === a long time :-) */ info = IRQ_ports[irq]; if (!info) { IRQ_timeout[irq] = 60*HZ; return; } while (info) { if (info->timeout < timeout) timeout = info->timeout; info = info->next_port; } if (!irq) timeout = timeout / 2; IRQ_timeout[irq] = (timeout > 3) ? timeout-2 : 1;}#ifdef CONFIG_SERIAL_RSA/* Attempts to turn on the RSA FIFO. Returns zero on failure */static int enable_rsa(struct async_struct *info){ unsigned char mode; int result; unsigned long flags; save_flags(flags); cli(); mode = serial_inp(info, UART_RSA_MSR); result = mode & UART_RSA_MSR_FIFO; if (!result) { serial_outp(info, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); mode = serial_inp(info, UART_RSA_MSR); result = mode & UART_RSA_MSR_FIFO; } restore_flags(flags); return result;}/* Attempts to turn off the RSA FIFO. Returns zero on failure */static int disable_rsa(struct async_struct *info){ unsigned char mode; int result; unsigned long flags; save_flags(flags); cli(); mode = serial_inp(info, UART_RSA_MSR); result = !(mode & UART_RSA_MSR_FIFO); if (!result) { serial_outp(info, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); mode = serial_inp(info, UART_RSA_MSR); result = !(mode & UART_RSA_MSR_FIFO); } restore_flags(flags); return result;}#endif /* CONFIG_SERIAL_RSA */static int startup(struct async_struct * info){ unsigned long flags; int retval=0; void (*handler)(int, void *, struct pt_regs *); struct serial_state *state= info->state; unsigned long page;#ifdef CONFIG_SERIAL_MANY_PORTS unsigned short ICP;#endif page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; save_flags(flags); cli(); if (info->flags & ASYNC_INITIALIZED) { free_page(page); goto errout; } if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); free_page(page); goto errout; } if (info->xmit.buf) free_page(page); else info->xmit.buf = (unsigned char *) page;#ifdef SERIAL_DEBUG_OPEN printk("starting up ttys%d (irq %d)...", info->line, state->irq);#endif if (uart_config[state->type].flags & UART_STARTECH) { /* Wake up UART */ serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, UART_EFR_ECB); /* * Turn off LCR == 0xBF so we actually set the IER * register on the XR16C850 */ serial_outp(info, UART_LCR, 0); serial_outp(info, UART_IER, 0); /* * Now reset LCR so we can turn off the ECB bit */ serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, 0); /* * For a XR16C850, we need to set the trigger levels */ if (state->type == PORT_16850) { serial_outp(info, UART_FCTR, UART_FCTR_TRGD | UART_FCTR_RX); serial_outp(info, UART_TRG, UART_TRG_96); serial_outp(info, UART_FCTR, UART_FCTR_TRGD | UART_FCTR_TX); serial_outp(info, UART_TRG, UART_TRG_96); } serial_outp(info, UART_LCR, 0); } if (state->type == PORT_16750) { /* Wake up UART */ serial_outp(info, UART_IER, 0); } if (state->type == PORT_16C950) { /* Wake up and initialize UART */ info->ACR = 0; serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, UART_EFR_ECB); serial_outp(info, UART_IER, 0); serial_outp(info, UART_LCR, 0); serial_icr_write(info, UART_CSR, 0); /* Reset the UART */ serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, UART_EFR_ECB); serial_outp(info, 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 (state->type == PORT_RSA) { if (state->baud_base != SERIAL_RSA_BAUD_BASE && enable_rsa(info)) state->baud_base = SERIAL_RSA_BAUD_BASE; if (state->baud_base == SERIAL_RSA_BAUD_BASE) serial_outp(info, UART_RSA_FRR, 0); }#endif#ifdef CONFIG_ARCH_PXA if (state->type == PORT_PXA) { switch ((long)state->iomem_base) { case (long)&FFUART: CKEN |= CKEN6_FFUART; break; case (long)&BTUART: CKEN |= CKEN7_BTUART; break; case (long)&STUART: CKEN |= CKEN5_STUART; break; } }#endif /* * Clear the FIFO buffers and disable them * (they will be reenabled in change_speed()) */ if (uart_config[state->type].flags & UART_CLEAR_FIFO) { serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); serial_outp(info, UART_FCR, 0); } /* * Clear the interrupt registers. */ (void) serial_inp(info, UART_LSR); (void) serial_inp(info, UART_RX); (void) serial_inp(info, UART_IIR); (void) serial_inp(info, 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 (!(info->flags & ASYNC_BUGGY_UART) && (serial_inp(info, UART_LSR) == 0xff)) { printk("ttyS%d: LSR safety check engaged!\n", state->line); if (capable(CAP_SYS_ADMIN)) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); } else retval = -ENODEV; goto errout; } /* * Allocate the IRQ if necessary */ if (state->irq && (!IRQ_ports[state->irq] || !IRQ_ports[state->irq]->next_port)) { if (IRQ_ports[state->irq]) {#ifdef CONFIG_SERIAL_SHARE_IRQ free_irq(state->irq, &IRQ_ports[state->irq]);#ifdef CONFIG_SERIAL_MULTIPORT if (rs_multiport[state->irq].port1) handler = rs_interrupt_multi; else#endif handler = rs_interrupt;#else retval = -EBUSY; goto errout;#endif /* CONFIG_SERIAL_SHARE_IRQ */ } else handler = rs_interrupt_single; retval = request_irq(state->irq, handler, SA_SHIRQ, "serial", &IRQ_ports[state->irq]); if (retval) { if (capable(CAP_SYS_ADMIN)) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); retval = 0; } goto errout; } } /* * Insert serial port into IRQ chain. */ info->prev_port = 0; info->next_port = IRQ_ports[state->irq]; if (info->next_port) info->next_port->prev_port = info; IRQ_ports[state->irq] = info; figure_IRQ_timeout(state->irq); /* * Now, initialize the UART */ serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ info->MCR = 0; if (info->tty->termios->c_cflag & CBAUD) info->MCR = UART_MCR_DTR | UART_MCR_RTS;#ifdef CONFIG_SERIAL_MANY_PORTS if (info->flags & ASYNC_FOURPORT) { if (state->irq == 0) info->MCR |= UART_MCR_OUT1; } else#endif { if (state->irq != 0) info->MCR |= UART_MCR_OUT2; if (pxa_buggy_port(state->type) && state->irq != 0) info->MCR ^= UART_MCR_OUT2; } info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ serial_outp(info, UART_MCR, info->MCR); /* * Finally, enable interrupts */ info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; if (pxa_port(state->type)) info->IER |= UART_IER_UUE | UART_IER_RTOIE; serial_outp(info, UART_IER, info->IER); /* enable interrupts */ #ifdef CONFIG_SERIAL_MANY_PORTS if (info->flags & ASYNC_FOURPORT) { /* Enable interrupts on the AST Fourport board */ ICP = (info->port & 0xFE0) | 0x01F; outb_p(0x80, ICP); (void) inb_p(ICP); }#endif /* * And clear the interrupt registers again for luck. */ (void)serial_inp(info, UART_LSR); (void)serial_inp(info, UART_RX); (void)serial_inp(info, UART_IIR); (void)serial_inp(info, UART_MSR); if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit.head = info->xmit.tail = 0; /* * Set up serial timers... */ mod_timer(&serial_timer, jiffies + 2*HZ/100); /* * Set up the tty->alt_speed kludge */#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ if (info->tty) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) info->tty->alt_speed = 115200; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) info->tty->alt_speed = 230400; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; }#endif /* * and set the speed of the serial port */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -