Skip to content

Commit

Permalink
tty: serial - fix various misuses/mishandlings of port->tty
Browse files Browse the repository at this point in the history
Make it robust against hang up events. In most cases we can do this simply
by passing the right things in the first place.

Signed-off-by: Alan Cox <alan@linux.intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Alan Cox authored and Greg Kroah-Hartman committed Aug 10, 2010
1 parent a360fae commit 1922513
Showing 1 changed file with 60 additions and 49 deletions.
109 changes: 60 additions & 49 deletions drivers/serial/serial_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ 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_change_pm(struct uart_state *state, int pm_state);
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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))
Expand All @@ -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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -1335,7 +1335,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
uart_wait_until_sent(tty, uport->timeout);
}

uart_shutdown(state);
uart_shutdown(tty, state);
uart_flush_buffer(tty);

tty_ldisc_flush(tty);
Expand Down Expand Up @@ -1436,7 +1436,7 @@ static void uart_hangup(struct tty_struct *tty)
mutex_lock(&port->mutex);
if (port->flags & ASYNC_NORMAL_ACTIVE) {
uart_flush_buffer(tty);
uart_shutdown(state);
uart_shutdown(tty, state);
port->count = 0;
clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
tty_port_tty_set(port, NULL);
Expand All @@ -1446,15 +1446,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) {
Expand All @@ -1471,7 +1475,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.
Expand Down Expand Up @@ -1668,7 +1672,7 @@ 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.
Expand All @@ -1683,7 +1687,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
if (retval == 0 && !(port->flags & ASYNC_NORMAL_ACTIVE)) {
set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);

uart_update_termios(state);
uart_update_termios(tty, state);
}

fail:
Expand Down Expand Up @@ -2010,9 +2014,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);
Expand Down Expand Up @@ -2105,9 +2113,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);
Expand All @@ -2119,7 +2130,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);
}
}

Expand Down

0 comments on commit 1922513

Please sign in to comment.