📄 su.c
字号:
else if (baud) quot = info->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->custom_divisor; else { if (baud == 134) /* Special case since 134 is really 134.5 */ quot = (2*info->baud_base / 269); else if (baud) quot = info->baud_base / baud; } } /* As a last resort, if the quotient is zero, default to 9600 bps */ if (!quot) quot = info->baud_base / 9600; info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / info->baud_base); info->timeout += HZ/50; /* Add .02 seconds of slop */ /* Set up FIFO's */ if (uart_config[info->type].flags & UART_USE_FIFO) { if ((info->baud_base / quot) < 9600) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; else fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; } if (info->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 (info->cflag & CRTSCTS) { info->flags |= ASYNC_CTS_FLOW; info->IER |= UART_IER_MSI; } else info->flags &= ~ASYNC_CTS_FLOW; if (info->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 */ if (info->tty) {#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 ((info->cflag & CREAD) == 0) info->ignore_status_mask |= UART_LSR_DR; } save_flags(flags); cli(); if (uart_config[info->type].flags & UART_STARTECH) { serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, (info->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->type == PORT_16750) serial_outp(info, UART_FCR, fcr); /* set fcr */ serial_outp(info, UART_LCR, cval); /* reset DLAB */ if (info->type != PORT_16750) serial_outp(info, UART_FCR, fcr); /* set fcr */ restore_flags(flags); info->quot = quot;}static voidsu_put_char(struct tty_struct *tty, unsigned char ch){ struct su_struct *info = (struct su_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "su_put_char")) return; if (!tty || !info->xmit_buf) return; save_flags(flags); cli(); if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { restore_flags(flags); return; } info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= SERIAL_XMIT_SIZE-1; info->xmit_cnt++; restore_flags(flags);}static void su_put_char_kbd(unsigned char c){ struct su_struct *info = su_table; int lsr; if (info->port_type != SU_PORT_KBD) ++info; if (info->port_type != SU_PORT_KBD) return; do { lsr = serial_in(info, UART_LSR); } while (!(lsr & UART_LSR_THRE)); /* Send the character out. */ su_outb(info, UART_TX, c);}static voidsu_change_mouse_baud(int baud){ struct su_struct *info = su_table; if (info->port_type != SU_PORT_MS) ++info; if (info->port_type != SU_PORT_MS) return; info->cflag &= ~CBAUD; switch (baud) { case 1200: info->cflag |= B1200; break; case 2400: info->cflag |= B2400; break; case 4800: info->cflag |= B4800; break; case 9600: info->cflag |= B9600; break; default: printk("su_change_mouse_baud: unknown baud rate %d, " "defaulting to 1200\n", baud); info->cflag |= 1200; break; } change_speed(info, 0);}static voidsu_flush_chars(struct tty_struct *tty){ struct su_struct *info = (struct su_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "su_flush_chars")) return; if (info->xmit_cnt <= 0 || 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 intsu_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count){ int c, ret = 0; struct su_struct *info = (struct su_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "su_write")) return 0; if (!tty || !info->xmit_buf || !tmp_buf) return 0; save_flags(flags); if (from_user) { down(&tmp_buf_sem); while (1) { c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) break; c -= copy_from_user(tmp_buf, buf, c); if (!c) { if (!ret) ret = -EFAULT; break; } cli(); c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); info->xmit_head = ((info->xmit_head + c) & (SERIAL_XMIT_SIZE-1)); info->xmit_cnt += c; restore_flags(flags); buf += c; count -= c; ret += c; } up(&tmp_buf_sem); } else { while (1) { cli(); c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) { restore_flags(flags); break; } memcpy(info->xmit_buf + info->xmit_head, buf, c); info->xmit_head = ((info->xmit_head + c) & (SERIAL_XMIT_SIZE-1)); info->xmit_cnt += c; restore_flags(flags); buf += c; count -= c; ret += c; } } if (info->xmit_cnt && !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 intsu_write_room(struct tty_struct *tty){ struct su_struct *info = (struct su_struct *)tty->driver_data; int ret; if (serial_paranoia_check(info, tty->device, "su_write_room")) return 0; ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; if (ret < 0) ret = 0; return ret;}static intsu_chars_in_buffer(struct tty_struct *tty){ struct su_struct *info = (struct su_struct *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "su_chars_in_buffer")) return 0; return info->xmit_cnt;}static voidsu_flush_buffer(struct tty_struct *tty){ struct su_struct *info = (struct su_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "su_flush_buffer")) return; save_flags(flags); cli(); info->xmit_cnt = 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 voidsu_send_xchar(struct tty_struct *tty, char ch){ struct su_struct *info = (struct su_struct *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "su_send_char")) return; if (!(info->flags & ASYNC_INITIALIZED)) 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); }}/* * ------------------------------------------------------------ * su_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static voidsu_throttle(struct tty_struct * tty){ struct su_struct *info = (struct su_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, "su_throttle")) return; if (I_IXOFF(tty)) su_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);}static voidsu_unthrottle(struct tty_struct * tty){ struct su_struct *info = (struct su_struct *)tty->driver_data; unsigned long flags;#ifdef SERIAL_DEBUG_THROTTLE char buf[64]; printk("unthrottle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty));#endif if (serial_paranoia_check(info, tty->device, "su_unthrottle")) return; if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else su_send_xchar(tty, START_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);}/* * ------------------------------------------------------------ * su_ioctl() and friends * ------------------------------------------------------------ *//* * get_serial_info - handle TIOCGSERIAL ioctl() * * Purpose: Return standard serial struct information about * a serial port handled by this driver. * * Added: 11-May-2001 Lars Kellogg-Stedman <lars@larsshack.org> */static int get_serial_info(struct su_struct * info, struct serial_struct * retinfo){ struct serial_struct tmp; if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); tmp.type = info->type; tmp.line = info->line; tmp.port = info->port; tmp.irq = info->irq; tmp.flags = info->flags; tmp.xmit_fifo_size = info->xmit_fifo_size; tmp.baud_base = info->baud_base; tmp.close_delay = info->close_delay; tmp.closing_wait = info->closing_wait; tmp.custom_divisor = info->custom_divisor; tmp.hub6 = 0; if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; return 0;}/* * get_lsr_info - get line status register info * * Purpose: Let user call ioctl() to get info when the UART physically * is emptied. On bus types like RS485, the transmitter must * release the bus after transmitting. This must be done when * the transmit shift register is empty, not be done when the * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space. */static intget_lsr_info(struct su_struct * info, unsigned int *value){ unsigned char status; unsigned int result; unsigned long flags; save_flags(flags); cli(); status = serial_in(info, UART_LSR); restore_flags(flags); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); return put_user(result,value);}static intget_modem_info(struct su_struct * info, unsigned int *value){ unsigned char control, status; unsigned int result; unsigned long flags; control = info->MCR; save_flags(flags); cli(); status = serial_in(info, UART_MSR); restore_flags(flags); result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)#ifdef TIOCM_OUT1 | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0)#endif | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); return put_user(result,value);}static intset_modem_info(struct su_struct * info, unsigned int cmd, unsigned int *value){ unsigned int arg; unsigned long flags; if (get_user(arg, value)) return -EFAULT; switch (cmd) { case TIOCMBIS: if (arg & TIOCM_RTS) info->MCR |= UART_MCR_RTS; if (arg & TIOCM_DTR) info->MCR |= UART_MCR_DTR;#ifdef TIOCM_OUT1 if (arg & TIOCM_OUT1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -