📄 serial_amba.c
字号:
info->tty->hw_stopped = 0; ambauart_enable_tx_interrupt(info); ambauart_event(info, EVT_WRITE_WAKEUP); } } else { if (!status) { info->tty->hw_stopped = 1; ambauart_disable_tx_interrupt(info); } } } } wake_up_interruptible(&info->delta_msr_wait);}static void ambauart_int(int irq, void *dev_id, struct pt_regs *regs){ struct amba_info *info = dev_id; unsigned int status, pass_counter = 0;#if DEBUG_LEDS // tell the world set_leds(get_leds() | RED_LED);#endif status = UART_GET_INT_STATUS(info->port); do { /* * FIXME: what about clearing the interrupts? */ if (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS))#ifdef SUPPORT_SYSRQ ambauart_rx_chars(info, regs);#else ambauart_rx_chars(info);#endif if (status & AMBA_UARTIIR_TIS) ambauart_tx_chars(info); if (status & AMBA_UARTIIR_MIS) ambauart_modem_status(info); if (pass_counter++ > AMBA_ISR_PASS_LIMIT) break; status = UART_GET_INT_STATUS(info->port); } while (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS | AMBA_UARTIIR_TIS));#if DEBUG_LEDS // tell the world set_leds(get_leds() & ~RED_LED);#endif}static void ambauart_tasklet_action(unsigned long data){ struct amba_info *info = (struct amba_info *)data; struct tty_struct *tty; tty = info->tty; if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event)) return; if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait);}static int ambauart_startup(struct amba_info *info){ unsigned long flags; unsigned long page; int retval = 0; page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; save_flags(flags); cli(); if (info->flags & ASYNC_INITIALIZED) { free_page(page); goto errout; } if (info->xmit.buf) free_page(page); else info->xmit.buf = (unsigned char *) page; /* * Allocate the IRQ */ retval = request_irq(info->port->irq, ambauart_int, 0, "amba", info); if (retval) { if (capable(CAP_SYS_ADMIN)) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); retval = 0; } goto errout; } info->mctrl = 0; if (info->tty->termios->c_cflag & CBAUD) info->mctrl = TIOCM_RTS | TIOCM_DTR; info->port->set_mctrl(info->port, info->mctrl); /* * initialise the old status of the modem signals */ info->old_status = UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY; /* * Finally, enable interrupts */ ambauart_enable_rx_interrupt(info); if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit.head = info->xmit.tail = 0; /* * Set up the tty->alt_speed kludge */ if (info->tty) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) info->tty->alt_speed = 115200; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) info->tty->alt_speed = 230400; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; } /* * and set the speed of the serial port */ ambauart_change_speed(info, 0); info->flags |= ASYNC_INITIALIZED; restore_flags(flags); return 0;errout: restore_flags(flags); return retval;}/* * 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 ambauart_shutdown(struct amba_info *info){ unsigned long flags; if (!(info->flags & ASYNC_INITIALIZED)) return; 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 woken up */ wake_up_interruptible(&info->delta_msr_wait); /* * Free the IRQ */ free_irq(info->port->irq, info); if (info->xmit.buf) { unsigned long pg = (unsigned long) info->xmit.buf; info->xmit.buf = NULL; free_page(pg); } /* * disable all interrupts, disable the port */ UART_PUT_CR(info->port, 0); /* disable break condition and fifos */ UART_PUT_LCRH(info->port, UART_GET_LCRH(info->port) & ~(AMBA_UARTLCR_H_BRK | AMBA_UARTLCR_H_FEN)); if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) info->mctrl &= ~(TIOCM_DTR|TIOCM_RTS); info->port->set_mctrl(info->port, info->mctrl); /* kill off our tasklet */ tasklet_kill(&info->tlet); if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); info->flags &= ~ASYNC_INITIALIZED; restore_flags(flags);}static void ambauart_change_speed(struct amba_info *info, struct termios *old_termios){ unsigned int lcr_h, baud, quot, cflag, old_cr, bits; unsigned long flags; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag;#if DEBUG printk("ambauart_set_cflag(0x%x) called\n", cflag);#endif /* byte size and parity */ switch (cflag & CSIZE) { case CS5: lcr_h = AMBA_UARTLCR_H_WLEN_5; bits = 7; break; case CS6: lcr_h = AMBA_UARTLCR_H_WLEN_6; bits = 8; break; case CS7: lcr_h = AMBA_UARTLCR_H_WLEN_7; bits = 9; break; default: lcr_h = AMBA_UARTLCR_H_WLEN_8; bits = 10; break; // CS8 } if (cflag & CSTOPB) { lcr_h |= AMBA_UARTLCR_H_STP2; bits ++; } if (cflag & PARENB) { lcr_h |= AMBA_UARTLCR_H_PEN; bits++; if (!(cflag & PARODD)) lcr_h |= AMBA_UARTLCR_H_EPS; } if (info->port->fifosize > 1) lcr_h |= AMBA_UARTLCR_H_FEN; do { /* Determine divisor based on baud rate */ baud = tty_get_baud_rate(info->tty); if (!baud) baud = 9600; if (baud == 38400 && ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) quot = info->state->custom_divisor; else quot = (info->port->uartclk / (16 * baud)) - 1; if (!quot && old_termios) { info->tty->termios->c_cflag &= ~CBAUD; info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); old_termios = NULL; } } while (quot == 0 && old_termios); /* As a last resort, if the quotient is zero, default to 9600 bps */ if (!quot) quot = (info->port->uartclk / (16 * 9600)) - 1; info->timeout = (info->port->fifosize * HZ * bits * quot) / (info->port->uartclk / 16); info->timeout += HZ/50; /* Add .02 seconds of slop */ if (cflag & CRTSCTS) info->flags |= ASYNC_CTS_FLOW; else info->flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) info->flags &= ~ASYNC_CHECK_CD; else info->flags |= ASYNC_CHECK_CD; /* * Set up parity check flag */#define RELEVENT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) info->read_status_mask = AMBA_UARTRSR_OE; if (I_INPCK(info->tty)) info->read_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) info->read_status_mask |= AMBA_UARTRSR_BE; /* * Characters to ignore */ info->ignore_status_mask = 0; if (I_IGNPAR(info->tty)) info->ignore_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; if (I_IGNBRK(info->tty)) { info->ignore_status_mask |= AMBA_UARTRSR_BE; /* * If we're ignoring parity and break indicators, * ignore overruns to (for real raw support). */ if (I_IGNPAR(info->tty)) info->ignore_status_mask |= AMBA_UARTRSR_OE; } /* first, disable everything */ save_flags(flags); cli(); old_cr = UART_GET_CR(info->port) &= ~AMBA_UARTCR_MSIE; if ((info->flags & ASYNC_HARDPPS_CD) || (cflag & CRTSCTS) || !(cflag & CLOCAL)) old_cr |= AMBA_UARTCR_MSIE; UART_PUT_CR(info->port, 0); restore_flags(flags); /* Set baud rate */ UART_PUT_LCRM(info->port, ((quot & 0xf00) >> 8)); UART_PUT_LCRL(info->port, (quot & 0xff)); /* * ----------v----------v----------v----------v----- * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L * ----------^----------^----------^----------^----- */ UART_PUT_LCRH(info->port, lcr_h); UART_PUT_CR(info->port, old_cr);}static void ambauart_put_char(struct tty_struct *tty, u_char ch){ struct amba_info *info = tty->driver_data; unsigned long flags; if (!tty || !info->xmit.buf) return; save_flags(flags); cli(); if (CIRC_SPACE(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE) != 0) { info->xmit.buf[info->xmit.head] = ch; info->xmit.head = (info->xmit.head + 1) & (AMBA_XMIT_SIZE - 1); } restore_flags(flags);}static void ambauart_flush_chars(struct tty_struct *tty){ struct amba_info *info = tty->driver_data; unsigned long flags; if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped || !info->xmit.buf) return; save_flags(flags); cli(); ambauart_enable_tx_interrupt(info); restore_flags(flags);}static int ambauart_write(struct tty_struct *tty, int from_user, const u_char * buf, int count){ struct amba_info *info = tty->driver_data; unsigned long flags; int c, ret = 0; if (!tty || !info->xmit.buf || !tmp_buf) return 0; save_flags(flags); if (from_user) { down(&tmp_buf_sem); while (1) { int c1; c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE); if (count < c) c = count; if (c <= 0) break; c -= copy_from_user(tmp_buf, buf, c); if (!c) { if (!ret) ret = -EFAULT; break; } cli(); c1 = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE); if (c1 < c) c = c1; memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); info->xmit.head = (info->xmit.head + c) & (AMBA_XMIT_SIZE - 1); restore_flags(flags); buf += c; count -= c; ret += c; } up(&tmp_buf_sem); } else { cli(); while (1) { c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE); if (count < c) c = count; if (c <= 0) break; memcpy(info->xmit.buf + info->xmit.head, buf, c); info->xmit.head = (info->xmit.head + c) & (AMBA_XMIT_SIZE - 1); buf += c; count -= c; ret += c; } restore_flags(flags); } if (info->xmit.head != info->xmit.tail && !tty->stopped && !tty->hw_stopped) ambauart_enable_tx_interrupt(info); return ret;}static int ambauart_write_room(struct tty_struct *tty){ struct amba_info *info = tty->driver_data; return CIRC_SPACE(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE);}static int ambauart_chars_in_buffer(struct tty_struct *tty){ struct amba_info *info = tty->driver_data; return CIRC_CNT(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE);}static void ambauart_flush_buffer(struct tty_struct *tty){ struct amba_info *info = tty->driver_data; unsigned long flags;#if DEBUG printk("ambauart_flush_buffer(%d) called\n", MINOR(tty->device) - tty->driver.minor_start);#endif save_flags(flags); cli(); info->xmit.head = info->xmit.tail = 0; restore_flags(flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty);}/* * This function is used to send a high-priority XON/XOFF character to * the device */static void ambauart_send_xchar(struct tty_struct *tty, char ch){ struct amba_info *info = tty->driver_data; info->x_char = ch; if (ch) ambauart_enable_tx_interrupt(info);}static void ambauart_throttle(struct tty_struct *tty){ struct amba_info *info = tty->driver_data; unsigned long flags; if (I_IXOFF(tty)) ambauart_send_xchar(tty, STOP_CHAR(tty)); if (tty->termios->c_cflag & CRTSCTS) { save_flags(flags); cli(); info->mctrl &= ~TIOCM_RTS; info->port->set_mctrl(info->port, info->mctrl); restore_flags(flags); }}static void ambauart_unthrottle(struct tty_struct *tty){ struct amba_info *info = (struct amba_info *) tty->driver_data; unsigned long flags; if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else ambauart_send_xchar(tty, START_CHAR(tty)); } if (tty->termios->c_cflag & CRTSCTS) { save_flags(flags); cli(); info->mctrl |= TIOCM_RTS; info->port->set_mctrl(info->port, info->mctrl); restore_flags(flags); }}static int get_serial_info(struct amba_info *info, struct serial_struct *retinfo)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -