📄 serial.c
字号:
#endif/* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */static _INLINE_ void rs_sched_event(struct async_struct *info, int event){ info->event |= 1 << event; queue_task_irq_off(&info->tqueue, &tq_serial); mark_bh(SERIAL_BH);}static _INLINE_ void receive_chars(struct async_struct *info, int *status){ struct tty_struct *tty = info->tty; unsigned char ch; int ignored = 0;#if defined(CONFIG_LEDMAN) ledman_cmd(LEDMAN_CMD_SET, info->line ? LEDMAN_COM2_RX : LEDMAN_COM1_RX);#endif do { ch = serial_inp(info, UART_RX); if (*status & info->ignore_status_mask) { if (++ignored > 100) break; goto ignore_char; } if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; tty->flip.count++; if (*status & (UART_LSR_BI)) {#ifdef SERIAL_DEBUG_INTR printk("handling break....");#endif *tty->flip.flag_buf_ptr++ = TTY_BREAK; if (info->flags & ASYNC_SAK) do_SAK(tty); } 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; else if (*status & UART_LSR_OE) *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; else *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = ch; ignore_char: *status = serial_inp(info, UART_LSR) & info->read_status_mask; } while (*status & UART_LSR_DR); queue_task_irq_off(&tty->flip.tqueue, &tq_timer);#ifdef SERIAL_DEBUG_INTR printk("DR...");#endif}static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done){ int count; #if defined(CONFIG_LEDMAN) ledman_cmd(LEDMAN_CMD_SET, info->line ? LEDMAN_COM2_TX : LEDMAN_COM1_TX);#endif if (info->x_char) { serial_outp(info, UART_TX, info->x_char); info->x_char = 0; if (intr_done) *intr_done = 0; return; } if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tty->hw_stopped) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); return; } count = info->xmit_fifo_size; do { serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]); info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); if (--info->xmit_cnt <= 0) break; } while (--count > 0); if (info->xmit_cnt < WAKEUP_CHARS) rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);#ifdef SERIAL_DEBUG_INTR printk("THRE...");#endif if (intr_done) *intr_done = 0; if (info->xmit_cnt <= 0) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); }}static _INLINE_ void check_modem_status(struct async_struct *info){ int status; status = serial_in(info, UART_MSR); if (status & UART_MSR_ANY_DELTA) { /* update input line counters */ if (status & UART_MSR_TERI) info->icount.rng++; if (status & UART_MSR_DDSR) info->icount.dsr++; if (status & UART_MSR_DDCD) { info->icount.dcd++;#ifdef CONFIG_LEDMAN ledman_cmd( (status & UART_MSR_DCD) ? LEDMAN_CMD_ON : LEDMAN_CMD_OFF, info->line ? LEDMAN_COM2_DCD : LEDMAN_COM1_DCD);#endif } if (status & UART_MSR_DCTS) info->icount.cts++; wake_up_interruptible(&info->delta_msr_wait); } if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) printk("ttys%d CD now %s...", info->line, (status & UART_MSR_DCD) ? "on" : "off");#endif if (status & UART_MSR_DCD) wake_up_interruptible(&info->open_wait); else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_CALLOUT_NOHUP))) {#ifdef SERIAL_DEBUG_OPEN printk("scheduling hangup...");#endif queue_task_irq_off(&info->tqueue_hangup, &tq_scheduler); } } if (info->flags & ASYNC_CTS_FLOW) { if (info->tty->hw_stopped) { if (status & UART_MSR_CTS) {#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx start...");#endif info->tty->hw_stopped = 0; info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); return; } } else { if (!(status & UART_MSR_CTS)) {#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx stop...");#endif info->tty->hw_stopped = 1; info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); } } }}/* * This is the serial driver's generic interrupt routine */static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs){ int status; struct async_struct * info; int pass_counter = 0; struct async_struct *end_mark = 0; int first_multi = 0; struct rs_multiport_struct *multi;#ifdef SERIAL_DEBUG_INTR printk("rs_interrupt(%d)...", irq);#endif info = IRQ_ports[IRQMASK(irq)]; if (!info) return; multi = &rs_multiport[IRQMASK(irq)]; if (multi->port_monitor) first_multi = inb(multi->port_monitor); do { if (!info->tty || (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) { if (!end_mark) end_mark = info; goto next; } end_mark = 0; info->last_active = jiffies; status = serial_inp(info, UART_LSR) & info->read_status_mask;#ifdef SERIAL_DEBUG_INTR printk("status = %x...", status);#endif if (status & UART_LSR_DR) { receive_chars(info, &status); } check_modem_status(info); if (status & UART_LSR_THRE) { transmit_chars(info, 0); } next: info = info->next_port; if (!info) { info = IRQ_ports[IRQMASK(irq)]; if (pass_counter++ > RS_ISR_PASS_LIMIT) {#if 0 printk("rs loop break\n");#endif break; /* Prevent infinite loops */ } continue; } } while (end_mark != info); if (multi->port_monitor) printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n", info->irq, first_multi, inb(multi->port_monitor));#ifdef SERIAL_DEBUG_INTR printk("end.\n");#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){ int status; int pass_counter = 0; int first_multi = 0; struct async_struct * info; struct rs_multiport_struct *multi; #ifdef SERIAL_DEBUG_INTR printk("rs_interrupt_single(%d)...", irq);#endif info = IRQ_ports[IRQMASK(irq)]; if (!info || !info->tty) return; multi = &rs_multiport[IRQMASK(irq)]; if (multi->port_monitor) first_multi = inb(multi->port_monitor); do { status = serial_inp(info, UART_LSR) & info->read_status_mask;#ifdef SERIAL_DEBUG_INTR printk("status = %x...", status);#endif if (status & UART_LSR_DR) { receive_chars(info, &status); } check_modem_status(info); if (status & UART_LSR_THRE) { transmit_chars(info, 0); } if (pass_counter++ > RS_ISR_PASS_LIMIT) {#if 0 printk("rs_single loop break.\n");#endif break; } } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); info->last_active = jiffies; if (multi->port_monitor) printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", info->irq, first_multi, inb(multi->port_monitor));#ifdef SERIAL_DEBUG_INTR printk("end.\n");#endif}/* * This is the serial driver's for multiport boards */static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs * regs){ int status; struct async_struct * info; int pass_counter = 0; int first_multi= 0; struct rs_multiport_struct *multi;#ifdef SERIAL_DEBUG_INTR printk("rs_interrupt_multi(%d)...", irq);#endif info = IRQ_ports[IRQMASK(irq)]; if (!info) return; multi = &rs_multiport[IRQMASK(irq)]; if (!multi->port1) { /* Should never happen */ printk("rs_interrupt_multi: NULL port1!\n"); return; } if (multi->port_monitor) first_multi = inb(multi->port_monitor); while (1) { if (!info->tty || (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) goto next; info->last_active = jiffies; status = serial_inp(info, UART_LSR) & info->read_status_mask;#ifdef SERIAL_DEBUG_INTR printk("status = %x...", status);#endif if (status & UART_LSR_DR) { receive_chars(info, &status); } check_modem_status(info); if (status & UART_LSR_THRE) { transmit_chars(info, 0); } next: info = info->next_port; if (info) continue; info = IRQ_ports[IRQMASK(irq)]; if (pass_counter++ > RS_ISR_PASS_LIMIT) {#if 1 printk("rs_multi loop break\n");#endif break; /* Prevent infinite loops */ } if (multi->port_monitor) printk("rs port monitor irq %d: 0x%x, 0x%x\n", info->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}/* * ------------------------------------------------------------------- * 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 (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); }}/* * This routine is called from the scheduler tqueue when the interrupt * routine has signalled that a hangup has occurred. The path of * hangup processing is: * * serial interrupt routine -> (scheduler tqueue) -> * do_serial_hangup() -> tty->hangup() -> rs_hangup() * */static void do_serial_hangup(void *private_){ struct async_struct *info = (struct async_struct *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; tty_hangup(tty);}/* * 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(void){ static unsigned long last_strobe = 0; struct async_struct *info; unsigned int i; if ((jiffies - last_strobe) >= RS_STROBE_TIME) { for (i=1; i < 16; i++) { info = IRQ_ports[IRQMASK(i)]; if (!info) continue; cli(); 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); if (rs_multiport[IRQMASK(i)].port1) rs_interrupt_multi(i, NULL, NULL); else rs_interrupt(i, NULL, NULL); } else rs_interrupt_single(i, NULL, NULL); sti(); } } last_strobe = jiffies; timer_table[RS_TIMER].expires = jiffies + RS_STROBE_TIME; timer_active |= 1 << RS_TIMER; if (IRQ_ports[IRQMASK(0)]) { cli(); rs_interrupt(0, NULL, NULL); sti(); timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2; }}/* * --------------------------------------------------------------- * 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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -