📄 serial.c
字号:
} 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[irq]; if (!info || !info->tty) return; multi = &rs_multiport[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[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; 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[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=0; i < 32; i++) { info = IRQ_ports[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[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[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. * --------------------------------------------------------------- *//* * Grab all interrupts in preparation for doing an automatic irq * detection. dontgrab is a mask of irq's _not_ to grab. Returns a * mask of irq's which were grabbed and should therefore be freed * using free_all_interrupts(). */static int grab_all_interrupts(int dontgrab){ int irq_lines = 0; int i, mask; for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { if (!(mask & dontgrab) && !request_irq(i, rs_probe, SA_INTERRUPT, "serial probe", NULL)) { irq_lines |= mask; } } return irq_lines;}/* * Release all interrupts grabbed by grab_all_interrupts */static void free_all_interrupts(int irq_lines){ int i; for (i = 0; i < 16; i++) { if (irq_lines & (1 << i)) free_irq(i, NULL); }}/* * 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 ? timeout : 1;}static int startup(struct async_struct * info){ unsigned short ICP; unsigned long flags; int retval; void (*handler)(int, void *, struct pt_regs *); unsigned long page; page = get_free_page(GFP_KERNEL); if (!page) return -ENOMEM; save_flags_cli(flags); if (info->flags & ASYNC_INITIALIZED) { free_page(page); restore_flags(flags); return 0; } if (!info->port || !info->type) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); free_page(page); restore_flags(flags); return 0; } 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, info->irq);#endif /* * Clear the FIFO buffers and disable them * (they will be reenabled in change_speed()) */ if (info->type == PORT_16650) { serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); info->xmit_fifo_size = 1; /* disabled for now */ } else if (info->type == PORT_16550A) { serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); info->xmit_fifo_size = 16; } else info->xmit_fifo_size = 1; /* * 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 (serial_inp(info, UART_LSR) == 0xff) { restore_flags(flags); if (suser()) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); return 0; } else return -ENODEV; } /* * Allocate the IRQ if necessary */ if (info->irq && (!IRQ_ports[info->irq] || !IRQ_ports[info->irq]->next_port)) { if (IRQ_ports[info->irq]) { free_irq(info->irq, NULL); if (rs_multiport[info->irq].port1) handler = rs_interrupt_multi; else handler = rs_interrupt; } else handler = rs_interrupt_single; retval = request_irq(info->irq, handler, IRQ_T(info), "serial", NULL); if (retval) { restore_flags(flags); if (suser()) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); return 0; } else return retval; } } /* * Clear the interrupt registers. */ /* (void) serial_inp(info, UART_LSR); */ /* (see above) */ (void) serial_inp(info, UART_RX); (void) serial_inp(info, UART_IIR); (void) serial_inp(info, UART_MSR); /* * Now, initialize the UART */ serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ if (info->flags & ASYNC_FOURPORT) { info->MCR = UART_MCR_DTR | UART_MCR_RTS; info->MCR_noint = UART_MCR_DTR | UART_MCR_OUT1; } else { info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS; }#if defined(__alpha__) && !defined(CONFIG_PCI) info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2; info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2;#endif if (info->irq == 0) info->MCR = info->MCR_noint; serial_outp(info, UART_MCR, info->MCR); /* * Finally, enable interrupts */ info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; serial_outp(info, UART_IER, info->IER); /* enable interrupts */ 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); } /* * 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_cnt = info->xmit_head = info->xmit_tail = 0; /* * Insert serial port into IRQ chain. */ info->prev_port = 0; info->next_port = IRQ_ports[info->irq]; if (info->next_port) info->next_port->prev_port = info; IRQ_ports[info->irq] = info; figure_IRQ_timeout(info->irq); /* * Set up serial timers... */ timer_table[RS_TIMER].expires = jiffies + 2*HZ/100; timer_active |= 1 << RS_TIMER; /* * and set the speed of the serial port */ change_speed(info); info->flags |= ASYNC_INITIALIZED; restore_flags(flags); return 0;}/* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */static void shutdown(struct async_struct * info){ unsigned long flags; int retval; if (!(info->flags & ASYNC_INITIALIZED)) return;#ifdef SERIAL_DEBUG_OPEN printk("Shutting down serial port %d (irq %d)....", info->line, info->irq);#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -