2009-11-03 14:30:34

by Alan

[permalink] [raw]
Subject: [PATCH 00/11] Sort out sdio_uart - stage two

With the bugs Nicolas reported hopefully now nailed.
---

Alan Cox (10):
sdio_uart: add modem functionality
sdio_uart: Fix the locking on "func" for new code
sdio_uart: Style fixes
sdio_uart: Use kfifo instead of the messy circ stuff
sdio_uart: Fix termios handling
sdio_uart: Switch to the open/close helpers
sdio_uart: Move the open lock
sdio_uart: refcount the tty objects
sdio_uart: use tty_port
tty_port: Move hupcl handling

Nicolas Pitre (1):
sdio_uart: Fix oops caused by the previous changeset


drivers/char/tty_port.c | 13 +
drivers/mmc/card/sdio_uart.c | 392 ++++++++++++++++++++++++------------------
2 files changed, 233 insertions(+), 172 deletions(-)

--
"The IETF already has more than enough RFCs that codify the obvious, make
stupidity illegal, support truth, justice, and the IETF way, and generally
demonstrate the author is a brilliant and valuable Contributor to The
Standards Process." -- Vernon Schryver


2009-11-03 14:30:48

by Alan

[permalink] [raw]
Subject: [PATCH 01/11] tty_port: Move hupcl handling

Move the HUCPL handling from the end of close_port_start to the beginning
of close_port_end. What this actually does is change the ordering from

port shutdown
port->dtr_rts

to

port->dtr_rts
port shutdown

Some hardware drops the physical connection on shutdown so we must perform
the port operations before the shutdown.

Signed-off-by: Alan Cox <[email protected]>
---

drivers/char/tty_port.c | 13 ++++++++-----
1 files changed, 8 insertions(+), 5 deletions(-)


diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c
index da12fae..cf4b16a 100644
--- a/drivers/char/tty_port.c
+++ b/drivers/char/tty_port.c
@@ -357,6 +357,14 @@ int tty_port_close_start(struct tty_port *port,
timeout = 2 * HZ;
schedule_timeout_interruptible(timeout);
}
+ /* Flush the ldisc buffering */
+ tty_ldisc_flush(tty);
+
+ /* Drop DTR/RTS if HUPCL is set. This causes any attached modem to
+ hang up the line */
+ if (tty->termios->c_cflag & HUPCL)
+ tty_port_lower_dtr_rts(port);
+
/* Don't call port->drop for the last reference. Callers will want
to drop the last active reference in ->shutdown() or the tty
shutdown path */
@@ -368,11 +376,6 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty)
{
unsigned long flags;

- tty_ldisc_flush(tty);
-
- if (tty->termios->c_cflag & HUPCL)
- tty_port_lower_dtr_rts(port);
-
spin_lock_irqsave(&port->lock, flags);
tty->closing = 0;

2009-11-03 14:30:59

by Alan

[permalink] [raw]
Subject: [PATCH 02/11] 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 <[email protected]>
---

drivers/mmc/card/sdio_uart.c | 41 +++++++++++++++++++++++------------------
1 files changed, 23 insertions(+), 18 deletions(-)


diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index b8e7c5a..c2759db 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) {

2009-11-03 14:31:10

by Alan

[permalink] [raw]
Subject: [PATCH 03/11] sdio_uart: Fix oops caused by the previous changeset

From: Nicolas Pitre <[email protected]>

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 <[email protected]>
---

drivers/mmc/card/sdio_uart.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)


diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index c2759db..671fe5e 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);

2009-11-03 14:31:24

by Alan

[permalink] [raw]
Subject: [PATCH 04/11] sdio_uart: refcount the tty objects

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 <[email protected]>
---

drivers/mmc/card/sdio_uart.c | 58 ++++++++++++++++++++++++++++++------------
1 files changed, 41 insertions(+), 17 deletions(-)


diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index 671fe5e..51aeaf6 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,7 +465,10 @@ 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);
return;
}
@@ -474,12 +487,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 +508,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 +524,7 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
}
}
}
+ tty_kref_put(tty);
}
}

@@ -548,8 +564,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 +578,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 +632,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 +641,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 +659,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 +712,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 +751,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;
}

2009-11-03 14:31:32

by Alan

[permalink] [raw]
Subject: [PATCH 05/11] 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 <[email protected]>
---

drivers/mmc/card/sdio_uart.c | 20 +++++++++-----------
1 files changed, 9 insertions(+), 11 deletions(-)


diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index 51aeaf6..f76741d 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);
@@ -698,14 +696,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;
}
@@ -717,13 +715,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;
}

@@ -734,7 +732,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);

/*
@@ -743,7 +741,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;
}

@@ -755,7 +753,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);
}

2009-11-03 14:31:41

by Alan

[permalink] [raw]
Subject: [PATCH 06/11] 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 <[email protected]>
---

drivers/mmc/card/sdio_uart.c | 201 +++++++++++++++++++++++++-----------------
1 files changed, 119 insertions(+), 82 deletions(-)


diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index f76741d..3e2ae66 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);
@@ -559,13 +558,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);
+}
+
+/**
+ * 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.
+ */

- /* FIXME: What if it is NULL ?? */
+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
@@ -576,7 +608,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);

@@ -630,7 +662,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:
@@ -639,15 +670,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)
@@ -655,14 +696,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;
@@ -687,74 +720,68 @@ skip:
free_page((unsigned long)port->xmit.buf);
}

-static int sdio_uart_open(struct tty_struct *tty, struct file *filp)
+/**
+ * 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)
+ */
+
+static int sdio_uart_install(struct tty_driver *driver, struct tty_struct *tty)
{
- struct sdio_uart_port *port;
- int ret;
+ 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 ret;
+
+}

- port = sdio_uart_port_get(tty->index);
- if (!port)
- return -ENODEV;
+/**
+ * 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
+ */

- mutex_lock(&port->port.mutex);
+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);
+}

- /*
- * 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;
- }
+/*
+ * Open/close/hangup is now entirely boilerplate
+ */

- 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 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);
}

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)
- return;
-
- mutex_lock(&port->port.mutex);
- BUG_ON(!port->opened);
-
- /*
- * 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;
- }
-
- 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,
@@ -1020,6 +1047,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,
@@ -1030,9 +1063,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,
};

@@ -1095,6 +1131,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) {

2009-11-03 14:31:48

by Alan

[permalink] [raw]
Subject: [PATCH 07/11] 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 <[email protected]>
---

drivers/mmc/card/sdio_uart.c | 6 ------
1 files changed, 0 insertions(+), 6 deletions(-)


diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index 3e2ae66..d41aa35 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -901,12 +901,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;

2009-11-03 14:31:58

by Alan

[permalink] [raw]
Subject: [PATCH 08/11] sdio_uart: Use kfifo instead of the messy circ stuff

Probably all the tty code should switch to this, especially when the new
lockless kfifo is merged.

Signed-off-by: Alan Cox <[email protected]>
---

drivers/mmc/card/sdio_uart.c | 86 +++++++++++++-----------------------------
1 files changed, 27 insertions(+), 59 deletions(-)


diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index d41aa35..674f3bf 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -32,7 +32,7 @@
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/serial_reg.h>
-#include <linux/circ_buf.h>
+#include <linux/kfifo.h>
#include <linux/gfp.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
@@ -46,18 +46,9 @@
#define UART_NR 8 /* Number of UARTs this driver can handle */


-#define UART_XMIT_SIZE PAGE_SIZE
+#define FIFO_SIZE PAGE_SIZE
#define WAKEUP_CHARS 256

-#define circ_empty(circ) ((circ)->head == (circ)->tail)
-#define circ_clear(circ) ((circ)->head = (circ)->tail = 0)
-
-#define circ_chars_pending(circ) \
- (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
-
-#define circ_chars_free(circ) \
- (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
-

struct uart_icount {
__u32 cts;
@@ -81,7 +72,7 @@ struct sdio_uart_port {
struct mutex func_lock;
struct task_struct *in_sdio_uart_irq;
unsigned int regs_offset;
- struct circ_buf xmit;
+ struct kfifo *xmit_fifo;
spinlock_t write_lock;
struct uart_icount icount;
unsigned int uartclk;
@@ -103,6 +94,9 @@ static int sdio_uart_add_port(struct sdio_uart_port *port)
kref_init(&port->kref);
mutex_init(&port->func_lock);
spin_lock_init(&port->write_lock);
+ port->xmit_fifo = kfifo_alloc(FIFO_SIZE, GFP_KERNEL, &port->write_lock);
+ if (port->xmit_fifo == NULL)
+ return -ENOMEM;

spin_lock(&sdio_uart_table_lock);
for (index = 0; index < UART_NR; index++) {
@@ -452,9 +446,11 @@ static void sdio_uart_receive_chars(struct sdio_uart_port *port,

static void sdio_uart_transmit_chars(struct sdio_uart_port *port)
{
- struct circ_buf *xmit = &port->xmit;
+ struct kfifo *xmit = port->xmit_fifo;
int count;
struct tty_struct *tty;
+ u8 iobuf[16];
+ int len;

if (port->x_char) {
sdio_out(port, UART_TX, port->x_char);
@@ -465,24 +461,21 @@ 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 || !kfifo_len(xmit) || tty->stopped || tty->hw_stopped) {
sdio_uart_stop_tx(port);
return;
}

- count = 16;
- do {
- sdio_out(port, UART_TX, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ len = kfifo_get(xmit, iobuf, 16);
+ for (count = 0; count < len; count++) {
+ sdio_out(port, UART_TX, iobuf[count]);
port->icount.tx++;
- if (circ_empty(xmit))
- break;
- } while (--count > 0);
+ }

- if (circ_chars_pending(xmit) < WAKEUP_CHARS)
+ if (len < WAKEUP_CHARS)
tty_wakeup(tty);

- if (circ_empty(xmit))
+ if (len == 0)
sdio_uart_stop_tx(port);
tty_kref_put(tty);
}
@@ -605,22 +598,17 @@ static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty)
*/
set_bit(TTY_IO_ERROR, &tty->flags);

- /* Initialise and allocate the transmit buffer. */
- page = __get_free_page(GFP_KERNEL);
- if (!page)
- return -ENOMEM;
- port->xmit.buf = (unsigned char *)page;
- circ_clear(&port->xmit);
+ kfifo_reset(port->xmit_fifo);

ret = sdio_uart_claim_func(port);
if (ret)
- goto err1;
+ return ret;
ret = sdio_enable_func(port->func);
if (ret)
- goto err2;
+ goto err1;
ret = sdio_claim_irq(port->func, sdio_uart_irq);
if (ret)
- goto err3;
+ goto err2;

/*
* Clear the FIFO buffers and disable them.
@@ -664,12 +652,10 @@ static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty)
sdio_uart_release_func(port);
return 0;

-err3:
- sdio_disable_func(port->func);
err2:
- sdio_uart_release_func(port);
+ sdio_disable_func(port->func);
err1:
- free_page((unsigned long)port->xmit.buf);
+ sdio_uart_release_func(port);
return ret;
}

@@ -692,7 +678,7 @@ static void sdio_uart_shutdown(struct tty_port *tport)

ret = sdio_uart_claim_func(port);
if (ret)
- goto skip;
+ return ret;

sdio_uart_stop_rx(port);

@@ -714,10 +700,6 @@ static void sdio_uart_shutdown(struct tty_port *tport)
sdio_disable_func(port->func);

sdio_uart_release_func(port);
-
-skip:
- /* Free the transmit buffer page. */
- free_page((unsigned long)port->xmit.buf);
}

/**
@@ -788,26 +770,12 @@ static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf,
int count)
{
struct sdio_uart_port *port = tty->driver_data;
- struct circ_buf *circ = &port->xmit;
- int c, ret = 0;
+ int ret;

if (!port->func)
return -ENODEV;

- spin_lock(&port->write_lock);
- while (1) {
- c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
- if (count < c)
- c = count;
- if (c <= 0)
- break;
- memcpy(circ->buf + circ->head, buf, c);
- circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
- buf += c;
- count -= c;
- ret += c;
- }
- spin_unlock(&port->write_lock);
+ ret = kfifo_put(port->xmit_fifo, buf, count);

if ( !(port->ier & UART_IER_THRI)) {
int err = sdio_uart_claim_func(port);
@@ -825,13 +793,13 @@ static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf,
static int sdio_uart_write_room(struct tty_struct *tty)
{
struct sdio_uart_port *port = tty->driver_data;
- return port ? circ_chars_free(&port->xmit) : 0;
+ return FIFO_SIZE - kfifo_len(port->xmit_fifo);
}

static int sdio_uart_chars_in_buffer(struct tty_struct *tty)
{
struct sdio_uart_port *port = tty->driver_data;
- return port ? circ_chars_pending(&port->xmit) : 0;
+ return kfifo_len(port->xmit_fifo);
}

static void sdio_uart_send_xchar(struct tty_struct *tty, char ch)

2009-11-03 14:32:10

by Alan

[permalink] [raw]
Subject: [PATCH 09/11] 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 <[email protected]>
---

drivers/mmc/card/sdio_uart.c | 33 +++++++++++++++------------------
1 files changed, 15 insertions(+), 18 deletions(-)


diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index 674f3bf..ebb5d4b 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -461,7 +461,8 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port)

tty = tty_port_tty_get(&port->port);

- if (tty == NULL || !kfifo_len(xmit) || tty->stopped || tty->hw_stopped) {
+ if (tty == NULL || !kfifo_len(xmit)
+ || tty->stopped || tty->hw_stopped) {
sdio_uart_stop_tx(port);
return;
}
@@ -589,7 +590,6 @@ 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;

/*
@@ -632,7 +632,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);
@@ -659,7 +659,6 @@ err1:
return ret;
}

-
/**
* sdio_uart_shutdown - stop hardware
* @tport: tty port to shut down
@@ -674,11 +673,8 @@ static void sdio_uart_shutdown(struct tty_port *tport)
{
struct sdio_uart_port *port =
container_of(tport, struct sdio_uart_port, port);
- int ret;
-
- ret = sdio_uart_claim_func(port);
- if (ret)
- return ret;
+ if (sdio_uart_claim_func(port))
+ return;

sdio_uart_stop_rx(port);

@@ -726,7 +722,6 @@ static int sdio_uart_install(struct tty_driver *driver, struct tty_struct *tty)
} else
sdio_uart_port_put(port);
return ret;
-
}

/**
@@ -766,7 +761,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;
@@ -777,7 +772,7 @@ static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf,

ret = kfifo_put(port->xmit_fifo, buf, count);

- 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);
@@ -864,7 +859,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;
@@ -943,7 +939,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);
}
@@ -961,7 +957,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)
@@ -1014,7 +1010,7 @@ static const struct tty_port_operations sdio_uart_port_ops = {
.shutdown = sdio_uart_shutdown,
.activate = sdio_uart_activate,
};
-
+
static const struct tty_operations sdio_uart_ops = {
.open = sdio_uart_open,
.close = sdio_uart_close,
@@ -1067,7 +1063,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;
@@ -1100,7 +1096,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);

2009-11-03 14:32:25

by Alan

[permalink] [raw]
Subject: [PATCH 10/11] 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 <[email protected]>
---

drivers/mmc/card/sdio_uart.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)


diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index ebb5d4b..0a3acfb 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -565,10 +565,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);
}

/**

2009-11-03 14:33:08

by Alan

[permalink] [raw]
Subject: [PATCH 11/11] 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 <[email protected]>
---

drivers/mmc/card/sdio_uart.c | 33 ++++++++++++++++++++++++++++++++-
1 files changed, 32 insertions(+), 1 deletions(-)


diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index 0a3acfb..e72e2f2 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -29,6 +29,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/serial_reg.h>
@@ -214,6 +216,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;
@@ -495,8 +499,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);
@@ -552,6 +568,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
@@ -1011,6 +1041,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,
};

2009-11-03 19:58:07

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH 00/11] Sort out sdio_uart - stage two

On Tue, 3 Nov 2009, Alan Cox wrote:

> With the bugs Nicolas reported hopefully now nailed.

Progress, but still not there yet.

I'm still using the same "cat /dev/ttySDIO0" test with a GPS card.

Yanking the card out does terminate the cat process properly. Inserting
the card back in and restarting cat always fails with:

cat: /dev/ttySDIO0: Input/output error

The same thing occurs if I terminate cat with ^C instead of pulling the
card out.

>From what I can see, sdio_uart_open() and sdio_uart_activate() are
called on the first cat invocation, and then sdio_uart_close() and
sdio_uart_shutdown() when cat is stopped with ^C. However neither
sdio_uart_open() nor sdio_uart_activate() is ever called anymore with
any subsequent cat invocations until a reboot. Some upper layer must
have taken upon itself to return -EIO to user space.


Nicolas

2009-11-04 00:52:48

by Alan

[permalink] [raw]
Subject: Re: [PATCH 00/11] Sort out sdio_uart - stage two

> >From what I can see, sdio_uart_open() and sdio_uart_activate() are
> called on the first cat invocation, and then sdio_uart_close() and
> sdio_uart_shutdown() when cat is stopped with ^C. However neither

I would have expected the cat to terminate when the port is unplugged
(and the uart remove method called - doing a hangup)

> sdio_uart_open() nor sdio_uart_activate() is ever called anymore with
> any subsequent cat invocations until a reboot. Some upper layer must
> have taken upon itself to return -EIO to user space.

The first call into the driver is to sdio_uart_install which looks up the
port from tty->index and would blow up if for some reason the get method
failed.

Still digging.

2009-11-04 01:48:10

by Nicolas Pitre

[permalink] [raw]
Subject: Re: [PATCH 00/11] Sort out sdio_uart - stage two

On Wed, 4 Nov 2009, Alan Cox wrote:

> > >From what I can see, sdio_uart_open() and sdio_uart_activate() are
> > called on the first cat invocation, and then sdio_uart_close() and
> > sdio_uart_shutdown() when cat is stopped with ^C. However neither
>
> I would have expected the cat to terminate when the port is unplugged
> (and the uart remove method called - doing a hangup)

That works fine... the first time only. After the card is inserted back
then -EIO is always returned on open() from user space, same thing as
subsequent open() after ^C.

> > sdio_uart_open() nor sdio_uart_activate() is ever called anymore with
> > any subsequent cat invocations until a reboot. Some upper layer must
> > have taken upon itself to return -EIO to user space.
>
> The first call into the driver is to sdio_uart_install which looks up the
> port from tty->index and would blow up if for some reason the get method
> failed.

Well, same issue: I see it being called the first time. No calls what
so ever afterward until the next reboot.


Nicolas