?? serial.c
字號:
if (recheck_count > 16) printk("recheck_count = %d\n", recheck_count);#endif }#ifdef ISR_HACK serial_outp(info, UART_IER, 0); serial_out(info, UART_IER, info->IER);#endif info = info->next_port; if (!info && !done) { info = IRQ_ports[irq]; done = 1; if (pass_number++ > 64) break; /* Prevent infinite loops */ } } if ((info = IRQ_ports[irq]) != NULL) {#ifdef 0 do { serial_outp(info, UART_IER, 0); serial_out(info, UART_IER, info->IER); info = info->next_port; } while (info);#endif if (irq && !done_work) IRQ_timer[irq] = jiffies + 1500; else IRQ_timer[irq] = jiffies + IRQ_timeout[irq]; IRQ_active |= 1 << irq; } figure_RS_timer();}/* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. * ------------------------------------------------------------------- *//* * This routine is called when we receive a break on a serial line. * It is executed out of the software interrupt routine. */static inline void handle_rs_break(struct async_struct *info){ if (info->flags & ASYNC_SAK) do_SAK(info->tty); if (!I_IGNBRK(info->tty) && I_BRKINT(info->tty)) { flush_input(info->tty); flush_output(info->tty); if (info->tty->pgrp > 0) kill_pg(info->tty->pgrp, SIGINT,1); }}/* * 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_softint(void *unused){ int i; struct async_struct *info; for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) { if (clear_bit(i, rs_event)) { if (!info->tty) continue; if (clear_bit(RS_EVENT_READ_PROCESS, &info->event)) { TTY_READ_FLUSH(info->tty); } if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { wake_up_interruptible(&info->tty->write_q.proc_list); } if (clear_bit(RS_EVENT_HANGUP, &info->event)) { tty_hangup(info->tty); wake_up_interruptible(&info->open_wait); info->flags &= ~(ASYNC_NORMAL_ACTIVE| ASYNC_CALLOUT_ACTIVE); } if (clear_bit(RS_EVENT_BREAK, &info->event)) handle_rs_break(info); if (clear_bit(RS_EVENT_OPEN_WAKEUP, &info->event)) { wake_up_interruptible(&info->open_wait); } } }}/* * This subroutine is called when the RS_TIMER goes off. It is used * by the serial driver to run the rs_interrupt routine at certain * intervals, either because a serial interrupt might have been lost, * or because (in the case of IRQ=0) the serial port does not have an * interrupt, and is being checked only via the timer interrupts. */static void rs_timer(void){ int i, mask; int timeout = 0; for (i = 0, mask = 1; mask <= IRQ_active; i++, mask <<= 1) { if ((mask & IRQ_active) && (IRQ_timer[i] <= jiffies)) { IRQ_active &= ~mask; cli();#ifdef SERIAL_DEBUG_TIMER printk("rs_timer: rs_interrupt(%d)...", i);#endif rs_interrupt(i); sti(); } if (mask & IRQ_active) { if (!timeout || (IRQ_timer[i] < timeout)) timeout = IRQ_timer[i]; } } if (timeout) { timer_table[RS_TIMER].expires = timeout; timer_active |= 1 << RS_TIMER; }}/* * --------------------------------------------------------------- * 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; struct sigaction sa; sa.sa_handler = rs_probe; sa.sa_flags = (SA_INTERRUPT); sa.sa_mask = 0; sa.sa_restorer = NULL; for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { if (!(mask & dontgrab) && !irqaction(i, &sa)) { 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); }}/* * 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. */static void figure_IRQ_timeout(int irq){ struct async_struct *info; int timeout = 6000; /* 60 seconds === a long time :-) */ info = IRQ_ports[irq]; if (!info) { IRQ_timeout[irq] = 6000; 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, int get_irq){ unsigned short ICP; unsigned long flags; struct sigaction sa; int retval; if (info->flags & ASYNC_INITIALIZED) return 0; if (!info->port || !info->type) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); return 0; } save_flags(flags); cli();#ifdef SERIAL_DEBUG_OPEN printk("starting up ttys%d (irq %d)...", info->line, info->irq);#endif /* * Allocate the IRQ if necessary */ if (get_irq && info->irq && !IRQ_ports[info->irq]) { sa.sa_handler = rs_interrupt; sa.sa_flags = (SA_INTERRUPT); sa.sa_mask = 0; sa.sa_restorer = NULL; retval = irqaction(info->irq,&sa); if (retval) { restore_flags(flags); return retval; } } /* * Clear the FIFO buffers and disable them * (they will be reenabled in change_speed()) */ 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; /* * 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); /* * Now, initialize the UART */ serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ if (info->flags & ASYNC_FOURPORT) serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); else serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); /* * Finally, enable interrupts */#ifdef ISR_HACK info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; serial_outp(info, UART_IER, info->IER); /* enable interrupts */#else info->IER = (UART_IER_MSI | UART_IER_RLSI | UART_IER_THRI | UART_IER_RDI); serial_outp(info, UART_IER, info->IER); /* enable all intrs */#endif 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); /* * Set up parity check flag */ if (info->tty && info->tty->termios && I_INPCK(info->tty)) info->read_status_mask = (UART_LSR_OE | UART_LSR_BI | UART_LSR_FE | UART_LSR_PE); else info->read_status_mask = (UART_LSR_OE | UART_LSR_BI | UART_LSR_FE); /* * 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... */ IRQ_active |= 1 << info->irq; figure_RS_timer(); /* * and set the speed of the serial port */ change_speed(info->line); 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, int do_free_irq){ unsigned long flags; if (!(info->flags & ASYNC_INITIALIZED)) return;#ifdef SERIAL_DEBUG_OPEN printk("Shutting down serial port %d (irq %d)....", info->line, info->irq);#endif save_flags(flags); cli(); /* Disable interrupts */ /* * First unlink the serial port from the IRQ chain... */ if (info->next_port) info->next_port->prev_port = info->prev_port; if (info->prev_port) info->prev_port->next_port = info->next_port; else IRQ_ports[info->irq] = info->next_port; figure_IRQ_timeout(info->irq); /* * Free the IRQ, if necessary */ if (do_free_irq && info->irq && !IRQ_ports[info->irq]) free_irq(info->irq); info->IER = 0; serial_outp(info, UART_IER, 0x00); /* disable all intrs */ if (info->flags & ASYNC_FOURPORT) { /* reset interrupts on the AST Fourport board */ (void) inb((info->port & 0xFE0) | 0x01F); } if (info->tty && !(info->tty->termios->c_cflag & HUPCL)) serial_outp(info, UART_MCR, UART_MCR_DTR); else /* reset DTR,RTS,OUT_2 */ serial_outp(info, UART_MCR, 0x00); /* disable FIFO's */ serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); (void)serial_in(info, UART_RX); /* read data port to reset things */ if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); info->flags &= ~ASYNC_INITIALIZED; restore_flags(flags);}/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static void change_speed(unsigned int line){ struct async_struct * info; unsigned short port; int quot = 0; unsigned cflag,cval,mcr,fcr; int i; if (line >= NR_PORTS) return; info = rs_table + line; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; if (!(port = info->port)) return; i = cflag & CBAUD; if (i == 15) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) i += 1; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) i += 2; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) quot = info->custom_divisor; } if (quot) { info->timeout = ((info->xmit_fifo_size*HZ*15*quot) / info->baud_base) + 2; } else if (baud_table[i] == 134) { quot = (2*info->baud_base / 269); info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; } else if (baud_table[i]) { quot = info->baud_base / baud_table[i]; info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2; } else { quot = 0; info->timeout = 0; } cli(); mcr = serial_in(info, UART_MCR); if (quot) { serial_out(info, UART_MCR, mcr | UART_MCR_DTR); } else { serial_out(info, UART_MCR, mcr & ~UART_MCR_DTR); sti(); return; } sti(); /* byte size and parity */ cval = cflag & (CSIZE | CSTOPB); cval >>= 4; if (cflag & PARENB) cval |= UART_LCR_PARITY; if (!(cflag & PARODD)) cval |= UART_LCR_EPAR; if (info->type == PORT_16550A) { if ((info->baud_base / quot) < 2400) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; else fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; } else fcr = 0; cli(); serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */ serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */ serial_outp(info, UART_LCR, cval); /* reset DLAB */ serial_outp(info, UART_FCR, fcr); /* set fcr */ sti();}/* * ------------------------------------------------------------ * rs_write() and friends * ------------------------------------------------------------ *//* * This routine is used by rs_write to restart transmitter interrupts, * which are disabled after we have a transmitter interrupt which went * unacknowledged because we had run out of data to transmit. * * Note: this subroutine must be called with the interrupts *off* */static inline void restart_port(struct async_struct *info){ struct tty_queue * queue; int head, tail, count; if (!info) return; if (serial_inp(info, UART_LSR) & UART_LSR_THRE) { if (info->x_char) { serial_outp(info, UART_TX, info->x_char); info->x_char = 0; } else { queue = &info->tty->write_q; head = queue->head; tail = queue->tail; count = info->xmit_fifo_size; while (count--) { if (tail == head) break; serial_outp(info, UART_TX, queue->buf[tail++]); tail &= TTY_BUF_SIZE-1; } queue->tail = tail; } }} /* * This routine gets called when tty_write has put something into * the write_queue. */void rs_write(struct tty_struct * tty){ struct async_struct *info; if (!tty || tty->stopped || tty->hw_stopped) return; info = rs_table + DEV_TO_SL(tty->line); cli();
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -