diff options
Diffstat (limited to 'drivers/serial/serial_core.c')
-rw-r--r-- | drivers/serial/serial_core.c | 288 |
1 files changed, 136 insertions, 152 deletions
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 7f2830709512..cd8511298bcb 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -58,9 +58,9 @@ static struct lock_class_key port_lock_key; #define uart_console(port) (0) #endif -static void uart_change_speed(struct uart_state *state, +static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, struct ktermios *old_termios); -static void uart_wait_until_sent(struct tty_struct *tty, int timeout); +static void __uart_wait_until_sent(struct uart_port *port, int timeout); static void uart_change_pm(struct uart_state *state, int pm_state); /* @@ -137,7 +137,7 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) * Startup the port. This will be called once per open. All calls * will be serialised by the per-port mutex. */ -static int uart_startup(struct uart_state *state, int init_hw) +static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw) { struct uart_port *uport = state->uart_port; struct tty_port *port = &state->port; @@ -152,7 +152,7 @@ static int uart_startup(struct uart_state *state, int init_hw) * once we have successfully opened the port. Also set * up the tty->alt_speed kludge */ - set_bit(TTY_IO_ERROR, &port->tty->flags); + set_bit(TTY_IO_ERROR, &tty->flags); if (uport->type == PORT_UNKNOWN) return 0; @@ -177,26 +177,26 @@ static int uart_startup(struct uart_state *state, int init_hw) /* * Initialise the hardware port settings. */ - uart_change_speed(state, NULL); + uart_change_speed(tty, state, NULL); /* * Setup the RTS and DTR signals once the * port is open and ready to respond. */ - if (port->tty->termios->c_cflag & CBAUD) + if (tty->termios->c_cflag & CBAUD) uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); } if (port->flags & ASYNC_CTS_FLOW) { spin_lock_irq(&uport->lock); if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) - port->tty->hw_stopped = 1; + tty->hw_stopped = 1; spin_unlock_irq(&uport->lock); } set_bit(ASYNCB_INITIALIZED, &port->flags); - clear_bit(TTY_IO_ERROR, &port->tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); } if (retval && capable(CAP_SYS_ADMIN)) @@ -210,11 +210,10 @@ static int uart_startup(struct uart_state *state, int init_hw) * DTR is dropped if the hangup on close termio flag is on. Calls to * uart_shutdown are serialised by the per-port semaphore. */ -static void uart_shutdown(struct uart_state *state) +static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) { struct uart_port *uport = state->uart_port; struct tty_port *port = &state->port; - struct tty_struct *tty = port->tty; /* * Set the TTY IO error marker @@ -430,11 +429,10 @@ uart_get_divisor(struct uart_port *port, unsigned int baud) EXPORT_SYMBOL(uart_get_divisor); /* FIXME: Consistent locking policy */ -static void -uart_change_speed(struct uart_state *state, struct ktermios *old_termios) +static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, + struct ktermios *old_termios) { struct tty_port *port = &state->port; - struct tty_struct *tty = port->tty; struct uart_port *uport = state->uart_port; struct ktermios *termios; @@ -463,8 +461,8 @@ uart_change_speed(struct uart_state *state, struct ktermios *old_termios) uport->ops->set_termios(uport, termios, old_termios); } -static inline int -__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c) +static inline int __uart_put_char(struct uart_port *port, + struct circ_buf *circ, unsigned char c) { unsigned long flags; int ret = 0; @@ -494,8 +492,8 @@ static void uart_flush_chars(struct tty_struct *tty) uart_start(tty); } -static int -uart_write(struct tty_struct *tty, const unsigned char *buf, int count) +static int uart_write(struct tty_struct *tty, + const unsigned char *buf, int count) { struct uart_state *state = tty->driver_data; struct uart_port *port; @@ -675,7 +673,7 @@ static int uart_get_info(struct uart_state *state, return 0; } -static int uart_set_info(struct uart_state *state, +static int uart_set_info(struct tty_struct *tty, struct uart_state *state, struct serial_struct __user *newinfo) { struct serial_struct new_serial; @@ -770,7 +768,7 @@ static int uart_set_info(struct uart_state *state, * We need to shutdown the serial port at the old * port/type/irq combination. */ - uart_shutdown(state); + uart_shutdown(tty, state); } if (change_port) { @@ -869,25 +867,27 @@ static int uart_set_info(struct uart_state *state, "is deprecated.\n", current->comm, tty_name(port->tty, buf)); } - uart_change_speed(state, NULL); + uart_change_speed(tty, state, NULL); } } else - retval = uart_startup(state, 1); + retval = uart_startup(tty, state, 1); exit: mutex_unlock(&port->mutex); return retval; } - -/* - * uart_get_lsr_info - get line status register info. - * Note: uart_ioctl protects us against hangups. +/** + * uart_get_lsr_info - get line status register info + * @tty: tty associated with the UART + * @state: UART being queried + * @value: returned modem value + * + * Note: uart_ioctl protects us against hangups. */ -static int uart_get_lsr_info(struct uart_state *state, - unsigned int __user *value) +static int uart_get_lsr_info(struct tty_struct *tty, + struct uart_state *state, unsigned int __user *value) { struct uart_port *uport = state->uart_port; - struct tty_port *port = &state->port; unsigned int result; result = uport->ops->tx_empty(uport); @@ -900,7 +900,7 @@ static int uart_get_lsr_info(struct uart_state *state, */ if (uport->x_char || ((uart_circ_chars_pending(&state->xmit) > 0) && - !port->tty->stopped && !port->tty->hw_stopped)) + !tty->stopped && !tty->hw_stopped)) result &= ~TIOCSER_TEMT; return put_user(result, value); @@ -961,7 +961,7 @@ static int uart_break_ctl(struct tty_struct *tty, int break_state) return 0; } -static int uart_do_autoconfig(struct uart_state *state) +static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state) { struct uart_port *uport = state->uart_port; struct tty_port *port = &state->port; @@ -980,7 +980,7 @@ static int uart_do_autoconfig(struct uart_state *state) ret = -EBUSY; if (tty_port_users(port) == 1) { - uart_shutdown(state); + uart_shutdown(tty, state); /* * If we already have a port type configured, @@ -999,7 +999,7 @@ static int uart_do_autoconfig(struct uart_state *state) */ uport->ops->config_port(uport, flags); - ret = uart_startup(state, 1); + ret = uart_startup(tty, state, 1); } mutex_unlock(&port->mutex); return ret; @@ -1122,11 +1122,11 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, break; case TIOCSSERIAL: - ret = uart_set_info(state, uarg); + ret = uart_set_info(tty, state, uarg); break; case TIOCSERCONFIG: - ret = uart_do_autoconfig(state); + ret = uart_do_autoconfig(tty, state); break; case TIOCSERGWILD: /* obsolete */ @@ -1172,7 +1172,7 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, */ switch (cmd) { case TIOCSERGETLSR: /* Get line status register */ - ret = uart_get_lsr_info(state, uarg); + ret = uart_get_lsr_info(tty, state, uarg); break; default: { @@ -1194,7 +1194,7 @@ static void uart_set_ldisc(struct tty_struct *tty) struct uart_port *uport = state->uart_port; if (uport->ops->set_ldisc) - uport->ops->set_ldisc(uport); + uport->ops->set_ldisc(uport, tty->termios->c_line); } static void uart_set_termios(struct tty_struct *tty, @@ -1219,7 +1219,7 @@ static void uart_set_termios(struct tty_struct *tty, return; } - uart_change_speed(state, old_termios); + uart_change_speed(tty, state, old_termios); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) @@ -1272,8 +1272,9 @@ static void uart_close(struct tty_struct *tty, struct file *filp) struct uart_state *state = tty->driver_data; struct tty_port *port; struct uart_port *uport; + unsigned long flags; - BUG_ON(!kernel_locked()); + BUG_ON(!tty_locked()); if (!state) return; @@ -1284,9 +1285,12 @@ static void uart_close(struct tty_struct *tty, struct file *filp) pr_debug("uart_close(%d) called\n", uport->line); mutex_lock(&port->mutex); + spin_lock_irqsave(&port->lock, flags); - if (tty_hung_up_p(filp)) + if (tty_hung_up_p(filp)) { + spin_unlock_irqrestore(&port->lock, flags); goto done; + } if ((tty->count == 1) && (port->count != 1)) { /* @@ -1305,8 +1309,10 @@ static void uart_close(struct tty_struct *tty, struct file *filp) tty->name, port->count); port->count = 0; } - if (port->count) + if (port->count) { + spin_unlock_irqrestore(&port->lock, flags); goto done; + } /* * Now we wait for the transmit buffer to clear; and we notify @@ -1314,9 +1320,18 @@ static void uart_close(struct tty_struct *tty, struct file *filp) * setting tty->closing. */ tty->closing = 1; + spin_unlock_irqrestore(&port->lock, flags); - if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, msecs_to_jiffies(port->closing_wait)); + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) { + /* + * hack: open-coded tty_wait_until_sent to avoid + * recursive tty_lock + */ + long timeout = msecs_to_jiffies(port->closing_wait); + if (wait_event_interruptible_timeout(tty->write_wait, + !tty_chars_in_buffer(tty), timeout) >= 0) + __uart_wait_until_sent(uport, timeout); + } /* * At this point, we stop accepting input. To do this, we @@ -1332,45 +1347,47 @@ static void uart_close(struct tty_struct *tty, struct file *filp) * has completely drained; this is especially * important if there is a transmit FIFO! */ - uart_wait_until_sent(tty, uport->timeout); + __uart_wait_until_sent(uport, uport->timeout); } - uart_shutdown(state); + uart_shutdown(tty, state); uart_flush_buffer(tty); tty_ldisc_flush(tty); - tty->closing = 0; tty_port_tty_set(port, NULL); + spin_lock_irqsave(&port->lock, flags); + tty->closing = 0; if (port->blocked_open) { + spin_unlock_irqrestore(&port->lock, flags); if (port->close_delay) msleep_interruptible(port->close_delay); + spin_lock_irqsave(&port->lock, flags); } else if (!uart_console(uport)) { + spin_unlock_irqrestore(&port->lock, flags); uart_change_pm(state, 3); + spin_lock_irqsave(&port->lock, flags); } /* * Wake up anyone trying to open this port. */ clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); + spin_unlock_irqrestore(&port->lock, flags); wake_up_interruptible(&port->open_wait); done: mutex_unlock(&port->mutex); } -static void uart_wait_until_sent(struct tty_struct *tty, int timeout) +static void __uart_wait_until_sent(struct uart_port *port, int timeout) { - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; unsigned long char_time, expire; if (port->type == PORT_UNKNOWN || port->fifosize == 0) return; - lock_kernel(); - /* * Set the check interval to be 1/5 of the estimated time to * send a single character, and make it at least 1. The check @@ -1416,7 +1433,16 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) break; } set_current_state(TASK_RUNNING); /* might not be needed */ - unlock_kernel(); +} + +static void uart_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_port; + + tty_lock(); + __uart_wait_until_sent(port, timeout); + tty_unlock(); } /* @@ -1429,16 +1455,19 @@ static void uart_hangup(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; + unsigned long flags; - BUG_ON(!kernel_locked()); + BUG_ON(!tty_locked()); pr_debug("uart_hangup(%d)\n", state->uart_port->line); mutex_lock(&port->mutex); if (port->flags & ASYNC_NORMAL_ACTIVE) { uart_flush_buffer(tty); - uart_shutdown(state); + uart_shutdown(tty, state); + spin_lock_irqsave(&port->lock, flags); port->count = 0; clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); + spin_unlock_irqrestore(&port->lock, flags); tty_port_tty_set(port, NULL); wake_up_interruptible(&port->open_wait); wake_up_interruptible(&port->delta_msr_wait); @@ -1446,15 +1475,19 @@ static void uart_hangup(struct tty_struct *tty) mutex_unlock(&port->mutex); } -/* - * Copy across the serial console cflag setting into the termios settings - * for the initial open of the port. This allows continuity between the - * kernel settings, and the settings init adopts when it opens the port - * for the first time. +/** + * uart_update_termios - update the terminal hw settings + * @tty: tty associated with UART + * @state: UART to update + * + * Copy across the serial console cflag setting into the termios settings + * for the initial open of the port. This allows continuity between the + * kernel settings, and the settings init adopts when it opens the port + * for the first time. */ -static void uart_update_termios(struct uart_state *state) +static void uart_update_termios(struct tty_struct *tty, + struct uart_state *state) { - struct tty_struct *tty = state->port.tty; struct uart_port *port = state->uart_port; if (uart_console(port) && port->cons->cflag) { @@ -1471,7 +1504,7 @@ static void uart_update_termios(struct uart_state *state) /* * Make termios settings take effect. */ - uart_change_speed(state, NULL); + uart_change_speed(tty, state, NULL); /* * And finally enable the RTS and DTR signals. @@ -1481,90 +1514,37 @@ static void uart_update_termios(struct uart_state *state) } } -/* - * Block the open until the port is ready. We must be called with - * the per-port semaphore held. - */ -static int -uart_block_til_ready(struct file *filp, struct uart_state *state) +static int uart_carrier_raised(struct tty_port *port) { - DECLARE_WAITQUEUE(wait, current); + struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = state->uart_port; - struct tty_port *port = &state->port; - unsigned int mctrl; - - port->blocked_open++; - port->count--; - - add_wait_queue(&port->open_wait, &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - /* - * If we have been hung up, tell userspace/restart open. - */ - if (tty_hung_up_p(filp) || port->tty == NULL) - break; - - /* - * If the port has been closed, tell userspace/restart open. - */ - if (!(port->flags & ASYNC_INITIALIZED)) - break; + int mctrl; + spin_lock_irq(&uport->lock); + uport->ops->enable_ms(uport); + mctrl = uport->ops->get_mctrl(uport); + spin_unlock_irq(&uport->lock); + if (mctrl & TIOCM_CAR) + return 1; + return 0; +} - /* - * If non-blocking mode is set, or CLOCAL mode is set, - * we don't want to wait for the modem status lines to - * indicate that the port is ready. - * - * Also, if the port is not enabled/configured, we want - * to allow the open to succeed here. Note that we will - * have set TTY_IO_ERROR for a non-existant port. - */ - if ((filp->f_flags & O_NONBLOCK) || - (port->tty->termios->c_cflag & CLOCAL) || - (port->tty->flags & (1 << TTY_IO_ERROR))) - break; +static void uart_dtr_rts(struct tty_port *port, int onoff) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; - /* - * Set DTR to allow modem to know we're waiting. Do - * not set RTS here - we want to make sure we catch - * the data from the modem. - */ - if (port->tty->termios->c_cflag & CBAUD) - uart_set_mctrl(uport, TIOCM_DTR); + if (onoff) { + uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS); /* - * and wait for the carrier to indicate that the - * modem is ready for us. + * If this is the first open to succeed, + * adjust things to suit. */ - spin_lock_irq(&uport->lock); - uport->ops->enable_ms(uport); - mctrl = uport->ops->get_mctrl(uport); - spin_unlock_irq(&uport->lock); - if (mctrl & TIOCM_CAR) - break; - - mutex_unlock(&port->mutex); - schedule(); - mutex_lock(&port->mutex); - - if (signal_pending(current)) - break; + if (!test_and_set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags)) + uart_update_termios(port->tty, state); } - set_current_state(TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); - - port->count++; - port->blocked_open--; - - if (signal_pending(current)) - return -ERESTARTSYS; - - if (!port->tty || tty_hung_up_p(filp)) - return -EAGAIN; - - return 0; + else + uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); } static struct uart_state *uart_get(struct uart_driver *drv, int line) @@ -1611,7 +1591,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp) struct tty_port *port; int retval, line = tty->index; - BUG_ON(!kernel_locked()); + BUG_ON(!tty_locked()); pr_debug("uart_open(%d) called\n", line); /* @@ -1668,23 +1648,14 @@ static int uart_open(struct tty_struct *tty, struct file *filp) /* * Start up the serial port. */ - retval = uart_startup(state, 0); + retval = uart_startup(tty, state, 0); /* * If we succeeded, wait until the port is ready. */ - if (retval == 0) - retval = uart_block_til_ready(filp, state); mutex_unlock(&port->mutex); - - /* - * If this is the first open to succeed, adjust things to suit. - */ - if (retval == 0 && !(port->flags & ASYNC_NORMAL_ACTIVE)) { - set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); - - uart_update_termios(state); - } + if (retval == 0) + retval = tty_port_block_til_ready(port, tty, filp); fail: return retval; @@ -2010,9 +1981,13 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) struct tty_port *port = &state->port; struct device *tty_dev; struct uart_match match = {uport, drv}; + struct tty_struct *tty; mutex_lock(&port->mutex); + /* Must be inside the mutex lock until we convert to tty_port */ + tty = port->tty; + tty_dev = device_find_child(uport->dev, &match, serial_match_port); if (device_may_wakeup(tty_dev)) { enable_irq_wake(uport->irq); @@ -2105,9 +2080,12 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) ops->set_mctrl(uport, 0); spin_unlock_irq(&uport->lock); if (console_suspend_enabled || !uart_console(uport)) { + /* Protected by port mutex for now */ + struct tty_struct *tty = port->tty; ret = ops->startup(uport); if (ret == 0) { - uart_change_speed(state, NULL); + if (tty) + uart_change_speed(tty, state, NULL); spin_lock_irq(&uport->lock); ops->set_mctrl(uport, uport->mctrl); ops->start_tx(uport); @@ -2119,7 +2097,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) * Clear the "initialized" flag so we won't try * to call the low level drivers shutdown method. */ - uart_shutdown(state); + uart_shutdown(tty, state); } } @@ -2312,6 +2290,11 @@ static const struct tty_operations uart_ops = { #endif }; +static const struct tty_port_operations uart_port_ops = { + .carrier_raised = uart_carrier_raised, + .dtr_rts = uart_dtr_rts, +}; + /** * uart_register_driver - register a driver with the uart core layer * @drv: low level driver structure @@ -2368,6 +2351,7 @@ int uart_register_driver(struct uart_driver *drv) struct tty_port *port = &state->port; tty_port_init(port); + port->ops = &uart_port_ops; port->close_delay = 500; /* .5 seconds */ port->closing_wait = 30000; /* 30 seconds */ tasklet_init(&state->tlet, uart_tasklet_action, |