📄 serial_core.c
字号:
if (tty->termios->c_cflag & CRTSCTS) { save_flags(flags); cli(); info->mctrl |= TIOCM_RTS; info->ops->set_mctrl(info->port, info->mctrl); restore_flags(flags); }}static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo){ struct uart_state *state = info->state; struct uart_port *port = info->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 = 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; tmp.closing_wait = state->closing_wait; tmp.custom_divisor = state->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_info *info, struct serial_struct *newinfo){ 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.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; save_flags(flags); cli(); result = info->ops->tx_empty(info->port); restore_flags(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; unsigned long flags; int ret = 0; if (get_user(arg, value)) return -EFAULT; save_flags(flags); cli(); 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); restore_flags(flags); 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) { save_flags(flags); cli(); info->ops->break_ctl(info->port, break_state); restore_flags(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;}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; unsigned long flags; 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: save_flags(flags); cli(); /* note the counters on entry */ cprev = info->port->icount; /* Force modem status interrupts on */ info->ops->enable_ms(info->port); restore_flags(flags); while (1) { interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) { ret = -ERESTARTSYS; break; } save_flags(flags); cli(); cnow = info->port->icount; /* atomic copy */ restore_flags(flags); 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: save_flags(flags); cli(); cnow = info->port->icount; restore_flags(flags); 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); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) { save_flags(flags); cli(); info->mctrl &= ~(TIOCM_RTS | TIOCM_DTR); info->ops->set_mctrl(info->port, info->mctrl); restore_flags(flags); } /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { save_flags(flags); cli(); 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); restore_flags(flags); } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { tty->hw_stopped = 0; uart_start(tty); }#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 */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 down(&state->count_sem); save_flags(flags); cli(); if (tty_hung_up_p(filp)) { restore_flags(flags); up(&state->count_sem); goto done; } if ((tty->count == 1) && (state->count != 1)) { /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -