From b5849b1a82853171ce8a35220204f17ec282a9a8 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 5 Nov 2009 13:28:06 +0000 Subject: sdio_uart: use tty_port Add a tty_port object to the sdio uart. For the moment just begin using the tty field of the port, as this is the critical one to clean up. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/card/sdio_uart.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) (limited to 'drivers/mmc/card/sdio_uart.c') diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index b8e7c5ae981..c2759dbfdfd 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -73,6 +73,7 @@ struct uart_icount { }; struct sdio_uart_port { + struct tty_port port; struct kref kref; struct tty_struct *tty; unsigned int index; @@ -172,7 +173,7 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port) port->func = NULL; mutex_unlock(&port->func_lock); if (port->opened) - tty_hangup(port->tty); + tty_hangup(port->port.tty); mutex_unlock(&port->open_lock); sdio_release_irq(func); sdio_disable_func(func); @@ -391,7 +392,7 @@ static void sdio_uart_stop_rx(struct sdio_uart_port *port) static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *status) { - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; unsigned int ch, flag; int max_count = 256; @@ -446,6 +447,7 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port) { struct circ_buf *xmit = &port->xmit; int count; + struct tty_struct *tty = port->port.tty; if (port->x_char) { sdio_out(port, UART_TX, port->x_char); @@ -453,7 +455,7 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port) port->x_char = 0; return; } - if (circ_empty(xmit) || port->tty->stopped || port->tty->hw_stopped) { + if (circ_empty(xmit) || tty->stopped || tty->hw_stopped) { sdio_uart_stop_tx(port); return; } @@ -468,7 +470,7 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port) } while (--count > 0); if (circ_chars_pending(xmit) < WAKEUP_CHARS) - tty_wakeup(port->tty); + tty_wakeup(tty); if (circ_empty(xmit)) sdio_uart_stop_tx(port); @@ -477,6 +479,7 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port) static void sdio_uart_check_modem_status(struct sdio_uart_port *port) { int status; + struct tty_struct *tty = port->port.tty; status = sdio_in(port, UART_MSR); @@ -491,17 +494,17 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port) port->icount.dcd++; if (status & UART_MSR_DCTS) { port->icount.cts++; - if (port->tty->termios->c_cflag & CRTSCTS) { + if (tty->termios->c_cflag & CRTSCTS) { int cts = (status & UART_MSR_CTS); - if (port->tty->hw_stopped) { + if (tty->hw_stopped) { if (cts) { - port->tty->hw_stopped = 0; + tty->hw_stopped = 0; sdio_uart_start_tx(port); - tty_wakeup(port->tty); + tty_wakeup(tty); } } else { if (!cts) { - port->tty->hw_stopped = 1; + tty->hw_stopped = 1; sdio_uart_stop_tx(port); } } @@ -546,12 +549,13 @@ static int sdio_uart_startup(struct sdio_uart_port *port) { unsigned long page; int ret; + struct tty_struct *tty = port->port.tty; /* * Set the TTY IO error marker - we will only clear this * once we have successfully opened the port. */ - set_bit(TTY_IO_ERROR, &port->tty->flags); + set_bit(TTY_IO_ERROR, &tty->flags); /* Initialise and allocate the transmit buffer. */ page = __get_free_page(GFP_KERNEL); @@ -595,14 +599,14 @@ static int sdio_uart_startup(struct sdio_uart_port *port) port->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE; port->mctrl = TIOCM_OUT2; - sdio_uart_change_speed(port, port->tty->termios, NULL); + sdio_uart_change_speed(port, tty->termios, NULL); - if (port->tty->termios->c_cflag & CBAUD) + if (tty->termios->c_cflag & CBAUD) sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); - if (port->tty->termios->c_cflag & CRTSCTS) + if (tty->termios->c_cflag & CRTSCTS) if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) - port->tty->hw_stopped = 1; + tty->hw_stopped = 1; clear_bit(TTY_IO_ERROR, &port->tty->flags); @@ -634,7 +638,7 @@ static void sdio_uart_shutdown(struct sdio_uart_port *port) /* TODO: wait here for TX FIFO to drain */ /* Turn off DTR and RTS early. */ - if (port->tty->termios->c_cflag & HUPCL) + if (port->port.tty->termios->c_cflag & HUPCL) sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); /* Disable interrupts from this port */ @@ -684,11 +688,11 @@ static int sdio_uart_open(struct tty_struct *tty, struct file *filp) if (!port->opened) { tty->driver_data = port; - port->tty = tty; + port->port.tty = tty; ret = sdio_uart_startup(port); if (ret) { tty->driver_data = NULL; - port->tty = NULL; + port->port.tty = NULL; mutex_unlock(&port->open_lock); sdio_uart_port_put(port); return ret; @@ -723,7 +727,7 @@ static void sdio_uart_close(struct tty_struct *tty, struct file * filp) tty->closing = 1; sdio_uart_shutdown(port); tty_ldisc_flush(tty); - port->tty = NULL; + port->port.tty = NULL; tty->driver_data = NULL; tty->closing = 0; } @@ -1068,6 +1072,7 @@ static int sdio_uart_probe(struct sdio_func *func, port->func = func; sdio_set_drvdata(func, port); + tty_port_init(&port->port); ret = sdio_uart_add_port(port); if (ret) { -- cgit v1.2.3 From 0395b48c78ed822f251ab15d0fbc3ce06f41ffb1 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 5 Nov 2009 13:28:17 +0000 Subject: sdio_uart: Fix oops caused by the previous changeset Now... testing reveals that the very first patch "sdio_uart: use tty_port" causes a segmentation fault in sdio_uart_open(): Unable to handle kernel NULL pointer dereference at virtual address 00000084 pgd = dfb44000 [00000084] *pgd=1fb99031, *pte=00000000, *ppte=00000000 Internal error: Oops: 17 [#1] PREEMPT last sysfs file: /sys/devices/platform/mvsdio/mmc_host/mmc0/mmc0:f111/uevent Modules linked in: CPU: 0 Not tainted (2.6.32-rc5-next-20091102-00001-gb36eae9 #10) PC is at sdio_uart_open+0x204/0x2cc [...] Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/card/sdio_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/card/sdio_uart.c') diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index c2759dbfdfd..671fe5efabf 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -608,7 +608,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port) if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) tty->hw_stopped = 1; - clear_bit(TTY_IO_ERROR, &port->tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); /* Kick the IRQ handler once while we're still holding the host lock */ sdio_uart_irq(port->func); -- cgit v1.2.3 From 530646f4695b396aeeec2ca912dcc3a9c95e0f52 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 5 Nov 2009 13:28:29 +0000 Subject: sdio_uart: refcount the tty objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tty can go away underneath us, so we must refcount it. Do the naïve implementation initially. We will worry about startup shortly. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/card/sdio_uart.c | 59 +++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 17 deletions(-) (limited to 'drivers/mmc/card/sdio_uart.c') diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index 671fe5efabf..86ad5434112 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -172,8 +172,13 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port) sdio_claim_host(func); port->func = NULL; mutex_unlock(&port->func_lock); - if (port->opened) - tty_hangup(port->port.tty); + if (port->opened) { + struct tty_struct *tty = tty_port_tty_get(&port->port); + /* tty_hangup is async so is this safe as is ?? */ + if (tty) + tty_hangup(tty); + tty_kref_put(tty); + } mutex_unlock(&port->open_lock); sdio_release_irq(func); sdio_disable_func(func); @@ -392,7 +397,7 @@ static void sdio_uart_stop_rx(struct sdio_uart_port *port) static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *status) { - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port); unsigned int ch, flag; int max_count = 256; @@ -429,25 +434,30 @@ static void sdio_uart_receive_chars(struct sdio_uart_port *port, } if ((*status & port->ignore_status_mask & ~UART_LSR_OE) == 0) - tty_insert_flip_char(tty, ch, flag); + if (tty) + tty_insert_flip_char(tty, ch, flag); /* * Overrun is special. Since it's reported immediately, * it doesn't affect the current character. */ if (*status & ~port->ignore_status_mask & UART_LSR_OE) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + if (tty) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); *status = sdio_in(port, UART_LSR); } while ((*status & UART_LSR_DR) && (max_count-- > 0)); - tty_flip_buffer_push(tty); + if (tty) { + tty_flip_buffer_push(tty); + tty_kref_put(tty); + } } static void sdio_uart_transmit_chars(struct sdio_uart_port *port) { struct circ_buf *xmit = &port->xmit; int count; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty; if (port->x_char) { sdio_out(port, UART_TX, port->x_char); @@ -455,8 +465,12 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port) port->x_char = 0; return; } - if (circ_empty(xmit) || tty->stopped || tty->hw_stopped) { + + tty = tty_port_tty_get(&port->port); + + if (tty == NULL || circ_empty(xmit) || tty->stopped || tty->hw_stopped) { sdio_uart_stop_tx(port); + tty_kref_put(tty); return; } @@ -474,12 +488,13 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port) if (circ_empty(xmit)) sdio_uart_stop_tx(port); + tty_kref_put(tty); } static void sdio_uart_check_modem_status(struct sdio_uart_port *port) { int status; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty; status = sdio_in(port, UART_MSR); @@ -494,7 +509,8 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port) port->icount.dcd++; if (status & UART_MSR_DCTS) { port->icount.cts++; - if (tty->termios->c_cflag & CRTSCTS) { + tty = tty_port_tty_get(&port->port); + if (tty && (tty->termios->c_cflag & CRTSCTS)) { int cts = (status & UART_MSR_CTS); if (tty->hw_stopped) { if (cts) { @@ -509,6 +525,7 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port) } } } + tty_kref_put(tty); } } @@ -548,8 +565,10 @@ static void sdio_uart_irq(struct sdio_func *func) static int sdio_uart_startup(struct sdio_uart_port *port) { unsigned long page; - int ret; - struct tty_struct *tty = port->port.tty; + int ret = -ENOMEM; + struct tty_struct *tty = tty_port_tty_get(&port->port); + + /* FIXME: What if it is NULL ?? */ /* * Set the TTY IO error marker - we will only clear this @@ -560,7 +579,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port) /* Initialise and allocate the transmit buffer. */ page = __get_free_page(GFP_KERNEL); if (!page) - return -ENOMEM; + goto err0; port->xmit.buf = (unsigned char *)page; circ_clear(&port->xmit); @@ -614,6 +633,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port) sdio_uart_irq(port->func); sdio_uart_release_func(port); + tty_kref_put(tty); return 0; err3: @@ -622,12 +642,15 @@ err2: sdio_uart_release_func(port); err1: free_page((unsigned long)port->xmit.buf); +err0: + tty_kref_put(tty); return ret; } static void sdio_uart_shutdown(struct sdio_uart_port *port) { int ret; + struct tty_struct *tty; ret = sdio_uart_claim_func(port); if (ret) @@ -637,9 +660,11 @@ static void sdio_uart_shutdown(struct sdio_uart_port *port) /* TODO: wait here for TX FIFO to drain */ + tty = tty_port_tty_get(&port->port); /* Turn off DTR and RTS early. */ - if (port->port.tty->termios->c_cflag & HUPCL) + 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 */ sdio_release_irq(port->func); @@ -688,11 +713,11 @@ static int sdio_uart_open(struct tty_struct *tty, struct file *filp) if (!port->opened) { tty->driver_data = port; - port->port.tty = tty; + tty_port_tty_set(&port->port, tty); ret = sdio_uart_startup(port); if (ret) { tty->driver_data = NULL; - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); mutex_unlock(&port->open_lock); sdio_uart_port_put(port); return ret; @@ -727,7 +752,7 @@ static void sdio_uart_close(struct tty_struct *tty, struct file * filp) tty->closing = 1; sdio_uart_shutdown(port); tty_ldisc_flush(tty); - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); tty->driver_data = NULL; tty->closing = 0; } -- cgit v1.2.3 From 0a68f64febf365313987c570ad59c9069f61306d Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 5 Nov 2009 13:28:38 +0000 Subject: sdio_uart: Move the open lock When we move to the tty_port logic the port mutex will protect open v close v hangup. Move to this first in the existing open code so we have a bisection point. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/card/sdio_uart.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'drivers/mmc/card/sdio_uart.c') diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index 86ad5434112..31f70233133 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -78,7 +78,6 @@ struct sdio_uart_port { struct tty_struct *tty; unsigned int index; unsigned int opened; - struct mutex open_lock; struct sdio_func *func; struct mutex func_lock; struct task_struct *in_sdio_uart_irq; @@ -103,7 +102,6 @@ static int sdio_uart_add_port(struct sdio_uart_port *port) int index, ret = -EBUSY; kref_init(&port->kref); - mutex_init(&port->open_lock); mutex_init(&port->func_lock); spin_lock_init(&port->write_lock); @@ -166,7 +164,7 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port) * give up on that port ASAP. * Beware: the lock ordering is critical. */ - mutex_lock(&port->open_lock); + mutex_lock(&port->port.mutex); mutex_lock(&port->func_lock); func = port->func; sdio_claim_host(func); @@ -179,7 +177,7 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port) tty_hangup(tty); tty_kref_put(tty); } - mutex_unlock(&port->open_lock); + mutex_unlock(&port->port.mutex); sdio_release_irq(func); sdio_disable_func(func); sdio_release_host(func); @@ -699,14 +697,14 @@ static int sdio_uart_open(struct tty_struct *tty, struct file *filp) if (!port) return -ENODEV; - mutex_lock(&port->open_lock); + 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->open_lock); + mutex_unlock(&port->port.mutex); sdio_uart_port_put(port); return -EBUSY; } @@ -718,13 +716,13 @@ static int sdio_uart_open(struct tty_struct *tty, struct file *filp) if (ret) { tty->driver_data = NULL; tty_port_tty_set(&port->port, NULL); - mutex_unlock(&port->open_lock); + mutex_unlock(&port->port.mutex); sdio_uart_port_put(port); return ret; } } port->opened++; - mutex_unlock(&port->open_lock); + mutex_unlock(&port->port.mutex); return 0; } @@ -735,7 +733,7 @@ static void sdio_uart_close(struct tty_struct *tty, struct file * filp) if (!port) return; - mutex_lock(&port->open_lock); + mutex_lock(&port->port.mutex); BUG_ON(!port->opened); /* @@ -744,7 +742,7 @@ static void sdio_uart_close(struct tty_struct *tty, struct file * filp) * is larger than port->count. */ if (tty->count > port->opened) { - mutex_unlock(&port->open_lock); + mutex_unlock(&port->port.mutex); return; } @@ -756,7 +754,7 @@ static void sdio_uart_close(struct tty_struct *tty, struct file * filp) tty->driver_data = NULL; tty->closing = 0; } - mutex_unlock(&port->open_lock); + mutex_unlock(&port->port.mutex); sdio_uart_port_put(port); } -- cgit v1.2.3 From 584abc3775e76c1a2abe725355915851ed23ed6c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 30 Nov 2009 13:16:09 +0000 Subject: 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: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/card/sdio_uart.c | 201 +++++++++++++++++++++++++------------------ 1 file changed, 119 insertions(+), 82 deletions(-) (limited to 'drivers/mmc/card/sdio_uart.c') diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index 31f70233133..95027b03c64 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -77,7 +77,6 @@ struct sdio_uart_port { struct kref kref; struct tty_struct *tty; unsigned int index; - unsigned int opened; struct sdio_func *func; struct mutex func_lock; struct task_struct *in_sdio_uart_irq; @@ -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) { struct sdio_func *func; + struct tty_struct *tty; BUG_ON(sdio_uart_table[port->index] != port); @@ -170,11 +170,10 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port) sdio_claim_host(func); port->func = NULL; mutex_unlock(&port->func_lock); - if (port->opened) { - struct tty_struct *tty = tty_port_tty_get(&port->port); - /* tty_hangup is async so is this safe as is ?? */ - if (tty) - tty_hangup(tty); + tty = tty_port_tty_get(&port->port); + /* tty_hangup is async so is this safe as is ?? */ + if (tty) { + tty_hangup(tty); tty_kref_put(tty); } mutex_unlock(&port->port.mutex); @@ -560,13 +559,46 @@ static void sdio_uart_irq(struct sdio_func *func) 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; - int ret = -ENOMEM; - struct tty_struct *tty = tty_port_tty_get(&port->port); + struct sdio_uart_port *port = + container_of(tport, struct sdio_uart_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 @@ -577,7 +609,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port) /* Initialise and allocate the transmit buffer. */ page = __get_free_page(GFP_KERNEL); if (!page) - goto err0; + return -ENOMEM; port->xmit.buf = (unsigned char *)page; circ_clear(&port->xmit); @@ -631,7 +663,6 @@ static int sdio_uart_startup(struct sdio_uart_port *port) sdio_uart_irq(port->func); sdio_uart_release_func(port); - tty_kref_put(tty); return 0; err3: @@ -640,15 +671,25 @@ err2: sdio_uart_release_func(port); err1: free_page((unsigned long)port->xmit.buf); -err0: - tty_kref_put(tty); 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; - struct tty_struct *tty; ret = sdio_uart_claim_func(port); if (ret) @@ -656,14 +697,6 @@ static void sdio_uart_shutdown(struct sdio_uart_port *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 */ sdio_release_irq(port->func); port->ier = 0; @@ -688,74 +721,68 @@ skip: free_page((unsigned long)port->xmit.buf); } -static int sdio_uart_open(struct tty_struct *tty, struct file *filp) -{ - struct sdio_uart_port *port; - int ret; - - port = sdio_uart_port_get(tty->index); - if (!port) - return -ENODEV; - - mutex_lock(&port->port.mutex); +/** + * sdio_uart_install - install method + * @driver: the driver in use (sdio_uart in our case) + * @tty: the tty being bound + * + * Look up and bind the tty and the driver together. Initialize + * any needed private data (in our case the termios) + */ - /* - * 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); +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; + driver->ttys[idx] = tty; + } else sdio_uart_port_put(port); - return -EBUSY; - } + return ret; - if (!port->opened) { - tty->driver_data = port; - tty_port_tty_set(&port->port, tty); - ret = sdio_uart_startup(port); - if (ret) { - tty->driver_data = NULL; - tty_port_tty_set(&port->port, NULL); - mutex_unlock(&port->port.mutex); - sdio_uart_port_put(port); - 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; + 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); - BUG_ON(!port->opened); +static int sdio_uart_open(struct tty_struct *tty, struct file *filp) +{ + struct sdio_uart_port *port = tty->driver_data; + return tty_port_open(&port->port, tty, filp); +} - /* - * This is messy. The tty layer calls us even when open() - * returned an error. Ignore this close request if tty->count - * is larger than port->count. - */ - if (tty->count > port->opened) { - mutex_unlock(&port->port.mutex); - return; - } +static void sdio_uart_close(struct tty_struct *tty, struct file * filp) +{ + struct sdio_uart_port *port = tty->driver_data; + tty_port_close(&port->port, tty, filp); +} - if (--port->opened == 0) { - tty->closing = 1; - sdio_uart_shutdown(port); - tty_ldisc_flush(tty); - 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 void sdio_uart_hangup(struct tty_struct *tty) +{ + struct sdio_uart_port *port = tty->driver_data; + tty_port_hangup(&port->port); } 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 = { .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 = { .open = sdio_uart_open, .close = sdio_uart_close, @@ -1031,9 +1064,12 @@ static const struct tty_operations sdio_uart_ops = { .throttle = sdio_uart_throttle, .unthrottle = sdio_uart_unthrottle, .set_termios = sdio_uart_set_termios, + .hangup = sdio_uart_hangup, .break_ctl = sdio_uart_break_ctl, .tiocmget = sdio_uart_tiocmget, .tiocmset = sdio_uart_tiocmset, + .install = sdio_uart_install, + .cleanup = sdio_uart_cleanup, .proc_fops = &sdio_uart_proc_fops, }; @@ -1096,6 +1132,7 @@ static int sdio_uart_probe(struct sdio_func *func, port->func = func; sdio_set_drvdata(func, port); tty_port_init(&port->port); + port->port.ops = &sdio_uart_port_ops; ret = sdio_uart_add_port(port); if (ret) { -- cgit v1.2.3 From 6238e712aff51ae74177cee5b2a63c0e37044e8f Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 30 Nov 2009 13:16:14 +0000 Subject: tty: sdio_uart: Fix termios handling Switching between two non standard baud rates fails because of the cflag test. Do as we did elsewhere and just kill the "optimisation". Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/card/sdio_uart.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/mmc/card/sdio_uart.c') diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index 95027b03c64..21043974213 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -902,12 +902,6 @@ static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_t struct sdio_uart_port *port = tty->driver_data; unsigned int cflag = tty->termios->c_cflag; -#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - - if ((cflag ^ old_termios->c_cflag) == 0 && - RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) - return; - if (sdio_uart_claim_func(port) != 0) return; -- cgit v1.2.3 From c271cf37ba17631e371c97e2e8c8c353a83793e2 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 30 Nov 2009 13:16:25 +0000 Subject: tty: sdio_uart: Style fixes Running the current code through checkpatch shows a few bits of noise mostly but not entirely from before the changes. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/card/sdio_uart.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/mmc/card/sdio_uart.c') diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index 21043974213..b999972eaf2 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -465,7 +465,8 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port) tty = tty_port_tty_get(&port->port); - if (tty == NULL || circ_empty(xmit) || tty->stopped || tty->hw_stopped) { + if (tty == NULL || circ_empty(xmit) || + tty->stopped || tty->hw_stopped) { sdio_uart_stop_tx(port); tty_kref_put(tty); return; @@ -645,7 +646,7 @@ static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty) */ sdio_out(port, UART_LCR, UART_LCR_WLEN8); - port->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE; + port->ier = UART_IER_RLSI|UART_IER_RDI|UART_IER_RTOIE|UART_IER_UUE; port->mctrl = TIOCM_OUT2; sdio_uart_change_speed(port, tty->termios, NULL); @@ -674,7 +675,6 @@ err1: return ret; } - /** * sdio_uart_shutdown - stop hardware * @tport: tty port to shut down @@ -745,7 +745,6 @@ static int sdio_uart_install(struct tty_driver *driver, struct tty_struct *tty) } else sdio_uart_port_put(port); return ret; - } /** @@ -785,7 +784,7 @@ static void sdio_uart_hangup(struct tty_struct *tty) tty_port_hangup(&port->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, int count) { struct sdio_uart_port *port = tty->driver_data; @@ -810,7 +809,7 @@ static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf, } spin_unlock(&port->write_lock); - if ( !(port->ier & UART_IER_THRI)) { + if (!(port->ier & UART_IER_THRI)) { int err = sdio_uart_claim_func(port); if (!err) { sdio_uart_start_tx(port); @@ -897,7 +896,8 @@ static void sdio_uart_unthrottle(struct tty_struct *tty) sdio_uart_release_func(port); } -static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios) +static void sdio_uart_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) { struct sdio_uart_port *port = tty->driver_data; unsigned int cflag = tty->termios->c_cflag; @@ -976,7 +976,7 @@ static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file, int result; result = sdio_uart_claim_func(port); - if(!result) { + if (!result) { sdio_uart_update_mctrl(port, set, clear); sdio_uart_release_func(port); } @@ -994,7 +994,7 @@ static int sdio_uart_proc_show(struct seq_file *m, void *v) struct sdio_uart_port *port = sdio_uart_port_get(i); if (port) { seq_printf(m, "%d: uart:SDIO", i); - if(capable(CAP_SYS_ADMIN)) { + if (capable(CAP_SYS_ADMIN)) { seq_printf(m, " tx:%d rx:%d", port->icount.tx, port->icount.rx); if (port->icount.frame) @@ -1100,7 +1100,7 @@ static int sdio_uart_probe(struct sdio_func *func, } if (!tpl) { printk(KERN_WARNING - "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n", + "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n", sdio_func_id(func)); kfree(port); return -EINVAL; @@ -1133,7 +1133,8 @@ static int sdio_uart_probe(struct sdio_func *func, kfree(port); } else { struct device *dev; - dev = tty_register_device(sdio_uart_tty_driver, port->index, &func->dev); + dev = tty_register_device(sdio_uart_tty_driver, + port->index, &func->dev); if (IS_ERR(dev)) { sdio_uart_port_remove(port); ret = PTR_ERR(dev); -- cgit v1.2.3 From 4b3b49bb77eddb540e7c69e2129f5334cf713bf8 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 30 Nov 2009 13:16:36 +0000 Subject: tty: sdio_uart: add modem functionality Add the POSIX block for carrier Linux TIOCMIWAIT functionality is still lacking from the driver. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/card/sdio_uart.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/card/sdio_uart.c') diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index b999972eaf2..2a13db54ffc 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,7 @@ struct sdio_uart_port { struct uart_icount icount; unsigned int uartclk; unsigned int mctrl; + unsigned int rx_mctrl; unsigned int read_status_mask; unsigned int ignore_status_mask; unsigned char x_char; @@ -220,6 +222,8 @@ static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port) unsigned char status; unsigned int ret; + /* FIXME: What stops this losing the delta bits and breaking + sdio_uart_check_modem_status ? */ status = sdio_in(port, UART_MSR); ret = 0; @@ -503,8 +507,20 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port) port->icount.rng++; if (status & UART_MSR_DDSR) port->icount.dsr++; - if (status & UART_MSR_DDCD) + if (status & UART_MSR_DDCD) { port->icount.dcd++; + /* DCD raise - wake for open */ + if (status & UART_MSR_DCD) + wake_up_interruptible(&port->port.open_wait); + else { + /* DCD drop - hang up if tty attached */ + tty = tty_port_tty_get(&port->port); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } + } + } if (status & UART_MSR_DCTS) { port->icount.cts++; tty = tty_port_tty_get(&port->port); @@ -560,6 +576,20 @@ static void sdio_uart_irq(struct sdio_func *func) port->in_sdio_uart_irq = NULL; } +static int uart_carrier_raised(struct tty_port *tport) +{ + struct sdio_uart_port *port = + container_of(tport, struct sdio_uart_port, port); + unsigned int ret = sdio_uart_claim_func(port); + if (ret) /* Missing hardware shoudn't block for carrier */ + return 1; + ret = sdio_uart_get_mctrl(port); + sdio_uart_release_func(port); + if (ret & TIOCM_CAR) + return 1; + return 0; +} + /** * uart_dtr_rts - port helper to set uart signals * @tport: tty port to be updated @@ -1044,6 +1074,7 @@ static const struct file_operations sdio_uart_proc_fops = { static const struct tty_port_operations sdio_uart_port_ops = { .dtr_rts = uart_dtr_rts, + .carrier_raised = uart_carrier_raised, .shutdown = sdio_uart_shutdown, .activate = sdio_uart_activate, }; -- cgit v1.2.3 From 1f100b323d19469b06a63ccd6130ed71760145cc Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 30 Nov 2009 13:16:30 +0000 Subject: tty: sdio_uart: Fix the locking on "func" for new code The new dtr_rts function didn't take the port->func lock as it should so add use of the lock there. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/card/sdio_uart.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/mmc/card/sdio_uart.c') diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index 2a13db54ffc..f53755533e7 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -603,10 +603,14 @@ static void uart_dtr_rts(struct tty_port *tport, int onoff) { struct sdio_uart_port *port = container_of(tport, struct sdio_uart_port, port); + int ret = sdio_uart_claim_func(port); + if (ret) + return; if (onoff == 0) sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); else sdio_uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); + sdio_uart_release_func(port); } /** -- cgit v1.2.3