📄 serial_core.c
字号:
static void uart_unthrottle(struct tty_struct *tty){ struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; if (I_IXOFF(tty)) { if (port->x_char) port->x_char = 0; else uart_send_xchar(tty, START_CHAR(tty)); } if (tty->termios->c_cflag & CRTSCTS) uart_set_mctrl(port, TIOCM_RTS);}static int uart_get_info(struct uart_state *state, struct serial_struct __user *retinfo){ struct uart_port *port = state->port; struct serial_struct tmp; memset(&tmp, 0, sizeof(tmp)); tmp.type = port->type; tmp.line = port->line; tmp.port = port->iobase; if (HIGH_BITS_OFFSET) tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; tmp.irq = port->irq; tmp.flags = port->flags; tmp.xmit_fifo_size = port->fifosize; tmp.baud_base = port->uartclk / 16; tmp.close_delay = state->close_delay / 10; tmp.closing_wait = state->closing_wait == USF_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : state->closing_wait / 10; tmp.custom_divisor = port->custom_divisor; tmp.hub6 = port->hub6; tmp.io_type = port->iotype; tmp.iomem_reg_shift = port->regshift; tmp.iomem_base = (void *)port->mapbase; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; return 0;}static int uart_set_info(struct uart_state *state, struct serial_struct __user *newinfo){ struct serial_struct new_serial; struct uart_port *port = state->port; unsigned long new_port; unsigned int change_irq, change_port, closing_wait; unsigned int old_custom_divisor, close_delay; upf_t old_flags, new_flags; int retval = 0; if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) return -EFAULT; new_port = new_serial.port; if (HIGH_BITS_OFFSET) new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; new_serial.irq = irq_canonicalize(new_serial.irq); close_delay = new_serial.close_delay * 10; closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? USF_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; /* * This semaphore protects state->count. It is also * very useful to prevent opens. Also, take the * port configuration semaphore to make sure that a * module insertion/removal doesn't change anything * under us. */ mutex_lock(&state->mutex); change_irq = new_serial.irq != port->irq; /* * Since changing the 'type' of the port changes its resource * allocations, we should treat type changes the same as * IO port changes. */ change_port = new_port != port->iobase || (unsigned long)new_serial.iomem_base != port->mapbase || new_serial.hub6 != port->hub6 || new_serial.io_type != port->iotype || new_serial.iomem_reg_shift != port->regshift || new_serial.type != port->type; old_flags = port->flags; new_flags = new_serial.flags; old_custom_divisor = port->custom_divisor; if (!capable(CAP_SYS_ADMIN)) { retval = -EPERM; if (change_irq || change_port || (new_serial.baud_base != port->uartclk / 16) || (close_delay != state->close_delay) || (closing_wait != state->closing_wait) || (new_serial.xmit_fifo_size != port->fifosize) || (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0)) goto exit; port->flags = ((port->flags & ~UPF_USR_MASK) | (new_flags & UPF_USR_MASK)); port->custom_divisor = new_serial.custom_divisor; goto check_and_exit; } /* * Ask the low level driver to verify the settings. */ if (port->ops->verify_port) retval = port->ops->verify_port(port, &new_serial); if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || (new_serial.baud_base < 9600)) retval = -EINVAL; if (retval) goto exit; if (change_port || change_irq) { retval = -EBUSY; /* * Make sure that we are the sole user of this port. */ if (uart_users(state) > 1) goto exit; /* * We need to shutdown the serial port at the old * port/type/irq combination. */ uart_shutdown(state); } if (change_port) { unsigned long old_iobase, old_mapbase; unsigned int old_type, old_iotype, old_hub6, old_shift; old_iobase = port->iobase; old_mapbase = port->mapbase; old_type = port->type; old_hub6 = port->hub6; old_iotype = port->iotype; old_shift = port->regshift; /* * Free and release old regions */ if (old_type != PORT_UNKNOWN) port->ops->release_port(port); port->iobase = new_port; port->type = new_serial.type; port->hub6 = new_serial.hub6; port->iotype = new_serial.io_type; port->regshift = new_serial.iomem_reg_shift; port->mapbase = (unsigned long)new_serial.iomem_base; /* * Claim and map the new regions */ if (port->type != PORT_UNKNOWN) { retval = port->ops->request_port(port); } else { /* Always success - Jean II */ retval = 0; } /* * If we fail to request resources for the * new port, try to restore the old settings. */ if (retval && old_type != PORT_UNKNOWN) { port->iobase = old_iobase; port->type = old_type; port->hub6 = old_hub6; port->iotype = old_iotype; port->regshift = old_shift; port->mapbase = old_mapbase; retval = port->ops->request_port(port); /* * If we failed to restore the old settings, * we fail like this. */ if (retval) port->type = PORT_UNKNOWN; /* * We failed anyway. */ retval = -EBUSY; } } port->irq = new_serial.irq; port->uartclk = new_serial.baud_base * 16; port->flags = (port->flags & ~UPF_CHANGE_MASK) | (new_flags & UPF_CHANGE_MASK); port->custom_divisor = new_serial.custom_divisor; state->close_delay = close_delay; state->closing_wait = closing_wait; port->fifosize = new_serial.xmit_fifo_size; if (state->info->tty) state->info->tty->low_latency = (port->flags & UPF_LOW_LATENCY) ? 1 : 0; check_and_exit: retval = 0; if (port->type == PORT_UNKNOWN) goto exit; if (state->info->flags & UIF_INITIALIZED) { if (((old_flags ^ port->flags) & UPF_SPD_MASK) || old_custom_divisor != port->custom_divisor) { /* * If they're setting up a custom divisor or speed, * instead of clearing it, then bitch about it. No * need to rate-limit; it's CAP_SYS_ADMIN only. */ if (port->flags & UPF_SPD_MASK) { char buf[64]; printk(KERN_NOTICE "%s sets custom speed on %s. This " "is deprecated.\n", current->comm, tty_name(state->info->tty, buf)); } uart_change_speed(state, NULL); } } else retval = uart_startup(state, 1); exit: mutex_unlock(&state->mutex); return retval;}/* * uart_get_lsr_info - get line status register info. * Note: uart_ioctl protects us against hangups. */static int uart_get_lsr_info(struct uart_state *state, unsigned int __user *value){ struct uart_port *port = state->port; unsigned int result; result = port->ops->tx_empty(port); /* * If we're about to load something into the transmit * register, we'll pretend the transmitter isn't empty to * avoid a race condition (depending on when the transmit * interrupt happens). */ if (port->x_char || ((uart_circ_chars_pending(&state->info->xmit) > 0) && !state->info->tty->stopped && !state->info->tty->hw_stopped)) result &= ~TIOCSER_TEMT; return put_user(result, value);}static int uart_tiocmget(struct tty_struct *tty, struct file *file){ struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; int result = -EIO; mutex_lock(&state->mutex); if ((!file || !tty_hung_up_p(file)) && !(tty->flags & (1 << TTY_IO_ERROR))) { result = port->mctrl; spin_lock_irq(&port->lock); result |= port->ops->get_mctrl(port); spin_unlock_irq(&port->lock); } mutex_unlock(&state->mutex); return result;}static intuart_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear){ struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; int ret = -EIO; mutex_lock(&state->mutex); if ((!file || !tty_hung_up_p(file)) && !(tty->flags & (1 << TTY_IO_ERROR))) { uart_update_mctrl(port, set, clear); ret = 0; } mutex_unlock(&state->mutex); return ret;}static void uart_break_ctl(struct tty_struct *tty, int break_state){ struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; BUG_ON(!kernel_locked()); mutex_lock(&state->mutex); if (port->type != PORT_UNKNOWN) port->ops->break_ctl(port, break_state); mutex_unlock(&state->mutex);}static int uart_do_autoconfig(struct uart_state *state){ struct uart_port *port = state->port; int flags, ret; if (!capable(CAP_SYS_ADMIN)) return -EPERM; /* * Take the per-port semaphore. This prevents count from * changing, and hence any extra opens of the port while * we're auto-configuring. */ if (mutex_lock_interruptible(&state->mutex)) return -ERESTARTSYS; ret = -EBUSY; if (uart_users(state) == 1) { uart_shutdown(state); /* * If we already have a port type configured, * we must release its resources. */ if (port->type != PORT_UNKNOWN) port->ops->release_port(port); flags = UART_CONFIG_TYPE; if (port->flags & UPF_AUTO_IRQ) flags |= UART_CONFIG_IRQ; /* * This will claim the ports resources if * a port is found. */ port->ops->config_port(port, flags); ret = uart_startup(state, 1); } mutex_unlock(&state->mutex); return ret;}/* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was */static intuart_wait_modem_status(struct uart_state *state, unsigned long arg){ struct uart_port *port = state->port; DECLARE_WAITQUEUE(wait, current); struct uart_icount cprev, cnow; int ret; /* * note the counters on entry */ spin_lock_irq(&port->lock); memcpy(&cprev, &port->icount, sizeof(struct uart_icount)); /* * Force modem status interrupts on */ port->ops->enable_ms(port); spin_unlock_irq(&port->lock); add_wait_queue(&state->info->delta_msr_wait, &wait); for (;;) { spin_lock_irq(&port->lock); memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); spin_unlock_irq(&port->lock); set_current_state(TASK_INTERRUPTIBLE); if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { ret = 0; break; } schedule(); /* see if a signal did it */ if (signal_pending(current)) { ret = -ERESTARTSYS; break; } cprev = cnow; } current->state = TASK_RUNNING; remove_wait_queue(&state->info->delta_msr_wait, &wait); return ret;}/* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) * Return: write counters to the user passed counter struct * NB: both 1->0 and 0->1 transitions are counted except for * RI where only 0->1 is counted. */static int uart_get_count(struct uart_state *state, struct serial_icounter_struct __user *icnt){ struct serial_icounter_struct icount; struct uart_icount cnow; struct uart_port *port = state->port; spin_lock_irq(&port->lock); memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); spin_unlock_irq(&port->lock); icount.cts = cnow.cts; icount.dsr = cnow.dsr; icount.rng = cnow.rng; icount.dcd = cnow.dcd; icount.rx = cnow.rx; icount.tx = cnow.tx; icount.frame = cnow.frame; icount.overrun = cnow.overrun; icount.parity = cnow.parity; icount.brk = cnow.brk; icount.buf_overrun = cnow.buf_overrun; return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0;}/* * Called via sys_ioctl under the BKL. We can use spin_lock_irq() here. */static intuart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg){ struct uart_state *state = tty->driver_data; void __user *uarg = (void __user *)arg; int ret = -ENOIOCTLCMD; BUG_ON(!kernel_locked()); /* * These ioctls don't rely on the hardware to be present. */ switch (cmd) { case TIOCGSERIAL: ret = uart_get_info(state, uarg); break; case TIOCSSERIAL: ret = uart_set_info(state, uarg); break; case TIOCSERCONFIG: ret = uart_do_autoconfig(state); break; case TIOCSERGWILD: /* obsolete */ case TIOCSERSWILD: /* obsolete */ ret = 0; break; } if (ret != -ENOIOCTLCMD) goto out; if (tty->flags & (1 << TTY_IO_ERROR)) { ret = -EIO; goto out; } /* * The following should only be used when hardware is present. */ switch (cmd) { case TIOCMIWAIT: ret = uart_wait_modem_status(state, arg); break; case TIOCGICOUNT: ret = uart_get_count(state, uarg); break; } if (ret != -ENOIOCTLCMD) goto out; mutex_lock(&state->mutex); if (tty_hung_up_p(filp)) { ret = -EIO; goto out_up; } /* * All these rely on hardware being present and need to be * protected against the tty being hung up. */ switch (cmd) { case TIOCSERGETLSR: /* Get line status register */ ret = uart_get_lsr_info(state, uarg); break; default: { struct uart_port *port = state->port; if (port->ops->ioctl) ret = port->ops->ioctl(port, cmd, arg); break; } } out_up: mutex_unlock(&state->mutex); out: return ret;}static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios){ struct uart_state *state = tty->driver_data; unsigned long flags; unsigned int cflag = tty->termios->c_cflag; BUG_ON(!kernel_locked()); /* * These are the bits that are used to setup various * flags in the low level driver. */#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) if ((cflag ^ old_termios->c_cflag) == 0 && RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) return; uart_change_speed(state, old_termios); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR); /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { unsigned int mask = TIOCM_DTR; if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) mask |= TIOCM_RTS; uart_set_mctrl(state->port, mask); } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { spin_lock_irqsave(&state->port->lock, flags); tty->hw_stopped = 0; __uart_start(tty); spin_unlock_irqrestore(&state->port->lock, flags); } /* Handle turning on CRTSCTS */ if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { spin_lock_irqsave(&state->port->lock, flags); if (!(state->port->ops->get_mctrl(state->port) & TIOCM_CTS)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -