📄 serial.c
字号:
serial_icr_write(info, UART_ACR, info->ACR | UART_ACR_ICRRD); serial_out(info, UART_SCR, offset); value = serial_in(info, UART_ICR); serial_icr_write(info, UART_ACR, info->ACR); return value;}/* * ------------------------------------------------------------ * rs_stop() and rs_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. * ------------------------------------------------------------ */static void rs_stop(struct tty_struct *tty){ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_stop")) return; save_flags(flags); cli(); if (info->IER & UART_IER_THRI) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); } if (info->state->type == PORT_16C950) { info->ACR |= UART_ACR_TXDIS; serial_icr_write(info, UART_ACR, info->ACR); } restore_flags(flags);}static void rs_start(struct tty_struct *tty){ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_start")) return; save_flags(flags); cli(); if (info->xmit.head != info->xmit.tail && info->xmit.buf && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); } if (info->state->type == PORT_16C950) { info->ACR &= ~UART_ACR_TXDIS; serial_icr_write(info, UART_ACR, info->ACR); } restore_flags(flags);}/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines. All of the following * subroutines are declared as inline and are folded into * rs_interrupt(). They were separated out for readability's sake. * * Note: rs_interrupt() is a "fast" interrupt, which means that it * runs with interrupts turned off. People who may want to modify * rs_interrupt() should try to keep the interrupt handler as fast as * possible. After you are done making modifications, it is not a bad * idea to do: * * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c * * and look at the resulting assemble code in serial.s. * * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 * ----------------------------------------------------------------------- *//* * 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(&info->tqueue, &tq_serial); mark_bh(SERIAL_BH);}static _INLINE_ void receive_chars(struct async_struct *info, int *status, struct pt_regs * regs){ struct tty_struct *tty = info->tty; unsigned char ch; struct async_icount *icount; int max_count = 256; icount = &info->state->icount; 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(info, UART_RX); *tty->flip.char_buf_ptr = ch; icount->rx++; #ifdef SERIAL_DEBUG_INTR printk("DR%02x:%02x...", ch, *status);#endif *tty->flip.flag_buf_ptr = 0; 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); 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. */#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) if (info->line == sercons.index) { if (!break_pressed) { break_pressed = jiffies; goto ignore_char; } break_pressed = 0; }#endif if (info->flags & ASYNC_SAK) do_SAK(tty); } else if (*status & UART_LSR_PE) icount->parity++; else if (*status & UART_LSR_FE) icount->frame++; if (*status & UART_LSR_OE) icount->overrun++; /* * Mask off conditions which should be ignored. */ *status &= info->read_status_mask;#ifdef CONFIG_SERIAL_CONSOLE if (info->line == sercons.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 defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) if (break_pressed && info->line == sercons.index) { if (ch != 0 && time_before(jiffies, break_pressed + HZ*5)) { handle_sysrq(ch, regs, NULL, NULL); break_pressed = 0; goto ignore_char; } break_pressed = 0; }#endif if ((*status & info->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.count++; tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; }#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) ignore_char:#endif *status = serial_inp(info, UART_LSR); } while ((*status & UART_LSR_DR) && (max_count-- > 0));#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */ tty_flip_buffer_push(tty);#else queue_task_irq_off(&tty->flip.tqueue, &tq_timer);#endif }static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done){ int count; if (info->x_char) { serial_outp(info, UART_TX, info->x_char); info->state->icount.tx++; info->x_char = 0; if (intr_done) *intr_done = 0; return; } if (info->xmit.head == info->xmit.tail || 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 + 1) & (SERIAL_XMIT_SIZE-1); info->state->icount.tx++; if (info->xmit.head == info->xmit.tail) break; } while (--count > 0); if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) < 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.head == info->xmit.tail) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); }}static _INLINE_ void check_modem_status(struct async_struct *info){ int status; struct async_icount *icount; status = serial_in(info, UART_MSR); if (status & UART_MSR_ANY_DELTA) { icount = &info->state->icount; /* update input line counters */ if (status & UART_MSR_TERI) icount->rng++; if (status & UART_MSR_DDSR) icount->dsr++; if (status & UART_MSR_DDCD) { icount->dcd++;#ifdef CONFIG_HARD_PPS if ((info->flags & ASYNC_HARDPPS_CD) && (status & UART_MSR_DCD)) hardpps();#endif } if (status & UART_MSR_DCTS) 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("doing serial hangup...");#endif if (info->tty) tty_hangup(info->tty); } } 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); } } }}#ifdef CONFIG_SERIAL_SHARE_IRQ/* * 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;#ifdef CONFIG_SERIAL_MULTIPORT int first_multi = 0; struct rs_multiport_struct *multi;#endif#ifdef SERIAL_DEBUG_INTR printk("rs_interrupt(%d)...", irq);#endif info = IRQ_ports[irq]; if (!info) return;#ifdef CONFIG_SERIAL_MULTIPORT multi = &rs_multiport[irq]; if (multi->port_monitor) first_multi = inb(multi->port_monitor);#endif do { if (!info->tty || (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) { if (!end_mark) end_mark = info; goto next; }#ifdef SERIAL_DEBUG_INTR printk("IIR = %x...", serial_in(info, UART_IIR));#endif end_mark = 0; 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) { info = IRQ_ports[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);#ifdef CONFIG_SERIAL_MULTIPORT if (multi->port_monitor) printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n", info->state->irq, first_multi, inb(multi->port_monitor));#endif#ifdef SERIAL_DEBUG_INTR printk("end.\n");#endif}#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ *//* * 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; struct async_struct * info;#ifdef CONFIG_SERIAL_MULTIPORT int first_multi = 0; struct rs_multiport_struct *multi;#endif #ifdef SERIAL_DEBUG_INTR printk("rs_interrupt_single(%d)...", irq);#endif info = IRQ_ports[irq]; if (!info || !info->tty) return;#ifdef CONFIG_SERIAL_MULTIPORT multi = &rs_multiport[irq]; if (multi->port_monitor) first_multi = inb(multi->port_monitor);#endif do { 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); 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, UART_IIR));#endif } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); info->last_active = jiffies;#ifdef CONFIG_SERIAL_MULTIPORT if (multi->port_monitor) printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", info->state->irq, first_multi, inb(multi->port_monitor));#endif#ifdef SERIAL_DEBUG_INTR printk("end.\n");#endif}#ifdef CONFIG_SERIAL_MULTIPORT /* * 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[irq]; if (!info) return; multi = &rs_multiport[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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -