📄 core.c
字号:
{ struct serial_struct new_serial; struct uart_state *state = info->state; struct uart_port *port = info->port; unsigned long new_port; unsigned int change_irq, change_port, old_flags; unsigned int old_custom_divisor; 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_cannonicalize(new_serial.irq); /* * 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. */ down(&port_sem); down(&state->count_sem); 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; old_custom_divisor = state->custom_divisor; if (!capable(CAP_SYS_ADMIN)) { retval = -EPERM; if (change_irq || change_port || (new_serial.baud_base != port->uartclk / 16) || (new_serial.close_delay != state->close_delay) || (new_serial.closing_wait != state->closing_wait) || (new_serial.xmit_fifo_size != port->fifosize) || ((new_serial.flags & ~ASYNC_USR_MASK) != (port->flags & ~ASYNC_USR_MASK))) goto exit; port->flags = ((port->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); info->flags = ((info->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); state->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 (state->count > 1) goto exit; /* * We need to shutdown the serial port at the old * port/type/irq combination. */ uart_shutdown(info); } 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); /* * 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 & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); info->flags = ((port->flags & ~ASYNC_INTERNAL_FLAGS) | (info->flags & ASYNC_INTERNAL_FLAGS)); state->custom_divisor = new_serial.custom_divisor; state->close_delay = new_serial.close_delay * HZ / 100; state->closing_wait = new_serial.closing_wait * HZ / 100; info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; port->fifosize = new_serial.xmit_fifo_size;check_and_exit: retval = 0; if (port->type == PORT_UNKNOWN) goto exit; if (info->flags & ASYNC_INITIALIZED) { if (((old_flags & info->flags) & ASYNC_SPD_MASK) || old_custom_divisor != state->custom_divisor) { uart_update_altspeed(info); uart_change_speed(info, NULL); } } else retval = uart_startup(info);exit: up(&state->count_sem); up(&port_sem); return retval;}/* * uart_get_lsr_info - get line status register info */static int uart_get_lsr_info(struct uart_info *info, unsigned int *value){ u_int result; unsigned long flags; spin_lock_irqsave(&info->lock, flags); result = info->ops->tx_empty(info->port); spin_unlock_irqrestore(&info->lock, flags); /* * 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 (info->port->x_char || ((CIRC_CNT(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE) > 0) && !info->tty->stopped && !info->tty->hw_stopped)) result &= ~TIOCSER_TEMT; return put_user(result, value);}static int uart_get_modem_info(struct uart_info *info, unsigned int *value){ unsigned int result = info->mctrl; result |= info->ops->get_mctrl(info->port); return put_user(result, value);}static int uart_set_modem_info(struct uart_info *info, unsigned int cmd, unsigned int *value){ unsigned int arg, old; int ret = 0; if (get_user(arg, value)) return -EFAULT; spin_lock_irq(&info->lock); old = info->mctrl; switch (cmd) { case TIOCMBIS: info->mctrl |= arg; break; case TIOCMBIC: info->mctrl &= ~arg; break; case TIOCMSET: info->mctrl = arg; break; default: ret = -EINVAL; break; } if (old != info->mctrl) info->ops->set_mctrl(info->port, info->mctrl); spin_unlock_irq(&info->lock); return ret;}static void uart_break_ctl(struct tty_struct *tty, int break_state){ struct uart_info *info = tty->driver_data; unsigned long flags; if (info->port->type != PORT_UNKNOWN) { spin_lock_irqsave(&info->lock, flags); info->ops->break_ctl(info->port, break_state); spin_unlock_irqrestore(&info->lock, flags); }}static int uart_do_autoconfig(struct uart_info *info){ struct uart_port *port = info->port; int flags, ret; if (!capable(CAP_SYS_ADMIN)) return -EPERM; /* * Take the 'count' lock. This prevents count * from incrementing, and hence any extra opens * of the port while we're auto-configging. */ down(&info->state->count_sem); ret = -EBUSY; if (info->state->count == 1) { uart_shutdown(info); /* * 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 & ASYNC_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(info); } up(&info->state->count_sem); return ret;}/* * Called from userspace. We can use spin_lock_irq() here. */static int uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ struct uart_info *info = tty->driver_data; struct uart_icount cprev, cnow; struct serial_icounter_struct icount; int ret = -ENOIOCTLCMD; if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; } switch (cmd) { case TIOCMGET: ret = uart_get_modem_info(info, (unsigned int *)arg); break; case TIOCMBIS: case TIOCMBIC: case TIOCMSET: ret = uart_set_modem_info(info, cmd, (unsigned int *)arg); break; case TIOCGSERIAL: ret = uart_get_info(info, (struct serial_struct *)arg); break; case TIOCSSERIAL: ret = uart_set_info(info, (struct serial_struct *)arg); break; case TIOCSERCONFIG: ret = uart_do_autoconfig(info); break; case TIOCSERGETLSR: /* Get line status register */ ret = uart_get_lsr_info(info, (unsigned int *)arg); break; /* * 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 */ case TIOCMIWAIT: spin_lock_irq(&info->lock); /* note the counters on entry */ cprev = info->port->icount; /* Force modem status interrupts on */ info->ops->enable_ms(info->port); spin_unlock_irq(&info->lock); while (1) { interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) { ret = -ERESTARTSYS; break; } spin_lock_irq(&info->lock); cnow = info->port->icount; /* atomic copy */ spin_unlock_irq(&info->lock); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { ret = -EIO; /* no change => error */ break; } 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; } cprev = cnow; } break; /* * 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. */ case TIOCGICOUNT: spin_lock_irq(&info->lock); cnow = info->port->icount; spin_unlock_irq(&info->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; ret = copy_to_user((void *)arg, &icount, sizeof(icount)) ? -EFAULT : 0; break; case TIOCSERGWILD: /* obsolete */ case TIOCSERSWILD: /* obsolete */ ret = 0; break; default: if (info->ops->ioctl) ret = info->ops->ioctl(info->port, cmd, arg); break; } return ret;}static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios){ struct uart_info *info = tty->driver_data; unsigned long flags; unsigned int cflag = tty->termios->c_cflag; if ((cflag ^ old_termios->c_cflag) == 0 && RELEVENT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) return; uart_change_speed(info, old_termios); spin_lock_irqsave(&info->lock, flags); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) { info->mctrl &= ~(TIOCM_RTS | TIOCM_DTR); info->ops->set_mctrl(info->port, info->mctrl); } /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { info->mctrl |= TIOCM_DTR; if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) info->mctrl |= TIOCM_RTS; info->ops->set_mctrl(info->port, info->mctrl); } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { tty->hw_stopped = 0; __uart_start(tty); } spin_unlock_irqrestore(&info->lock, flags);#if 0 /* * No need to wake up processes in open wait, since they * sample the CLOCAL flag once, and don't recheck it. * XXX It's not clear whether the current behavior is correct * or not. Hence, this may change..... */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) wake_up_interruptible(&info->open_wait);#endif}/* * In 2.4.5, calls to this will be serialized via the BKL in * linux/drivers/char/tty_io.c:tty_release() * linux/drivers/char/tty_io.c:do_tty_handup() */static void uart_close(struct tty_struct *tty, struct file *filp){ struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; struct uart_info *info = tty->driver_data; struct uart_state *state; unsigned long flags; if (!info) return; state = info->state;#ifdef DEBUG printk("uart_close() called\n");#endif /* * This is safe, as long as the BKL exists in * do_tty_hangup(), and we're protected by the BKL. */ if (tty_hung_up_p(filp)) goto done; down(&state->count_sem); spin_lock_irqsave(&info->lock, flags); if ((tty->count == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. state->count should always * be one in these conditions. If it's greater than * one, we've got real problems, since it means the * serial port won't be shutdown. */ printk("uart_close: bad serial port count; tty->count is 1, " "state->count is %d\n", state->count); state->count = 1; } if (--state->count < 0) { printk("rs_close: bad serial port count for %s%d: %d\n", tty->driver.name, info->port->line, state->count); state->count = 0; } if (state->count) { spin_unlock_irqrestore(&info->lock, flags); up(&state->count_sem); goto done; } info->flags |= ASYNC_CLOSING; spin_unlock_irqrestore(&info->lock, flags); up(&state->count_sem); /* * Save the termios structure, since this port may have * separate termios for callout and dialin. */ if (info->flags & ASYNC_NORMAL_ACTIVE) info->state->normal_termios = *tty->termios; if (info->flags & ASYNC_CALLOUT_ACTIVE) info->state->callout_termios = *tty->termios; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; if (info->state->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, info->state->closing_wait); /* * At this point, we stop accepting input. To do this, we * disable the receive line status interrupts. */ if (info->flags & ASYNC_INITIALIZED) { info->ops->stop_rx(info->port); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ uart_wait_until_sent(tty, info->timeout); } uart_shutdown(info); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); tty->closing = 0; info->event = 0; info->tty = NULL; if (info->blocked_open) { if (info->state->close_delay) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(info->state->close_delay); set_current_state(TASK_RUNNING);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -