📄 serial.c
字号:
* --------------------------------------------------------------- */#ifndef CONFIG_COLDFIRE/* * 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); }}#endif /* CONFIG_COLDFIRE *//* * 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[IRQMASK(irq)]; if (!info) { IRQ_timeout[IRQMASK(irq)] = 60*HZ; return; } while (info) { if (info->timeout < timeout) timeout = info->timeout; info = info->next_port; } if (!irq) timeout = timeout / 2; IRQ_timeout[IRQMASK(irq)] = timeout ? timeout : 1;}static int startup(struct async_struct * info){ unsigned int 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(flags); cli(); 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; } #ifndef CONFIG_COLDFIRE /* * Allocate the IRQ if necessary */ if (info->irq && (!IRQ_ports[IRQMASK(info->irq)] || !IRQ_ports[IRQMASK(info->irq)]->next_port)) { if (IRQ_ports[IRQMASK(info->irq)]) { free_irq(info->irq, NULL); if (rs_multiport[IRQMASK(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; } }#endif /* * 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[IRQMASK(info->irq)]; if (info->next_port) info->next_port->prev_port = info; IRQ_ports[IRQMASK(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 save_flags(flags); cli(); /* Disable interrupts */ /* * clear delta_msr_wait queue to avoid mem leaks: we may free the irq * here so the queue might never be waken up */ wake_up_interruptible(&info->delta_msr_wait); /* * 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[IRQMASK(info->irq)] = info->next_port; figure_IRQ_timeout(info->irq); #ifndef CONFIG_COLDFIRE /* * Free the IRQ, if necessary */ if (info->irq && (!IRQ_ports[IRQMASK(info->irq)] || !IRQ_ports[IRQMASK(info->irq)]->next_port)) { if (IRQ_ports[IRQMASK(info->irq)]) { free_irq(info->irq, NULL); retval = request_irq(info->irq, rs_interrupt_single, IRQ_T(info), "serial", NULL); if (retval) printk("serial shutdown: request_irq: error %d" " Couldn't reacquire IRQ.\n", retval); } else free_irq(info->irq, NULL); }#endif /* CONFIG_COLDFIRE */ if (info->xmit_buf) { free_page((unsigned long) info->xmit_buf); info->xmit_buf = 0; } 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)) { info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); info->MCR_noint &= ~(UART_MCR_DTR|UART_MCR_RTS); } serial_outp(info, UART_MCR, info->MCR_noint); /* 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);#ifdef CONFIG_LEDMAN ledman_cmd(LEDMAN_CMD_OFF, info->line ? LEDMAN_COM2_DCD : LEDMAN_COM1_DCD);#endif 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(struct async_struct *info){ unsigned int port; int quot = 0; unsigned cflag,cval,fcr; int i; unsigned long flags; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; if (!(port = info->port)) return; i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; if (i < 1 || i > 2) info->tty->termios->c_cflag &= ~CBAUDEX; else i += 15; } 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; } if (quot) { info->MCR |= UART_MCR_DTR; info->MCR_noint |= UART_MCR_DTR; save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); restore_flags(flags); } else { info->MCR &= ~UART_MCR_DTR; info->MCR_noint &= ~UART_MCR_DTR; save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); restore_flags(flags); return; } /* byte size and parity */ switch (cflag & CSIZE) { case CS5: cval = 0x00; break; case CS6: cval = 0x01; break; case CS7: cval = 0x02; break; case CS8: cval = 0x03; break; default: cval = 0x00; break; /* too keep GCC shut... */ } if (cflag & CSTOPB) { cval |= 0x04; } 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 if (info->type == PORT_16650) { /* * On the 16650, we disable the FIFOs altogether * because of a design bug in how the implement * things. We could support it by completely changing * how we handle the interrupt driver, but not today.... * * N.B. Because there's no way to set a FIFO trigger * at 1 char, we'd probably disable at speed below * 2400 baud anyway... */ fcr = 0; } else fcr = 0; /* CTS flow control flag and modem status interrupts */ info->IER &= ~UART_IER_MSI; if (cflag & CRTSCTS) { info->flags |= ASYNC_CTS_FLOW; info->IER |= UART_IER_MSI; } else info->flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) info->flags &= ~ASYNC_CHECK_CD; else { info->flags |= ASYNC_CHECK_CD; info->IER |= UART_IER_MSI; } serial_out(info, UART_IER, info->IER); /* * Set up parity check flag */#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (I_INPCK(info->tty)) info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) info->read_status_mask |= UART_LSR_BI; info->ignore_status_mask = 0;#if 0 /* This should be safe, but for some broken bits of hardware... */ if (I_IGNPAR(info->tty)) { info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; info->read_status_mask |= UART_LSR_PE | UART_LSR_FE; }#endif if (I_IGNBRK(info->tty)) { info->ignore_status_mask |= UART_LSR_BI; info->read_status_mask |= UART_LSR_BI; /* * If we're ignore parity and break indicators, ignore * overruns too. (For real raw support). */ if (I_IGNPAR(info->tty)) { info->ignore_status_mask |= UART_LSR_OE | \ UART_LSR_PE | UART_LSR_FE; info->read_status_mask |= UART_LSR_OE | \ UART_LSR_PE | UART_LSR_FE; } } save_flags(flags); 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 */ restore_flags(flags);}static void rs_put_char(struct tty_struct *tty, unsigned char ch){ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_put_char")) return; if (!tty || !info->xmit_buf) return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -