📄 serial.c
字号:
if (info->xmit.buf) { unsigned long pg = (unsigned long) info->xmit.buf; info->xmit.buf = 0; free_page(pg); } info->IER = 0; serial_outp(info, UART_IER, 0x00); /* disable all intrs */#ifdef CONFIG_SERIAL_MANY_PORTS if (info->flags & ASYNC_FOURPORT) { /* reset interrupts on the AST Fourport board */ (void) inb((info->port & 0xFE0) | 0x01F); info->MCR |= UART_MCR_OUT1; } else#endif info->MCR &= ~UART_MCR_OUT2; info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ /* disable break condition */ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); serial_outp(info, UART_MCR, info->MCR); /* disable FIFO's */ serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); serial_outp(info, UART_FCR, 0);#ifdef CONFIG_SERIAL_RSA /* * Reset the RSA board back to 115kbps compat mode. */ if ((state->type == PORT_RSA) && (state->baud_base == SERIAL_RSA_BAUD_BASE && disable_rsa(info))) state->baud_base = SERIAL_RSA_BAUD_BASE_LO;#endif (void)serial_in(info, UART_RX); /* read data port to reset things */ if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); if (uart_config[info->state->type].flags & UART_STARTECH) { /* Arrange to enter sleep mode */ serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, UART_EFR_ECB); serial_outp(info, UART_LCR, 0); serial_outp(info, UART_IER, UART_IERX_SLEEP); serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, 0); serial_outp(info, UART_LCR, 0); } if (info->state->type == PORT_16750) { /* Arrange to enter sleep mode */ serial_outp(info, UART_IER, UART_IERX_SLEEP); } info->flags &= ~ASYNC_INITIALIZED; restore_flags(flags);}#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };static int tty_get_baud_rate(struct tty_struct *tty){ struct async_struct * info = (struct async_struct *)tty->driver_data; unsigned int cflag, i; cflag = tty->termios->c_cflag; i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; if (i < 1 || i > 2) 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; } return baud_table[i];}#endif/* * 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, struct termios *old_termios){ int quot = 0, baud_base, baud; unsigned cflag, cval, fcr = 0; int bits; unsigned long flags; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; if (!CONFIGURED_SERIAL_PORT(info)) return; /* byte size and parity */ switch (cflag & CSIZE) { case CS5: cval = 0x00; bits = 7; break; case CS6: cval = 0x01; bits = 8; break; case CS7: cval = 0x02; bits = 9; break; case CS8: cval = 0x03; bits = 10; break; /* Never happens, but GCC is too dumb to figure it out */ default: cval = 0x00; bits = 7; break; } if (cflag & CSTOPB) { cval |= 0x04; bits++; } if (cflag & PARENB) { cval |= UART_LCR_PARITY; bits++; } if (!(cflag & PARODD)) cval |= UART_LCR_EPAR;#ifdef CMSPAR if (cflag & CMSPAR) cval |= UART_LCR_SPAR;#endif /* Determine divisor based on baud rate */ baud = tty_get_baud_rate(info->tty); if (!baud) baud = 9600; /* B0 transition handled in rs_set_termios */#ifdef CONFIG_SERIAL_RSA if ((info->state->type == PORT_RSA) && (info->state->baud_base != SERIAL_RSA_BAUD_BASE) && enable_rsa(info)) info->state->baud_base = SERIAL_RSA_BAUD_BASE;#endif baud_base = info->state->baud_base; if (info->state->type == PORT_16C950) { if (baud <= baud_base) serial_icr_write(info, UART_TCR, 0); else if (baud <= 2*baud_base) { serial_icr_write(info, UART_TCR, 0x8); baud_base = baud_base * 2; } else if (baud <= 4*baud_base) { serial_icr_write(info, UART_TCR, 0x4); baud_base = baud_base * 4; } else serial_icr_write(info, UART_TCR, 0); } if (baud == 38400 && ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) quot = info->state->custom_divisor; else { if (baud == 134) /* Special case since 134 is really 134.5 */ quot = (2*baud_base / 269); else if (baud) quot = baud_base / baud; } /* If the quotient is zero refuse the change */ if (!quot && old_termios) { info->tty->termios->c_cflag &= ~CBAUD; info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); 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 { if (baud == 134) /* Special case since 134 is really 134.5 */ quot = (2*baud_base / 269); else if (baud) quot = baud_base / baud; } } /* As a last resort, if the quotient is zero, default to 9600 bps */ if (!quot) quot = baud_base / 9600; /* * Work around a bug in the Oxford Semiconductor 952 rev B * chip which causes it to seriously miscalculate baud rates * when DLL is 0. */ if (((quot & 0xFF) == 0) && (info->state->type == PORT_16C950) && (info->state->revision == 0x5201)) quot++; info->quot = quot; info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); info->timeout += HZ/50; /* Add .02 seconds of slop */ /* Set up FIFO's */ if (uart_config[info->state->type].flags & UART_USE_FIFO) { if ((info->state->baud_base / quot) < 2400) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;#ifdef CONFIG_SERIAL_RSA else if (info->state->type == PORT_RSA) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14;#endif else fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; } if (info->state->type == PORT_16750) fcr |= UART_FCR7_64BYTE; /* CTS flow control flag and modem status interrupts */ info->IER &= ~UART_IER_MSI; if (info->flags & ASYNC_HARDPPS_CD) 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; /* * Characters to ignore */ info->ignore_status_mask = 0; if (I_IGNPAR(info->tty)) info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; if (I_IGNBRK(info->tty)) { info->ignore_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; } /* * !!! ignore all characters if CREAD is not set */ if ((cflag & CREAD) == 0) info->ignore_status_mask |= UART_LSR_DR; save_flags(flags); cli(); if (uart_config[info->state->type].flags & UART_STARTECH) { serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, (cflag & CRTSCTS) ? UART_EFR_CTS : 0); } 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 */ if (info->state->type == PORT_16750) serial_outp(info, UART_FCR, fcr); /* set fcr */ serial_outp(info, UART_LCR, cval); /* reset DLAB */ info->LCR = cval; /* Save LCR */ if (info->state->type != PORT_16750) { if (fcr & UART_FCR_ENABLE_FIFO) { /* emulated UARTs (Lucent Venus 167x) need two steps */ serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); } 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; save_flags(flags); cli(); if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) { restore_flags(flags); return; } info->xmit.buf[info->xmit.head] = ch; info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); restore_flags(flags);}static void rs_flush_chars(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_flush_chars")) return; if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped || !info->xmit.buf) return; save_flags(flags); cli(); info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); restore_flags(flags);}static int rs_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count){ int c, ret = 0; struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_write")) return 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, SERIAL_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, SERIAL_XMIT_SIZE); if (c1 < c) c = c1; memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); info->xmit.head = ((info->xmit.head + c) & (SERIAL_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, SERIAL_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) & (SERIAL_XMIT_SIZE-1)); buf += c; count -= c; ret += c; } restore_flags(flags); } if (info->xmit.head != info->xmit.tail && !tty->stopped && !tty->hw_stopped && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); } return ret;}static int rs_write_room(struct tty_struct *tty){ struct async_struct *info = (struct async_struct *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_write_room")) return 0; return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);}static int rs_chars_in_buffer(struct tty_struct *tty){ struct async_struct *info = (struct async_struct *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) return 0; return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);}static void rs_flush_buffer(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_flush_buffer")) return; save_flags(flags); cli(); info->xmit.head = info->xmit.tail = 0; restore_flags(flags); wake_up_interruptible(&tty->write_wait);#ifdef SERIAL_HAVE_POLL_WAIT wake_up_interruptible(&tty->poll_wait);#endif 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 rs_send_xchar(struct tty_struct *tty, char ch){ struct async_struct *info = (struct async_struct *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_send_char")) return; info->x_char = ch; if (ch) { /* Make sure transmit interrupts are on */ info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); }}/* * ------------------------------------------------------------ * rs_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static void rs_throttle(struct tty_struct * tty){ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags;#ifdef SERIAL_DEBUG_THROTTLE char buf[64]; printk("throttle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty));#endif if (serial_paranoia_check(info, tty->device, "rs_throttle")) return; if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty)); if (tty->termios->c_cflag & CRTSCTS) info->MCR &= ~UART_MCR_RTS; save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); restore_flags(flags);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -