Commit 584abc37 authored by Alan Cox's avatar Alan Cox Committed by Greg Kroah-Hartman

tty: sdio_uart: Switch to the open/close helpers

Gets us proper tty semantics, removes some code and fixes up a few corner
case races (hangup during open etc)
Signed-off-by: default avatarAlan Cox <alan@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 0a68f64f
...@@ -77,7 +77,6 @@ struct sdio_uart_port { ...@@ -77,7 +77,6 @@ struct sdio_uart_port {
struct kref kref; struct kref kref;
struct tty_struct *tty; struct tty_struct *tty;
unsigned int index; unsigned int index;
unsigned int opened;
struct sdio_func *func; struct sdio_func *func;
struct mutex func_lock; struct mutex func_lock;
struct task_struct *in_sdio_uart_irq; struct task_struct *in_sdio_uart_irq;
...@@ -150,6 +149,7 @@ static void sdio_uart_port_put(struct sdio_uart_port *port) ...@@ -150,6 +149,7 @@ static void sdio_uart_port_put(struct sdio_uart_port *port)
static void sdio_uart_port_remove(struct sdio_uart_port *port) static void sdio_uart_port_remove(struct sdio_uart_port *port)
{ {
struct sdio_func *func; struct sdio_func *func;
struct tty_struct *tty;
BUG_ON(sdio_uart_table[port->index] != port); BUG_ON(sdio_uart_table[port->index] != port);
...@@ -170,10 +170,9 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port) ...@@ -170,10 +170,9 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port)
sdio_claim_host(func); sdio_claim_host(func);
port->func = NULL; port->func = NULL;
mutex_unlock(&port->func_lock); mutex_unlock(&port->func_lock);
if (port->opened) { tty = tty_port_tty_get(&port->port);
struct tty_struct *tty = tty_port_tty_get(&port->port);
/* tty_hangup is async so is this safe as is ?? */ /* tty_hangup is async so is this safe as is ?? */
if (tty) if (tty) {
tty_hangup(tty); tty_hangup(tty);
tty_kref_put(tty); tty_kref_put(tty);
} }
...@@ -560,13 +559,46 @@ static void sdio_uart_irq(struct sdio_func *func) ...@@ -560,13 +559,46 @@ static void sdio_uart_irq(struct sdio_func *func)
port->in_sdio_uart_irq = NULL; port->in_sdio_uart_irq = NULL;
} }
static int sdio_uart_startup(struct sdio_uart_port *port) /**
* uart_dtr_rts - port helper to set uart signals
* @tport: tty port to be updated
* @onoff: set to turn on DTR/RTS
*
* Called by the tty port helpers when the modem signals need to be
* adjusted during an open, close and hangup.
*/
static void uart_dtr_rts(struct tty_port *tport, int onoff)
{ {
unsigned long page; struct sdio_uart_port *port =
int ret = -ENOMEM; container_of(tport, struct sdio_uart_port, port);
struct tty_struct *tty = tty_port_tty_get(&port->port); if (onoff == 0)
sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
else
sdio_uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
}
/* FIXME: What if it is NULL ?? */ /**
* sdio_uart_activate - start up hardware
* @tport: tty port to activate
* @tty: tty bound to this port
*
* Activate a tty port. The port locking guarantees us this will be
* run exactly once per set of opens, and if successful will see the
* shutdown method run exactly once to match. Start up and shutdown are
* protected from each other by the internal locking and will not run
* at the same time even during a hangup event.
*
* If we successfully start up the port we take an extra kref as we
* will keep it around until shutdown when the kref is dropped.
*/
static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty)
{
struct sdio_uart_port *port =
container_of(tport, struct sdio_uart_port, port);
unsigned long page;
int ret;
/* /*
* Set the TTY IO error marker - we will only clear this * Set the TTY IO error marker - we will only clear this
...@@ -577,7 +609,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port) ...@@ -577,7 +609,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port)
/* Initialise and allocate the transmit buffer. */ /* Initialise and allocate the transmit buffer. */
page = __get_free_page(GFP_KERNEL); page = __get_free_page(GFP_KERNEL);
if (!page) if (!page)
goto err0; return -ENOMEM;
port->xmit.buf = (unsigned char *)page; port->xmit.buf = (unsigned char *)page;
circ_clear(&port->xmit); circ_clear(&port->xmit);
...@@ -631,7 +663,6 @@ static int sdio_uart_startup(struct sdio_uart_port *port) ...@@ -631,7 +663,6 @@ static int sdio_uart_startup(struct sdio_uart_port *port)
sdio_uart_irq(port->func); sdio_uart_irq(port->func);
sdio_uart_release_func(port); sdio_uart_release_func(port);
tty_kref_put(tty);
return 0; return 0;
err3: err3:
...@@ -640,15 +671,25 @@ err2: ...@@ -640,15 +671,25 @@ err2:
sdio_uart_release_func(port); sdio_uart_release_func(port);
err1: err1:
free_page((unsigned long)port->xmit.buf); free_page((unsigned long)port->xmit.buf);
err0:
tty_kref_put(tty);
return ret; return ret;
} }
static void sdio_uart_shutdown(struct sdio_uart_port *port)
/**
* sdio_uart_shutdown - stop hardware
* @tport: tty port to shut down
*
* Deactivate a tty port. The port locking guarantees us this will be
* run only if a successful matching activate already ran. The two are
* protected from each other by the internal locking and will not run
* at the same time even during a hangup event.
*/
static void sdio_uart_shutdown(struct tty_port *tport)
{ {
struct sdio_uart_port *port =
container_of(tport, struct sdio_uart_port, port);
int ret; int ret;
struct tty_struct *tty;
ret = sdio_uart_claim_func(port); ret = sdio_uart_claim_func(port);
if (ret) if (ret)
...@@ -656,14 +697,6 @@ static void sdio_uart_shutdown(struct sdio_uart_port *port) ...@@ -656,14 +697,6 @@ static void sdio_uart_shutdown(struct sdio_uart_port *port)
sdio_uart_stop_rx(port); sdio_uart_stop_rx(port);
/* TODO: wait here for TX FIFO to drain */
tty = tty_port_tty_get(&port->port);
/* Turn off DTR and RTS early. */
if (tty && (tty->termios->c_cflag & HUPCL))
sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
tty_kref_put(tty);
/* Disable interrupts from this port */ /* Disable interrupts from this port */
sdio_release_irq(port->func); sdio_release_irq(port->func);
port->ier = 0; port->ier = 0;
...@@ -688,74 +721,68 @@ skip: ...@@ -688,74 +721,68 @@ skip:
free_page((unsigned long)port->xmit.buf); free_page((unsigned long)port->xmit.buf);
} }
static int sdio_uart_open(struct tty_struct *tty, struct file *filp) /**
{ * sdio_uart_install - install method
struct sdio_uart_port *port; * @driver: the driver in use (sdio_uart in our case)
int ret; * @tty: the tty being bound
*
port = sdio_uart_port_get(tty->index); * Look up and bind the tty and the driver together. Initialize
if (!port) * any needed private data (in our case the termios)
return -ENODEV;
mutex_lock(&port->port.mutex);
/*
* Make sure not to mess up with a dead port
* which has not been closed yet.
*/ */
if (tty->driver_data && tty->driver_data != port) {
mutex_unlock(&port->port.mutex);
sdio_uart_port_put(port);
return -EBUSY;
}
if (!port->opened) { static int sdio_uart_install(struct tty_driver *driver, struct tty_struct *tty)
{
int idx = tty->index;
struct sdio_uart_port *port = sdio_uart_port_get(idx);
int ret = tty_init_termios(tty);
if (ret == 0) {
tty_driver_kref_get(driver);
tty->count++;
/* This is the ref sdio_uart_port get provided */
tty->driver_data = port; tty->driver_data = port;
tty_port_tty_set(&port->port, tty); driver->ttys[idx] = tty;
ret = sdio_uart_startup(port); } else
if (ret) {
tty->driver_data = NULL;
tty_port_tty_set(&port->port, NULL);
mutex_unlock(&port->port.mutex);
sdio_uart_port_put(port); sdio_uart_port_put(port);
return ret; return ret;
}
}
port->opened++;
mutex_unlock(&port->port.mutex);
return 0;
} }
static void sdio_uart_close(struct tty_struct *tty, struct file * filp) /**
* sdio_uart_cleanup - called on the last tty kref drop
* @tty: the tty being destroyed
*
* Called asynchronously when the last reference to the tty is dropped.
* We cannot destroy the tty->driver_data port kref until this point
*/
static void sdio_uart_cleanup(struct tty_struct *tty)
{ {
struct sdio_uart_port *port = tty->driver_data; struct sdio_uart_port *port = tty->driver_data;
tty->driver_data = NULL; /* Bug trap */
sdio_uart_port_put(port);
}
if (!port) /*
return; * Open/close/hangup is now entirely boilerplate
*/
mutex_lock(&port->port.mutex); static int sdio_uart_open(struct tty_struct *tty, struct file *filp)
BUG_ON(!port->opened); {
struct sdio_uart_port *port = tty->driver_data;
return tty_port_open(&port->port, tty, filp);
}
/* static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
* This is messy. The tty layer calls us even when open() {
* returned an error. Ignore this close request if tty->count struct sdio_uart_port *port = tty->driver_data;
* is larger than port->count. tty_port_close(&port->port, tty, filp);
*/ }
if (tty->count > port->opened) {
mutex_unlock(&port->port.mutex);
return;
}
if (--port->opened == 0) { static void sdio_uart_hangup(struct tty_struct *tty)
tty->closing = 1; {
sdio_uart_shutdown(port); struct sdio_uart_port *port = tty->driver_data;
tty_ldisc_flush(tty); tty_port_hangup(&port->port);
tty_port_tty_set(&port->port, NULL);
tty->driver_data = NULL;
tty->closing = 0;
}
mutex_unlock(&port->port.mutex);
sdio_uart_port_put(port);
} }
static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf, static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf,
...@@ -1021,6 +1048,12 @@ static const struct file_operations sdio_uart_proc_fops = { ...@@ -1021,6 +1048,12 @@ static const struct file_operations sdio_uart_proc_fops = {
.release = single_release, .release = single_release,
}; };
static const struct tty_port_operations sdio_uart_port_ops = {
.dtr_rts = uart_dtr_rts,
.shutdown = sdio_uart_shutdown,
.activate = sdio_uart_activate,
};
static const struct tty_operations sdio_uart_ops = { static const struct tty_operations sdio_uart_ops = {
.open = sdio_uart_open, .open = sdio_uart_open,
.close = sdio_uart_close, .close = sdio_uart_close,
...@@ -1031,9 +1064,12 @@ static const struct tty_operations sdio_uart_ops = { ...@@ -1031,9 +1064,12 @@ static const struct tty_operations sdio_uart_ops = {
.throttle = sdio_uart_throttle, .throttle = sdio_uart_throttle,
.unthrottle = sdio_uart_unthrottle, .unthrottle = sdio_uart_unthrottle,
.set_termios = sdio_uart_set_termios, .set_termios = sdio_uart_set_termios,
.hangup = sdio_uart_hangup,
.break_ctl = sdio_uart_break_ctl, .break_ctl = sdio_uart_break_ctl,
.tiocmget = sdio_uart_tiocmget, .tiocmget = sdio_uart_tiocmget,
.tiocmset = sdio_uart_tiocmset, .tiocmset = sdio_uart_tiocmset,
.install = sdio_uart_install,
.cleanup = sdio_uart_cleanup,
.proc_fops = &sdio_uart_proc_fops, .proc_fops = &sdio_uart_proc_fops,
}; };
...@@ -1096,6 +1132,7 @@ static int sdio_uart_probe(struct sdio_func *func, ...@@ -1096,6 +1132,7 @@ static int sdio_uart_probe(struct sdio_func *func,
port->func = func; port->func = func;
sdio_set_drvdata(func, port); sdio_set_drvdata(func, port);
tty_port_init(&port->port); tty_port_init(&port->port);
port->port.ops = &sdio_uart_port_ops;
ret = sdio_uart_add_port(port); ret = sdio_uart_add_port(port);
if (ret) { if (ret) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment