Linux Serial subsystem development
 help / color / mirror / Atom feed
* [PATCH] mpc5200b/uart: select more tolerant uart prescaler on low baudrates
From: Frank Benkert @ 2012-03-05 15:14 UTC (permalink / raw)
  To: alan; +Cc: gregkh, linux-serial, linux-kernel, Frank Benkert

In addition to the /32 prescaler, the MPC5200B supports a second baudrate 
prescaler /4 to reach higher baudrates.
The current calculation (introduced with commit 0d1f22e4) in the kernel 
preferes this low prescaler as often as possible, but with some imprecise 
counterparts the communication on low baudrates fails. 
According a support-mail from freescale the low prescaler (/4) allows just 
1% tolerance in bittiming in contrast to 4% of the high prescaler (/32). 
The prescaler not only affects the baudrate-calculation, but also the sampling 
of the bits on the wire.

With this patch, we use the slightly less precise, but higher tolerant 
prescaler calculation on low baudrates up to (and including) 115200 baud and 
the more precise calculation above.

Tested on a custom MPC5200B board with "fsl,mpc5200b-psc-uart".

Signed-off-by: Frank Benkert <frank.benkert@avat.de>

---

Calculation Examples with prescaler (PS) 4 and 32 and divisor (DIV) on various 
baudrates. Real stands for the real baudrate generated and Diff for the 
differences between:
     50 Baud PS 32 DIV 0xa122 Real      50 Diff   0.00%
     75 Baud PS 32 DIV 0x6b6c Real      75 Diff   0.00%
    110 Baud PS 32 DIV 0x493e Real     110 Diff   0.00%
    134 Baud PS 32 DIV 0x3c20 Real     133 Diff   0.75%
    150 Baud PS 32 DIV 0x35b6 Real     150 Diff   0.00%
    200 Baud PS 32 DIV 0x2849 Real     199 Diff   0.50%
    300 Baud PS  4 DIV 0xd6d8 Real     300 Diff   0.00%
             PS 32 DIV 0x1adb Real     300 Diff   0.00%
    600 Baud PS  4 DIV 0x6b6c Real     600 Diff   0.00%
             PS 32 DIV 0x0d6e Real     599 Diff   0.17%
   1200 Baud PS  4 DIV 0x35b6 Real    1200 Diff   0.00%
             PS 32 DIV 0x06b7 Real    1199 Diff   0.08%
   1800 Baud PS  4 DIV 0x23cf Real    1799 Diff   0.06%
             PS 32 DIV 0x047a Real    1799 Diff   0.06%
   2400 Baud PS  4 DIV 0x1adb Real    2400 Diff   0.00%
             PS 32 DIV 0x035b Real    2401 Diff - 0.04%
   4800 Baud PS  4 DIV 0x0d6e Real    4799 Diff   0.02%
             PS 32 DIV 0x01ae Real    4796 Diff   0.08%
   9600 Baud PS  4 DIV 0x06b7 Real    9598 Diff   0.02%
             PS 32 DIV 0x00d7 Real    9593 Diff   0.07%
  19200 Baud PS  4 DIV 0x035b Real   19208 Diff - 0.04%
             PS 32 DIV 0x006b Real   19275 Diff - 0.39%
  38400 Baud PS  4 DIV 0x01ae Real   38372 Diff   0.07%
             PS 32 DIV 0x0036 Real   38194 Diff   0.54%
  57600 Baud PS  4 DIV 0x011e Real   57692 Diff - 0.16%
             PS 32 DIV 0x0024 Real   57291 Diff   0.54%
  76800 Baud PS  4 DIV 0x00d7 Real   76744 Diff   0.07%
             PS 32 DIV 0x001b Real   76388 Diff   0.54%
 115200 Baud PS  4 DIV 0x008f Real  115384 Diff - 0.16%
             PS 32 DIV 0x0012 Real  114583 Diff   0.54%
 153600 Baud PS  4 DIV 0x006b Real  154205 Diff - 0.39%
             PS 32 DIV 0x000d Real  158653 Diff - 3.29%
 230400 Baud PS  4 DIV 0x0048 Real  229166 Diff   0.54%
             PS 32 DIV 0x0009 Real  229166 Diff   0.54%
 307200 Baud PS  4 DIV 0x0036 Real  305555 Diff   0.54%
             PS 32 DIV 0x0007 Real  294642 Diff   4.09%
 460800 Baud PS  4 DIV 0x0024 Real  458333 Diff   0.54%
             PS 32 DIV 0x0005 Real  412500 Diff  10.48%
 500000 Baud PS  4 DIV 0x0021 Real  500000 Diff   0.00%
             PS 32 DIV 0x0004 Real  515625 Diff - 3.13%
 576000 Baud PS  4 DIV 0x001d Real  568965 Diff   1.22%
             PS 32 DIV 0x0004 Real  515625 Diff  10.48%
 614400 Baud PS  4 DIV 0x001b Real  611111 Diff   0.54%
             PS 32 DIV 0x0003 Real  687500 Diff -11.90%
 921600 Baud PS  4 DIV 0x0012 Real  916666 Diff   0.54%
             PS 32 DIV 0x0002 Real 1031250 Diff -11.90%
1000000 Baud PS  4 DIV 0x0011 Real  970588 Diff   2.94%
             PS 32 DIV 0x0002 Real 1031250 Diff - 3.13%
1152000 Baud PS  4 DIV 0x000e Real 1178571 Diff - 2.31%
             PS 32 DIV 0x0002 Real 1031250 Diff  10.48%
1500000 Baud PS  4 DIV 0x000b Real 1500000 Diff   0.00%
             PS 32 DIV 0x0001 Real 2062500 Diff -37.50%
2000000 Baud PS  4 DIV 0x0008 Real 2062500 Diff - 3.13%
             PS 32 DIV 0x0001 Real 2062500 Diff - 3.13%
2500000 Baud PS  4 DIV 0x0007 Real 2357142 Diff   5.71%
             PS 32 DIV 0x0001 Real 2062500 Diff  17.50%
3000000 Baud PS  4 DIV 0x0006 Real 2750000 Diff   8.33%
             PS 32 DIV 0x0001 Real 2062500 Diff  31.25%
3500000 Baud PS  4 DIV 0x0005 Real 3300000 Diff   5.71%
             PS 32 DIV 0x0001 Real 2062500 Diff  41.07%
4000000 Baud PS  4 DIV 0x0004 Real 4125000 Diff - 3.13%
             PS 32 DIV 0x0001 Real 2062500 Diff  48.44%

 drivers/tty/serial/mpc52xx_uart.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c
index 1093a88..5e2c6fe 100644
--- a/drivers/tty/serial/mpc52xx_uart.c
+++ b/drivers/tty/serial/mpc52xx_uart.c
@@ -262,8 +262,9 @@ static unsigned int mpc5200b_psc_set_baudrate(struct uart_port *port,
 				  port->uartclk / 4);
 	divisor = (port->uartclk + 2 * baud) / (4 * baud);
 
-	/* select the proper prescaler and set the divisor */
-	if (divisor > 0xffff) {
+	/* select the proper prescaler and set the divisor
+	 * prefer high prescaler for more tolerance on low baudrates */
+	if (divisor > 0xffff || baud <= 115200) {
 		divisor = (divisor + 4) / 8;
 		prescaler = 0xdd00; /* /32 */
 	} else
-- 
1.5.2.1



^ permalink raw reply related

* Re: [PATCH 01/68] USB: cdc-acm, use tty_standard_install
From: Oliver Neukum @ 2012-03-05 14:14 UTC (permalink / raw)
  To: Jiri Slaby
  Cc: Jiri Slaby, gregkh, alan, linux-serial, linux-kernel,
	Oliver Neukum, USB list
In-Reply-To: <4F54C7BD.1020105@suse.cz>

Am Montag, 5. März 2012, 15:03:41 schrieb Jiri Slaby:
> Forgot to CC maintainers. Done now.
> 
> On 03/05/2012 02:51 PM, Jiri Slaby wrote:
> > This is a piece I missed the last time.
> > 
> > Do not copy the functionality all over the tree. Instead, use the
> > helper the tty layer provides us with.
> > 
> > Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Acked-by: Oliver Neukum <oneukum@suse.de>

^ permalink raw reply

* Re: [PATCH 01/68] USB: cdc-acm, use tty_standard_install
From: Jiri Slaby @ 2012-03-05 14:03 UTC (permalink / raw)
  To: Jiri Slaby
  Cc: gregkh, alan, linux-serial, linux-kernel, Oliver Neukum, USB list
In-Reply-To: <1330955575-26641-2-git-send-email-jslaby@suse.cz>

Forgot to CC maintainers. Done now.

On 03/05/2012 02:51 PM, Jiri Slaby wrote:
> This is a piece I missed the last time.
> 
> Do not copy the functionality all over the tree. Instead, use the
> helper the tty layer provides us with.
> 
> Signed-off-by: Jiri Slaby <jslaby@suse.cz>
> ---
>  drivers/usb/class/cdc-acm.c |    7 +------
>  1 file changed, 1 insertion(+), 6 deletions(-)
> 
> diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
> index 6dcc3a3..b3fd449 100644
> --- a/drivers/usb/class/cdc-acm.c
> +++ b/drivers/usb/class/cdc-acm.c
> @@ -509,17 +509,12 @@ static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
>  	if (!acm)
>  		return -ENODEV;
>  
> -	retval = tty_init_termios(tty);
> +	retval = tty_standard_install(driver, tty);
>  	if (retval)
>  		goto error_init_termios;
>  
>  	tty->driver_data = acm;
>  
> -	/* Final install (we use the default method) */
> -	tty_driver_kref_get(driver);
> -	tty->count++;
> -	driver->ttys[tty->index] = tty;
> -
>  	return 0;
>  
>  error_init_termios:


-- 
js
suse labs

^ permalink raw reply

* Re: [PATCH 15/68] TTY: ipwireless, fix tty->index handling
From: Jiri Kosina @ 2012-03-05 14:03 UTC (permalink / raw)
  To: Jiri Slaby
  Cc: gregkh, alan, linux-serial, linux-kernel, jirislaby, David Sterba
In-Reply-To: <1330955575-26641-16-git-send-email-jslaby@suse.cz>

On Mon, 5 Mar 2012, Jiri Slaby wrote:

> * do not test if tty->index is in bounds. It is always.
> * tty->index is not a minor! Fix that.
> 
> >From now on, let's assume that the parameter of the function is tty
> index with base being zero. This makes also the code more readable.
> 
> Factually, there is no real change as tty_driver->minor_start is zero,
> so the tests are equivalent. But it did not make sense. And if this
> had changed eventually, it would have caused troubles.
> 
> Signed-off-by: Jiri Slaby <jslaby@suse.cz>
> Cc: Jiri Kosina <jkosina@suse.cz>
> Cc: David Sterba <dsterba@suse.cz>

Acked-by: Jiri Kosina <jkosina@suse.cz>

Thanks Jiri.

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply

* [PATCH 36/68] TTY: simserial, pass tty down to functions
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh; +Cc: alan, linux-serial, linux-kernel, jirislaby, Tony Luck,
	Fenghua Yu
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

This avoids pain with tty refcounting and touching tty_port in the
future. It allows us to remove some state->tty tests because the tty
passed down to them can never be NULL.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
---
 arch/ia64/hp/sim/simserial.c |   50 ++++++++++++++++++++----------------------
 1 file changed, 24 insertions(+), 26 deletions(-)

diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c
index 7b6e60e..a76a27e 100644
--- a/arch/ia64/hp/sim/simserial.c
+++ b/arch/ia64/hp/sim/simserial.c
@@ -202,7 +202,8 @@ static int rs_put_char(struct tty_struct *tty, unsigned char ch)
 	return 1;
 }
 
-static void transmit_chars(struct serial_state *info, int *intr_done)
+static void transmit_chars(struct tty_struct *tty, struct serial_state *info,
+		int *intr_done)
 {
 	int count;
 	unsigned long flags;
@@ -220,10 +221,11 @@ static void transmit_chars(struct serial_state *info, int *intr_done)
 		goto out;
 	}
 
-	if (info->xmit.head == info->xmit.tail || info->tty->stopped || info->tty->hw_stopped) {
+	if (info->xmit.head == info->xmit.tail || tty->stopped ||
+			tty->hw_stopped) {
 #ifdef SIMSERIAL_DEBUG
 		printk("transmit_chars: head=%d, tail=%d, stopped=%d\n",
-		       info->xmit.head, info->xmit.tail, info->tty->stopped);
+		       info->xmit.head, info->xmit.tail, tty->stopped);
 #endif
 		goto out;
 	}
@@ -261,7 +263,7 @@ static void rs_flush_chars(struct tty_struct *tty)
 	    !info->xmit.buf)
 		return;
 
-	transmit_chars(info, NULL);
+	transmit_chars(tty, info, NULL);
 }
 
 
@@ -295,7 +297,7 @@ static int rs_write(struct tty_struct * tty,
 	 */
 	if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE)
 	    && !tty->stopped && !tty->hw_stopped) {
-		transmit_chars(info, NULL);
+		transmit_chars(tty, info, NULL);
 	}
 	return ret;
 }
@@ -340,7 +342,7 @@ static void rs_send_xchar(struct tty_struct *tty, char ch)
 		 * I guess we could call console->write() directly but
 		 * let's do that for now.
 		 */
-		transmit_chars(info, NULL);
+		transmit_chars(tty, info, NULL);
 	}
 }
 
@@ -442,7 +444,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
  * This routine will shutdown a serial port; interrupts are disabled, and
  * DTR is dropped if the hangup on close termio flag is on.
  */
-static void shutdown(struct serial_state *info)
+static void shutdown(struct tty_struct *tty, struct serial_state *info)
 {
 	unsigned long	flags;
 
@@ -464,7 +466,7 @@ static void shutdown(struct serial_state *info)
 			info->xmit.buf = NULL;
 		}
 
-		if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
+		set_bit(TTY_IO_ERROR, &tty->flags);
 
 		info->flags &= ~ASYNC_INITIALIZED;
 	}
@@ -528,7 +530,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 	 * Now we wait for the transmit buffer to clear; and we notify
 	 * the line discipline to only process XON/XOFF characters.
 	 */
-	shutdown(info);
+	shutdown(tty, info);
 	rs_flush_buffer(tty);
 	tty_ldisc_flush(tty);
 	info->tty = NULL;
@@ -563,7 +565,7 @@ static void rs_hangup(struct tty_struct *tty)
 	rs_flush_buffer(tty);
 	if (info->flags & ASYNC_CLOSING)
 		return;
-	shutdown(info);
+	shutdown(tty, info);
 
 	info->count = 0;
 	info->flags &= ~ASYNC_NORMAL_ACTIVE;
@@ -572,7 +574,7 @@ static void rs_hangup(struct tty_struct *tty)
 }
 
 
-static int startup(struct serial_state *state)
+static int startup(struct tty_struct *tty, struct serial_state *state)
 {
 	unsigned long flags;
 	int	retval=0;
@@ -590,8 +592,7 @@ static int startup(struct serial_state *state)
 	}
 
 	if (!state->port || !state->type) {
-		if (state->tty)
-			set_bit(TTY_IO_ERROR, &state->tty->flags);
+		set_bit(TTY_IO_ERROR, &tty->flags);
 		free_page(page);
 		goto errout;
 	}
@@ -614,8 +615,7 @@ static int startup(struct serial_state *state)
 			goto errout;
 	}
 
-	if (state->tty)
-		clear_bit(TTY_IO_ERROR, &state->tty->flags);
+	clear_bit(TTY_IO_ERROR, &tty->flags);
 
 	state->xmit.head = state->xmit.tail = 0;
 
@@ -630,16 +630,14 @@ static int startup(struct serial_state *state)
 	/*
 	 * Set up the tty->alt_speed kludge
 	 */
-	if (state->tty) {
-		if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-			state->tty->alt_speed = 57600;
-		if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-			state->tty->alt_speed = 115200;
-		if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-			state->tty->alt_speed = 230400;
-		if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-			state->tty->alt_speed = 460800;
-	}
+	if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+		tty->alt_speed = 57600;
+	if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+		tty->alt_speed = 115200;
+	if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+		tty->alt_speed = 230400;
+	if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+		tty->alt_speed = 460800;
 
 	state->flags |= ASYNC_INITIALIZED;
 	local_irq_restore(flags);
@@ -699,7 +697,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
 	/*
 	 * Start up serial port
 	 */
-	retval = startup(info);
+	retval = startup(tty, info);
 	if (retval) {
 		return retval;
 	}
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 33/68] TTY: serialP, merge serial_state and async_struct
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh
  Cc: alan, linux-serial, linux-kernel, jirislaby, Geert Uytterhoeven,
	Tony Luck, Fenghua Yu
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

This is the final step to get rid of the one of the structures.  A
further cleanup will follow. And I struct serial_state deserves cease
to exist after a switch to tty_port too.

While changing the lines, it removes also pointless tty->driver_data
casts.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
---
 arch/ia64/hp/sim/simserial.c |  160 +++++++++-------------
 drivers/tty/amiserial.c      |  301 +++++++++++++++++-------------------------
 include/linux/serialP.h      |   14 +-
 3 files changed, 191 insertions(+), 284 deletions(-)

diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c
index 8b5a134..7b6e60e 100644
--- a/arch/ia64/hp/sim/simserial.c
+++ b/arch/ia64/hp/sim/simserial.c
@@ -163,7 +163,7 @@ static  void receive_chars(struct tty_struct *tty)
  */
 static irqreturn_t rs_interrupt_single(int irq, void *dev_id)
 {
-	struct async_struct *info = dev_id;
+	struct serial_state *info = dev_id;
 
 	if (!info->tty) {
 		printk(KERN_INFO "simrs_interrupt_single: info|tty=0 info=%p problem\n", info);
@@ -185,7 +185,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id)
 
 static int rs_put_char(struct tty_struct *tty, unsigned char ch)
 {
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 
 	if (!tty || !info->xmit.buf)
@@ -202,12 +202,11 @@ static int rs_put_char(struct tty_struct *tty, unsigned char ch)
 	return 1;
 }
 
-static void transmit_chars(struct async_struct *info, int *intr_done)
+static void transmit_chars(struct serial_state *info, int *intr_done)
 {
 	int count;
 	unsigned long flags;
 
-
 	local_irq_save(flags);
 
 	if (info->x_char) {
@@ -215,7 +214,7 @@ static void transmit_chars(struct async_struct *info, int *intr_done)
 
 		console->write(console, &c, 1);
 
-		info->state->icount.tx++;
+		info->icount.tx++;
 		info->x_char = 0;
 
 		goto out;
@@ -256,7 +255,7 @@ out:
 
 static void rs_flush_chars(struct tty_struct *tty)
 {
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 
 	if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped ||
 	    !info->xmit.buf)
@@ -269,8 +268,8 @@ static void rs_flush_chars(struct tty_struct *tty)
 static int rs_write(struct tty_struct * tty,
 		    const unsigned char *buf, int count)
 {
+	struct serial_state *info = tty->driver_data;
 	int	c, ret = 0;
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
 	unsigned long flags;
 
 	if (!tty || !info->xmit.buf || !tmp_buf) return 0;
@@ -303,21 +302,21 @@ static int rs_write(struct tty_struct * tty,
 
 static int rs_write_room(struct tty_struct *tty)
 {
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 
 	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
 }
 
 static int rs_chars_in_buffer(struct tty_struct *tty)
 {
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 
 	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
 }
 
 static void rs_flush_buffer(struct tty_struct *tty)
 {
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 
 	local_irq_save(flags);
@@ -333,7 +332,7 @@ static void rs_flush_buffer(struct tty_struct *tty)
  */
 static void rs_send_xchar(struct tty_struct *tty, char ch)
 {
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 
 	info->x_char = ch;
 	if (ch) {
@@ -362,7 +361,7 @@ static void rs_throttle(struct tty_struct * tty)
 
 static void rs_unthrottle(struct tty_struct * tty)
 {
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 
 	if (I_IXOFF(tty)) {
 		if (info->x_char)
@@ -443,23 +442,22 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
  * This routine will shutdown a serial port; interrupts are disabled, and
  * DTR is dropped if the hangup on close termio flag is on.
  */
-static void shutdown(struct async_struct * info)
+static void shutdown(struct serial_state *info)
 {
 	unsigned long	flags;
-	struct serial_state *state = info->state;
 
-	if (!(state->flags & ASYNC_INITIALIZED))
+	if (!(info->flags & ASYNC_INITIALIZED))
 		return;
 
 #ifdef SIMSERIAL_DEBUG
-	printk("Shutting down serial port %d (irq %d)....", info->line,
-	       state->irq);
+	printk("Shutting down serial port %d (irq %d)...\n", info->line,
+	       info->irq);
 #endif
 
 	local_irq_save(flags);
 	{
-		if (state->irq)
-			free_irq(state->irq, info);
+		if (info->irq)
+			free_irq(info->irq, info);
 
 		if (info->xmit.buf) {
 			free_page((unsigned long) info->xmit.buf);
@@ -468,7 +466,7 @@ static void shutdown(struct async_struct * info)
 
 		if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
 
-		state->flags &= ~ASYNC_INITIALIZED;
+		info->flags &= ~ASYNC_INITIALIZED;
 	}
 	local_irq_restore(flags);
 }
@@ -485,13 +483,11 @@ static void shutdown(struct async_struct * info)
  */
 static void rs_close(struct tty_struct *tty, struct file * filp)
 {
-	struct async_struct * info = (struct async_struct *)tty->driver_data;
-	struct serial_state *state;
+	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 
-	if (!info ) return;
-
-	state = info->state;
+	if (!info)
+		return;
 
 	local_irq_save(flags);
 	if (tty_hung_up_p(filp)) {
@@ -502,30 +498,30 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 		return;
 	}
 #ifdef SIMSERIAL_DEBUG
-	printk("rs_close ttys%d, count = %d\n", info->line, state->count);
+	printk("rs_close ttys%d, count = %d\n", info->line, info->count);
 #endif
-	if ((tty->count == 1) && (state->count != 1)) {
+	if ((tty->count == 1) && (info->count != 1)) {
 		/*
 		 * Uh, oh.  tty->count is 1, which means that the tty
-		 * structure will be freed.  state->count should always
+		 * structure will be freed.  info->count should always
 		 * be one in these conditions.  If it's greater than
 		 * one, we've got real problems, since it means the
 		 * serial port won't be shutdown.
 		 */
 		printk(KERN_ERR "rs_close: bad serial port count; tty->count is 1, "
-		       "state->count is %d\n", state->count);
-		state->count = 1;
+		       "info->count is %d\n", info->count);
+		info->count = 1;
 	}
-	if (--state->count < 0) {
+	if (--info->count < 0) {
 		printk(KERN_ERR "rs_close: bad serial port count for ttys%d: %d\n",
-		       state->line, state->count);
-		state->count = 0;
+		       info->line, info->count);
+		info->count = 0;
 	}
-	if (state->count) {
+	if (info->count) {
 		local_irq_restore(flags);
 		return;
 	}
-	state->flags |= ASYNC_CLOSING;
+	info->flags |= ASYNC_CLOSING;
 	local_irq_restore(flags);
 
 	/*
@@ -537,11 +533,11 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 	tty_ldisc_flush(tty);
 	info->tty = NULL;
 	if (info->blocked_open) {
-		if (state->close_delay)
-			schedule_timeout_interruptible(state->close_delay);
+		if (info->close_delay)
+			schedule_timeout_interruptible(info->close_delay);
 		wake_up_interruptible(&info->open_wait);
 	}
-	state->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
 	wake_up_interruptible(&info->close_wait);
 }
 
@@ -558,59 +554,28 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
  */
 static void rs_hangup(struct tty_struct *tty)
 {
-	struct async_struct * info = (struct async_struct *)tty->driver_data;
-	struct serial_state *state = info->state;
+	struct serial_state *info = tty->driver_data;
 
 #ifdef SIMSERIAL_DEBUG
 	printk("rs_hangup: called\n");
 #endif
 
 	rs_flush_buffer(tty);
-	if (state->flags & ASYNC_CLOSING)
+	if (info->flags & ASYNC_CLOSING)
 		return;
 	shutdown(info);
 
-	state->count = 0;
-	state->flags &= ~ASYNC_NORMAL_ACTIVE;
+	info->count = 0;
+	info->flags &= ~ASYNC_NORMAL_ACTIVE;
 	info->tty = NULL;
 	wake_up_interruptible(&info->open_wait);
 }
 
 
-static int get_async_struct(int line, struct async_struct **ret_info)
-{
-	struct async_struct *info;
-	struct serial_state *sstate;
-
-	sstate = rs_table + line;
-	sstate->count++;
-	if (sstate->info) {
-		*ret_info = sstate->info;
-		return 0;
-	}
-	info = kzalloc(sizeof(struct async_struct), GFP_KERNEL);
-	if (!info) {
-		sstate->count--;
-		return -ENOMEM;
-	}
-	init_waitqueue_head(&info->open_wait);
-	init_waitqueue_head(&info->close_wait);
-	info->state = sstate;
-	if (sstate->info) {
-		kfree(info);
-		*ret_info = sstate->info;
-		return 0;
-	}
-	*ret_info = sstate->info = info;
-	return 0;
-}
-
-static int
-startup(struct async_struct *info)
+static int startup(struct serial_state *state)
 {
 	unsigned long flags;
 	int	retval=0;
-	struct serial_state *state= info->state;
 	unsigned long page;
 
 	page = get_zeroed_page(GFP_KERNEL);
@@ -625,17 +590,18 @@ startup(struct async_struct *info)
 	}
 
 	if (!state->port || !state->type) {
-		if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
+		if (state->tty)
+			set_bit(TTY_IO_ERROR, &state->tty->flags);
 		free_page(page);
 		goto errout;
 	}
-	if (info->xmit.buf)
+	if (state->xmit.buf)
 		free_page(page);
 	else
-		info->xmit.buf = (unsigned char *) page;
+		state->xmit.buf = (unsigned char *) page;
 
 #ifdef SIMSERIAL_DEBUG
-	printk("startup: ttys%d (irq %d)...", info->line, state->irq);
+	printk("startup: ttys%d (irq %d)...", state->line, state->irq);
 #endif
 
 	/*
@@ -643,14 +609,15 @@ startup(struct async_struct *info)
 	 */
 	if (state->irq) {
 		retval = request_irq(state->irq, rs_interrupt_single, 0,
-				"simserial", info);
+				"simserial", state);
 		if (retval)
 			goto errout;
 	}
 
-	if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	if (state->tty)
+		clear_bit(TTY_IO_ERROR, &state->tty->flags);
 
-	info->xmit.head = info->xmit.tail = 0;
+	state->xmit.head = state->xmit.tail = 0;
 
 #if 0
 	/*
@@ -663,15 +630,15 @@ startup(struct async_struct *info)
 	/*
 	 * Set up the tty->alt_speed kludge
 	 */
-	if (info->tty) {
+	if (state->tty) {
 		if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-			info->tty->alt_speed = 57600;
+			state->tty->alt_speed = 57600;
 		if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-			info->tty->alt_speed = 115200;
+			state->tty->alt_speed = 115200;
 		if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-			info->tty->alt_speed = 230400;
+			state->tty->alt_speed = 230400;
 		if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-			info->tty->alt_speed = 460800;
+			state->tty->alt_speed = 460800;
 	}
 
 	state->flags |= ASYNC_INITIALIZED;
@@ -692,20 +659,18 @@ errout:
  */
 static int rs_open(struct tty_struct *tty, struct file * filp)
 {
-	struct async_struct	*info;
+	struct serial_state *info = rs_table + tty->index;
 	int			retval;
 	unsigned long		page;
 
-	retval = get_async_struct(tty->index, &info);
-	if (retval)
-		return retval;
-	tty->driver_data = info;
+	info->count++;
 	info->tty = tty;
+	tty->driver_data = info;
 
 #ifdef SIMSERIAL_DEBUG
-	printk("rs_open %s, count = %d\n", tty->name, info->state->count);
+	printk("rs_open %s, count = %d\n", tty->name, info->count);
 #endif
-	info->tty->low_latency = (info->state->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+	tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
 	if (!tmp_buf) {
 		page = get_zeroed_page(GFP_KERNEL);
@@ -720,12 +685,11 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
 	/*
 	 * If the port is the middle of closing, bail out now
 	 */
-	if (tty_hung_up_p(filp) ||
-	    (info->state->flags & ASYNC_CLOSING)) {
-		if (info->state->flags & ASYNC_CLOSING)
+	if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) {
+		if (info->flags & ASYNC_CLOSING)
 			interruptible_sleep_on(&info->close_wait);
 #ifdef SERIAL_DO_RESTART
-		return ((info->state->flags & ASYNC_HUP_NOTIFY) ?
+		return ((info->flags & ASYNC_HUP_NOTIFY) ?
 			-EAGAIN : -ERESTARTSYS);
 #else
 		return -EAGAIN;
@@ -865,6 +829,8 @@ simrs_init (void)
 	 * Let's have a little bit of fun !
 	 */
 	for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
+		init_waitqueue_head(&state->open_wait);
+		init_waitqueue_head(&state->close_wait);
 
 		if (state->type == PORT_UNKNOWN) continue;
 
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
index 7607c6e..410e8e7 100644
--- a/drivers/tty/amiserial.c
+++ b/drivers/tty/amiserial.c
@@ -45,7 +45,7 @@
 
 #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
 #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
- tty->name, (info->state->flags), serial_driver->refcount,info->count,tty->count,s)
+ tty->name, (info->flags), serial_driver->refcount,info->count,tty->count,s)
 #else
 #define DBG_CNT(s)
 #endif
@@ -102,7 +102,7 @@ static struct tty_driver *serial_driver;
 
 static unsigned char current_ctl_bits;
 
-static void change_speed(struct async_struct *info, struct ktermios *old);
+static void change_speed(struct serial_state *info, struct ktermios *old);
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
 
 
@@ -115,7 +115,7 @@ static struct serial_state rs_table[1];
 #define serial_isroot()	(capable(CAP_SYS_ADMIN))
 
 
-static inline int serial_paranoia_check(struct async_struct *info,
+static inline int serial_paranoia_check(struct serial_state *info,
 					char *name, const char *routine)
 {
 #ifdef SERIAL_PARANOIA_CHECK
@@ -168,7 +168,7 @@ static __inline__ void rtsdtr_ctrl(int bits)
  */
 static void rs_stop(struct tty_struct *tty)
 {
-	struct async_struct *info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 
 	if (serial_paranoia_check(info, tty->name, "rs_stop"))
@@ -188,7 +188,7 @@ static void rs_stop(struct tty_struct *tty)
 
 static void rs_start(struct tty_struct *tty)
 {
-	struct async_struct *info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 
 	if (serial_paranoia_check(info, tty->name, "rs_start"))
@@ -229,7 +229,7 @@ static void rs_start(struct tty_struct *tty)
  * -----------------------------------------------------------------------
  */
 
-static void receive_chars(struct async_struct *info)
+static void receive_chars(struct serial_state *info)
 {
         int status;
 	int serdatr;
@@ -238,7 +238,7 @@ static void receive_chars(struct async_struct *info)
 	struct	async_icount *icount;
 	int oe = 0;
 
-	icount = &info->state->icount;
+	icount = &info->icount;
 
 	status = UART_LSR_DR; /* We obviously have a character! */
 	serdatr = custom.serdatr;
@@ -295,7 +295,7 @@ static void receive_chars(struct async_struct *info)
 	    printk("handling break....");
 #endif
 	    flag = TTY_BREAK;
-	    if (info->state->flags & ASYNC_SAK)
+	    if (info->flags & ASYNC_SAK)
 	      do_SAK(tty);
 	  } else if (status & UART_LSR_PE)
 	    flag = TTY_PARITY;
@@ -318,14 +318,14 @@ out:
 	return;
 }
 
-static void transmit_chars(struct async_struct *info)
+static void transmit_chars(struct serial_state *info)
 {
 	custom.intreq = IF_TBE;
 	mb();
 	if (info->x_char) {
 	        custom.serdat = info->x_char | 0x100;
 		mb();
-		info->state->icount.tx++;
+		info->icount.tx++;
 		info->x_char = 0;
 		return;
 	}
@@ -341,7 +341,7 @@ static void transmit_chars(struct async_struct *info)
 	custom.serdat = info->xmit.buf[info->xmit.tail++] | 0x100;
 	mb();
 	info->xmit.tail = info->xmit.tail & (SERIAL_XMIT_SIZE-1);
-	info->state->icount.tx++;
+	info->icount.tx++;
 
 	if (CIRC_CNT(info->xmit.head,
 		     info->xmit.tail,
@@ -358,7 +358,7 @@ static void transmit_chars(struct async_struct *info)
 	}
 }
 
-static void check_modem_status(struct async_struct *info)
+static void check_modem_status(struct serial_state *info)
 {
 	unsigned char status = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
 	unsigned char dstatus;
@@ -369,14 +369,14 @@ static void check_modem_status(struct async_struct *info)
 	current_ctl_bits = status;
 
 	if (dstatus) {
-		icount = &info->state->icount;
+		icount = &info->icount;
 		/* update input line counters */
 		if (dstatus & SER_DSR)
 			icount->dsr++;
 		if (dstatus & SER_DCD) {
 			icount->dcd++;
 #ifdef CONFIG_HARD_PPS
-			if ((info->state->flags & ASYNC_HARDPPS_CD) &&
+			if ((info->flags & ASYNC_HARDPPS_CD) &&
 			    !(status & SER_DCD))
 				hardpps();
 #endif
@@ -386,7 +386,7 @@ static void check_modem_status(struct async_struct *info)
 		wake_up_interruptible(&info->delta_msr_wait);
 	}
 
-	if ((info->state->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) {
+	if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) {
 #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
 		printk("ttyS%d CD now %s...", info->line,
 		       (!(status & SER_DCD)) ? "on" : "off");
@@ -401,7 +401,7 @@ static void check_modem_status(struct async_struct *info)
 				tty_hangup(info->tty);
 		}
 	}
-	if (info->state->flags & ASYNC_CTS_FLOW) {
+	if (info->flags & ASYNC_CTS_FLOW) {
 		if (info->tty->hw_stopped) {
 			if (!(status & SER_CTS)) {
 #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
@@ -437,7 +437,7 @@ static void check_modem_status(struct async_struct *info)
 static irqreturn_t ser_vbl_int( int irq, void *data)
 {
         /* vbl is just a periodic interrupt we tie into to update modem status */
-	struct async_struct *info = data;
+	struct serial_state *info = data;
 	/*
 	 * TBD - is it better to unregister from this interrupt or to
 	 * ignore it if MSI is clear ?
@@ -449,14 +449,13 @@ static irqreturn_t ser_vbl_int( int irq, void *data)
 
 static irqreturn_t ser_rx_int(int irq, void *dev_id)
 {
-	struct serial_state *state = dev_id;
-	struct async_struct *info = state->info;
+	struct serial_state *info = dev_id;
 
 #ifdef SERIAL_DEBUG_INTR
 	printk("ser_rx_int...");
 #endif
 
-	if (!info || !info->tty)
+	if (!info->tty)
 		return IRQ_NONE;
 
 	receive_chars(info);
@@ -468,15 +467,14 @@ static irqreturn_t ser_rx_int(int irq, void *dev_id)
 
 static irqreturn_t ser_tx_int(int irq, void *dev_id)
 {
-	struct serial_state *state = dev_id;
-	struct async_struct *info = state->info;
+	struct serial_state *info = dev_id;
 
 	if (custom.serdatr & SDR_TBE) {
 #ifdef SERIAL_DEBUG_INTR
 	  printk("ser_tx_int...");
 #endif
 
-	  if (!info || !info->tty)
+	  if (!info->tty)
 		return IRQ_NONE;
 
 	  transmit_chars(info);
@@ -502,7 +500,7 @@ static irqreturn_t ser_tx_int(int irq, void *dev_id)
  * ---------------------------------------------------------------
  */
 
-static int startup(struct async_struct * info)
+static int startup(struct serial_state *info)
 {
 	unsigned long flags;
 	int	retval=0;
@@ -514,7 +512,7 @@ static int startup(struct async_struct * info)
 
 	local_irq_save(flags);
 
-	if (info->state->flags & ASYNC_INITIALIZED) {
+	if (info->flags & ASYNC_INITIALIZED) {
 		free_page(page);
 		goto errout;
 	}
@@ -565,13 +563,13 @@ static int startup(struct async_struct * info)
 	 * Set up the tty->alt_speed kludge
 	 */
 	if (info->tty) {
-		if ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
 			info->tty->alt_speed = 57600;
-		if ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
 			info->tty->alt_speed = 115200;
-		if ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
 			info->tty->alt_speed = 230400;
-		if ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
 			info->tty->alt_speed = 460800;
 	}
 
@@ -580,7 +578,7 @@ static int startup(struct async_struct * info)
 	 */
 	change_speed(info, NULL);
 
-	info->state->flags |= ASYNC_INITIALIZED;
+	info->flags |= ASYNC_INITIALIZED;
 	local_irq_restore(flags);
 	return 0;
 
@@ -593,15 +591,15 @@ errout:
  * This routine will shutdown a serial port; interrupts are disabled, and
  * DTR is dropped if the hangup on close termio flag is on.
  */
-static void shutdown(struct async_struct * info)
+static void shutdown(struct serial_state *info)
 {
 	unsigned long	flags;
 	struct serial_state *state;
 
-	if (!(info->state->flags & ASYNC_INITIALIZED))
+	if (!(info->flags & ASYNC_INITIALIZED))
 		return;
 
-	state = info->state;
+	state = info;
 
 #ifdef SERIAL_DEBUG_OPEN
 	printk("Shutting down serial port %d ....\n", info->line);
@@ -640,7 +638,7 @@ static void shutdown(struct async_struct * info)
 	if (info->tty)
 		set_bit(TTY_IO_ERROR, &info->tty->flags);
 
-	info->state->flags &= ~ASYNC_INITIALIZED;
+	info->flags &= ~ASYNC_INITIALIZED;
 	local_irq_restore(flags);
 }
 
@@ -649,7 +647,7 @@ static void shutdown(struct async_struct * info)
  * This routine is called to set the UART divisor registers to match
  * the specified baud rate for a serial port.
  */
-static void change_speed(struct async_struct *info,
+static void change_speed(struct serial_state *info,
 			 struct ktermios *old_termios)
 {
 	int	quot = 0, baud_base, baud;
@@ -683,10 +681,10 @@ static void change_speed(struct async_struct *info,
 	baud = tty_get_baud_rate(info->tty);
 	if (!baud)
 		baud = 9600;	/* B0 transition handled in rs_set_termios */
-	baud_base = info->state->baud_base;
+	baud_base = info->baud_base;
 	if (baud == 38400 &&
-	    ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
-		quot = info->state->custom_divisor;
+	    ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+		quot = info->custom_divisor;
 	else {
 		if (baud == 134)
 			/* Special case since 134 is really 134.5 */
@@ -703,8 +701,8 @@ static void change_speed(struct async_struct *info,
 		if (!baud)
 			baud = 9600;
 		if (baud == 38400 &&
-		    ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
-			quot = info->state->custom_divisor;
+		    ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+			quot = info->custom_divisor;
 		else {
 			if (baud == 134)
 				/* Special case since 134 is really 134.5 */
@@ -717,22 +715,22 @@ static void change_speed(struct async_struct *info,
 	if (!quot)
 		quot = baud_base / 9600;
 	info->quot = quot;
-	info->timeout = ((info->state->xmit_fifo_size*HZ*bits*quot) / baud_base);
+	info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
 	info->timeout += HZ/50;		/* Add .02 seconds of slop */
 
 	/* CTS flow control flag and modem status interrupts */
 	info->IER &= ~UART_IER_MSI;
-	if (info->state->flags & ASYNC_HARDPPS_CD)
+	if (info->flags & ASYNC_HARDPPS_CD)
 		info->IER |= UART_IER_MSI;
 	if (cflag & CRTSCTS) {
-		info->state->flags |= ASYNC_CTS_FLOW;
+		info->flags |= ASYNC_CTS_FLOW;
 		info->IER |= UART_IER_MSI;
 	} else
-		info->state->flags &= ~ASYNC_CTS_FLOW;
+		info->flags &= ~ASYNC_CTS_FLOW;
 	if (cflag & CLOCAL)
-		info->state->flags &= ~ASYNC_CHECK_CD;
+		info->flags &= ~ASYNC_CHECK_CD;
 	else {
-		info->state->flags |= ASYNC_CHECK_CD;
+		info->flags |= ASYNC_CHECK_CD;
 		info->IER |= UART_IER_MSI;
 	}
 	/* TBD:
@@ -791,7 +789,7 @@ static void change_speed(struct async_struct *info,
 
 static int rs_put_char(struct tty_struct *tty, unsigned char ch)
 {
-	struct async_struct *info;
+	struct serial_state *info;
 	unsigned long flags;
 
 	info = tty->driver_data;
@@ -818,7 +816,7 @@ static int rs_put_char(struct tty_struct *tty, unsigned char ch)
 
 static void rs_flush_chars(struct tty_struct *tty)
 {
-	struct async_struct *info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 
 	if (serial_paranoia_check(info, tty->name, "rs_flush_chars"))
@@ -843,11 +841,9 @@ static void rs_flush_chars(struct tty_struct *tty)
 static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count)
 {
 	int	c, ret = 0;
-	struct async_struct *info;
+	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 
-	info = tty->driver_data;
-
 	if (serial_paranoia_check(info, tty->name, "rs_write"))
 		return 0;
 
@@ -891,7 +887,7 @@ static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count
 
 static int rs_write_room(struct tty_struct *tty)
 {
-	struct async_struct *info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 
 	if (serial_paranoia_check(info, tty->name, "rs_write_room"))
 		return 0;
@@ -900,7 +896,7 @@ static int rs_write_room(struct tty_struct *tty)
 
 static int rs_chars_in_buffer(struct tty_struct *tty)
 {
-	struct async_struct *info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 
 	if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer"))
 		return 0;
@@ -909,7 +905,7 @@ static int rs_chars_in_buffer(struct tty_struct *tty)
 
 static void rs_flush_buffer(struct tty_struct *tty)
 {
-	struct async_struct *info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 
 	if (serial_paranoia_check(info, tty->name, "rs_flush_buffer"))
@@ -926,7 +922,7 @@ static void rs_flush_buffer(struct tty_struct *tty)
  */
 static void rs_send_xchar(struct tty_struct *tty, char ch)
 {
-	struct async_struct *info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
         unsigned long flags;
 
 	if (serial_paranoia_check(info, tty->name, "rs_send_char"))
@@ -961,7 +957,7 @@ static void rs_send_xchar(struct tty_struct *tty, char ch)
  */
 static void rs_throttle(struct tty_struct * tty)
 {
-	struct async_struct *info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 #ifdef SERIAL_DEBUG_THROTTLE
 	char	buf[64];
@@ -986,7 +982,7 @@ static void rs_throttle(struct tty_struct * tty)
 
 static void rs_unthrottle(struct tty_struct * tty)
 {
-	struct async_struct *info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 #ifdef SERIAL_DEBUG_THROTTLE
 	char	buf[64];
@@ -1017,11 +1013,10 @@ static void rs_unthrottle(struct tty_struct * tty)
  * ------------------------------------------------------------
  */
 
-static int get_serial_info(struct async_struct * info,
+static int get_serial_info(struct serial_state *state,
 			   struct serial_struct __user * retinfo)
 {
 	struct serial_struct tmp;
-	struct serial_state *state = info->state;
    
 	if (!retinfo)
 		return -EFAULT;
@@ -1043,11 +1038,11 @@ static int get_serial_info(struct async_struct * info,
 	return 0;
 }
 
-static int set_serial_info(struct async_struct * info,
+static int set_serial_info(struct serial_state *state,
 			   struct serial_struct __user * new_info)
 {
 	struct serial_struct new_serial;
- 	struct serial_state old_state, *state;
+	struct serial_state old_state;
 	unsigned int		change_irq,change_port;
 	int 			retval = 0;
 
@@ -1055,7 +1050,6 @@ static int set_serial_info(struct async_struct * info,
 		return -EFAULT;
 
 	tty_lock();
-	state = info->state;
 	old_state = *state;
   
 	change_irq = new_serial.irq != state->irq;
@@ -1094,7 +1088,7 @@ static int set_serial_info(struct async_struct * info,
 	state->custom_divisor = new_serial.custom_divisor;
 	state->close_delay = new_serial.close_delay * HZ/100;
 	state->closing_wait = new_serial.closing_wait * HZ/100;
-	info->tty->low_latency = (state->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+	state->tty->low_latency = (state->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
 check_and_exit:
 	if (state->flags & ASYNC_INITIALIZED) {
@@ -1102,17 +1096,17 @@ check_and_exit:
 		     (state->flags & ASYNC_SPD_MASK)) ||
 		    (old_state.custom_divisor != state->custom_divisor)) {
 			if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-				info->tty->alt_speed = 57600;
+				state->tty->alt_speed = 57600;
 			if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-				info->tty->alt_speed = 115200;
+				state->tty->alt_speed = 115200;
 			if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-				info->tty->alt_speed = 230400;
+				state->tty->alt_speed = 230400;
 			if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-				info->tty->alt_speed = 460800;
-			change_speed(info, NULL);
+				state->tty->alt_speed = 460800;
+			change_speed(state, NULL);
 		}
 	} else
-		retval = startup(info);
+		retval = startup(state);
 	tty_unlock();
 	return retval;
 }
@@ -1128,7 +1122,7 @@ check_and_exit:
  * 	    transmit holding register is empty.  This functionality
  * 	    allows an RS485 driver to be written in user space. 
  */
-static int get_lsr_info(struct async_struct * info, unsigned int __user *value)
+static int get_lsr_info(struct serial_state *info, unsigned int __user *value)
 {
 	unsigned char status;
 	unsigned int result;
@@ -1147,7 +1141,7 @@ static int get_lsr_info(struct async_struct * info, unsigned int __user *value)
 
 static int rs_tiocmget(struct tty_struct *tty)
 {
-	struct async_struct * info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	unsigned char control, status;
 	unsigned long flags;
 
@@ -1170,7 +1164,7 @@ static int rs_tiocmget(struct tty_struct *tty)
 static int rs_tiocmset(struct tty_struct *tty, unsigned int set,
 						unsigned int clear)
 {
-	struct async_struct * info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 
 	if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
@@ -1197,7 +1191,7 @@ static int rs_tiocmset(struct tty_struct *tty, unsigned int set,
  */
 static int rs_break(struct tty_struct *tty, int break_state)
 {
-	struct async_struct * info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 
 	if (serial_paranoia_check(info, tty->name, "rs_break"))
@@ -1222,12 +1216,12 @@ static int rs_break(struct tty_struct *tty, int break_state)
 static int rs_get_icount(struct tty_struct *tty,
 				struct serial_icounter_struct *icount)
 {
-	struct async_struct *info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	struct async_icount cnow;
 	unsigned long flags;
 
 	local_irq_save(flags);
-	cnow = info->state->icount;
+	cnow = info->icount;
 	local_irq_restore(flags);
 	icount->cts = cnow.cts;
 	icount->dsr = cnow.dsr;
@@ -1247,7 +1241,7 @@ static int rs_get_icount(struct tty_struct *tty,
 static int rs_ioctl(struct tty_struct *tty,
 		    unsigned int cmd, unsigned long arg)
 {
-	struct async_struct * info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	struct async_icount cprev, cnow;	/* kernel counter temps */
 	void __user *argp = (void __user *)arg;
 	unsigned long flags;
@@ -1275,7 +1269,7 @@ static int rs_ioctl(struct tty_struct *tty,
 
 		case TIOCSERGSTRUCT:
 			if (copy_to_user(argp,
-					 info, sizeof(struct async_struct)))
+					 info, sizeof(struct serial_state)))
 				return -EFAULT;
 			return 0;
 
@@ -1288,7 +1282,7 @@ static int rs_ioctl(struct tty_struct *tty,
 		case TIOCMIWAIT:
 			local_irq_save(flags);
 			/* note the counters on entry */
-			cprev = info->state->icount;
+			cprev = info->icount;
 			local_irq_restore(flags);
 			while (1) {
 				interruptible_sleep_on(&info->delta_msr_wait);
@@ -1296,7 +1290,7 @@ static int rs_ioctl(struct tty_struct *tty,
 				if (signal_pending(current))
 					return -ERESTARTSYS;
 				local_irq_save(flags);
-				cnow = info->state->icount; /* atomic copy */
+				cnow = info->icount; /* atomic copy */
 				local_irq_restore(flags);
 				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
 				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
@@ -1325,7 +1319,7 @@ static int rs_ioctl(struct tty_struct *tty,
 
 static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 {
-	struct async_struct *info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	unsigned long flags;
 	unsigned int cflag = tty->termios->c_cflag;
 
@@ -1385,15 +1379,12 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
  */
 static void rs_close(struct tty_struct *tty, struct file * filp)
 {
-	struct async_struct * info = tty->driver_data;
-	struct serial_state *state;
+	struct serial_state *state = tty->driver_data;
 	unsigned long flags;
 
-	if (!info || serial_paranoia_check(info, tty->name, "rs_close"))
+	if (!state || serial_paranoia_check(state, tty->name, "rs_close"))
 		return;
 
-	state = info->state;
-
 	local_irq_save(flags);
 
 	if (tty_hung_up_p(filp)) {
@@ -1403,7 +1394,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 	}
 
 #ifdef SERIAL_DEBUG_OPEN
-	printk("rs_close ttys%d, count = %d\n", info->line, state->count);
+	printk("rs_close ttys%d, count = %d\n", state->line, state->count);
 #endif
 	if ((tty->count == 1) && (state->count != 1)) {
 		/*
@@ -1441,7 +1432,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 	 * interrupt driver to stop checking the data ready bit in the
 	 * line status register.
 	 */
-	info->read_status_mask &= ~UART_LSR_DR;
+	state->read_status_mask &= ~UART_LSR_DR;
 	if (state->flags & ASYNC_INITIALIZED) {
 	        /* disable receive interrupts */
 	        custom.intena = IF_RBF;
@@ -1455,22 +1446,22 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 		 * has completely drained; this is especially
 		 * important if there is a transmit FIFO!
 		 */
-		rs_wait_until_sent(tty, info->timeout);
+		rs_wait_until_sent(tty, state->timeout);
 	}
-	shutdown(info);
+	shutdown(state);
 	rs_flush_buffer(tty);
 		
 	tty_ldisc_flush(tty);
 	tty->closing = 0;
-	info->tty = NULL;
-	if (info->blocked_open) {
+	state->tty = NULL;
+	if (state->blocked_open) {
 		if (state->close_delay) {
 			msleep_interruptible(jiffies_to_msecs(state->close_delay));
 		}
-		wake_up_interruptible(&info->open_wait);
+		wake_up_interruptible(&state->open_wait);
 	}
 	state->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
-	wake_up_interruptible(&info->close_wait);
+	wake_up_interruptible(&state->close_wait);
 	local_irq_restore(flags);
 }
 
@@ -1479,14 +1470,14 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
  */
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
 {
-	struct async_struct * info = tty->driver_data;
+	struct serial_state *info = tty->driver_data;
 	unsigned long orig_jiffies, char_time;
 	int lsr;
 
 	if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
 		return;
 
-	if (info->state->xmit_fifo_size == 0)
+	if (info->xmit_fifo_size == 0)
 		return; /* Just in case.... */
 
 	orig_jiffies = jiffies;
@@ -1499,7 +1490,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
 	 * Note: we have to use pretty tight timings here to satisfy
 	 * the NIST-PCTS.
 	 */
-	char_time = (info->timeout - HZ/50) / info->state->xmit_fifo_size;
+	char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
 	char_time = char_time / 5;
 	if (char_time == 0)
 		char_time = 1;
@@ -1542,18 +1533,15 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
  */
 static void rs_hangup(struct tty_struct *tty)
 {
-	struct async_struct * info = tty->driver_data;
-	struct serial_state *state = info->state;
+	struct serial_state *info = tty->driver_data;
 
 	if (serial_paranoia_check(info, tty->name, "rs_hangup"))
 		return;
 
-	state = info->state;
-
 	rs_flush_buffer(tty);
 	shutdown(info);
-	state->count = 0;
-	state->flags &= ~ASYNC_NORMAL_ACTIVE;
+	info->count = 0;
+	info->flags &= ~ASYNC_NORMAL_ACTIVE;
 	info->tty = NULL;
 	wake_up_interruptible(&info->open_wait);
 }
@@ -1564,14 +1552,13 @@ static void rs_hangup(struct tty_struct *tty)
  * ------------------------------------------------------------
  */
 static int block_til_ready(struct tty_struct *tty, struct file * filp,
-			   struct async_struct *info)
+			   struct serial_state *info)
 {
 #ifdef DECLARE_WAITQUEUE
 	DECLARE_WAITQUEUE(wait, current);
 #else
 	struct wait_queue wait = { current, NULL };
 #endif
-	struct serial_state *state = info->state;
 	int		retval;
 	int		do_clocal = 0, extra_count = 0;
 	unsigned long	flags;
@@ -1581,11 +1568,11 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 	 * until it's done, and then try again.
 	 */
 	if (tty_hung_up_p(filp) ||
-	    (state->flags & ASYNC_CLOSING)) {
-		if (state->flags & ASYNC_CLOSING)
+	    (info->flags & ASYNC_CLOSING)) {
+		if (info->flags & ASYNC_CLOSING)
 			interruptible_sleep_on(&info->close_wait);
 #ifdef SERIAL_DO_RESTART
-		return ((state->flags & ASYNC_HUP_NOTIFY) ?
+		return ((info->flags & ASYNC_HUP_NOTIFY) ?
 			-EAGAIN : -ERESTARTSYS);
 #else
 		return -EAGAIN;
@@ -1598,7 +1585,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 	 */
 	if ((filp->f_flags & O_NONBLOCK) ||
 	    (tty->flags & (1 << TTY_IO_ERROR))) {
-		state->flags |= ASYNC_NORMAL_ACTIVE;
+		info->flags |= ASYNC_NORMAL_ACTIVE;
 		return 0;
 	}
 
@@ -1608,7 +1595,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 	/*
 	 * Block waiting for the carrier detect and the line to become
 	 * free (i.e., not in use by the callout).  While we are in
-	 * this loop, state->count is dropped by one, so that
+	 * this loop, info->count is dropped by one, so that
 	 * rs_close() knows when to free things.  We restore it upon
 	 * exit, either normal or abnormal.
 	 */
@@ -1616,12 +1603,12 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 	add_wait_queue(&info->open_wait, &wait);
 #ifdef SERIAL_DEBUG_OPEN
 	printk("block_til_ready before block: ttys%d, count = %d\n",
-	       state->line, state->count);
+	       info->line, info->count);
 #endif
 	local_irq_save(flags);
 	if (!tty_hung_up_p(filp)) {
 		extra_count = 1;
-		state->count--;
+		info->count--;
 	}
 	local_irq_restore(flags);
 	info->blocked_open++;
@@ -1632,9 +1619,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 		local_irq_restore(flags);
 		set_current_state(TASK_INTERRUPTIBLE);
 		if (tty_hung_up_p(filp) ||
-		    !(state->flags & ASYNC_INITIALIZED)) {
+		    !(info->flags & ASYNC_INITIALIZED)) {
 #ifdef SERIAL_DO_RESTART
-			if (state->flags & ASYNC_HUP_NOTIFY)
+			if (info->flags & ASYNC_HUP_NOTIFY)
 				retval = -EAGAIN;
 			else
 				retval = -ERESTARTSYS;
@@ -1643,7 +1630,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 #endif
 			break;
 		}
-		if (!(state->flags & ASYNC_CLOSING) &&
+		if (!(info->flags & ASYNC_CLOSING) &&
 		    (do_clocal || (!(ciab.pra & SER_DCD)) ))
 			break;
 		if (signal_pending(current)) {
@@ -1652,7 +1639,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 		}
 #ifdef SERIAL_DEBUG_OPEN
 		printk("block_til_ready blocking: ttys%d, count = %d\n",
-		       info->line, state->count);
+		       info->line, info->count);
 #endif
 		tty_unlock();
 		schedule();
@@ -1661,46 +1648,15 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(&info->open_wait, &wait);
 	if (extra_count)
-		state->count++;
+		info->count++;
 	info->blocked_open--;
 #ifdef SERIAL_DEBUG_OPEN
 	printk("block_til_ready after blocking: ttys%d, count = %d\n",
-	       info->line, state->count);
+	       info->line, info->count);
 #endif
 	if (retval)
 		return retval;
-	state->flags |= ASYNC_NORMAL_ACTIVE;
-	return 0;
-}
-
-static int get_async_struct(int line, struct async_struct **ret_info)
-{
-	struct async_struct *info;
-	struct serial_state *sstate;
-
-	sstate = rs_table + line;
-	sstate->count++;
-	if (sstate->info) {
-		*ret_info = sstate->info;
-		return 0;
-	}
-	info = kzalloc(sizeof(struct async_struct), GFP_KERNEL);
-	if (!info) {
-		sstate->count--;
-		return -ENOMEM;
-	}
-#ifdef DECLARE_WAITQUEUE
-	init_waitqueue_head(&info->open_wait);
-	init_waitqueue_head(&info->close_wait);
-	init_waitqueue_head(&info->delta_msr_wait);
-#endif
-	info->state = sstate;
-	if (sstate->info) {
-		kfree(info);
-		*ret_info = sstate->info;
-		return 0;
-	}
-	*ret_info = sstate->info = info;
+	info->flags |= ASYNC_NORMAL_ACTIVE;
 	return 0;
 }
 
@@ -1712,32 +1668,29 @@ static int get_async_struct(int line, struct async_struct **ret_info)
  */
 static int rs_open(struct tty_struct *tty, struct file * filp)
 {
-	struct async_struct	*info;
+	struct serial_state *info = rs_table + tty->index;
 	int retval;
 
-	retval = get_async_struct(tty->index, &info);
-	if (retval) {
-		return retval;
-	}
-	tty->driver_data = info;
+	info->count++;
 	info->tty = tty;
+	tty->driver_data = info;
 	if (serial_paranoia_check(info, tty->name, "rs_open"))
 		return -ENODEV;
 
 #ifdef SERIAL_DEBUG_OPEN
-	printk("rs_open %s, count = %d\n", tty->name, info->state->count);
+	printk("rs_open %s, count = %d\n", tty->name, info->count);
 #endif
-	info->tty->low_latency = (info->state->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+	tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
 	/*
 	 * If the port is the middle of closing, bail out now
 	 */
 	if (tty_hung_up_p(filp) ||
-	    (info->state->flags & ASYNC_CLOSING)) {
-		if (info->state->flags & ASYNC_CLOSING)
+	    (info->flags & ASYNC_CLOSING)) {
+		if (info->flags & ASYNC_CLOSING)
 			interruptible_sleep_on(&info->close_wait);
 #ifdef SERIAL_DO_RESTART
-		return ((info->state->flags & ASYNC_HUP_NOTIFY) ?
+		return ((info->flags & ASYNC_HUP_NOTIFY) ?
 			-EAGAIN : -ERESTARTSYS);
 #else
 		return -EAGAIN;
@@ -1773,24 +1726,14 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
 
 static inline void line_info(struct seq_file *m, struct serial_state *state)
 {
-	struct async_struct *info = state->info, scr_info;
 	char	stat_buf[30], control, status;
 	unsigned long flags;
 
 	seq_printf(m, "%d: uart:amiga_builtin",state->line);
 
-	/*
-	 * Figure out the current RS-232 lines
-	 */
-	if (!info) {
-		info = &scr_info;	/* This is just for serial_{in,out} */
-
-		info->quot = 0;
-		info->tty = NULL;
-	}
 	local_irq_save(flags);
 	status = ciab.pra;
-	control = info ? info->MCR : status;
+	control = (state->flags & ASYNC_INITIALIZED) ? state->MCR : status;
 	local_irq_restore(flags);
 
 	stat_buf[0] = 0;
@@ -1806,9 +1749,8 @@ static inline void line_info(struct seq_file *m, struct serial_state *state)
 	if(!(status & SER_DCD))
 		strcat(stat_buf, "|CD");
 
-	if (info->quot) {
-		seq_printf(m, " baud:%d", state->baud_base / info->quot);
-	}
+	if (state->quot)
+		seq_printf(m, " baud:%d", state->baud_base / state->quot);
 
 	seq_printf(m, " tx:%d rx:%d", state->icount.tx, state->icount.rx);
 
@@ -1938,6 +1880,9 @@ static int __init amiga_serial_probe(struct platform_device *pdev)
 	state->icount.rx = state->icount.tx = 0;
 	state->icount.frame = state->icount.parity = 0;
 	state->icount.overrun = state->icount.brk = 0;
+	init_waitqueue_head(&state->open_wait);
+	init_waitqueue_head(&state->close_wait);
+	init_waitqueue_head(&state->delta_msr_wait);
 
 	printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n",
 		       state->line);
@@ -1993,7 +1938,6 @@ static int __exit amiga_serial_remove(struct platform_device *pdev)
 {
 	int error;
 	struct serial_state *state = platform_get_drvdata(pdev);
-	struct async_struct *info = state->info;
 
 	/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
 	if ((error = tty_unregister_driver(serial_driver)))
@@ -2001,11 +1945,8 @@ static int __exit amiga_serial_remove(struct platform_device *pdev)
 		       error);
 	put_tty_driver(serial_driver);
 
-	rs_table[0].info = NULL;
-	kfree(info);
-
-	free_irq(IRQ_AMIGA_TBE, rs_table);
-	free_irq(IRQ_AMIGA_RBF, rs_table);
+	free_irq(IRQ_AMIGA_TBE, state);
+	free_irq(IRQ_AMIGA_RBF, state);
 
 	platform_set_drvdata(pdev, NULL);
 
diff --git a/include/linux/serialP.h b/include/linux/serialP.h
index b8543f9..984f5ba 100644
--- a/include/linux/serialP.h
+++ b/include/linux/serialP.h
@@ -38,24 +38,24 @@ struct serial_state {
 	unsigned short	close_delay;
 	unsigned short	closing_wait; /* time to wait before closing */
 	struct async_icount	icount;	
-	struct async_struct *info;
-};
 
-struct async_struct {
-	struct serial_state	*state;
-	struct tty_struct 	*tty;
+	/* amiserial */
 	int			read_status_mask;
 	int			ignore_status_mask;
 	int			timeout;
 	int			quot;
-	int			x_char;	/* xon/xoff character */
 	int			IER; 	/* Interrupt Enable Register */
 	int			MCR; 	/* Modem control register */
+	wait_queue_head_t	delta_msr_wait;
+	/* simserial */
+	int			x_char;	/* xon/xoff character */
 	int			blocked_open; /* # of blocked opens */
  	struct circ_buf		xmit;
 	wait_queue_head_t	open_wait;
 	wait_queue_head_t	close_wait;
-	wait_queue_head_t	delta_msr_wait;
+	struct tty_struct 	*tty;
+	/* /simserial */
+	/* /amiserial */
 };
 
 #endif /* _LINUX_SERIAL_H */
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 27/68] TTY: amiserial, use only one copy of async flags
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh; +Cc: alan, linux-serial, linux-kernel, jirislaby, Geert Uytterhoeven
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

Huh, why would one want to store two copies of them? Get rid of the
one from async_struct. That structure is going away as a whole soon.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/tty/amiserial.c |   82 ++++++++++++++++++++++-------------------------
 1 file changed, 38 insertions(+), 44 deletions(-)

diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
index d5fac86..8556ca0 100644
--- a/drivers/tty/amiserial.c
+++ b/drivers/tty/amiserial.c
@@ -45,7 +45,7 @@
 
 #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
 #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
- tty->name, (info->flags), serial_driver->refcount,info->count,tty->count,s)
+ tty->name, (info->state->flags), serial_driver->refcount,info->count,tty->count,s)
 #else
 #define DBG_CNT(s)
 #endif
@@ -297,7 +297,7 @@ static void receive_chars(struct async_struct *info)
 	    printk("handling break....");
 #endif
 	    flag = TTY_BREAK;
-	    if (info->flags & ASYNC_SAK)
+	    if (info->state->flags & ASYNC_SAK)
 	      do_SAK(tty);
 	  } else if (status & UART_LSR_PE)
 	    flag = TTY_PARITY;
@@ -378,7 +378,7 @@ static void check_modem_status(struct async_struct *info)
 		if (dstatus & SER_DCD) {
 			icount->dcd++;
 #ifdef CONFIG_HARD_PPS
-			if ((info->flags & ASYNC_HARDPPS_CD) &&
+			if ((info->state->flags & ASYNC_HARDPPS_CD) &&
 			    !(status & SER_DCD))
 				hardpps();
 #endif
@@ -388,7 +388,7 @@ static void check_modem_status(struct async_struct *info)
 		wake_up_interruptible(&info->delta_msr_wait);
 	}
 
-	if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) {
+	if ((info->state->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) {
 #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
 		printk("ttyS%d CD now %s...", info->line,
 		       (!(status & SER_DCD)) ? "on" : "off");
@@ -403,7 +403,7 @@ static void check_modem_status(struct async_struct *info)
 				tty_hangup(info->tty);
 		}
 	}
-	if (info->flags & ASYNC_CTS_FLOW) {
+	if (info->state->flags & ASYNC_CTS_FLOW) {
 		if (info->tty->hw_stopped) {
 			if (!(status & SER_CTS)) {
 #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
@@ -516,7 +516,7 @@ static int startup(struct async_struct * info)
 
 	local_irq_save(flags);
 
-	if (info->flags & ASYNC_INITIALIZED) {
+	if (info->state->flags & ASYNC_INITIALIZED) {
 		free_page(page);
 		goto errout;
 	}
@@ -569,13 +569,13 @@ static int startup(struct async_struct * info)
 	 * Set up the tty->alt_speed kludge
 	 */
 	if (info->tty) {
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+		if ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
 			info->tty->alt_speed = 57600;
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+		if ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
 			info->tty->alt_speed = 115200;
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+		if ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
 			info->tty->alt_speed = 230400;
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+		if ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
 			info->tty->alt_speed = 460800;
 	}
 
@@ -584,7 +584,7 @@ static int startup(struct async_struct * info)
 	 */
 	change_speed(info, NULL);
 
-	info->flags |= ASYNC_INITIALIZED;
+	info->state->flags |= ASYNC_INITIALIZED;
 	local_irq_restore(flags);
 	return 0;
 
@@ -602,7 +602,7 @@ static void shutdown(struct async_struct * info)
 	unsigned long	flags;
 	struct serial_state *state;
 
-	if (!(info->flags & ASYNC_INITIALIZED))
+	if (!(info->state->flags & ASYNC_INITIALIZED))
 		return;
 
 	state = info->state;
@@ -646,7 +646,7 @@ static void shutdown(struct async_struct * info)
 	if (info->tty)
 		set_bit(TTY_IO_ERROR, &info->tty->flags);
 
-	info->flags &= ~ASYNC_INITIALIZED;
+	info->state->flags &= ~ASYNC_INITIALIZED;
 	local_irq_restore(flags);
 }
 
@@ -691,7 +691,7 @@ static void change_speed(struct async_struct *info,
 		baud = 9600;	/* B0 transition handled in rs_set_termios */
 	baud_base = info->state->baud_base;
 	if (baud == 38400 &&
-	    ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+	    ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
 		quot = info->state->custom_divisor;
 	else {
 		if (baud == 134)
@@ -709,7 +709,7 @@ static void change_speed(struct async_struct *info,
 		if (!baud)
 			baud = 9600;
 		if (baud == 38400 &&
-		    ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+		    ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
 			quot = info->state->custom_divisor;
 		else {
 			if (baud == 134)
@@ -728,17 +728,17 @@ static void change_speed(struct async_struct *info,
 
 	/* CTS flow control flag and modem status interrupts */
 	info->IER &= ~UART_IER_MSI;
-	if (info->flags & ASYNC_HARDPPS_CD)
+	if (info->state->flags & ASYNC_HARDPPS_CD)
 		info->IER |= UART_IER_MSI;
 	if (cflag & CRTSCTS) {
-		info->flags |= ASYNC_CTS_FLOW;
+		info->state->flags |= ASYNC_CTS_FLOW;
 		info->IER |= UART_IER_MSI;
 	} else
-		info->flags &= ~ASYNC_CTS_FLOW;
+		info->state->flags &= ~ASYNC_CTS_FLOW;
 	if (cflag & CLOCAL)
-		info->flags &= ~ASYNC_CHECK_CD;
+		info->state->flags &= ~ASYNC_CHECK_CD;
 	else {
-		info->flags |= ASYNC_CHECK_CD;
+		info->state->flags |= ASYNC_CHECK_CD;
 		info->IER |= UART_IER_MSI;
 	}
 	/* TBD:
@@ -1080,8 +1080,6 @@ static int set_serial_info(struct async_struct * info,
 			return -EPERM;
 		state->flags = ((state->flags & ~ASYNC_USR_MASK) |
 			       (new_serial.flags & ASYNC_USR_MASK));
-		info->flags = ((info->flags & ~ASYNC_USR_MASK) |
-			       (new_serial.flags & ASYNC_USR_MASK));
 		state->custom_divisor = new_serial.custom_divisor;
 		goto check_and_exit;
 	}
@@ -1099,15 +1097,13 @@ static int set_serial_info(struct async_struct * info,
 	state->baud_base = new_serial.baud_base;
 	state->flags = ((state->flags & ~ASYNC_FLAGS) |
 			(new_serial.flags & ASYNC_FLAGS));
-	info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) |
-		       (info->flags & ASYNC_INTERNAL_FLAGS));
 	state->custom_divisor = new_serial.custom_divisor;
 	state->close_delay = new_serial.close_delay * HZ/100;
 	state->closing_wait = new_serial.closing_wait * HZ/100;
-	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+	info->tty->low_latency = (state->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
 check_and_exit:
-	if (info->flags & ASYNC_INITIALIZED) {
+	if (state->flags & ASYNC_INITIALIZED) {
 		if (((old_state.flags & ASYNC_SPD_MASK) !=
 		     (state->flags & ASYNC_SPD_MASK)) ||
 		    (old_state.custom_divisor != state->custom_divisor)) {
@@ -1437,7 +1433,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 		local_irq_restore(flags);
 		return;
 	}
-	info->flags |= ASYNC_CLOSING;
+	state->flags |= ASYNC_CLOSING;
 	/*
 	 * Now we wait for the transmit buffer to clear; and we notify 
 	 * the line discipline to only process XON/XOFF characters.
@@ -1452,7 +1448,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 	 * line status register.
 	 */
 	info->read_status_mask &= ~UART_LSR_DR;
-	if (info->flags & ASYNC_INITIALIZED) {
+	if (state->flags & ASYNC_INITIALIZED) {
 	        /* disable receive interrupts */
 	        custom.intena = IF_RBF;
 		mb();
@@ -1479,7 +1475,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 		}
 		wake_up_interruptible(&info->open_wait);
 	}
-	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
+	state->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
 	wake_up_interruptible(&info->close_wait);
 	local_irq_restore(flags);
 }
@@ -1563,7 +1559,7 @@ static void rs_hangup(struct tty_struct *tty)
 	rs_flush_buffer(tty);
 	shutdown(info);
 	state->count = 0;
-	info->flags &= ~ASYNC_NORMAL_ACTIVE;
+	state->flags &= ~ASYNC_NORMAL_ACTIVE;
 	info->tty = NULL;
 	wake_up_interruptible(&info->open_wait);
 }
@@ -1591,11 +1587,11 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 	 * until it's done, and then try again.
 	 */
 	if (tty_hung_up_p(filp) ||
-	    (info->flags & ASYNC_CLOSING)) {
-		if (info->flags & ASYNC_CLOSING)
+	    (state->flags & ASYNC_CLOSING)) {
+		if (state->flags & ASYNC_CLOSING)
 			interruptible_sleep_on(&info->close_wait);
 #ifdef SERIAL_DO_RESTART
-		return ((info->flags & ASYNC_HUP_NOTIFY) ?
+		return ((state->flags & ASYNC_HUP_NOTIFY) ?
 			-EAGAIN : -ERESTARTSYS);
 #else
 		return -EAGAIN;
@@ -1608,7 +1604,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 	 */
 	if ((filp->f_flags & O_NONBLOCK) ||
 	    (tty->flags & (1 << TTY_IO_ERROR))) {
-		info->flags |= ASYNC_NORMAL_ACTIVE;
+		state->flags |= ASYNC_NORMAL_ACTIVE;
 		return 0;
 	}
 
@@ -1642,9 +1638,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 		local_irq_restore(flags);
 		set_current_state(TASK_INTERRUPTIBLE);
 		if (tty_hung_up_p(filp) ||
-		    !(info->flags & ASYNC_INITIALIZED)) {
+		    !(state->flags & ASYNC_INITIALIZED)) {
 #ifdef SERIAL_DO_RESTART
-			if (info->flags & ASYNC_HUP_NOTIFY)
+			if (state->flags & ASYNC_HUP_NOTIFY)
 				retval = -EAGAIN;
 			else
 				retval = -ERESTARTSYS;
@@ -1653,7 +1649,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 #endif
 			break;
 		}
-		if (!(info->flags & ASYNC_CLOSING) &&
+		if (!(state->flags & ASYNC_CLOSING) &&
 		    (do_clocal || (!(ciab.pra & SER_DCD)) ))
 			break;
 		if (signal_pending(current)) {
@@ -1679,7 +1675,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 #endif
 	if (retval)
 		return retval;
-	info->flags |= ASYNC_NORMAL_ACTIVE;
+	state->flags |= ASYNC_NORMAL_ACTIVE;
 	return 0;
 }
 
@@ -1705,7 +1701,6 @@ static int get_async_struct(int line, struct async_struct **ret_info)
 	init_waitqueue_head(&info->delta_msr_wait);
 #endif
 	info->port = sstate->port;
-	info->flags = sstate->flags;
 	info->xmit_fifo_size = sstate->xmit_fifo_size;
 	info->line = line;
 	info->state = sstate;
@@ -1741,17 +1736,17 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
 #ifdef SERIAL_DEBUG_OPEN
 	printk("rs_open %s, count = %d\n", tty->name, info->state->count);
 #endif
-	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+	info->tty->low_latency = (info->state->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
 	/*
 	 * If the port is the middle of closing, bail out now
 	 */
 	if (tty_hung_up_p(filp) ||
-	    (info->flags & ASYNC_CLOSING)) {
-		if (info->flags & ASYNC_CLOSING)
+	    (info->state->flags & ASYNC_CLOSING)) {
+		if (info->state->flags & ASYNC_CLOSING)
 			interruptible_sleep_on(&info->close_wait);
 #ifdef SERIAL_DO_RESTART
-		return ((info->flags & ASYNC_HUP_NOTIFY) ?
+		return ((info->state->flags & ASYNC_HUP_NOTIFY) ?
 			-EAGAIN : -ERESTARTSYS);
 #else
 		return -EAGAIN;
@@ -1799,7 +1794,6 @@ static inline void line_info(struct seq_file *m, struct serial_state *state)
 	if (!info) {
 		info = &scr_info;	/* This is just for serial_{in,out} */
 
-		info->flags = state->flags;
 		info->quot = 0;
 		info->tty = NULL;
 	}
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 23/68] TTY: remove serialP.h inclusion from some files
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh; +Cc: alan, linux-serial, linux-kernel, jirislaby
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

All of them do not use the ugly interface defined in that header.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
---
 arch/xtensa/platforms/iss/console.c |    1 -
 drivers/tty/serial/ioc4_serial.c    |    1 -
 drivers/tty/serial/m32r_sio.c       |    1 -
 3 files changed, 3 deletions(-)

diff --git a/arch/xtensa/platforms/iss/console.c b/arch/xtensa/platforms/iss/console.c
index d1a7861..f9726f6 100644
--- a/arch/xtensa/platforms/iss/console.c
+++ b/arch/xtensa/platforms/iss/console.c
@@ -19,7 +19,6 @@
 #include <linux/param.h>
 #include <linux/seq_file.h>
 #include <linux/serial.h>
-#include <linux/serialP.h>
 
 #include <asm/uaccess.h>
 #include <asm/irq.h>
diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c
index dfec69a..e16894f 100644
--- a/drivers/tty/serial/ioc4_serial.c
+++ b/drivers/tty/serial/ioc4_serial.c
@@ -16,7 +16,6 @@
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial.h>
-#include <linux/serialP.h>
 #include <linux/circ_buf.h>
 #include <linux/serial_reg.h>
 #include <linux/module.h>
diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c
index e465dda..a070362 100644
--- a/drivers/tty/serial/m32r_sio.c
+++ b/drivers/tty/serial/m32r_sio.c
@@ -38,7 +38,6 @@
 #include <linux/console.h>
 #include <linux/sysrq.h>
 #include <linux/serial.h>
-#include <linux/serialP.h>
 #include <linux/delay.h>
 
 #include <asm/m32r.h>
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 30/68] TTY: simserial, remove support of shared interrupts
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh; +Cc: alan, linux-serial, linux-kernel, jirislaby, Tony Luck,
	Fenghua Yu
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

It never worked there. The ISR was never written for that kind of
stuff. So remove all that crap with a hash of linked lists and pass
the pointer directly to the ISR.

BTW this answers the question there:
  * I don't know exactly why they don't use the dev_id opaque data
  * pointer instead of this extra lookup table

-> Because they thought they will support more devices bound to a
single interrupt w/o IRQF_SHARED. They would need exactly the hash
there.

What I don't understand is rebinding of the interrupt in the shutdown
path. They perhaps meant to do just synchronize_irq? In any case, this
is all gone and free_irq there properly.

By removing the hash we save some bits (exactly NR_IRQS * 8 bytes of
.bss and over a kilo of .text):
before:
   text    data     bss     dec     hex filename
  19600     320    8227   28147    6df3 ../a/ia64/arch/ia64/hp/sim/simserial.o
after:
   text    data     bss     dec     hex filename
  18568     320      28   18916    49e4 ../a/ia64/arch/ia64/hp/sim/simserial.o

Note that a shared interrupt could not work too. request_irq requires
data parameter to be non-NULL. So the whole IRQ_T exercise was
pointless.

Finally, this helps us remove another two members of async_struct :).

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
---
 arch/ia64/hp/sim/simserial.c |   64 +++++-------------------------------------
 include/linux/serialP.h      |    2 --
 2 files changed, 7 insertions(+), 59 deletions(-)

diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c
index d32b759..c35552d 100644
--- a/arch/ia64/hp/sim/simserial.c
+++ b/arch/ia64/hp/sim/simserial.c
@@ -92,8 +92,6 @@ static struct serial_uart_config uart_config[] = {
 
 struct tty_driver *hp_simserial_driver;
 
-static struct async_struct *IRQ_ports[NR_IRQS];
-
 static struct console *console;
 
 static unsigned char *tmp_buf;
@@ -167,14 +165,9 @@ static  void receive_chars(struct tty_struct *tty)
  */
 static irqreturn_t rs_interrupt_single(int irq, void *dev_id)
 {
-	struct async_struct * info;
+	struct async_struct *info = dev_id;
 
-	/*
-	 * I don't know exactly why they don't use the dev_id opaque data
-	 * pointer instead of this extra lookup table
-	 */
-	info = IRQ_ports[irq];
-	if (!info || !info->tty) {
+	if (!info->tty) {
 		printk(KERN_INFO "simrs_interrupt_single: info|tty=0 info=%p problem\n", info);
 		return IRQ_NONE;
 	}
@@ -456,7 +449,6 @@ static void shutdown(struct async_struct * info)
 {
 	unsigned long	flags;
 	struct serial_state *state = info->state;
-	int		retval;
 
 	if (!(state->flags & ASYNC_INITIALIZED))
 		return;
@@ -468,33 +460,8 @@ static void shutdown(struct async_struct * info)
 
 	local_irq_save(flags);
 	{
-		/*
-		 * First unlink the serial port from the IRQ chain...
-		 */
-		if (info->next_port)
-			info->next_port->prev_port = info->prev_port;
-		if (info->prev_port)
-			info->prev_port->next_port = info->next_port;
-		else
-			IRQ_ports[state->irq] = info->next_port;
-
-		/*
-		 * Free the IRQ, if necessary
-		 */
-		if (state->irq && (!IRQ_ports[state->irq] ||
-				   !IRQ_ports[state->irq]->next_port)) {
-			if (IRQ_ports[state->irq]) {
-				free_irq(state->irq, NULL);
-				retval = request_irq(state->irq, rs_interrupt_single,
-						     IRQ_T(state), "serial",
-						     NULL);
-
-				if (retval)
-					printk(KERN_ERR "serial shutdown: request_irq: error %d"
-					       "  Couldn't reacquire IRQ.\n", retval);
-			} else
-				free_irq(state->irq, NULL);
-		}
+		if (state->irq)
+			free_irq(state->irq, info);
 
 		if (info->xmit.buf) {
 			free_page((unsigned long) info->xmit.buf);
@@ -645,7 +612,6 @@ startup(struct async_struct *info)
 {
 	unsigned long flags;
 	int	retval=0;
-	irq_handler_t handler;
 	struct serial_state *state= info->state;
 	unsigned long page;
 
@@ -677,29 +643,13 @@ startup(struct async_struct *info)
 	/*
 	 * Allocate the IRQ if necessary
 	 */
-	if (state->irq && (!IRQ_ports[state->irq] ||
-			  !IRQ_ports[state->irq]->next_port)) {
-		if (IRQ_ports[state->irq]) {
-			retval = -EBUSY;
-			goto errout;
-		} else
-			handler = rs_interrupt_single;
-
-		retval = request_irq(state->irq, handler, IRQ_T(state),
-				"simserial", NULL);
+	if (state->irq) {
+		retval = request_irq(state->irq, rs_interrupt_single,
+				IRQ_T(state), "simserial", info);
 		if (retval)
 			goto errout;
 	}
 
-	/*
-	 * Insert serial port into IRQ chain.
-	 */
-	info->prev_port = NULL;
-	info->next_port = IRQ_ports[state->irq];
-	if (info->next_port)
-		info->next_port->prev_port = info;
-	IRQ_ports[state->irq] = info;
-
 	if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags);
 
 	info->xmit.head = info->xmit.tail = 0;
diff --git a/include/linux/serialP.h b/include/linux/serialP.h
index 6ce488c..b8543f9 100644
--- a/include/linux/serialP.h
+++ b/include/linux/serialP.h
@@ -56,8 +56,6 @@ struct async_struct {
 	wait_queue_head_t	open_wait;
 	wait_queue_head_t	close_wait;
 	wait_queue_head_t	delta_msr_wait;
-	struct async_struct	*next_port; /* For the linked list */
-	struct async_struct	*prev_port;
 };
 
 #endif /* _LINUX_SERIAL_H */
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 65/68] TTY: pdc_cons, fix open vs pdc_console_tty_driver race
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh
  Cc: alan, linux-serial, linux-kernel, jirislaby, Kyle McMartin,
	Helge Deller, James E.J. Bottomley
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

Assign the pointer to pdc_console_tty_driver (a tty_driver) earlier.
Otherwise the timer may dereference NULL.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Kyle McMartin <kyle@mcmartin.ca>
Cc: Helge Deller <deller@gmx.de>
Cc: "James E.J. Bottomley" <jejb@parisc-linux.org>
---
 arch/parisc/kernel/pdc_cons.c |   29 +++++++++++++----------------
 1 file changed, 13 insertions(+), 16 deletions(-)

diff --git a/arch/parisc/kernel/pdc_cons.c b/arch/parisc/kernel/pdc_cons.c
index 8ad0521..d14e20f 100644
--- a/arch/parisc/kernel/pdc_cons.c
+++ b/arch/parisc/kernel/pdc_cons.c
@@ -160,9 +160,7 @@ static void pdc_console_poll(unsigned long unused)
 
 static int __init pdc_console_tty_driver_init(void)
 {
-
 	int err;
-	struct tty_driver *drv;
 
 	/* Check if the console driver is still registered.
 	 * It is unregistered if the pdc console was not selected as the
@@ -184,28 +182,27 @@ static int __init pdc_console_tty_driver_init(void)
 	printk(KERN_INFO "The PDC console driver is still registered, removing CON_BOOT flag\n");
 	pdc_cons.flags &= ~CON_BOOT;
 
-	drv = alloc_tty_driver(1);
+	pdc_console_tty_driver = alloc_tty_driver(1);
 
-	if (!drv)
+	if (!pdc_console_tty_driver)
 		return -ENOMEM;
 
-	drv->driver_name = "pdc_cons";
-	drv->name = "ttyB";
-	drv->major = MUX_MAJOR;
-	drv->minor_start = 0;
-	drv->type = TTY_DRIVER_TYPE_SYSTEM;
-	drv->init_termios = tty_std_termios;
-	drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
-	tty_set_operations(drv, &pdc_console_tty_ops);
-
-	err = tty_register_driver(drv);
+	pdc_console_tty_driver->driver_name = "pdc_cons";
+	pdc_console_tty_driver->name = "ttyB";
+	pdc_console_tty_driver->major = MUX_MAJOR;
+	pdc_console_tty_driver->minor_start = 0;
+	pdc_console_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM;
+	pdc_console_tty_driver->init_termios = tty_std_termios;
+	pdc_console_tty_driver->flags = TTY_DRIVER_REAL_RAW |
+		TTY_DRIVER_RESET_TERMIOS;
+	tty_set_operations(pdc_console_tty_driver, &pdc_console_tty_ops);
+
+	err = tty_register_driver(pdc_console_tty_driver);
 	if (err) {
 		printk(KERN_ERR "Unable to register the PDC console TTY driver\n");
 		return err;
 	}
 
-	pdc_console_tty_driver = drv;
-
 	return 0;
 }
 
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 37/68] TTY: amiserial/simserial, use tty_port
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh
  Cc: alan, linux-serial, linux-kernel, jirislaby, Geert Uytterhoeven,
	Tony Luck, Fenghua Yu
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

Add tty_port to serial_state and start using common tty port members
from tty_port in amiserial and simserial. The rest will follow one by
one.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
---
 arch/ia64/hp/sim/simserial.c |   24 ++++++++--------
 drivers/tty/amiserial.c      |   63 +++++++++++++++++++++---------------------
 include/linux/serialP.h      |    7 ++---
 3 files changed, 45 insertions(+), 49 deletions(-)

diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c
index a76a27e..614c091 100644
--- a/arch/ia64/hp/sim/simserial.c
+++ b/arch/ia64/hp/sim/simserial.c
@@ -165,7 +165,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id)
 {
 	struct serial_state *info = dev_id;
 
-	if (!info->tty) {
+	if (!info->tport.tty) {
 		printk(KERN_INFO "simrs_interrupt_single: info|tty=0 info=%p problem\n", info);
 		return IRQ_NONE;
 	}
@@ -173,7 +173,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id)
 	 * pretty simple in our case, because we only get interrupts
 	 * on inbound traffic
 	 */
-	receive_chars(info->tty);
+	receive_chars(info->tport.tty);
 	return IRQ_HANDLED;
 }
 
@@ -533,14 +533,14 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 	shutdown(tty, info);
 	rs_flush_buffer(tty);
 	tty_ldisc_flush(tty);
-	info->tty = NULL;
-	if (info->blocked_open) {
+	info->tport.tty = NULL;
+	if (info->tport.blocked_open) {
 		if (info->close_delay)
 			schedule_timeout_interruptible(info->close_delay);
-		wake_up_interruptible(&info->open_wait);
+		wake_up_interruptible(&info->tport.open_wait);
 	}
 	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
-	wake_up_interruptible(&info->close_wait);
+	wake_up_interruptible(&info->tport.close_wait);
 }
 
 /*
@@ -569,8 +569,8 @@ static void rs_hangup(struct tty_struct *tty)
 
 	info->count = 0;
 	info->flags &= ~ASYNC_NORMAL_ACTIVE;
-	info->tty = NULL;
-	wake_up_interruptible(&info->open_wait);
+	info->tport.tty = NULL;
+	wake_up_interruptible(&info->tport.open_wait);
 }
 
 
@@ -662,8 +662,9 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
 	unsigned long		page;
 
 	info->count++;
-	info->tty = tty;
+	info->tport.tty = tty;
 	tty->driver_data = info;
+	tty->port = &info->tport;
 
 #ifdef SIMSERIAL_DEBUG
 	printk("rs_open %s, count = %d\n", tty->name, info->count);
@@ -685,7 +686,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
 	 */
 	if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) {
 		if (info->flags & ASYNC_CLOSING)
-			interruptible_sleep_on(&info->close_wait);
+			interruptible_sleep_on(&info->tport.close_wait);
 #ifdef SERIAL_DO_RESTART
 		return ((info->flags & ASYNC_HUP_NOTIFY) ?
 			-EAGAIN : -ERESTARTSYS);
@@ -827,8 +828,7 @@ simrs_init (void)
 	 * Let's have a little bit of fun !
 	 */
 	for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
-		init_waitqueue_head(&state->open_wait);
-		init_waitqueue_head(&state->close_wait);
+		tty_port_init(&state->tport);
 
 		if (state->type == PORT_UNKNOWN) continue;
 
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
index 5b87744..71d3331 100644
--- a/drivers/tty/amiserial.c
+++ b/drivers/tty/amiserial.c
@@ -234,7 +234,7 @@ static void receive_chars(struct serial_state *info)
 {
         int status;
 	int serdatr;
-	struct tty_struct *tty = info->tty;
+	struct tty_struct *tty = info->tport.tty;
 	unsigned char ch, flag;
 	struct	async_icount *icount;
 	int oe = 0;
@@ -331,8 +331,8 @@ static void transmit_chars(struct serial_state *info)
 		return;
 	}
 	if (info->xmit.head == info->xmit.tail
-	    || info->tty->stopped
-	    || info->tty->hw_stopped) {
+	    || info->tport.tty->stopped
+	    || info->tport.tty->hw_stopped) {
 		info->IER &= ~UART_IER_THRI;
 	        custom.intena = IF_TBE;
 		mb();
@@ -347,7 +347,7 @@ static void transmit_chars(struct serial_state *info)
 	if (CIRC_CNT(info->xmit.head,
 		     info->xmit.tail,
 		     SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
-		tty_wakeup(info->tty);
+		tty_wakeup(info->tport.tty);
 
 #ifdef SERIAL_DEBUG_INTR
 	printk("THRE...");
@@ -384,7 +384,7 @@ static void check_modem_status(struct serial_state *info)
 		}
 		if (dstatus & SER_CTS)
 			icount->cts++;
-		wake_up_interruptible(&info->delta_msr_wait);
+		wake_up_interruptible(&info->tport.delta_msr_wait);
 	}
 
 	if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) {
@@ -393,29 +393,29 @@ static void check_modem_status(struct serial_state *info)
 		       (!(status & SER_DCD)) ? "on" : "off");
 #endif
 		if (!(status & SER_DCD))
-			wake_up_interruptible(&info->open_wait);
+			wake_up_interruptible(&info->tport.open_wait);
 		else {
 #ifdef SERIAL_DEBUG_OPEN
 			printk("doing serial hangup...");
 #endif
-			if (info->tty)
-				tty_hangup(info->tty);
+			if (info->tport.tty)
+				tty_hangup(info->tport.tty);
 		}
 	}
 	if (info->flags & ASYNC_CTS_FLOW) {
-		if (info->tty->hw_stopped) {
+		if (info->tport.tty->hw_stopped) {
 			if (!(status & SER_CTS)) {
 #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
 				printk("CTS tx start...");
 #endif
-				info->tty->hw_stopped = 0;
+				info->tport.tty->hw_stopped = 0;
 				info->IER |= UART_IER_THRI;
 				custom.intena = IF_SETCLR | IF_TBE;
 				mb();
 				/* set a pending Tx Interrupt, transmitter should restart now */
 				custom.intreq = IF_SETCLR | IF_TBE;
 				mb();
-				tty_wakeup(info->tty);
+				tty_wakeup(info->tport.tty);
 				return;
 			}
 		} else {
@@ -423,7 +423,7 @@ static void check_modem_status(struct serial_state *info)
 #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
 				printk("CTS tx stop...");
 #endif
-				info->tty->hw_stopped = 1;
+				info->tport.tty->hw_stopped = 1;
 				info->IER &= ~UART_IER_THRI;
 				/* disable Tx interrupt and remove any pending interrupts */
 				custom.intena = IF_TBE;
@@ -456,7 +456,7 @@ static irqreturn_t ser_rx_int(int irq, void *dev_id)
 	printk("ser_rx_int...");
 #endif
 
-	if (!info->tty)
+	if (!info->tport.tty)
 		return IRQ_NONE;
 
 	receive_chars(info);
@@ -475,7 +475,7 @@ static irqreturn_t ser_tx_int(int irq, void *dev_id)
 	  printk("ser_tx_int...");
 #endif
 
-	  if (!info->tty)
+	  if (!info->tport.tty)
 		return IRQ_NONE;
 
 	  transmit_chars(info);
@@ -607,7 +607,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info)
 	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
 	 * here so the queue might never be waken up
 	 */
-	wake_up_interruptible(&info->delta_msr_wait);
+	wake_up_interruptible(&info->tport.delta_msr_wait);
 
 	/*
 	 * Free the IRQ, if necessary
@@ -1274,7 +1274,7 @@ static int rs_ioctl(struct tty_struct *tty,
 			cprev = info->icount;
 			local_irq_restore(flags);
 			while (1) {
-				interruptible_sleep_on(&info->delta_msr_wait);
+				interruptible_sleep_on(&info->tport.delta_msr_wait);
 				/* see if a signal did it */
 				if (signal_pending(current))
 					return -ERESTARTSYS;
@@ -1442,15 +1442,15 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 		
 	tty_ldisc_flush(tty);
 	tty->closing = 0;
-	state->tty = NULL;
-	if (state->blocked_open) {
+	state->tport.tty = NULL;
+	if (state->tport.blocked_open) {
 		if (state->close_delay) {
 			msleep_interruptible(jiffies_to_msecs(state->close_delay));
 		}
-		wake_up_interruptible(&state->open_wait);
+		wake_up_interruptible(&state->tport.open_wait);
 	}
 	state->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
-	wake_up_interruptible(&state->close_wait);
+	wake_up_interruptible(&state->tport.close_wait);
 	local_irq_restore(flags);
 }
 
@@ -1531,8 +1531,8 @@ static void rs_hangup(struct tty_struct *tty)
 	shutdown(tty, info);
 	info->count = 0;
 	info->flags &= ~ASYNC_NORMAL_ACTIVE;
-	info->tty = NULL;
-	wake_up_interruptible(&info->open_wait);
+	info->tport.tty = NULL;
+	wake_up_interruptible(&info->tport.open_wait);
 }
 
 /*
@@ -1559,7 +1559,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
 		if (info->flags & ASYNC_CLOSING)
-			interruptible_sleep_on(&info->close_wait);
+			interruptible_sleep_on(&info->tport.close_wait);
 #ifdef SERIAL_DO_RESTART
 		return ((info->flags & ASYNC_HUP_NOTIFY) ?
 			-EAGAIN : -ERESTARTSYS);
@@ -1589,7 +1589,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 	 * exit, either normal or abnormal.
 	 */
 	retval = 0;
-	add_wait_queue(&info->open_wait, &wait);
+	add_wait_queue(&info->tport.open_wait, &wait);
 #ifdef SERIAL_DEBUG_OPEN
 	printk("block_til_ready before block: ttys%d, count = %d\n",
 	       info->line, info->count);
@@ -1600,7 +1600,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 		info->count--;
 	}
 	local_irq_restore(flags);
-	info->blocked_open++;
+	info->tport.blocked_open++;
 	while (1) {
 		local_irq_save(flags);
 		if (tty->termios->c_cflag & CBAUD)
@@ -1635,10 +1635,10 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 		tty_lock();
 	}
 	__set_current_state(TASK_RUNNING);
-	remove_wait_queue(&info->open_wait, &wait);
+	remove_wait_queue(&info->tport.open_wait, &wait);
 	if (extra_count)
 		info->count++;
-	info->blocked_open--;
+	info->tport.blocked_open--;
 #ifdef SERIAL_DEBUG_OPEN
 	printk("block_til_ready after blocking: ttys%d, count = %d\n",
 	       info->line, info->count);
@@ -1661,8 +1661,9 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
 	int retval;
 
 	info->count++;
-	info->tty = tty;
+	info->tport.tty = tty;
 	tty->driver_data = info;
+	tty->port = &info->tport;
 	if (serial_paranoia_check(info, tty->name, "rs_open"))
 		return -ENODEV;
 
@@ -1677,7 +1678,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
 		if (info->flags & ASYNC_CLOSING)
-			interruptible_sleep_on(&info->close_wait);
+			interruptible_sleep_on(&info->tport.close_wait);
 #ifdef SERIAL_DO_RESTART
 		return ((info->flags & ASYNC_HUP_NOTIFY) ?
 			-EAGAIN : -ERESTARTSYS);
@@ -1869,9 +1870,7 @@ static int __init amiga_serial_probe(struct platform_device *pdev)
 	state->icount.rx = state->icount.tx = 0;
 	state->icount.frame = state->icount.parity = 0;
 	state->icount.overrun = state->icount.brk = 0;
-	init_waitqueue_head(&state->open_wait);
-	init_waitqueue_head(&state->close_wait);
-	init_waitqueue_head(&state->delta_msr_wait);
+	tty_port_init(&state->tport);
 
 	printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n",
 		       state->line);
diff --git a/include/linux/serialP.h b/include/linux/serialP.h
index 984f5ba..32d45b8 100644
--- a/include/linux/serialP.h
+++ b/include/linux/serialP.h
@@ -23,6 +23,7 @@
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/circ_buf.h>
+#include <linux/tty.h>
 #include <linux/wait.h>
 
 struct serial_state {
@@ -38,6 +39,7 @@ struct serial_state {
 	unsigned short	close_delay;
 	unsigned short	closing_wait; /* time to wait before closing */
 	struct async_icount	icount;	
+	struct tty_port tport;
 
 	/* amiserial */
 	int			read_status_mask;
@@ -46,14 +48,9 @@ struct serial_state {
 	int			quot;
 	int			IER; 	/* Interrupt Enable Register */
 	int			MCR; 	/* Modem control register */
-	wait_queue_head_t	delta_msr_wait;
 	/* simserial */
 	int			x_char;	/* xon/xoff character */
-	int			blocked_open; /* # of blocked opens */
  	struct circ_buf		xmit;
-	wait_queue_head_t	open_wait;
-	wait_queue_head_t	close_wait;
-	struct tty_struct 	*tty;
 	/* /simserial */
 	/* /amiserial */
 };
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 03/68] TTY: let alloc_tty_driver deduce the owner automatically
From: Jiri Slaby @ 2012-03-05 13:51 UTC (permalink / raw)
  To: gregkh; +Cc: alan, linux-serial, linux-kernel, jirislaby
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

Like the rest of the kernel, make a stub from alloc_tty_driver which
calls __alloc_tty_driver with proper owner. This will save us one more
assignment on the driver side.

Also this fixes some drivers which didn't set the owner. This allowed
user to remove the module from the system even though a tty from the
driver is still open.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
---
 drivers/tty/tty_io.c       |    5 +++--
 include/linux/tty_driver.h |    5 ++++-
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index f105ce5..bd95cea 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -3049,7 +3049,7 @@ void tty_unregister_device(struct tty_driver *driver, unsigned index)
 }
 EXPORT_SYMBOL(tty_unregister_device);
 
-struct tty_driver *alloc_tty_driver(int lines)
+struct tty_driver *__alloc_tty_driver(int lines, struct module *owner)
 {
 	struct tty_driver *driver;
 
@@ -3058,11 +3058,12 @@ struct tty_driver *alloc_tty_driver(int lines)
 		kref_init(&driver->kref);
 		driver->magic = TTY_DRIVER_MAGIC;
 		driver->num = lines;
+		driver->owner = owner;
 		/* later we'll move allocation of tables here */
 	}
 	return driver;
 }
-EXPORT_SYMBOL(alloc_tty_driver);
+EXPORT_SYMBOL(__alloc_tty_driver);
 
 static void destruct_tty_driver(struct kref *kref)
 {
diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h
index 5cf6850..6e65493 100644
--- a/include/linux/tty_driver.h
+++ b/include/linux/tty_driver.h
@@ -234,6 +234,7 @@
  *	if provided (otherwise EINVAL will be returned).
  */
 
+#include <linux/export.h>
 #include <linux/fs.h>
 #include <linux/list.h>
 #include <linux/cdev.h>
@@ -324,7 +325,7 @@ struct tty_driver {
 
 extern struct list_head tty_drivers;
 
-extern struct tty_driver *alloc_tty_driver(int lines);
+extern struct tty_driver *__alloc_tty_driver(int lines, struct module *owner);
 extern void put_tty_driver(struct tty_driver *driver);
 extern void tty_set_operations(struct tty_driver *driver,
 			const struct tty_operations *op);
@@ -332,6 +333,8 @@ extern struct tty_driver *tty_find_polling_driver(char *name, int *line);
 
 extern void tty_driver_kref_put(struct tty_driver *driver);
 
+#define alloc_tty_driver(lines) __alloc_tty_driver(lines, THIS_MODULE)
+
 static inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d)
 {
 	kref_get(&d->kref);
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 09/68] TTY: tty_driver, document tty->ops->shutdown limitation
From: Jiri Slaby @ 2012-03-05 13:51 UTC (permalink / raw)
  To: gregkh; +Cc: alan, linux-serial, linux-kernel, jirislaby
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

Note that tty->ops->shutdown is called from whatever context the user
drops the last tty reference from. E.g. if one takes a reference in
an ISR, tty close happens on other CPU and the final tty put is from
the ISR, tty->ops->shutdown will be called from that hard irq context.

We would have a problem in vt if we start using tty refcounting from
other contexts than user there. It is because vt's shutdown uses
mutexes. This is yet to be fixed.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Reported-by: Al Viro <viro@zeniv.linux.org.uk>
---
 include/linux/tty_driver.h |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h
index e064f17..6e6dbb7 100644
--- a/include/linux/tty_driver.h
+++ b/include/linux/tty_driver.h
@@ -50,6 +50,8 @@
  *	Note that tty_shutdown() is not called if ops->shutdown is defined.
  *	This means one is responsible to take care of calling ops->remove (e.g.
  *	via tty_driver_remove_tty) and releasing tty->termios.
+ *	Note that this hook may be called from *all* the contexts where one
+ *	uses tty refcounting (e.g. tty_port_tty_get).
  *
  *
  * void (*cleanup)(struct tty_struct * tty);
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 46/68] TTY: simserial, remove some tty ops
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh; +Cc: alan, linux-serial, linux-kernel, jirislaby, Tony Luck,
	Fenghua Yu
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

All ->start, ->stop and ->wait_until_sent are empty and need not be
defined. The time to remove them is now.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
---
 arch/ia64/hp/sim/simserial.c |   39 +--------------------------------------
 1 file changed, 1 insertion(+), 38 deletions(-)

diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c
index 909357e..d173dba 100644
--- a/arch/ia64/hp/sim/simserial.c
+++ b/arch/ia64/hp/sim/simserial.c
@@ -64,32 +64,7 @@ static struct console *console;
 
 extern struct console *console_drivers; /* from kernel/printk.c */
 
-/*
- * ------------------------------------------------------------
- * rs_stop() and rs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter interrupts, as necessary.
- * ------------------------------------------------------------
- */
-static void rs_stop(struct tty_struct *tty)
-{
-#ifdef SIMSERIAL_DEBUG
-	printk("rs_stop: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",
-		tty->stopped, tty->hw_stopped, tty->flow_stopped);
-#endif
-
-}
-
-static void rs_start(struct tty_struct *tty)
-{
-#ifdef SIMSERIAL_DEBUG
-	printk("rs_start: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",
-		tty->stopped, tty->hw_stopped, tty->flow_stopped);
-#endif
-}
-
-static  void receive_chars(struct tty_struct *tty)
+static void receive_chars(struct tty_struct *tty)
 {
 	unsigned char ch;
 	static unsigned char seen_esc = 0;
@@ -406,7 +381,6 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	    !(tty->termios->c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
-		rs_start(tty);
 	}
 }
 /*
@@ -514,14 +488,6 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 }
 
 /*
- * rs_wait_until_sent() --- wait until the transmitter is empty
- */
-static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
-{
-}
-
-
-/*
  * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
  */
 static void rs_hangup(struct tty_struct *tty)
@@ -736,10 +702,7 @@ static const struct tty_operations hp_ops = {
 	.unthrottle = rs_unthrottle,
 	.send_xchar = rs_send_xchar,
 	.set_termios = rs_set_termios,
-	.stop = rs_stop,
-	.start = rs_start,
 	.hangup = rs_hangup,
-	.wait_until_sent = rs_wait_until_sent,
 	.proc_fops = &rs_proc_fops,
 };
 
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 49/68] TTY: simserial, properly refcount tty_port->tty
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh; +Cc: alan, linux-serial, linux-kernel, jirislaby, Tony Luck,
	Fenghua Yu
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

So that we will not be surprised in the ISR anymore.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
---
 arch/ia64/hp/sim/simserial.c |    9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c
index 2cd6d23..b3ec91c 100644
--- a/arch/ia64/hp/sim/simserial.c
+++ b/arch/ia64/hp/sim/simserial.c
@@ -107,7 +107,7 @@ static void receive_chars(struct tty_struct *tty)
 static irqreturn_t rs_interrupt_single(int irq, void *dev_id)
 {
 	struct serial_state *info = dev_id;
-	struct tty_struct *tty = info->port.tty;
+	struct tty_struct *tty = tty_port_tty_get(&info->port);
 
 	if (!tty) {
 		printk(KERN_INFO "simrs_interrupt_single: info|tty=0 info=%p problem\n", info);
@@ -118,6 +118,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id)
 	 * on inbound traffic
 	 */
 	receive_chars(tty);
+	tty_kref_put(tty);
 	return IRQ_HANDLED;
 }
 
@@ -443,9 +444,9 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 	 */
 	shutdown(tty, info);
 	rs_flush_buffer(tty);
-	port->tty = NULL;
 
 	tty_port_close_end(port, tty);
+	tty_port_tty_set(port, NULL);
 }
 
 /*
@@ -467,7 +468,7 @@ static void rs_hangup(struct tty_struct *tty)
 
 	port->count = 0;
 	port->flags &= ~ASYNC_NORMAL_ACTIVE;
-	port->tty = NULL;
+	tty_port_tty_set(port, NULL);
 	wake_up_interruptible(&port->open_wait);
 }
 
@@ -556,7 +557,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
 	int retval;
 
 	port->count++;
-	port->tty = tty;
+	tty_port_tty_set(port, tty);
 	tty->driver_data = info;
 	tty->port = port;
 
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 12/68] TTY: srmcons, convert to use tty_port
From: Jiri Slaby @ 2012-03-05 13:51 UTC (permalink / raw)
  To: gregkh
  Cc: alan, linux-serial, linux-kernel, jirislaby, Richard Henderson,
	Ivan Kokshaysky, Matt Turner
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

This is needed because the tty buffer will become a tty_port member
later. That will help us to wipe out most of the races and checks for
the tty pointer in hot paths.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/kernel/srmcons.c |   32 +++++++++++++++++---------------
 1 file changed, 17 insertions(+), 15 deletions(-)

diff --git a/arch/alpha/kernel/srmcons.c b/arch/alpha/kernel/srmcons.c
index 2c89ce5c..3ea8094 100644
--- a/arch/alpha/kernel/srmcons.c
+++ b/arch/alpha/kernel/srmcons.c
@@ -30,9 +30,8 @@ static int srm_is_registered_console = 0;
 #define MAX_SRM_CONSOLE_DEVICES 1	/* only support 1 console device */
 
 struct srmcons_private {
-	struct tty_struct *tty;
+	struct tty_port port;
 	struct timer_list timer;
-	spinlock_t lock;
 } srmcons_singleton;
 
 typedef union _srmcons_result {
@@ -68,20 +67,21 @@ static void
 srmcons_receive_chars(unsigned long data)
 {
 	struct srmcons_private *srmconsp = (struct srmcons_private *)data;
+	struct tty_port *port = &srmconsp->port;
 	unsigned long flags;
 	int incr = 10;
 
 	local_irq_save(flags);
 	if (spin_trylock(&srmcons_callback_lock)) {
-		if (!srmcons_do_receive_chars(srmconsp->tty))
+		if (!srmcons_do_receive_chars(port->tty))
 			incr = 100;
 		spin_unlock(&srmcons_callback_lock);
 	} 
 
-	spin_lock(&srmconsp->lock);
-	if (srmconsp->tty)
+	spin_lock(&port->lock);
+	if (port->tty)
 		mod_timer(&srmconsp->timer, jiffies + incr);
-	spin_unlock(&srmconsp->lock);
+	spin_unlock(&port->lock);
 
 	local_irq_restore(flags);
 }
@@ -157,18 +157,19 @@ static int
 srmcons_open(struct tty_struct *tty, struct file *filp)
 {
 	struct srmcons_private *srmconsp = &srmcons_singleton;
+	struct tty_port *port = &srmconsp->port;
 	unsigned long flags;
 
-	spin_lock_irqsave(&srmconsp->lock, flags);
+	spin_lock_irqsave(&port->lock, flags);
 
-	if (!srmconsp->tty) {
+	if (!port->tty) {
 		tty->driver_data = srmconsp;
-
-		srmconsp->tty = tty;
+		tty->port = port;
+		port->tty = tty; /* XXX proper refcounting */
 		mod_timer(&srmconsp->timer, jiffies + 10);
 	}
 
-	spin_unlock_irqrestore(&srmconsp->lock, flags);
+	spin_unlock_irqrestore(&port->lock, flags);
 
 	return 0;
 }
@@ -177,16 +178,17 @@ static void
 srmcons_close(struct tty_struct *tty, struct file *filp)
 {
 	struct srmcons_private *srmconsp = tty->driver_data;
+	struct tty_port *port = &srmconsp->port;
 	unsigned long flags;
 
-	spin_lock_irqsave(&srmconsp->lock, flags);
+	spin_lock_irqsave(&port->lock, flags);
 
 	if (tty->count == 1) {
-		srmconsp->tty = NULL;
+		port->tty = NULL;
 		del_timer(&srmconsp->timer);
 	}
 
-	spin_unlock_irqrestore(&srmconsp->lock, flags);
+	spin_unlock_irqrestore(&port->lock, flags);
 }
 
 
@@ -203,7 +205,7 @@ static const struct tty_operations srmcons_ops = {
 static int __init
 srmcons_init(void)
 {
-	spin_lock_init(&srmcons_singleton.lock);
+	tty_port_init(&srmcons_singleton.port);
 	setup_timer(&srmcons_singleton.timer, srmcons_receive_chars,
 			(unsigned long)&srmcons_singleton);
 	if (srm_is_registered_console) {
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 10/68] ALPHA: srmcons, use timer functions
From: Jiri Slaby @ 2012-03-05 13:51 UTC (permalink / raw)
  To: gregkh
  Cc: alan, linux-serial, linux-kernel, jirislaby, Richard Henderson,
	Ivan Kokshaysky, Matt Turner
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

It makes the code more readable. We move the setup to the allocation
location because we need to initialize timers only once.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/kernel/srmcons.c |   14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/arch/alpha/kernel/srmcons.c b/arch/alpha/kernel/srmcons.c
index 783f4e5..f1fdf17 100644
--- a/arch/alpha/kernel/srmcons.c
+++ b/arch/alpha/kernel/srmcons.c
@@ -79,10 +79,8 @@ srmcons_receive_chars(unsigned long data)
 	} 
 
 	spin_lock(&srmconsp->lock);
-	if (srmconsp->tty) {
-		srmconsp->timer.expires = jiffies + incr;
-		add_timer(&srmconsp->timer);
-	}
+	if (srmconsp->tty)
+		mod_timer(&srmconsp->timer, jiffies + incr);
 	spin_unlock(&srmconsp->lock);
 
 	local_irq_restore(flags);
@@ -172,7 +170,8 @@ srmcons_get_private_struct(struct srmcons_private **ps)
 		else {
 			srmconsp->tty = NULL;
 			spin_lock_init(&srmconsp->lock);
-			init_timer(&srmconsp->timer);
+			setup_timer(&srmconsp->timer, srmcons_receive_chars,
+					(unsigned long)srmconsp);
 		}
 
 		spin_unlock_irqrestore(&srmconsp_lock, flags);
@@ -199,10 +198,7 @@ srmcons_open(struct tty_struct *tty, struct file *filp)
 		tty->driver_data = srmconsp;
 
 		srmconsp->tty = tty;
-		srmconsp->timer.function = srmcons_receive_chars;
-		srmconsp->timer.data = (unsigned long)srmconsp;
-		srmconsp->timer.expires = jiffies + 10;
-		add_timer(&srmconsp->timer);
+		mod_timer(&srmconsp->timer, jiffies + 10);
 	}
 
 	spin_unlock_irqrestore(&srmconsp->lock, flags);
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 44/68] TTY: simserial no longer needs serialP
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh; +Cc: alan, linux-serial, linux-kernel, jirislaby, Tony Luck,
	Fenghua Yu
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

Let's do a spin-off of serial_state structure with only needed
elements.

And remove serialP crap from includes.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
---
 arch/ia64/hp/sim/simserial.c |    9 ++++++++-
 include/linux/serialP.h      |    2 --
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c
index 3698a2f..120aad4 100644
--- a/arch/ia64/hp/sim/simserial.c
+++ b/arch/ia64/hp/sim/simserial.c
@@ -27,10 +27,10 @@
 #include <linux/seq_file.h>
 #include <linux/slab.h>
 #include <linux/capability.h>
+#include <linux/circ_buf.h>
 #include <linux/console.h>
 #include <linux/module.h>
 #include <linux/serial.h>
-#include <linux/serialP.h>
 #include <linux/sysrq.h>
 
 #include <asm/irq.h>
@@ -46,6 +46,13 @@
 
 #define NR_PORTS	1	/* only one port for now */
 
+struct serial_state {
+	struct tty_port tport;
+	struct circ_buf xmit;
+	int irq;
+	int x_char;
+};
+
 static char *serial_name = "SimSerial driver";
 static char *serial_version = "0.6";
 
diff --git a/include/linux/serialP.h b/include/linux/serialP.h
index e5e8442..9a04dec 100644
--- a/include/linux/serialP.h
+++ b/include/linux/serialP.h
@@ -44,10 +44,8 @@ struct serial_state {
 	int			quot;
 	int			IER; 	/* Interrupt Enable Register */
 	int			MCR; 	/* Modem control register */
-	/* simserial */
 	int			x_char;	/* xon/xoff character */
  	struct circ_buf		xmit;
-	/* /simserial */
 	/* /amiserial */
 };
 
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 01/68] USB: cdc-acm, use tty_standard_install
From: Jiri Slaby @ 2012-03-05 13:51 UTC (permalink / raw)
  To: gregkh; +Cc: alan, linux-serial, linux-kernel, jirislaby
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

This is a piece I missed the last time.

Do not copy the functionality all over the tree. Instead, use the
helper the tty layer provides us with.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
---
 drivers/usb/class/cdc-acm.c |    7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 6dcc3a3..b3fd449 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -509,17 +509,12 @@ static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
 	if (!acm)
 		return -ENODEV;
 
-	retval = tty_init_termios(tty);
+	retval = tty_standard_install(driver, tty);
 	if (retval)
 		goto error_init_termios;
 
 	tty->driver_data = acm;
 
-	/* Final install (we use the default method) */
-	tty_driver_kref_get(driver);
-	tty->count++;
-	driver->ttys[tty->index] = tty;
-
 	return 0;
 
 error_init_termios:
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 02/68] TTY: tty_io, remove buffer re-assignments
From: Jiri Slaby @ 2012-03-05 13:51 UTC (permalink / raw)
  To: gregkh; +Cc: alan, linux-serial, linux-kernel, jirislaby
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

TTY buffer head and tail are initialized in tty_buffer_init. No need
to do it once again in initialize_tty_struct where tty_buffer_init is
called.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
---
 drivers/tty/tty_io.c |    1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 44736f9..f105ce5 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -2933,7 +2933,6 @@ void initialize_tty_struct(struct tty_struct *tty,
 	tty->session = NULL;
 	tty->pgrp = NULL;
 	tty->overrun_time = jiffies;
-	tty->buf.head = tty->buf.tail = NULL;
 	tty_buffer_init(tty);
 	mutex_init(&tty->termios_mutex);
 	mutex_init(&tty->ldisc_mutex);
-- 
1.7.9.2



^ permalink raw reply related

* [PATCH 68/68] TTY: isdn/gigaset, use tty_port
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh
  Cc: alan, linux-serial, linux-kernel, jirislaby, Hansjoerg Lipp,
	Tilman Schmidt, gigaset307x-common
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

Let us port the code to use tty_port. We now use open_count and tty
from there. This allows us also to use tty_port_tty_set with tty
refcounting instead of hand-written locking and logic.

Note that tty and open_count are no longer protected by cs->lock. It is
protected by tty_port->lock. But since all the places where they were
used are now switched to the helpers, we are fine.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Hansjoerg Lipp <hjlipp@web.de>
Cc: Tilman Schmidt <tilman@imap.cc>
Cc: gigaset307x-common@lists.sourceforge.net
---
 drivers/isdn/gigaset/common.c    |    3 +--
 drivers/isdn/gigaset/gigaset.h   |    3 +--
 drivers/isdn/gigaset/interface.c |   46 ++++++++++++++++----------------------
 3 files changed, 21 insertions(+), 31 deletions(-)

diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c
index db6e434..7679270 100644
--- a/drivers/isdn/gigaset/common.c
+++ b/drivers/isdn/gigaset/common.c
@@ -720,12 +720,11 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
 
 	tasklet_init(&cs->event_tasklet, gigaset_handle_event,
 		     (unsigned long) cs);
+	tty_port_init(&cs->port);
 	cs->commands_pending = 0;
 	cs->cur_at_seq = 0;
 	cs->gotfwver = -1;
-	cs->open_count = 0;
 	cs->dev = NULL;
-	cs->tty = NULL;
 	cs->tty_dev = NULL;
 	cs->cidmode = cidmode != 0;
 	cs->tabnocid = gigaset_tab_nocid;
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index 8fad99e..1dc2513 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -433,8 +433,7 @@ struct cardstate {
 	spinlock_t cmdlock;
 	unsigned curlen, cmdbytes;
 
-	unsigned open_count;
-	struct tty_struct *tty;
+	struct tty_port port;
 	struct tasklet_struct if_wake_tasklet;
 	unsigned control_state;
 
diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c
index 040f5f9..b3d6ac1 100644
--- a/drivers/isdn/gigaset/interface.c
+++ b/drivers/isdn/gigaset/interface.c
@@ -146,7 +146,6 @@ static const struct tty_operations if_ops = {
 static int if_open(struct tty_struct *tty, struct file *filp)
 {
 	struct cardstate *cs;
-	unsigned long flags;
 
 	gig_dbg(DEBUG_IF, "%d+%d: %s()",
 		tty->driver->minor_start, tty->index, __func__);
@@ -161,12 +160,10 @@ static int if_open(struct tty_struct *tty, struct file *filp)
 	}
 	tty->driver_data = cs;
 
-	++cs->open_count;
+	++cs->port.count;
 
-	if (cs->open_count == 1) {
-		spin_lock_irqsave(&cs->lock, flags);
-		cs->tty = tty;
-		spin_unlock_irqrestore(&cs->lock, flags);
+	if (cs->port.count == 1) {
+		tty_port_tty_set(&cs->port, tty);
 		tty->low_latency = 1;
 	}
 
@@ -177,7 +174,6 @@ static int if_open(struct tty_struct *tty, struct file *filp)
 static void if_close(struct tty_struct *tty, struct file *filp)
 {
 	struct cardstate *cs = tty->driver_data;
-	unsigned long flags;
 
 	if (!cs) { /* happens if we didn't find cs in open */
 		printk(KERN_DEBUG "%s: no cardstate\n", __func__);
@@ -190,15 +186,10 @@ static void if_close(struct tty_struct *tty, struct file *filp)
 
 	if (!cs->connected)
 		gig_dbg(DEBUG_IF, "not connected");	/* nothing to do */
-	else if (!cs->open_count)
+	else if (!cs->port.count)
 		dev_warn(cs->dev, "%s: device not opened\n", __func__);
-	else {
-		if (!--cs->open_count) {
-			spin_lock_irqsave(&cs->lock, flags);
-			cs->tty = NULL;
-			spin_unlock_irqrestore(&cs->lock, flags);
-		}
-	}
+	else if (!--cs->port.count)
+		tty_port_tty_set(&cs->port, NULL);
 
 	mutex_unlock(&cs->mutex);
 
@@ -511,10 +502,13 @@ out:
 /* wakeup tasklet for the write operation */
 static void if_wake(unsigned long data)
 {
-	struct cardstate *cs = (struct cardstate *) data;
+	struct cardstate *cs = (struct cardstate *)data;
+	struct tty_struct *tty = tty_port_tty_get(&cs->port);
 
-	if (cs->tty)
-		tty_wakeup(cs->tty);
+	if (tty) {
+		tty_wakeup(tty);
+		tty_kref_put(tty);
+	}
 }
 
 /*** interface to common ***/
@@ -567,18 +561,16 @@ void gigaset_if_free(struct cardstate *cs)
 void gigaset_if_receive(struct cardstate *cs,
 			unsigned char *buffer, size_t len)
 {
-	unsigned long flags;
-	struct tty_struct *tty;
+	struct tty_struct *tty = tty_port_tty_get(&cs->port);
 
-	spin_lock_irqsave(&cs->lock, flags);
-	tty = cs->tty;
-	if (tty == NULL)
+	if (tty == NULL) {
 		gig_dbg(DEBUG_IF, "receive on closed device");
-	else {
-		tty_insert_flip_string(tty, buffer, len);
-		tty_flip_buffer_push(tty);
+		return;
 	}
-	spin_unlock_irqrestore(&cs->lock, flags);
+
+	tty_insert_flip_string(tty, buffer, len);
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
 }
 EXPORT_SYMBOL_GPL(gigaset_if_receive);
 
-- 
1.7.9.2

^ permalink raw reply related

* [PATCH 67/68] TTY: isdn/gigaset, do not set tty->driver_data to NULL
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh
  Cc: alan, linux-serial, linux-kernel, jirislaby, Hansjoerg Lipp,
	Tilman Schmidt, gigaset307x-common
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

Close the window in open where driver_data is reset to NULL on each
open. It could cause other processes to get invalid retval from the
tty->ops operations because of the checks all over the code.

With this change we may do other cleanups. Now, the only valid check
for tty->driver_data != NULL is in close. This can happen only if open
fails at gigaset_get_cs_by_tty or try_module_get. The rest of checks
in various tty->ops->* are invalid as driver_data cannot be NULL
there. The same holds for cs->open_count. So remove them.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Hansjoerg Lipp <hjlipp@web.de>
Cc: Tilman Schmidt <tilman@imap.cc>
Cc: gigaset307x-common@lists.sourceforge.net
---
 drivers/isdn/gigaset/interface.c |  105 +++++---------------------------------
 1 file changed, 14 insertions(+), 91 deletions(-)

diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c
index 7e54577..040f5f9 100644
--- a/drivers/isdn/gigaset/interface.c
+++ b/drivers/isdn/gigaset/interface.c
@@ -151,8 +151,6 @@ static int if_open(struct tty_struct *tty, struct file *filp)
 	gig_dbg(DEBUG_IF, "%d+%d: %s()",
 		tty->driver->minor_start, tty->index, __func__);
 
-	tty->driver_data = NULL;
-
 	cs = gigaset_get_cs_by_tty(tty);
 	if (!cs || !try_module_get(cs->driver->owner))
 		return -ENODEV;
@@ -178,12 +176,11 @@ static int if_open(struct tty_struct *tty, struct file *filp)
 
 static void if_close(struct tty_struct *tty, struct file *filp)
 {
-	struct cardstate *cs;
+	struct cardstate *cs = tty->driver_data;
 	unsigned long flags;
 
-	cs = (struct cardstate *) tty->driver_data;
-	if (!cs) {
-		pr_err("%s: no cardstate\n", __func__);
+	if (!cs) { /* happens if we didn't find cs in open */
+		printk(KERN_DEBUG "%s: no cardstate\n", __func__);
 		return;
 	}
 
@@ -211,18 +208,12 @@ static void if_close(struct tty_struct *tty, struct file *filp)
 static int if_ioctl(struct tty_struct *tty,
 		    unsigned int cmd, unsigned long arg)
 {
-	struct cardstate *cs;
+	struct cardstate *cs = tty->driver_data;
 	int retval = -ENODEV;
 	int int_arg;
 	unsigned char buf[6];
 	unsigned version[4];
 
-	cs = (struct cardstate *) tty->driver_data;
-	if (!cs) {
-		pr_err("%s: no cardstate\n", __func__);
-		return -ENODEV;
-	}
-
 	gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
 
 	if (mutex_lock_interruptible(&cs->mutex))
@@ -231,9 +222,7 @@ static int if_ioctl(struct tty_struct *tty,
 	if (!cs->connected) {
 		gig_dbg(DEBUG_IF, "not connected");
 		retval = -ENODEV;
-	} else if (!cs->open_count)
-		dev_warn(cs->dev, "%s: device not opened\n", __func__);
-	else {
+	} else {
 		retval = 0;
 		switch (cmd) {
 		case GIGASET_REDIR:
@@ -285,15 +274,9 @@ static int if_ioctl(struct tty_struct *tty,
 
 static int if_tiocmget(struct tty_struct *tty)
 {
-	struct cardstate *cs;
+	struct cardstate *cs = tty->driver_data;
 	int retval;
 
-	cs = (struct cardstate *) tty->driver_data;
-	if (!cs) {
-		pr_err("%s: no cardstate\n", __func__);
-		return -ENODEV;
-	}
-
 	gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 
 	if (mutex_lock_interruptible(&cs->mutex))
@@ -309,16 +292,10 @@ static int if_tiocmget(struct tty_struct *tty)
 static int if_tiocmset(struct tty_struct *tty,
 		       unsigned int set, unsigned int clear)
 {
-	struct cardstate *cs;
+	struct cardstate *cs = tty->driver_data;
 	int retval;
 	unsigned mc;
 
-	cs = (struct cardstate *) tty->driver_data;
-	if (!cs) {
-		pr_err("%s: no cardstate\n", __func__);
-		return -ENODEV;
-	}
-
 	gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)",
 		cs->minor_index, __func__, set, clear);
 
@@ -341,16 +318,10 @@ static int if_tiocmset(struct tty_struct *tty,
 
 static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
 {
-	struct cardstate *cs;
+	struct cardstate *cs = tty->driver_data;
 	struct cmdbuf_t *cb;
 	int retval;
 
-	cs = (struct cardstate *) tty->driver_data;
-	if (!cs) {
-		pr_err("%s: no cardstate\n", __func__);
-		return -ENODEV;
-	}
-
 	gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 
 	if (mutex_lock_interruptible(&cs->mutex))
@@ -361,11 +332,6 @@ static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
 		retval = -ENODEV;
 		goto done;
 	}
-	if (!cs->open_count) {
-		dev_warn(cs->dev, "%s: device not opened\n", __func__);
-		retval = -ENODEV;
-		goto done;
-	}
 	if (cs->mstate != MS_LOCKED) {
 		dev_warn(cs->dev, "can't write to unlocked device\n");
 		retval = -EBUSY;
@@ -397,15 +363,9 @@ done:
 
 static int if_write_room(struct tty_struct *tty)
 {
-	struct cardstate *cs;
+	struct cardstate *cs = tty->driver_data;
 	int retval = -ENODEV;
 
-	cs = (struct cardstate *) tty->driver_data;
-	if (!cs) {
-		pr_err("%s: no cardstate\n", __func__);
-		return -ENODEV;
-	}
-
 	gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 
 	if (mutex_lock_interruptible(&cs->mutex))
@@ -414,9 +374,7 @@ static int if_write_room(struct tty_struct *tty)
 	if (!cs->connected) {
 		gig_dbg(DEBUG_IF, "not connected");
 		retval = -ENODEV;
-	} else if (!cs->open_count)
-		dev_warn(cs->dev, "%s: device not opened\n", __func__);
-	else if (cs->mstate != MS_LOCKED) {
+	} else if (cs->mstate != MS_LOCKED) {
 		dev_warn(cs->dev, "can't write to unlocked device\n");
 		retval = -EBUSY;
 	} else
@@ -429,23 +387,15 @@ static int if_write_room(struct tty_struct *tty)
 
 static int if_chars_in_buffer(struct tty_struct *tty)
 {
-	struct cardstate *cs;
+	struct cardstate *cs = tty->driver_data;
 	int retval = 0;
 
-	cs = (struct cardstate *) tty->driver_data;
-	if (!cs) {
-		pr_err("%s: no cardstate\n", __func__);
-		return 0;
-	}
-
 	gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 
 	mutex_lock(&cs->mutex);
 
 	if (!cs->connected)
 		gig_dbg(DEBUG_IF, "not connected");
-	else if (!cs->open_count)
-		dev_warn(cs->dev, "%s: device not opened\n", __func__);
 	else if (cs->mstate != MS_LOCKED)
 		dev_warn(cs->dev, "can't write to unlocked device\n");
 	else
@@ -458,13 +408,7 @@ static int if_chars_in_buffer(struct tty_struct *tty)
 
 static void if_throttle(struct tty_struct *tty)
 {
-	struct cardstate *cs;
-
-	cs = (struct cardstate *) tty->driver_data;
-	if (!cs) {
-		pr_err("%s: no cardstate\n", __func__);
-		return;
-	}
+	struct cardstate *cs = tty->driver_data;
 
 	gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 
@@ -472,8 +416,6 @@ static void if_throttle(struct tty_struct *tty)
 
 	if (!cs->connected)
 		gig_dbg(DEBUG_IF, "not connected");	/* nothing to do */
-	else if (!cs->open_count)
-		dev_warn(cs->dev, "%s: device not opened\n", __func__);
 	else
 		gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
 
@@ -482,13 +424,7 @@ static void if_throttle(struct tty_struct *tty)
 
 static void if_unthrottle(struct tty_struct *tty)
 {
-	struct cardstate *cs;
-
-	cs = (struct cardstate *) tty->driver_data;
-	if (!cs) {
-		pr_err("%s: no cardstate\n", __func__);
-		return;
-	}
+	struct cardstate *cs = tty->driver_data;
 
 	gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 
@@ -496,8 +432,6 @@ static void if_unthrottle(struct tty_struct *tty)
 
 	if (!cs->connected)
 		gig_dbg(DEBUG_IF, "not connected");	/* nothing to do */
-	else if (!cs->open_count)
-		dev_warn(cs->dev, "%s: device not opened\n", __func__);
 	else
 		gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
 
@@ -506,18 +440,12 @@ static void if_unthrottle(struct tty_struct *tty)
 
 static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
 {
-	struct cardstate *cs;
+	struct cardstate *cs = tty->driver_data;
 	unsigned int iflag;
 	unsigned int cflag;
 	unsigned int old_cflag;
 	unsigned int control_state, new_state;
 
-	cs = (struct cardstate *) tty->driver_data;
-	if (!cs) {
-		pr_err("%s: no cardstate\n", __func__);
-		return;
-	}
-
 	gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
 
 	mutex_lock(&cs->mutex);
@@ -527,11 +455,6 @@ static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
 		goto out;
 	}
 
-	if (!cs->open_count) {
-		dev_warn(cs->dev, "%s: device not opened\n", __func__);
-		goto out;
-	}
-
 	iflag = tty->termios->c_iflag;
 	cflag = tty->termios->c_cflag;
 	old_cflag = old ? old->c_cflag : cflag;
-- 
1.7.9.2

^ permalink raw reply related

* [PATCH 66/68] TTY: pdc_cons, use tty_port
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh
  Cc: alan, linux-serial, linux-kernel, jirislaby, Kyle McMartin,
	Helge Deller, James E.J. Bottomley
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

Instead of digging a tty out of the tty_driver struct, which is not
defined to work, use tty_port properly. This includes proper tty
refcounting even though there is no possible race currently. But we
will need tty_port for tty buffers in the future anyway.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Kyle McMartin <kyle@mcmartin.ca>
Cc: Helge Deller <deller@gmx.de>
Cc: "James E.J. Bottomley" <jejb@parisc-linux.org>
---
 arch/parisc/kernel/pdc_cons.c |   19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/arch/parisc/kernel/pdc_cons.c b/arch/parisc/kernel/pdc_cons.c
index d14e20f..4f00459 100644
--- a/arch/parisc/kernel/pdc_cons.c
+++ b/arch/parisc/kernel/pdc_cons.c
@@ -92,10 +92,11 @@ static int pdc_console_setup(struct console *co, char *options)
 
 static void pdc_console_poll(unsigned long unused);
 static DEFINE_TIMER(pdc_console_timer, pdc_console_poll, 0, 0);
+static struct tty_port tty_port;
 
 static int pdc_console_tty_open(struct tty_struct *tty, struct file *filp)
 {
-
+	tty_port_tty_set(&tty_port, tty);
 	mod_timer(&pdc_console_timer, jiffies + PDC_CONS_POLL_DELAY);
 
 	return 0;
@@ -103,8 +104,10 @@ static int pdc_console_tty_open(struct tty_struct *tty, struct file *filp)
 
 static void pdc_console_tty_close(struct tty_struct *tty, struct file *filp)
 {
-	if (!tty->count)
+	if (!tty->count) {
 		del_timer_sync(&pdc_console_timer);
+		tty_port_tty_set(&tty_port, NULL);
+	}
 }
 
 static int pdc_console_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
@@ -123,8 +126,6 @@ static int pdc_console_tty_chars_in_buffer(struct tty_struct *tty)
 	return 0; /* no buffer */
 }
 
-static struct tty_driver *pdc_console_tty_driver;
-
 static const struct tty_operations pdc_console_tty_ops = {
 	.open = pdc_console_tty_open,
 	.close = pdc_console_tty_close,
@@ -135,10 +136,8 @@ static const struct tty_operations pdc_console_tty_ops = {
 
 static void pdc_console_poll(unsigned long unused)
 {
-
 	int data, count = 0;
-
-	struct tty_struct *tty = pdc_console_tty_driver->ttys[0];
+	struct tty_struct *tty = tty_port_tty_get(&tty_port);
 
 	if (!tty)
 		return;
@@ -154,10 +153,14 @@ static void pdc_console_poll(unsigned long unused)
 	if (count)
 		tty_flip_buffer_push(tty);
 
+	tty_kref_put(tty);
+
 	if (pdc_cons.flags & CON_ENABLED)
 		mod_timer(&pdc_console_timer, jiffies + PDC_CONS_POLL_DELAY);
 }
 
+static struct tty_driver *pdc_console_tty_driver;
+
 static int __init pdc_console_tty_driver_init(void)
 {
 	int err;
@@ -182,6 +185,8 @@ static int __init pdc_console_tty_driver_init(void)
 	printk(KERN_INFO "The PDC console driver is still registered, removing CON_BOOT flag\n");
 	pdc_cons.flags &= ~CON_BOOT;
 
+	tty_port_init(&tty_port);
+
 	pdc_console_tty_driver = alloc_tty_driver(1);
 
 	if (!pdc_console_tty_driver)
-- 
1.7.9.2

^ permalink raw reply related

* [PATCH 64/68] TTY: pdc_cons, fix open vs timer race
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh
  Cc: alan, linux-serial, linux-kernel, jirislaby, Kyle McMartin,
	Helge Deller, James E.J. Bottomley
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

The timer is initialized too late. tty->open may fire an invalid
timer. So initialize the timer earlier using DEFINE_TIMER.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Kyle McMartin <kyle@mcmartin.ca>
Cc: Helge Deller <deller@gmx.de>
Cc: "James E.J. Bottomley" <jejb@parisc-linux.org>
---
 arch/parisc/kernel/pdc_cons.c |    7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/arch/parisc/kernel/pdc_cons.c b/arch/parisc/kernel/pdc_cons.c
index c1db65f..8ad0521 100644
--- a/arch/parisc/kernel/pdc_cons.c
+++ b/arch/parisc/kernel/pdc_cons.c
@@ -90,7 +90,8 @@ static int pdc_console_setup(struct console *co, char *options)
 
 #define PDC_CONS_POLL_DELAY (30 * HZ / 1000)
 
-static struct timer_list pdc_console_timer;
+static void pdc_console_poll(unsigned long unused);
+static DEFINE_TIMER(pdc_console_timer, pdc_console_poll, 0, 0);
 
 static int pdc_console_tty_open(struct tty_struct *tty, struct file *filp)
 {
@@ -205,10 +206,6 @@ static int __init pdc_console_tty_driver_init(void)
 
 	pdc_console_tty_driver = drv;
 
-	/* No need to initialize the pdc_console_timer if tty isn't allocated */
-	init_timer(&pdc_console_timer);
-	pdc_console_timer.function = pdc_console_poll;
-
 	return 0;
 }
 
-- 
1.7.9.2

^ permalink raw reply related

* [PATCH 63/68] TTY: pdc_cons, fix racy tty test
From: Jiri Slaby @ 2012-03-05 13:52 UTC (permalink / raw)
  To: gregkh
  Cc: alan, linux-serial, linux-kernel, jirislaby, Kyle McMartin,
	Helge Deller, James E.J. Bottomley
In-Reply-To: <1330955575-26641-1-git-send-email-jslaby@suse.cz>

The tty->count test in the timer was racy. Let's remove the test and
properly delete the timer and wait for the body to finish using _sync
version of del_timer.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Kyle McMartin <kyle@mcmartin.ca>
Cc: Helge Deller <deller@gmx.de>
Cc: "James E.J. Bottomley" <jejb@parisc-linux.org>
---
 arch/parisc/kernel/pdc_cons.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/parisc/kernel/pdc_cons.c b/arch/parisc/kernel/pdc_cons.c
index fc770be..c1db65f 100644
--- a/arch/parisc/kernel/pdc_cons.c
+++ b/arch/parisc/kernel/pdc_cons.c
@@ -103,7 +103,7 @@ static int pdc_console_tty_open(struct tty_struct *tty, struct file *filp)
 static void pdc_console_tty_close(struct tty_struct *tty, struct file *filp)
 {
 	if (!tty->count)
-		del_timer(&pdc_console_timer);
+		del_timer_sync(&pdc_console_timer);
 }
 
 static int pdc_console_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
@@ -153,7 +153,7 @@ static void pdc_console_poll(unsigned long unused)
 	if (count)
 		tty_flip_buffer_push(tty);
 
-	if (tty->count && (pdc_cons.flags & CON_ENABLED))
+	if (pdc_cons.flags & CON_ENABLED)
 		mod_timer(&pdc_console_timer, jiffies + PDC_CONS_POLL_DELAY);
 }
 
-- 
1.7.9.2

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox