r2013 - in trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian: . patches patches/series

Simon Horman horms@haydn.debian.org
Tue, 21 Dec 2004 02:35:18 -0700


Author: horms
Date: 2004-12-21 02:34:50 -0700 (Tue, 21 Dec 2004)
New Revision: 2013

Added:
   trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/093-tty_lockup-3.diff
Removed:
   trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/115_tty_lockup-3.diff
Modified:
   trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/changelog
   trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/series/2.4.27-7
Log:
updated tty/ldisc fix (CAN-2004-0814)

Modified: trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/changelog
===================================================================
--- trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/changelog	2004-12-21 08:22:52 UTC (rev 2012)
+++ trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/changelog	2004-12-21 09:34:50 UTC (rev 2013)
@@ -13,10 +13,12 @@
     (dann frazier)
   * 117-igmp-source-filter-fixes.patch: IGMP source filter fixes
     (CAN-2004-1137) (dann frazier)
-  * 119-acpi_early-build.diff: Build fix for ACPI 
+  * 119-acpi_early-build.diff: Build fix for ACPI
     (Closes: #286226) (Simon Horman)
+  * 093-tty_lockup-3.diff: Updateded patch for race conditions in 
+      linux terminal subsystem from uptream (CAN-2004-0814) (Simon Horman)
 
- -- Simon Horman <horms@debian.org>  Tue, 21 Dec 2004 17:11:10 +0900
+ -- Simon Horman <horms@debian.org>  Tue, 21 Dec 2004 18:27:09 +0900
 
 kernel-source-2.4.27 (2.4.27-6) unstable; urgency=low
 

Added: trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/093-tty_lockup-3.diff
===================================================================
--- trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/093-tty_lockup-3.diff	2004-12-21 08:22:52 UTC (rev 2012)
+++ trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/093-tty_lockup-3.diff	2004-12-21 09:34:50 UTC (rev 2013)
@@ -0,0 +1,4732 @@
+# origin: marcelo (BitKeeper)
+# cset: 1.1546 (2.4) key=41c3801dxJnuBSRCpUiMRkwItPHjWA
+# inclusion: upstream
+# descrition: [PATCH] Backport v2.6 tty/ldisc locking fixes
+# revision date: Tue, 21 Dec 2004 17:33:16 +0900
+#
+# S rset: ChangeSet|1.1545..1.1546
+# I rset: drivers/char/rio/riointr.c|1.4..1.5
+# I rset: drivers/char/mux.c|1.1..1.2
+# I rset: drivers/char/esp.c|1.8..1.9
+# I rset: drivers/net/wan/8253x/8253xutl.c|1.2..1.3
+# I rset: drivers/char/vme_scc.c|1.5..1.6
+# I rset: drivers/char/n_tty.c|1.6..1.7
+# I rset: drivers/char/isicom.c|1.8..1.9
+# I rset: drivers/usb/serial/io_edgeport.c|1.29..1.30
+# I rset: drivers/char/dz.c|1.11..1.12
+# I rset: drivers/char/serial_amba.c|1.5..1.6
+# I rset: drivers/sbus/char/aurora.c|1.15..1.16
+# I rset: drivers/net/hamradio/6pack.c|1.8..1.9
+# I rset: drivers/sbus/char/su.c|1.13..1.14
+# I rset: drivers/char/stallion.c|1.9..1.10
+# I rset: drivers/usb/bluetooth.c|1.18..1.19
+# I rset: drivers/net/wan/x25_asy.c|1.3..1.4
+# I rset: drivers/char/cyclades.c|1.10..1.11
+# I rset: net/bluetooth/rfcomm/tty.c|1.11..1.12
+# I rset: drivers/net/irda/irtty.c|1.11..1.12
+# I rset: drivers/char/sgiserial.c|1.9..1.10
+# I rset: drivers/char/n_hdlc.c|1.5..1.6
+# I rset: drivers/char/ser_a2232.c|1.3..1.4
+# I rset: drivers/char/serial.c|1.39..1.40
+# I rset: drivers/char/hvc_console.c|1.2..1.3
+# I rset: drivers/char/pty.c|1.5..1.6
+# I rset: net/irda/ircomm/ircomm_tty.c|1.8..1.9
+# I rset: drivers/char/synclink.c|1.16..1.17
+# I rset: fs/proc/proc_tty.c|1.2..1.3
+# I rset: drivers/net/wan/pc300_tty.c|1.2..1.3
+# I rset: drivers/char/serial_txx927.c|1.4..1.5
+# I rset: drivers/sbus/char/sab82532.c|1.11..1.12
+# I rset: drivers/char/sx.c|1.14..1.15
+# I rset: drivers/usb/serial/usbserial.c|1.32..1.33
+# I rset: drivers/net/ppp_synctty.c|1.6..1.7
+# I rset: drivers/char/serial167.c|1.7..1.8
+# I rset: drivers/macintosh/macserial.c|1.8..1.9
+# I rset: drivers/net/hamradio/mkiss.c|1.7..1.8
+# I rset: drivers/char/pdc_console.c|1.2..1.3
+# I rset: drivers/char/riscom8.c|1.5..1.6
+# I rset: drivers/char/synclinkmp.c|1.5..1.6
+# I rset: drivers/usb/serial/digi_acceleport.c|1.14..1.15
+# I rset: drivers/net/wan/sdla_chdlc.c|1.15..1.16
+# I rset: drivers/char/pcmcia/synclink_cs.c|1.3..1.4
+# I rset: include/linux/tty.h|1.6..1.7
+# I rset: drivers/char/generic_serial.c|1.5..1.6
+# I rset: drivers/char/serial_tx3912.c|1.6..1.7
+# I rset: drivers/s390/net/ctctty.c|1.5..1.6
+# I rset: drivers/char/epca.c|1.10..1.11
+# I rset: drivers/char/tty_io.c|1.32..1.33
+# I rset: drivers/char/pcxx.c|1.6..1.7
+# I rset: drivers/net/wan/8253x/8253xtty.c|1.2..1.3
+# I rset: drivers/char/istallion.c|1.10..1.11
+# I rset: drivers/usb/acm.c|1.16..1.17
+# I rset: drivers/net/wan/8253x/8253xsyn.c|1.1..1.2
+# I rset: drivers/usb/serial/keyspan_pda.c|1.11..1.12
+# I rset: drivers/usb/serial/mct_u232.c|1.20..1.21
+# I rset: arch/cris/drivers/serial.c|1.16..1.17
+# I rset: drivers/char/mxser.c|1.12..1.13
+# I rset: drivers/char/rocket.c|1.10..1.11
+# I rset: drivers/s390/char/con3215.c|1.7..1.8
+# I rset: drivers/char/vt.c|1.14..1.15
+# I rset: drivers/net/strip.c|1.5..1.6
+# I rset: drivers/isdn/isdn_tty.c|1.9..1.10
+# I rset: arch/ppc/8xx_io/uart.c|1.23..1.24
+# I rset: drivers/net/ppp_async.c|1.8..1.9
+# I rset: drivers/s390/char/tubtty.c|1.7..1.8
+# I rset: drivers/tc/zs.c|1.7..1.8
+# I rset: drivers/bluetooth/hci_ldisc.c|1.7..1.8
+# I rset: drivers/char/specialix.c|1.5..1.6
+# I rset: drivers/char/moxa.c|1.7..1.8
+# I rset: drivers/char/n_r3964.c|1.8..1.9
+# I rset: drivers/char/sh-sci.c|1.14..1.15
+# I rset: drivers/char/selection.c|1.3..1.4
+# I rset: drivers/usb/serial/io_ti.c|1.8..1.9
+# I rset: drivers/char/ip2/i2lib.c|1.4..1.5
+# I rset: include/linux/tty_ldisc.h|1.2..1.3
+# I rset: drivers/char/amiserial.c|1.8..1.9
+# I rset: drivers/char/tty_ioctl.c|1.4..1.5
+# I rset: drivers/sbus/char/zs.c|1.11..1.12
+# I rset: drivers/net/slip.c|1.7..1.8
+# I rset: drivers/char/ip2main.c|1.10..1.11
+#
+# This is a BitKeeper generated diff -Nru style patch.
+#
+# ChangeSet
+#   2004/12/17 22:55:57-02:00 marcelo@logos.cnet 
+#   [PATCH] Backport v2.6 tty/ldisc locking fixes
+# 
+# net/irda/ircomm/ircomm_tty.c
+#   2004/12/16 12:09:13-02:00 marcelo@logos.cnet +2 -8
+#   Import patch 2.4-tty-V8.patch
+# 
+# net/bluetooth/rfcomm/tty.c
+#   2004/12/16 12:20:47-02:00 marcelo@logos.cnet +2 -5
+#   Import patch 2.4-tty-V8.patch
+# 
+# include/linux/tty_ldisc.h
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +9 -0
+#   Import patch 2.4-tty-V8.patch
+# 
+# include/linux/tty.h
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +28 -14
+#   Import patch 2.4-tty-V8.patch
+# 
+# fs/proc/proc_tty.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +6 -5
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/usb/serial/usbserial.c
+#   2004/12/16 12:55:02-02:00 marcelo@logos.cnet +10 -3
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/usb/serial/mct_u232.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +1 -5
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/usb/serial/keyspan_pda.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +1 -7
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/usb/serial/io_ti.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +1 -6
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/usb/serial/io_edgeport.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +1 -6
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/usb/serial/digi_acceleport.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +2 -10
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/usb/bluetooth.c
+#   2004/12/16 16:49:03-02:00 marcelo@logos.cnet +10 -3
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/usb/acm.c
+#   2004/12/16 12:55:33-02:00 marcelo@logos.cnet +1 -4
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/tc/zs.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +3 -10
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/sbus/char/zs.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +5 -12
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/sbus/char/su.c
+#   2004/12/16 13:07:37-02:00 marcelo@logos.cnet +3 -10
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/sbus/char/sab82532.c
+#   2004/12/16 13:06:52-02:00 marcelo@logos.cnet +3 -10
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/sbus/char/aurora.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +4 -11
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/s390/net/ctctty.c
+#   2004/12/16 12:59:01-02:00 marcelo@logos.cnet +4 -9
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/s390/char/tubtty.c
+#   2004/12/16 12:56:31-02:00 marcelo@logos.cnet +2 -8
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/s390/char/con3215.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +2 -8
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/net/wan/x25_asy.c
+#   2004/12/16 13:02:12-02:00 marcelo@logos.cnet +1 -3
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/net/wan/sdla_chdlc.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +8 -11
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/net/wan/pc300_tty.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +15 -19
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/net/wan/8253x/8253xutl.c
+#   2004/12/16 13:01:56-02:00 marcelo@logos.cnet +1 -6
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/net/wan/8253x/8253xtty.c
+#   2004/12/16 13:01:04-02:00 marcelo@logos.cnet +2 -8
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/net/wan/8253x/8253xsyn.c
+#   2004/12/16 13:00:20-02:00 marcelo@logos.cnet +1 -4
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/net/strip.c
+#   2004/12/16 13:02:33-02:00 marcelo@logos.cnet +1 -2
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/net/slip.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +12 -8
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/net/ppp_synctty.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +15 -0
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/net/ppp_async.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +28 -3
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/net/irda/irtty.c
+#   2004/12/16 12:59:36-02:00 marcelo@logos.cnet +2 -3
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/net/hamradio/mkiss.c
+#   2004/12/16 13:03:13-02:00 marcelo@logos.cnet +1 -2
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/net/hamradio/6pack.c
+#   2004/12/16 13:02:53-02:00 marcelo@logos.cnet +1 -2
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/macintosh/macserial.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +4 -12
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/isdn/isdn_tty.c
+#   2004/12/16 13:05:22-02:00 marcelo@logos.cnet +5 -16
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/vt.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +1 -2
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/vme_scc.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +2 -6
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/tty_ioctl.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +43 -11
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/tty_io.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +497 -87
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/synclinkmp.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +28 -21
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/synclink.c
+#   2004/12/16 16:43:15-02:00 marcelo@logos.cnet +30 -25
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/sx.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +1 -3
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/stallion.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +3 -10
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/specialix.c
+#   2004/12/16 13:28:34-02:00 marcelo@logos.cnet +4 -12
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/sh-sci.c
+#   2004/12/16 12:37:32-02:00 marcelo@logos.cnet +1 -4
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/sgiserial.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +2 -2
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/serial_txx927.c
+#   2004/12/16 12:38:45-02:00 marcelo@logos.cnet +3 -10
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/serial_tx3912.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +1 -4
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/serial_amba.c
+#   2004/12/16 12:40:14-02:00 marcelo@logos.cnet +3 -10
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/serial167.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +3 -18
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/serial.c
+#   2004/12/16 16:42:06-02:00 marcelo@logos.cnet +6 -12
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/ser_a2232.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +1 -4
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/selection.c
+#   2004/12/16 13:23:54-02:00 marcelo@logos.cnet +6 -2
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/rocket.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +10 -16
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/riscom8.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +3 -9
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/rio/riointr.c
+#   2004/12/16 12:41:40-02:00 marcelo@logos.cnet +1 -4
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/pty.c
+#   2004/12/16 12:40:52-02:00 marcelo@logos.cnet +7 -7
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/pdc_console.c
+#   2004/12/16 12:36:12-02:00 marcelo@logos.cnet +1 -2
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/pcxx.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +5 -31
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/pcmcia/synclink_cs.c
+#   2004/12/16 13:22:51-02:00 marcelo@logos.cnet +29 -19
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/n_tty.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +283 -48
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/n_r3964.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +2 -1
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/n_hdlc.c
+#   2004/12/16 12:36:41-02:00 marcelo@logos.cnet +2 -3
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/mxser.c
+#   2004/12/16 13:14:49-02:00 marcelo@logos.cnet +4 -10
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/mux.c
+#   2004/12/16 12:40:33-02:00 marcelo@logos.cnet +1 -2
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/moxa.c
+#   2004/12/16 13:14:38-02:00 marcelo@logos.cnet +5 -14
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/istallion.c
+#   2004/12/16 12:35:07-02:00 marcelo@logos.cnet +12 -10
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/isicom.c
+#   2004/12/16 13:13:54-02:00 marcelo@logos.cnet +5 -11
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/ip2main.c
+#   2004/12/16 12:27:10-02:00 marcelo@logos.cnet +2 -4
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/ip2/i2lib.c
+#   2004/12/16 12:44:31-02:00 marcelo@logos.cnet +2 -8
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/hvc_console.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +1 -4
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/generic_serial.c
+#   2004/12/16 13:13:15-02:00 marcelo@logos.cnet +5 -12
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/esp.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +3 -10
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/epca.c
+#   2004/12/16 13:13:00-02:00 marcelo@logos.cnet +9 -22
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/dz.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +5 -11
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/cyclades.c
+#   2004/12/16 11:57:23-02:00 marcelo@logos.cnet +3 -11
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/char/amiserial.c
+#   2004/12/16 13:11:41-02:00 marcelo@logos.cnet +3 -10
+#   Import patch 2.4-tty-V8.patch
+# 
+# drivers/bluetooth/hci_ldisc.c
+#   2004/12/16 13:03:36-02:00 marcelo@logos.cnet +5 -6
+#   Import patch 2.4-tty-V8.patch
+# 
+# arch/ppc/8xx_io/uart.c
+#   2004/12/16 12:22:28-02:00 marcelo@logos.cnet +3 -10
+#   Import patch 2.4-tty-V8.patch
+# 
+# arch/cris/drivers/serial.c
+#   2004/12/16 12:23:53-02:00 marcelo@logos.cnet +3 -11
+#   Import patch 2.4-tty-V8.patch
+# 
+#
+===== drivers/char/rio/riointr.c 1.4 vs 1.5 =====
+--- 1.4/drivers/char/rio/riointr.c	2002-02-05 16:45:07 +09:00
++++ 1.5/drivers/char/rio/riointr.c	2004-12-16 23:41:40 +09:00
+@@ -248,12 +248,9 @@
+     rio_dprintk (RIO_DEBUG_INTR, "Waking up.... ldisc:%d (%d/%d)....",
+ 		 (int)(PortP->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)),
+ 		 PortP->gs.wakeup_chars, PortP->gs.xmit_cnt); 
+-    if ((PortP->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	PortP->gs.tty->ldisc.write_wakeup)
+-      (PortP->gs.tty->ldisc.write_wakeup)(PortP->gs.tty);
++    tty_wakeup(PortP->gs.tty);
+     rio_dprintk (RIO_DEBUG_INTR, "(%d/%d)\n",
+ 		PortP->gs.wakeup_chars, PortP->gs.xmit_cnt); 
+-    wake_up_interruptible(&PortP->gs.tty->write_wait);
+   }
+ 
+ }
+===== drivers/char/mux.c 1.1 vs 1.2 =====
+--- 1.1/drivers/char/mux.c	2003-01-07 00:38:22 +09:00
++++ 1.2/drivers/char/mux.c	2004-12-16 23:40:33 +09:00
+@@ -429,8 +429,7 @@
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+===== drivers/char/esp.c 1.8 vs 1.9 =====
+--- 1.8/drivers/char/esp.c	2002-03-11 23:13:27 +09:00
++++ 1.9/drivers/char/esp.c	2004-12-16 22:57:23 +09:00
+@@ -786,10 +786,7 @@
+ 		return;
+ 
+ 	if (test_and_clear_bit(ESP_EVENT_WRITE_WAKEUP, &info->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ }
+ 
+@@ -1395,10 +1392,7 @@
+ 	cli();
+ 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ 	sti();
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -2115,8 +2109,7 @@
+ 	shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+===== drivers/net/wan/8253x/8253xutl.c 1.2 vs 1.3 =====
+--- 1.2/drivers/net/wan/8253x/8253xutl.c	2002-05-10 08:16:03 +09:00
++++ 1.3/drivers/net/wan/8253x/8253xutl.c	2004-12-17 00:01:56 +09:00
+@@ -1412,11 +1412,6 @@
+ 	WRITEB(port,cmdr,SAB82532_CMDR_XRES);
+ 	restore_flags(flags);
+ 	
+-	wake_up_interruptible(&tty->write_wait); /* wake up tty driver */
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-	{
+-		(*tty->ldisc.write_wakeup)(tty);
+-	}
++	tty_wakeup(tty);
+ }
+ 
+===== drivers/char/vme_scc.c 1.5 vs 1.6 =====
+--- 1.5/drivers/char/vme_scc.c	2002-03-31 00:45:50 +09:00
++++ 1.6/drivers/char/vme_scc.c	2004-12-16 22:57:23 +09:00
+@@ -569,12 +569,8 @@
+ 		SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET);   /* disable tx_int on next tx underrun? */
+ 		port->gs.flags &= ~GS_TX_INTEN;
+ 	}
+-	if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) {
+-		if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-				port->gs.tty->ldisc.write_wakeup)
+-			(port->gs.tty->ldisc.write_wakeup)(port->gs.tty);
+-		wake_up_interruptible(&port->gs.tty->write_wait);
+-	}
++	if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) 
++		tty_wakeup(port->gs.tty);
+ 
+ 	SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
+ }
+===== drivers/char/n_tty.c 1.6 vs 1.7 =====
+--- 1.6/drivers/char/n_tty.c	2003-08-04 03:15:35 +09:00
++++ 1.7/drivers/char/n_tty.c	2004-12-16 22:57:23 +09:00
+@@ -112,11 +112,18 @@
+ 	spin_unlock_irqrestore(&tty->read_lock, flags);
+ }
+ 
+-/* 
+- * Check whether to call the driver.unthrottle function.
+- * We test the TTY_THROTTLED bit first so that it always
+- * indicates the current state.
++/**
++ *	check_unthrottle	-	allow new receive data
++ *	@tty; tty device
++ *
++ *	Check whether to call the driver.unthrottle function.
++ *	We test the TTY_THROTTLED bit first so that it always
++ *	indicates the current state. The decision about whether
++ *	it is worth allowing more input has been taken by the caller.
++ *	Can sleep, may be called under the atomic_read semaphore but
++ *	this is not guaranteed.
+  */
++ 
+ static void check_unthrottle(struct tty_struct * tty)
+ {
+ 	if (tty->count &&
+@@ -125,10 +132,13 @@
+ 		tty->driver.unthrottle(tty);
+ }
+ 
+-/*
+- * Reset the read buffer counters, clear the flags, 
+- * and make sure the driver is unthrottled. Called
+- * from n_tty_open() and n_tty_flush_buffer().
++/**
++ *	reset_buffer_flags	-	reset buffer state
++ *	@tty: terminal to reset
++ *
++ *	Reset the read buffer counters, clear the flags, 
++ *	and make sure the driver is unthrottled. Called
++ *	from n_tty_open() and n_tty_flush_buffer().
+  */
+ static void reset_buffer_flags(struct tty_struct *tty)
+ {
+@@ -142,9 +152,19 @@
+ 	check_unthrottle(tty);
+ }
+ 
+-/*
+- * Flush the input buffer
++/**
++ *	n_tty_flush_buffer	-	clean input queue
++ *	@tty:	terminal device
++ *
++ *	Flush the input buffer. Called when the line discipline is
++ *	being closed, when the tty layer wants the buffer flushed (eg
++ *	at hangup) or when the N_TTY line discipline internally has to
++ *	clean the pending queue (for example some signals).
++ *
++ *	FIXME: tty->ctrl_status is not spinlocked and relies on
++ *	lock_kernel() still.
+  */
++ 
+ void n_tty_flush_buffer(struct tty_struct * tty)
+ {
+ 	/* clear everything and unthrottle the driver */
+@@ -159,9 +179,14 @@
+ 	}
+ }
+ 
+-/*
+- * Return number of characters buffered to be delivered to user
++/**
++ *	n_tty_chars_in_buffer	-	report available bytes
++ *	@tty: tty device
++ *
++ *	Report the number of characters buffered to be delivered to user
++ *	at this instant in time. 
+  */
++ 
+ ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
+ {
+ 	unsigned long flags;
+@@ -242,10 +267,20 @@
+ 	return 0;
+ }
+ 
+-/*
+- * opost_block --- to speed up block console writes, among other
+- * things.
++/**
++ *	opost_block		-	block postprocess
++ *	@tty: terminal device
++ *	@inbuf: user buffer
++ *	@nr: number of bytes
++ *
++ *	This path is used to speed up block console writes, among other
++ *	things when processing blocks of output data. It handles only
++ *	the simple cases normally found and helps to generate blocks of
++ *	symbols for the console driver and thus improve performance.
++ *
++ *	Called from write_chan under the tty layer write lock.
+  */
++ 
+ static ssize_t opost_block(struct tty_struct * tty,
+ 		       const unsigned char * inbuf, unsigned int nr)
+ {
+@@ -334,6 +369,16 @@
+ 	}
+ }
+ 
++/**
++ *	eraser		-	handle erase function
++ *	@c: character input
++ *	@tty: terminal device
++ *
++ *	Perform erase and neccessary output when an erase character is
++ *	present in the stream from the driver layer. Handles the complexities
++ *	of UTF-8 multibyte symbols.
++ */
++ 
+ static void eraser(unsigned char c, struct tty_struct *tty)
+ {
+ 	enum { ERASE, WERASE, KILL } kill_type;
+@@ -450,6 +495,18 @@
+ 		finish_erasing(tty);
+ }
+ 
++/**
++ *	isig		-	handle the ISIG optio
++ *	@sig: signal
++ *	@tty: terminal
++ *	@flush: force flush
++ *
++ *	Called when a signal is being sent due to terminal input. This
++ *	may caus terminal flushing to take place according to the termios
++ *	settings and character used. Called from the driver receive_buf
++ *	path so serialized.
++ */
++ 
+ static inline void isig(int sig, struct tty_struct *tty, int flush)
+ {
+ 	if (tty->pgrp > 0)
+@@ -461,6 +518,16 @@
+ 	}
+ }
+ 
++/**
++ *	n_tty_receive_break	-	handle break
++ *	@tty: terminal
++ *
++ *	An RS232 break event has been hit in the incoming bitstream. This
++ *	can cause a variety of events depending upon the termios settings.
++ *
++ *	Called from the receive_buf path so single threaded.
++ */
++ 
+ static inline void n_tty_receive_break(struct tty_struct *tty)
+ {
+ 	if (I_IGNBRK(tty))
+@@ -477,19 +544,40 @@
+ 	wake_up_interruptible(&tty->read_wait);
+ }
+ 
++/**
++ *	n_tty_receive_overrun	-	handle overrun reporting
++ *	@tty: terminal
++ *
++ *	Data arrived faster than we could process it. While the tty
++ *	driver has flagged this the bits that were missed are gone
++ *	forever.
++ *
++ *	Called from the receive_buf path so single threaded. Does not
++ *	need locking as num_overrun and overrun_time are function
++ *	private.
++ */
++ 
+ static inline void n_tty_receive_overrun(struct tty_struct *tty)
+ {
+ 	char buf[64];
+ 
+ 	tty->num_overrun++;
+ 	if (time_before(tty->overrun_time, jiffies - HZ)) {
+-		printk("%s: %d input overrun(s)\n", tty_name(tty, buf),
++		printk(KERN_WARNING "%s: %d input overrun(s)\n", tty_name(tty, buf),
+ 		       tty->num_overrun);
+ 		tty->overrun_time = jiffies;
+ 		tty->num_overrun = 0;
+ 	}
+ }
+ 
++/**
++ *	n_tty_receive_parity_error	-	error notifier
++ *	@tty: terminal device
++ *	@c: character
++ *
++ *	Process a parity error and queue the right data to indicate
++ *	the error case if neccessary. Locking as per n_tty_receive_buf.
++ */
+ static inline void n_tty_receive_parity_error(struct tty_struct *tty,
+ 					      unsigned char c)
+ {
+@@ -507,6 +595,16 @@
+ 	wake_up_interruptible(&tty->read_wait);
+ }
+ 
++/**
++ *	n_tty_receive_char	-	perform processing
++ *	@tty: terminal device
++ *	@c: character
++ *
++ *	Process an individual character of input received from the driver.
++ *	This is serialized with respect to itself by the rules for the 
++ *	driver above.
++ */
++
+ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
+ {
+ 	unsigned long flags;
+@@ -698,6 +796,16 @@
+ 	put_tty_queue(c, tty);
+ }	
+ 
++/**
++ *	n_tty_receive_room	-	receive space
++ *	@tty: terminal
++ *
++ *	Called by the driver to find out how much data it is
++ *	permitted to feed to the line discipline without any being lost
++ *	and thus to manage flow control. Not serialized. Answers for the
++ *	"instant".
++ */
++ 
+ static int n_tty_receive_room(struct tty_struct *tty)
+ {
+ 	int	left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
+@@ -716,10 +824,13 @@
+ 	return 0;
+ }
+ 
+-/*
+- * Required for the ptys, serial driver etc. since processes
+- * that attach themselves to the master and rely on ASYNC
+- * IO must be woken up
++/**
++ *	n_tty_write_wakeup	-	asynchronous I/O notifier
++ *	@tty: tty device
++ *
++ *	Required for the ptys, serial driver etc. since processes
++ *	that attach themselves to the master and rely on ASYNC
++ *	IO must be woken up
+  */
+ 
+ static void n_tty_write_wakeup(struct tty_struct *tty)
+@@ -732,6 +843,19 @@
+ 	return;
+ }
+ 
++/**
++ *	n_tty_receive_buf	-	data receive
++ *	@tty: terminal device
++ *	@cp: buffer
++ *	@fp: flag buffer
++ *	@count: characters
++ *
++ *	Called by the terminal driver when a block of characters has
++ *	been received. This function must be called from soft contexts
++ *	not from interrupt context. The driver is responsible for making
++ *	calls one at a time and in order (or using queue_ldisc)
++ */
++ 
+ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+ 			      char *fp, int count)
+ {
+@@ -813,6 +937,18 @@
+ 	        current->sig->action[sig-1].sa.sa_handler == SIG_IGN);
+ }
+ 
++/**
++ *	n_tty_set_termios	-	termios data changed
++ *	@tty: terminal
++ *	@old: previous data
++ *
++ *	Called by the tty layer when the user changes termios flags so
++ *	that the line discipline can plan ahead. This function cannot sleep
++ *	and is protected from re-entry by the tty layer. The user is 
++ *	guaranteed that this function will not be re-entered or in progress
++ *	when the ldisc is closed.
++ */
++ 
+ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old)
+ {
+ 	if (!tty)
+@@ -828,7 +964,6 @@
+ 	    I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
+ 	    I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
+ 	    I_PARMRK(tty)) {
+-		cli();
+ 		memset(tty->process_char_map, 0, 256/8);
+ 
+ 		if (I_IGNCR(tty) || I_ICRNL(tty))
+@@ -864,7 +999,6 @@
+ 			set_bit(SUSP_CHAR(tty), &tty->process_char_map);
+ 		}
+ 		clear_bit(__DISABLED_CHAR, &tty->process_char_map);
+-		sti();
+ 		tty->raw = 0;
+ 		tty->real_raw = 0;
+ 	} else {
+@@ -878,6 +1012,16 @@
+ 	}
+ }
+ 
++/**
++ *	n_tty_close		-	close the ldisc for this tty
++ *	@tty: device
++ *
++ *	Called from the terminal layer when this line discipline is 
++ *	being shut down, either because of a close or becsuse of a 
++ *	discipline change. The function will not be called while other
++ *	ldisc methods are in progress.
++ */
++ 
+ static void n_tty_close(struct tty_struct *tty)
+ {
+ 	n_tty_flush_buffer(tty);
+@@ -887,11 +1031,22 @@
+ 	}
+ }
+ 
++/**
++ *	n_tty_open		-	open an ldisc
++ *	@tty: terminal to open
++ *
++ *	Called when this line discipline is being attached to the 
++ *	terminal device. Can sleep. Called serialized so that no
++ *	other events will occur in parallel. No further open will occur
++ *	until a close.
++ */
++
+ static int n_tty_open(struct tty_struct *tty)
+ {
+ 	if (!tty)
+ 		return -EINVAL;
+ 
++	/* This one is ugly. Currently a malloc failure here can panic */
+ 	if (!tty->read_buf) {
+ 		tty->read_buf = alloc_buf();
+ 		if (!tty->read_buf)
+@@ -917,14 +1072,23 @@
+ 	return 0;
+ }
+ 
+-/*
+- * Helper function to speed up read_chan.  It is only called when
+- * ICANON is off; it copies characters straight from the tty queue to
+- * user space directly.  It can be profitably called twice; once to
+- * drain the space from the tail pointer to the (physical) end of the
+- * buffer, and once to drain the space from the (physical) beginning of
+- * the buffer to head pointer.
++/**
++ * 	copy_from_read_buf	-	copy read data directly
++ *	@tty: terminal device
++ *	@b: user data
++ *	@nr: size of data
++ *
++ *	Helper function to speed up read_chan.  It is only called when
++ *	ICANON is off; it copies characters straight from the tty queue to
++ *	user space directly.  It can be profitably called twice; once to
++ *	drain the space from the tail pointer to the (physical) end of the
++ *	buffer, and once to drain the space from the (physical) beginning of
++ *	the buffer to head pointer.
++ *
++ *	Called under the tty->atomic_read sem and with TTY_DONT_FLIP set
++ *
+  */
++ 
+ static inline int copy_from_read_buf(struct tty_struct *tty,
+ 				      unsigned char **b,
+ 				      size_t *nr)
+@@ -952,25 +1116,18 @@
+ 	return retval;
+ }
+ 
+-static ssize_t read_chan(struct tty_struct *tty, struct file *file,
+-			 unsigned char *buf, size_t nr)
+-{
+-	unsigned char *b = buf;
+-	DECLARE_WAITQUEUE(wait, current);
+-	int c;
+-	int minimum, time;
+-	ssize_t retval = 0;
+-	ssize_t size;
+-	long timeout;
+-	unsigned long flags;
+-
+-do_it_again:
+-
+-	if (!tty->read_buf) {
+-		printk("n_tty_read_chan: called with read_buf == NULL?!?\n");
+-		return -EIO;
+-	}
++/**
++ *     job_control             -       check job control
++ *     @tty: tty
++ *     @file: file handle
++ *
++ *     Perform job control management checks on this file/tty descriptor
++ *     and if appropriate send any needed signals and return a negative
++ *     error code if action should be taken.
++ */
+ 
++static int job_control(struct tty_struct *tty, struct file *file)
++{
+ 	/* Job control check -- must be done at start and after
+ 	   every sleep (POSIX.1 7.1.1.4). */
+ 	/* NOTE: not yet done after every sleep pending a thorough
+@@ -989,7 +1146,48 @@
+ 			return -ERESTARTSYS;
+ 		}
+ 	}
++	return 0;
++}
++ 
+ 
++/**
++ *	read_chan		-	read function for tty
++ *	@tty: tty device
++ *	@file: file object
++ *	@buf: userspace buffer pointer
++ *	@nr: size of I/O
++ *
++ *	Perform reads for the line discipline. We are guaranteed that the
++ *	line discipline will not be closed under us but we may get multiple
++ *	parallel readers and must handle this ourselves. We may also get
++ *	a hangup. Always called in user context, may sleep.
++ *
++ *	This code must be sure never to sleep through a hangup.
++ */
++ 
++static ssize_t read_chan(struct tty_struct *tty, struct file *file,
++			 unsigned char __user *buf, size_t nr)
++{
++	unsigned char __user *b = buf;
++	DECLARE_WAITQUEUE(wait, current);
++	int c;
++	int minimum, time;
++	ssize_t retval = 0;
++	ssize_t size;
++	long timeout;
++	unsigned long flags;
++
++do_it_again:
++
++	if (!tty->read_buf) {
++		printk("n_tty_read_chan: called with read_buf == NULL?!?\n");
++		return -EIO;
++	}
++
++	c = job_control(tty, file);
++	if(c < 0)
++		return c;
++	
+ 	minimum = time = 0;
+ 	timeout = MAX_SCHEDULE_TIMEOUT;
+ 	if (!tty->icanon) {
+@@ -1011,6 +1209,9 @@
+ 		}
+ 	}
+ 
++	/*
++	 *	Internal serialization of reads.
++	 */
+ 	if (file->f_flags & O_NONBLOCK) {
+ 		if (down_trylock(&tty->atomic_read))
+ 			return -EAGAIN;
+@@ -1146,6 +1347,21 @@
+ 	return retval;
+ }
+ 
++/**
++ *	write_chan		-	write function for tty
++ *	@tty: tty device
++ *	@file: file object
++ *	@buf: userspace buffer pointer
++ *	@nr: size of I/O
++ *
++ *	Write function of the terminal device. This is serialized with
++ *	respect to other write callers but not to termios changes, reads
++ *	and other such events. We must be careful with N_TTY as the receive
++ *	code will echo characters, thus calling driver write methods.
++ *
++ *	This code must be sure never to sleep through a hangup.
++ */
++ 
+ static ssize_t write_chan(struct tty_struct * tty, struct file * file,
+ 			  const unsigned char * buf, size_t nr)
+ {
+@@ -1217,7 +1433,25 @@
+ 	return (b - buf) ? b - buf : retval;
+ }
+ 
+-/* Called without the kernel lock held - fine */
++/**
++ *	normal_poll		-	poll method for N_TTY
++ *	@tty: terminal device
++ *	@file: file accessing it
++ *	@wait: poll table
++ *
++ *	Called when the line discipline is asked to poll() for data or
++ *	for special events. This code is not serialized with respect to
++ *	other events save open/close.
++ *
++ *	This code must be sure never to sleep through a hangup.
++ *	Called without the kernel lock held - fine
++ *
++ *	FIXME: if someone changes the VMIN or discipline settings for the
++ *	terminal while another process is in poll() the poll does not
++ *	recompute the new limits. Possibly set_termios should issue
++ *	a read wakeup to fix this bug.
++ */
++ 
+ static unsigned int normal_poll(struct tty_struct * tty, struct file * file, poll_table *wait)
+ {
+ 	unsigned int mask = 0;
+@@ -1258,6 +1492,7 @@
+ 	n_tty_ioctl,		/* ioctl */
+ 	n_tty_set_termios,	/* set_termios */
+ 	normal_poll,		/* poll */
++	NULL,			/* hangup */
+ 	n_tty_receive_buf,	/* receive_buf */
+ 	n_tty_receive_room,	/* receive_room */
+ 	n_tty_write_wakeup	/* write_wakeup */
+===== drivers/char/isicom.c 1.8 vs 1.9 =====
+--- 1.8/drivers/char/isicom.c	2002-10-31 00:50:25 +09:00
++++ 1.9/drivers/char/isicom.c	2004-12-17 00:13:54 +09:00
+@@ -502,11 +502,8 @@
+ 	
+ 	if (!tty)
+ 		return;
+-	
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
+-	wake_up_interruptible(&tty->write_wait);
++
++	tty_wakeup(tty);	
+ } 		
+  		
+ /* main interrupt handler routine */ 		
+@@ -1199,8 +1196,8 @@
+ 	isicom_shutdown_port(port);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	port->tty = 0;
+ 	if (port->blocked_open) {
+@@ -1670,10 +1667,7 @@
+ 	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+ 	restore_flags(flags);
+ 	
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ 
+===== drivers/usb/serial/io_edgeport.c 1.29 vs 1.30 =====
+--- 1.29/drivers/usb/serial/io_edgeport.c	2004-07-15 20:48:06 +09:00
++++ 1.30/drivers/usb/serial/io_edgeport.c	2004-12-16 22:57:23 +09:00
+@@ -902,12 +902,7 @@
+ 
+ 	if (tty && edge_port->open) {
+ 		/* let the tty driver wakeup if it has a special write_wakeup function */
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+-			(tty->ldisc.write_wakeup)(tty);
+-		}
+-
+-		/* tell the tty driver that something has changed */
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ 
+ 	// Release the Write URB
+===== drivers/char/dz.c 1.11 vs 1.12 =====
+--- 1.11/drivers/char/dz.c	2003-10-13 21:43:06 +09:00
++++ 1.12/drivers/char/dz.c	2004-12-16 22:57:23 +09:00
+@@ -402,9 +402,7 @@
+ 		return;
+ 
+ 	if (test_and_clear_bit(DZ_EVENT_WRITE_WAKEUP, &info->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup) (tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ }
+ 
+@@ -804,10 +802,7 @@
+ 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ 	sti();
+ 
+-	wake_up_interruptible(&tty->write_wait);
+-
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup) (tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1106,16 +1101,15 @@
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+ 
+-	if (tty->ldisc.num != ldiscs[N_TTY].num) {
++	if (tty->ldisc.num != N_TTY) {
+ 		if (tty->ldisc.close)
+ 			(tty->ldisc.close) (tty);
+-		tty->ldisc = ldiscs[N_TTY];
++		tty->ldisc = *(tty_ldisc_get(N_TTY));
+ 		tty->termios->c_line = N_TTY;
+ 		if (tty->ldisc.open)
+ 			(tty->ldisc.open) (tty);
+===== drivers/char/serial_amba.c 1.5 vs 1.6 =====
+--- 1.5/drivers/char/serial_amba.c	2002-03-31 00:45:50 +09:00
++++ 1.6/drivers/char/serial_amba.c	2004-12-16 23:40:14 +09:00
+@@ -569,10 +569,7 @@
+ 	if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event))
+ 		return;
+ 
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
+-	wake_up_interruptible(&tty->write_wait);
++	tty_wakeup(tty);
+ }
+ 
+ static int ambauart_startup(struct amba_info *info)
+@@ -958,10 +955,7 @@
+ 	save_flags(flags); cli();
+ 	info->xmit.head = info->xmit.tail = 0;
+ 	restore_flags(flags);
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1459,8 +1453,7 @@
+ 	ambauart_shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = NULL;
+===== drivers/sbus/char/aurora.c 1.15 vs 1.16 =====
+--- 1.15/drivers/sbus/char/aurora.c	2002-08-24 08:07:25 +09:00
++++ 1.16/drivers/sbus/char/aurora.c	2004-12-16 22:57:23 +09:00
+@@ -1573,8 +1573,7 @@
+ 	aurora_shutdown_port(bp, port);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	port->event = 0;
+ 	port->tty = 0;
+@@ -1785,11 +1784,8 @@
+ 	save_flags(flags); cli();
+ 	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+ 	restore_flags(flags);
+-	
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++
++	tty_wakeup(tty);	
+ #ifdef AURORA_DEBUG
+ 	printk("aurora_flush_buffer: end\n");
+ #endif
+@@ -2286,10 +2282,7 @@
+ 		return;
+ 
+ 	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ #ifdef AURORA_DEBUG
+ 	printk("do_softint: end\n");
+===== drivers/net/hamradio/6pack.c 1.8 vs 1.9 =====
+--- 1.8/drivers/net/hamradio/6pack.c	2003-10-22 03:28:22 +09:00
++++ 1.9/drivers/net/hamradio/6pack.c	2004-12-17 00:02:53 +09:00
+@@ -571,8 +571,7 @@
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+ 
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 
+ 	/* Restore default settings */
+ 	sp->dev->type = ARPHRD_AX25;
+===== drivers/sbus/char/su.c 1.13 vs 1.14 =====
+--- 1.13/drivers/sbus/char/su.c	2002-03-31 00:45:50 +09:00
++++ 1.14/drivers/sbus/char/su.c	2004-12-17 00:07:37 +09:00
+@@ -698,10 +698,7 @@
+ 		return;
+ 
+ 	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ }
+ 
+@@ -1313,10 +1310,7 @@
+ 	save_flags(flags); cli();
+ 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ 	restore_flags(flags);
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1815,8 +1809,7 @@
+ 	shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+===== drivers/char/stallion.c 1.9 vs 1.10 =====
+--- 1.9/drivers/char/stallion.c	2004-07-13 09:39:49 +09:00
++++ 1.10/drivers/char/stallion.c	2004-12-16 22:57:23 +09:00
+@@ -1238,8 +1238,7 @@
+ 		portp->tx.tail = (char *) NULL;
+ 	}
+ 	set_bit(TTY_IO_ERROR, &tty->flags);
+-	if (tty->ldisc.flush_buffer)
+-		(tty->ldisc.flush_buffer)(tty);
++	tty_ldisc_flush(tty);
+ 
+ 	tty->closing = 0;
+ 	portp->tty = (struct tty_struct *) NULL;
+@@ -1850,10 +1849,7 @@
+ 		return;
+ 
+ 	stl_flush(portp);
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*****************************************************************************/
+@@ -2224,10 +2220,7 @@
+ 
+ 	lock_kernel();
+ 	if (test_bit(ASYI_TXLOW, &portp->istate)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ 	if (test_bit(ASYI_DCDCHANGE, &portp->istate)) {
+ 		clear_bit(ASYI_DCDCHANGE, &portp->istate);
+===== drivers/usb/bluetooth.c 1.18 vs 1.19 =====
+--- 1.18/drivers/usb/bluetooth.c	2003-08-28 20:13:49 +09:00
++++ 1.19/drivers/usb/bluetooth.c	2004-12-17 03:49:03 +09:00
+@@ -1017,6 +1017,7 @@
+ {
+ 	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)private, __FUNCTION__);
+ 	struct tty_struct *tty;
++	struct tty_ldisc *ld;
+ 
+ 	dbg("%s", __FUNCTION__);
+ 
+@@ -1025,9 +1026,15 @@
+ 	}
+ 
+ 	tty = bluetooth->tty;
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+-		dbg("%s - write wakeup call.", __FUNCTION__);
+-		(tty->ldisc.write_wakeup)(tty);
++	if (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) {
++		ld = tty_ldisc_ref(tty);
++		if(ld) {
++			if(ld->write_wakeup) {
++				ld->write_wakeup(tty);
++				dbg("%s - write wakeup call.", __FUNCTION__);
++			}
++			tty_ldisc_deref(tty);
++		}
+ 	}
+ 
+ 	wake_up_interruptible(&tty->write_wait);
+===== drivers/net/wan/x25_asy.c 1.3 vs 1.4 =====
+--- 1.3/drivers/net/wan/x25_asy.c	2002-02-05 16:45:17 +09:00
++++ 1.4/drivers/net/wan/x25_asy.c	2004-12-17 00:02:12 +09:00
+@@ -619,9 +619,7 @@
+ 	if (tty->driver.flush_buffer)  {
+ 		tty->driver.flush_buffer(tty);
+ 	}
+-	if (tty->ldisc.flush_buffer)  {
+-		tty->ldisc.flush_buffer(tty);
+-	}
++	tty_ldisc_flush(tty);
+ 
+ 	/* Restore default settings */
+ 	sl->dev->type = ARPHRD_X25;
+===== drivers/char/cyclades.c 1.10 vs 1.11 =====
+--- 1.10/drivers/char/cyclades.c	2002-11-21 05:08:49 +09:00
++++ 1.11/drivers/char/cyclades.c	2004-12-16 22:57:23 +09:00
+@@ -1000,11 +1000,7 @@
+ 	wake_up_interruptible(&info->delta_msr_wait);
+     }
+     if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {
+-        if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))
+-        && tty->ldisc.write_wakeup){
+-            (tty->ldisc.write_wakeup)(tty);
+-        }
+-        wake_up_interruptible(&tty->write_wait);
++	tty_wakeup(tty);
+     }
+ #ifdef Z_WAKE
+     if (test_and_clear_bit(Cy_EVENT_SHUTDOWN_WAKEUP, &info->event)) {
+@@ -2918,8 +2914,7 @@
+     shutdown(info);
+     if (tty->driver.flush_buffer)
+         tty->driver.flush_buffer(tty);
+-    if (tty->ldisc.flush_buffer)
+-        tty->ldisc.flush_buffer(tty);
++    tty_ldisc_flush(tty);
+     CY_LOCK(info, flags);
+ 
+     tty->closing = 0;
+@@ -4689,10 +4684,7 @@
+ 	}
+ 	CY_UNLOCK(info, flags);
+     }
+-    wake_up_interruptible(&tty->write_wait);
+-    if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+-	&& tty->ldisc.write_wakeup)
+-	    (tty->ldisc.write_wakeup)(tty);
++    tty_wakeup(tty);
+ } /* cy_flush_buffer */
+ 
+ 
+===== net/bluetooth/rfcomm/tty.c 1.11 vs 1.12 =====
+--- 1.11/net/bluetooth/rfcomm/tty.c	2004-04-16 20:55:48 +09:00
++++ 1.12/net/bluetooth/rfcomm/tty.c	2004-12-16 23:20:47 +09:00
+@@ -532,10 +532,8 @@
+ 
+ 	BT_DBG("dev %p tty %p", dev, tty);
+ 
+-	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
+-                (tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ 
+-	wake_up_interruptible(&tty->write_wait);
+ #ifdef SERIAL_HAVE_POLL_WAIT
+ 	wake_up_interruptible(&tty->poll_wait);
+ #endif
+@@ -853,8 +851,7 @@
+ 
+ 	skb_queue_purge(&dev->dlc->tx_queue);
+ 
+-	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
+-		tty->ldisc.write_wakeup(tty);
++	tty_wakeup(tty);
+ }
+ 
+ static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch)
+===== drivers/net/irda/irtty.c 1.11 vs 1.12 =====
+--- 1.11/drivers/net/irda/irtty.c	2002-08-07 03:08:16 +09:00
++++ 1.12/drivers/net/irda/irtty.c	2004-12-16 23:59:36 +09:00
+@@ -179,9 +179,8 @@
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++
++	tty_ldisc_flush(tty);	
+ 	
+ 	self->magic = IRTTY_MAGIC;
+ 	self->mode = IRDA_IRLAP;
+===== drivers/char/sgiserial.c 1.9 vs 1.10 =====
+--- 1.9/drivers/char/sgiserial.c	2003-11-25 08:22:51 +09:00
++++ 1.10/drivers/char/sgiserial.c	2004-12-16 22:57:23 +09:00
+@@ -1498,10 +1498,10 @@
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+-	if (tty->ldisc.num != ldiscs[N_TTY].num) {
++	if (tty->ldisc.num != N_TTY) {
+ 		if (tty->ldisc.close)
+ 			(tty->ldisc.close)(tty);
+-		tty->ldisc = ldiscs[N_TTY];
++		tty->ldisc = *(tty_ldisc_get(N_TTY));
+ 		tty->termios->c_line = N_TTY;
+ 		if (tty->ldisc.open)
+ 			(tty->ldisc.open)(tty);
+===== drivers/char/n_hdlc.c 1.5 vs 1.6 =====
+--- 1.5/drivers/char/n_hdlc.c	2003-09-04 23:17:06 +09:00
++++ 1.6/drivers/char/n_hdlc.c	2004-12-16 23:36:41 +09:00
+@@ -351,9 +351,8 @@
+ #endif
+ 	
+ 	/* Flush any pending characters in the driver and discipline. */
+-	
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer (tty);
++
++	tty_ldisc_flush(tty);	
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer (tty);
+===== drivers/char/ser_a2232.c 1.3 vs 1.4 =====
+--- 1.3/drivers/char/ser_a2232.c	2002-12-03 11:10:12 +09:00
++++ 1.4/drivers/char/ser_a2232.c	2004-12-16 22:57:23 +09:00
+@@ -633,10 +633,7 @@
+ 					
+ 				/* WakeUp if output buffer runs low */
+ 				if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) {
+-					if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && port->gs.tty->ldisc.write_wakeup){
+-						(port->gs.tty->ldisc.write_wakeup)(port->gs.tty);
+-					}
+-					wake_up_interruptible(&port->gs.tty->write_wait);
++					tty_wakeup(port->gs.tty);
+ 				}
+ 			} // if the port is used
+ 		} // for every port on the board
+===== drivers/char/serial.c 1.39 vs 1.40 =====
+--- 1.39/drivers/char/serial.c	2004-09-29 23:09:07 +09:00
++++ 1.40/drivers/char/serial.c	2004-12-17 03:42:06 +09:00
+@@ -1068,17 +1068,15 @@
+ static void do_softint(void *private_)
+ {
+ 	struct async_struct	*info = (struct async_struct *) private_;
+-	struct tty_struct	*tty;
+-	
++	struct tty_struct       *tty;
++
+ 	tty = info->tty;
+ 	if (!tty)
+ 		return;
+ 
+ 	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
++		
+ #ifdef SERIAL_HAVE_POLL_WAIT
+ 		wake_up_interruptible(&tty->poll_wait);
+ #endif
+@@ -1976,13 +1974,10 @@
+ 	save_flags(flags); cli();
+ 	info->xmit.head = info->xmit.tail = 0;
+ 	restore_flags(flags);
+-	wake_up_interruptible(&tty->write_wait);
+ #ifdef SERIAL_HAVE_POLL_WAIT
+ 	wake_up_interruptible(&tty->poll_wait);
+ #endif
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -2867,8 +2862,7 @@
+ 	shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+===== drivers/char/hvc_console.c 1.2 vs 1.3 =====
+--- 1.2/drivers/char/hvc_console.c	2004-02-04 15:10:16 +09:00
++++ 1.3/drivers/char/hvc_console.c	2004-12-16 22:57:23 +09:00
+@@ -217,10 +217,7 @@
+ 
+ 		if (hp->do_wakeup) {
+ 			hp->do_wakeup = 0;
+-			if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+-			    && tty->ldisc.write_wakeup)
+-				(tty->ldisc.write_wakeup)(tty);
+-			wake_up_interruptible(&tty->write_wait);
++			tty_wakeup(tty);
+ 		}
+ 	}
+ 
+===== drivers/char/pty.c 1.5 vs 1.6 =====
+--- 1.5/drivers/char/pty.c	2004-12-17 02:29:44 +09:00
++++ 1.6/drivers/char/pty.c	2004-12-16 23:40:52 +09:00
+@@ -121,10 +121,7 @@
+ 	if (!o_tty)
+ 		return;
+ 
+-	if ((o_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    o_tty->ldisc.write_wakeup)
+-		(o_tty->ldisc.write_wakeup)(o_tty);
+-	wake_up_interruptible(&o_tty->write_wait);
++	tty_wakeup(o_tty);
+ 	set_bit(TTY_THROTTLED, &tty->flags);
+ }
+ 
+@@ -137,6 +134,10 @@
+  *   (2) avoid redundant copying for cases where count >> receive_room
+  * N.B. Calls from user space may now return an error code instead of
+  * a count.
++ *
++ * FIXME: Our pty_write method is called with our ldisc lock held but
++ * not our partners. We can't just take the other one blindly without
++ * risking deadlocks.  There is also the small matter of TTY_DONT_FLIP
+  */
+ static int pty_write(struct tty_struct * tty, int from_user,
+ 		       const unsigned char *buf, int count)
+@@ -299,9 +300,8 @@
+ 	
+ 	if (!to)
+ 		return;
+-	
+-	if (to->ldisc.flush_buffer)
+-		to->ldisc.flush_buffer(to);
++
++	tty_ldisc_flush(to);	
+ 	
+ 	if (to->packet) {
+ 		tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
+===== net/irda/ircomm/ircomm_tty.c 1.8 vs 1.9 =====
+--- 1.8/net/irda/ircomm/ircomm_tty.c	2002-09-12 20:37:06 +09:00
++++ 1.9/net/irda/ircomm/ircomm_tty.c	2004-12-16 23:09:13 +09:00
+@@ -566,8 +566,7 @@
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 
+ 	tty->closing = 0;
+ 	self->tty = 0;
+@@ -662,12 +661,7 @@
+ 		ircomm_tty_do_event(self, IRCOMM_TTY_DATA_REQUEST, skb, NULL);
+ 		
+ 	/* Check if user (still) wants to be waken up */
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && 
+-	    tty->ldisc.write_wakeup)
+-	{
+-		(tty->ldisc.write_wakeup)(tty);
+-	}
+-	wake_up_interruptible(&tty->write_wait);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+===== drivers/char/synclink.c 1.16 vs 1.17 =====
+--- 1.16/drivers/char/synclink.c	2003-11-19 02:31:14 +09:00
++++ 1.17/drivers/char/synclink.c	2004-12-17 03:43:15 +09:00
+@@ -1011,6 +1011,29 @@
+ 	return 0;
+ }
+ 
++/**
++ * line discipline callback wrappers
++ *
++ * The wrappers maintain line discipline references
++ * while calling into the line discipline.
++ *
++ * ldisc_receive_buf  - pass receive data to line discipline
++ */
++
++static void ldisc_receive_buf(struct tty_struct *tty,
++			      const __u8 *data, char *flags, int count)
++{
++	struct tty_ldisc *ld;
++	if (!tty)
++		return;
++	ld = tty_ldisc_ref(tty);
++	if (ld) {
++		if (ld->receive_buf)
++			ld->receive_buf(tty, data, flags, count);
++		tty_ldisc_deref(ld);
++	}
++}
++
+ /* mgsl_stop()		throttle (stop) transmitter
+  * 	
+  * Arguments:		tty	pointer to tty info structure
+@@ -1170,14 +1193,7 @@
+ 			__FILE__,__LINE__,info->device_name);
+ 
+ 	if (tty) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup) {
+-			if ( debug_level >= DEBUG_LEVEL_BH )
+-				printk( "%s(%d):calling ldisc.write_wakeup on %s\n",
+-					__FILE__,__LINE__,info->device_name);
+-			(tty->ldisc.write_wakeup)(tty);
+-		}
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ 
+ 	/* if transmitter idle and loopmode_send_done_requested
+@@ -2433,12 +2449,8 @@
+ 	del_timer(&info->tx_timer);	
+ 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
+ 	
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
+-		
+-}	/* end of mgsl_flush_buffer() */
++	tty_wakeup(tty);
++}
+ 
+ /* mgsl_send_xchar()
+  *
+@@ -3342,9 +3354,8 @@
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-		
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	
++	tty_ldisc_flush(tty);
+ 		
+ 	shutdown(info);
+ 	
+@@ -7007,11 +7018,7 @@
+ 			} 
+ 			else
+ #endif
+-			{
+-				/* Call the line discipline receive callback directly. */
+-				if ( tty && tty->ldisc.receive_buf )
+-				tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
+-			}
++				ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
+ 		}
+ 	}
+ 	/* Free the buffers used by this frame. */
+@@ -7183,9 +7190,7 @@
+ 			memcpy( info->intermediate_rxbuffer, pBufEntry->virt_addr, framesize);
+ 			info->icount.rxok++;
+ 
+-			/* Call the line discipline receive callback directly. */
+-			if ( tty && tty->ldisc.receive_buf )
+-				tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
++			ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
+ 		}
+ 
+ 		/* Free the buffers used by this frame. */
+===== fs/proc/proc_tty.c 1.2 vs 1.3 =====
+--- 1.2/fs/proc/proc_tty.c	2003-07-15 05:10:30 +09:00
++++ 1.3/fs/proc/proc_tty.c	2004-12-16 22:57:23 +09:00
+@@ -15,8 +15,6 @@
+ #include <asm/bitops.h>
+ 
+ extern struct tty_driver *tty_drivers;	/* linked list of tty drivers */
+-extern struct tty_ldisc ldiscs[];
+-
+ 
+ static int tty_drivers_read_proc(char *page, char **start, off_t off,
+ 				 int count, int *eof, void *data);
+@@ -106,12 +104,15 @@
+ 	int	i;
+ 	int	len = 0;
+ 	off_t	begin = 0;
+-
++	struct tty_ldisc *ld;
++	
+ 	for (i=0; i < NR_LDISCS; i++) {
+-		if (!(ldiscs[i].flags & LDISC_FLAG_DEFINED))
++		ld = tty_ldisc_get(i);
++		if (ld == NULL)
+ 			continue;
+ 		len += sprintf(page+len, "%-10s %2d\n",
+-			       ldiscs[i].name ? ldiscs[i].name : "???", i);
++			       ld->name ? ld->name : "???", i);
++		tty_ldisc_put(i);
+ 		if (len+begin > off+count)
+ 			break;
+ 		if (len+begin < off) {
+===== drivers/net/wan/pc300_tty.c 1.2 vs 1.3 =====
+--- 1.2/drivers/net/wan/pc300_tty.c	2004-08-12 05:05:57 +09:00
++++ 1.3/drivers/net/wan/pc300_tty.c	2004-12-16 22:57:23 +09:00
+@@ -627,14 +627,8 @@
+ 	}
+ 
+ 	CPC_TTY_DBG("%s: call wake_up_interruptible\n",cpc_tty->name);
+-	
+-	wake_up_interruptible(&tty->write_wait); 
+-
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup){
+-		CPC_TTY_DBG("%s: call line disc. wake up\n",cpc_tty->name);
+-		tty->ldisc.write_wakeup(tty); 
+-	} 
+ 
++	tty_wakeup(tty);	
+ 	return; 
+ } 
+ 
+@@ -696,16 +690,22 @@
+ 		flg_rx = 0;
+ 		port = (int) data;
+ 		for (j=0; j < CPC_TTY_NPORTS; j++) {
+-			cpc_tty = &cpc_tty_area[port];
+-		
++			cpc_tty = &cpc_tty_area[port]; 				
++
+ 			if ((buf=cpc_tty->buf_rx.first) != 0) {
+-															
+-				if (cpc_tty->tty && (cpc_tty->tty->ldisc.receive_buf)) { 
+-					CPC_TTY_DBG("%s: call line disc. receive_buf\n",cpc_tty->name);
+-					cpc_tty->tty->ldisc.receive_buf(cpc_tty->tty, buf->data, 
+-													&flags, buf->size);
+-				}	
+-				cpc_tty->buf_rx.first = cpc_tty->buf_rx.first->next;
++				if(cpc_tty->tty) 
++				{
++					struct tty_ldisc *ld = tty_ldisc_ref(cpc_tty->tty);
++					if(ld)
++					{
++						if (ld->receive_buf) {
++							CPC_TTY_DBG("%s: call line disc. receive_buf\n",cpc_tty->name);
++							ld->receive_buf(cpc_tty->tty, (char *)(buf->data), &flags, buf->size);
++						}
++						tty_ldisc_deref(ld);
++					}
++				}											
++  				cpc_tty->buf_rx.first = cpc_tty->buf_rx.first->next;
+ 				kfree((unsigned char *)buf);
+ 				buf = cpc_tty->buf_rx.first;
+ 				flg_rx = 1;
+@@ -908,12 +909,7 @@
+ 		return; 
+ 	}
+ 
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup){
+-		CPC_TTY_DBG("%s:call line disc. wakeup\n",cpc_tty->name);
+-		tty->ldisc.write_wakeup (tty); 
+-	}
+-
+-	wake_up_interruptible(&tty->write_wait); 
++	tty_wakeup(tty);
+ }
+ 
+ /*
+===== drivers/char/serial_txx927.c 1.4 vs 1.5 =====
+--- 1.4/drivers/char/serial_txx927.c	2002-03-31 00:45:50 +09:00
++++ 1.5/drivers/char/serial_txx927.c	2004-12-16 23:38:45 +09:00
+@@ -535,10 +535,7 @@
+ 		return;
+ 
+ 	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ #ifdef SERIAL_HAVE_POLL_WAIT
+ 		wake_up_interruptible(&tty->poll_wait);
+ #endif                                                    
+@@ -1104,13 +1101,10 @@
+ 	save_flags(flags); cli();
+ 	info->xmit.head = info->xmit.tail = 0;
+ 	restore_flags(flags);
+-	wake_up_interruptible(&tty->write_wait);
+ #ifdef SERIAL_HAVE_POLL_WAIT
+ 	wake_up_interruptible(&tty->poll_wait);
+ #endif
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1465,8 +1459,7 @@
+ 	shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+===== drivers/sbus/char/sab82532.c 1.11 vs 1.12 =====
+--- 1.11/drivers/sbus/char/sab82532.c	2002-03-31 00:45:50 +09:00
++++ 1.12/drivers/sbus/char/sab82532.c	2004-12-17 00:06:52 +09:00
+@@ -669,10 +669,7 @@
+ 		return;
+ 
+ 	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ }
+ 
+@@ -1206,10 +1203,7 @@
+ 	info->xmit.head = info->xmit.tail = 0;
+ 	restore_flags(flags);
+ 
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1667,8 +1661,7 @@
+ 	shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+===== drivers/char/sx.c 1.14 vs 1.15 =====
+--- 1.14/drivers/char/sx.c	2003-03-25 03:08:18 +09:00
++++ 1.15/drivers/char/sx.c	2004-12-16 22:57:23 +09:00
+@@ -1057,9 +1057,7 @@
+ 	}
+ 
+ 	if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) {
+-		if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    port->gs.tty->ldisc.write_wakeup)
+-			(port->gs.tty->ldisc.write_wakeup)(port->gs.tty);
++		tty_wakeup(port->gs.tty);
+ 		sx_dprintk (SX_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n",
+ 		            port->gs.wakeup_chars); 
+ 		wake_up_interruptible(&port->gs.tty->write_wait);
+===== drivers/usb/serial/usbserial.c 1.32 vs 1.33 =====
+--- 1.32/drivers/usb/serial/usbserial.c	2004-11-01 23:29:07 +09:00
++++ 1.33/drivers/usb/serial/usbserial.c	2004-12-16 23:55:02 +09:00
+@@ -1326,6 +1326,7 @@
+ 	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+ 	struct tty_struct *tty;
+ 	unsigned long flags;
++	struct tty_ldisc *ld;
+ 
+ 	dbg("%s - port %d", __FUNCTION__, port->number);
+ 	
+@@ -1341,9 +1342,15 @@
+ 	if (!tty)
+ 		return;
+ 
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+-		dbg("%s - write wakeup call.", __FUNCTION__);
+-		(tty->ldisc.write_wakeup)(tty);
++	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))) {
++		ld = tty_ldisc_ref(tty);
++		if(ld) {
++			if(ld->write_wakeup) {
++				ld->write_wakeup(tty);
++				dbg("%s - write wakeup call.", __FUNCTION__);
++			}
++			tty_ldisc_deref(ld);
++		}
+ 	}
+ 
+ 	wake_up_interruptible(&tty->write_wait);
+===== drivers/net/ppp_synctty.c 1.6 vs 1.7 =====
+--- 1.6/drivers/net/ppp_synctty.c	2002-03-11 23:13:28 +09:00
++++ 1.7/drivers/net/ppp_synctty.c	2004-12-16 22:57:23 +09:00
+@@ -172,6 +172,8 @@
+  * frees the memory that ppp_synctty_receive is using.  The best
+  * way to fix this is to use a rwlock in the tty struct, but for now
+  * we use a single global rwlock for all ttys in ppp line discipline.
++ *
++ * FIXME: Fixed in tty_io nowdays.
+  */
+ static rwlock_t disc_data_lock = RW_LOCK_UNLOCKED;
+ 
+@@ -280,6 +282,18 @@
+ }
+ 
+ /*
++ * Called on tty hangup in process context.
++ *
++ * Wait for I/O to driver to complete and unregister PPP channel.
++ * This is already done by the close routine, so just call that.
++ */
++static int ppp_sync_hangup(struct tty_struct *tty)
++{
++	ppp_sync_close(tty);
++	return 0;
++}
++
++/*
+  * Read does nothing - no data is ever available this way.
+  * Pppd reads and writes packets via /dev/ppp instead.
+  */
+@@ -412,6 +426,7 @@
+ 	write:	ppp_sync_write,
+ 	ioctl:	ppp_synctty_ioctl,
+ 	poll:	ppp_sync_poll,
++	hangup: ppp_sync_hangup,
+ 	receive_room: ppp_sync_room,
+ 	receive_buf: ppp_sync_receive,
+ 	write_wakeup: ppp_sync_wakeup,
+===== drivers/char/serial167.c 1.7 vs 1.8 =====
+--- 1.7/drivers/char/serial167.c	2002-08-05 21:48:52 +09:00
++++ 1.8/drivers/char/serial167.c	2004-12-16 22:57:23 +09:00
+@@ -773,11 +773,7 @@
+ 	wake_up_interruptible(&info->open_wait);
+     }
+     if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {
+-	if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))
+-	&& tty->ldisc.write_wakeup){
+-	    (tty->ldisc.write_wakeup)(tty);
+-	}
+-	wake_up_interruptible(&tty->write_wait);
++	tty_wakeup(tty);
+     }
+ } /* do_softint */
+ 
+@@ -1357,9 +1353,7 @@
+ 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+     restore_flags(flags);
+     wake_up_interruptible(&tty->write_wait);
+-    if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+-    && tty->ldisc.write_wakeup)
+-	(tty->ldisc.write_wakeup)(tty);
++    tty_wakeup(tty);
+ } /* cy_flush_buffer */
+ 
+ 
+@@ -1916,18 +1910,9 @@
+     shutdown(info);
+     if (tty->driver.flush_buffer)
+ 	tty->driver.flush_buffer(tty);
+-    if (tty->ldisc.flush_buffer)
+-	tty->ldisc.flush_buffer(tty);
++    tty_ldisc_flush(tty);
+     info->event = 0;
+     info->tty = 0;
+-    if (tty->ldisc.num != ldiscs[N_TTY].num) {
+-	if (tty->ldisc.close)
+-	    (tty->ldisc.close)(tty);
+-	tty->ldisc = ldiscs[N_TTY];
+-	tty->termios->c_line = N_TTY;
+-	if (tty->ldisc.open)
+-	    (tty->ldisc.open)(tty);
+-    }
+     if (info->blocked_open) {
+ 	if (info->close_delay) {
+ 	    current->state = TASK_INTERRUPTIBLE;
+===== drivers/macintosh/macserial.c 1.8 vs 1.9 =====
+--- 1.8/drivers/macintosh/macserial.c	2002-03-31 00:45:50 +09:00
++++ 1.9/drivers/macintosh/macserial.c	2004-12-16 22:57:23 +09:00
+@@ -735,12 +735,8 @@
+ 	if (!tty)
+ 		return;
+ 
+-	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
+-	}
++	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) 
++		tty_wakeup(tty);
+ }
+ 
+ static int startup(struct mac_serial * info)
+@@ -1595,10 +1591,7 @@
+ 	save_flags(flags); cli();
+ 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ 	restore_flags(flags);
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -2029,8 +2022,7 @@
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+===== drivers/net/hamradio/mkiss.c 1.7 vs 1.8 =====
+--- 1.7/drivers/net/hamradio/mkiss.c	2003-06-04 20:18:22 +09:00
++++ 1.8/drivers/net/hamradio/mkiss.c	2004-12-17 00:03:13 +09:00
+@@ -654,8 +654,7 @@
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 
+ 	/* Restore default settings */
+ 	ax->dev->type = ARPHRD_AX25;
+===== drivers/char/pdc_console.c 1.2 vs 1.3 =====
+--- 1.2/drivers/char/pdc_console.c	2002-12-31 02:27:35 +09:00
++++ 1.3/drivers/char/pdc_console.c	2004-12-16 23:36:12 +09:00
+@@ -463,8 +463,7 @@
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+===== drivers/char/riscom8.c 1.5 vs 1.6 =====
+--- 1.5/drivers/char/riscom8.c	2002-02-05 16:45:17 +09:00
++++ 1.6/drivers/char/riscom8.c	2004-12-16 22:57:23 +09:00
+@@ -1200,8 +1200,7 @@
+ 	rc_shutdown_port(bp, port);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	port->event = 0;
+ 	port->tty = 0;
+@@ -1375,9 +1374,7 @@
+ 	restore_flags(flags);
+ 	
+ 	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ static int rc_get_modem_info(struct riscom_port * port, unsigned int *value)
+@@ -1734,10 +1731,7 @@
+ 		return;
+ 
+ 	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ }
+ 
+===== drivers/char/synclinkmp.c 1.5 vs 1.6 =====
+--- 1.5/drivers/char/synclinkmp.c	2004-08-27 03:02:57 +09:00
++++ 1.6/drivers/char/synclinkmp.c	2004-12-16 22:57:23 +09:00
+@@ -735,6 +735,29 @@
+ 	return 0;
+ }
+ 
++/**
++ * line discipline callback wrappers
++ *
++ * The wrappers maintain line discipline references
++ * while calling into the line discipline.
++ *
++ * ldisc_receive_buf  - pass receive data to line discipline
++ */
++
++static void ldisc_receive_buf(struct tty_struct *tty,
++			      const __u8 *data, char *flags, int count)
++{
++	struct tty_ldisc *ld;
++	if (!tty)
++		return;
++	ld = tty_ldisc_ref(tty);
++	if (ld) {
++		if (ld->receive_buf)
++			ld->receive_buf(tty, data, flags, count);
++		tty_ldisc_deref(ld);
++	}
++}
++
+ /* tty callbacks */
+ 
+ /* Called when a port is opened.  Init and enable port.
+@@ -906,8 +929,7 @@
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+ 
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 
+ 	shutdown(info);
+ 
+@@ -1315,9 +1337,7 @@
+ 	spin_unlock_irqrestore(&info->lock,flags);
+ 
+ 	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /* throttle (stop) transmitter
+@@ -1983,13 +2003,7 @@
+ 			__FILE__,__LINE__,info->device_name);
+ 
+ 	if (tty) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup) {
+-			if ( debug_level >= DEBUG_LEVEL_BH )
+-				printk( "%s(%d):%s calling ldisc.write_wakeup\n",
+-					__FILE__,__LINE__,info->device_name);
+-			(tty->ldisc.write_wakeup)(tty);
+-		}
++		tty_wakeup(tty);
+ 		wake_up_interruptible(&tty->write_wait);
+ 	}
+ }
+@@ -4989,15 +5003,8 @@
+ 			}
+ 			else
+ #endif
+-			{
+-				if ( tty && tty->ldisc.receive_buf ) {
+-					/* Call the line discipline receive callback directly. */
+-					tty->ldisc.receive_buf(tty,
+-						info->tmp_rx_buf,
+-						info->flag_buf,
+-						framesize);
+-				}
+-			}
++				ldisc_receive_buf(tty,info->tmp_rx_buf,
++						  info->flag_buf, framesize);
+ 		}
+ 	}
+ 	/* Free the buffers used by this frame. */
+===== drivers/usb/serial/digi_acceleport.c 1.14 vs 1.15 =====
+--- 1.14/drivers/usb/serial/digi_acceleport.c	2002-08-30 02:51:26 +09:00
++++ 1.15/drivers/usb/serial/digi_acceleport.c	2004-12-16 22:57:23 +09:00
+@@ -614,14 +614,7 @@
+ 	wake_up_interruptible( &port->write_wait );
+ 
+ 	/* wake up line discipline */
+-	if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+-	&& tty->ldisc.write_wakeup )
+-		(tty->ldisc.write_wakeup)(tty);
+-
+-	/* wake up other tty processes */
+-	wake_up_interruptible( &tty->write_wait );
+-	/* For 2.2.16 backport -- wake_up_interruptible( &tty->poll_wait ); */
+-
++	tty_wakeup(tty);
+ }
+ 
+ 
+@@ -1557,8 +1550,7 @@
+ 	/* flush driver and line discipline buffers */
+ 	if( tty->driver.flush_buffer )
+ 		tty->driver.flush_buffer( tty );
+-	if( tty->ldisc.flush_buffer )
+-		tty->ldisc.flush_buffer( tty );
++	tty_ldisc_flush(tty);
+ 
+ 	if (port->serial->dev) {
+ 		/* wait for transmit idle */
+===== drivers/net/wan/sdla_chdlc.c 1.15 vs 1.16 =====
+--- 1.15/drivers/net/wan/sdla_chdlc.c	2002-12-03 11:10:12 +09:00
++++ 1.16/drivers/net/wan/sdla_chdlc.c	2004-12-16 22:57:23 +09:00
+@@ -3868,11 +3868,7 @@
+ 	if ((tty=card->tty)==NULL)
+ 		return;
+ 	
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup){
+-		(tty->ldisc.write_wakeup)(tty);
+-	}
+-	wake_up_interruptible(&tty->write_wait);
++	tty_wakeup(tty);
+ #if defined(SERIAL_HAVE_POLL_WAIT) || \
+          (defined LINUX_2_1 && LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,15))
+ 	wake_up_interruptible(&tty->poll_wait);
+@@ -4098,6 +4094,7 @@
+ 	char fp=0;
+ 	struct tty_struct *tty;
+ 	int i;
++	struct tty_ldisc *ld;
+ 	
+ 	if (!card->tty_open){
+ 		dbg_printk(KERN_INFO "%s: TTY not open during receive\n",
+@@ -4185,8 +4182,11 @@
+ 			len -= offset;
+ 		}
+ 		sdla_peek(&card->hw, addr, card->tty_rx+offset, len);
+-		if (tty->ldisc.receive_buf){
+-			tty->ldisc.receive_buf(tty,card->tty_rx,&fp,olen);
++		ld = tty_ldisc_ref(tty);
++		if (ld) {
++			if (ld->receive_buf)
++				ld->receive_buf(tty,card->tty_rx,&fp,olen);
++			tty_ldisc_deref(ld);
+ 		}else{
+ 			if (net_ratelimit()){
+ 				printk(KERN_INFO 
+@@ -4493,14 +4493,11 @@
+ 	if (!tty)
+ 		return;
+ 	
+-	wake_up_interruptible(&tty->write_wait);
+ #if defined(SERIAL_HAVE_POLL_WAIT) || \
+          (defined LINUX_2_1 && LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,15))
+ 	wake_up_interruptible(&tty->poll_wait);
+ #endif
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ 
+ 	return;
+ }
+===== drivers/char/pcmcia/synclink_cs.c 1.3 vs 1.4 =====
+--- 1.3/drivers/char/pcmcia/synclink_cs.c	2003-09-09 23:43:21 +09:00
++++ 1.4/drivers/char/pcmcia/synclink_cs.c	2004-12-17 00:22:51 +09:00
+@@ -553,6 +553,29 @@
+ static void* mgslpc_get_text_ptr(void);
+ static void* mgslpc_get_text_ptr() {return mgslpc_get_text_ptr;}
+ 
++/**
++ * line discipline callback wrappers
++ *
++ * The wrappers maintain line discipline references
++ * while calling into the line discipline.
++ *
++ * ldisc_receive_buf  - pass receive data to line discipline
++ */
++
++static void ldisc_receive_buf(struct tty_struct *tty,
++			      const __u8 *data, char *flags, int count)
++{
++	struct tty_ldisc *ld;
++	if (!tty)
++		return;
++	ld = tty_ldisc_ref(tty);
++	if (ld) {
++		if (ld->receive_buf)
++			ld->receive_buf(tty, data, flags, count);
++		tty_ldisc_deref(ld);
++	}
++}
++
+ static dev_link_t *mgslpc_attach(void)
+ {
+     MGSLPC_INFO *info;
+@@ -1027,13 +1050,7 @@
+ 		printk("bh_transmit() entry on %s\n", info->device_name);
+ 
+ 	if (tty) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup) {
+-			if ( debug_level >= DEBUG_LEVEL_BH )
+-				printk( "%s(%d):calling ldisc.write_wakeup on %s\n",
+-					__FILE__,__LINE__,info->device_name);
+-			(tty->ldisc.write_wakeup)(tty);
+-		}
++		tty_wakeup(tty);
+ 		wake_up_interruptible(&tty->write_wait);
+ 	}
+ }
+@@ -1917,11 +1934,9 @@
+ 	info->tx_count = info->tx_put = info->tx_get = 0;
+ 	del_timer(&info->tx_timer);	
+ 	spin_unlock_irqrestore(&info->lock,flags);
+-	
++
+ 	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /* Send a high-priority XON/XOFF character
+@@ -2685,9 +2700,8 @@
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-		
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	
++	tty_ldisc_flush(tty);
+ 		
+ 	shutdown(info);
+ 	
+@@ -4199,11 +4213,7 @@
+ 			} 
+ 			else
+ #endif
+-			{
+-				/* Call the line discipline receive callback directly. */
+-				if (tty && tty->ldisc.receive_buf)
+-					tty->ldisc.receive_buf(tty, buf->data, info->flag_buf, framesize);
+-			}
++				ldisc_receive_buf(tty, buf->data, info->flag_buf, framesize);
+ 		}
+ 	}
+ 
+===== include/linux/tty.h 1.6 vs 1.7 =====
+--- 1.6/include/linux/tty.h	2003-05-17 19:56:48 +09:00
++++ 1.7/include/linux/tty.h	2004-12-16 22:57:23 +09:00
+@@ -260,6 +260,7 @@
+ 	int	magic;
+ 	struct tty_driver driver;
+ 	struct tty_ldisc ldisc;
++	struct semaphore termios_sem;
+ 	struct termios *termios, *termios_locked;
+ 	int pgrp;
+ 	int session;
+@@ -322,26 +323,28 @@
+  * tty->write.  Thus, you must use the inline functions set_bit() and
+  * clear_bit() to make things atomic.
+  */
+-#define TTY_THROTTLED 0
+-#define TTY_IO_ERROR 1
+-#define TTY_OTHER_CLOSED 2
+-#define TTY_EXCLUSIVE 3
+-#define TTY_DEBUG 4
+-#define TTY_DO_WRITE_WAKEUP 5
+-#define TTY_PUSH 6
+-#define TTY_CLOSING 7
+-#define TTY_DONT_FLIP 8
+-#define TTY_HW_COOK_OUT 14
+-#define TTY_HW_COOK_IN 15
+-#define TTY_PTY_LOCK 16
+-#define TTY_NO_WRITE_SPLIT 17
++#define TTY_THROTTLED 		0	/* Call unthrottle() at threshold min */
++#define TTY_IO_ERROR 		1	/* Canse an I/O error (may be no ldisc too) */
++#define TTY_OTHER_CLOSED 	2	/* Other side (if any) has closed */
++#define TTY_EXCLUSIVE 		3	/* Exclusive open mode */
++#define TTY_DEBUG 		4	/* Debugging */
++#define TTY_DO_WRITE_WAKEUP 	5	/* Call write_wakeup after queuing new */
++#define TTY_PUSH 		6	/* n_tty private */
++#define TTY_CLOSING 		7	/* ->close() in progress */
++#define TTY_DONT_FLIP 		8	/* Defer buffer flip */
++#define TTY_LDISC 		9	/* Line discipline attached */
++#define TTY_HW_COOK_OUT 	14	/* Hardware can do output cooking */
++#define TTY_HW_COOK_IN 	15	/* Hardware can do input cooking */
++#define TTY_PTY_LOCK 		16	/* pty private */
++#define TTY_NO_WRITE_SPLIT 	17	/* Preserve write boundaries to driver */
++#define TTY_HUPPED 		18	/* Post driver->hangup() */
+ 
+ #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
+ 
+ extern void tty_write_flush(struct tty_struct *);
+ 
+ extern struct termios tty_std_termios;
+-extern struct tty_ldisc ldiscs[];
++extern struct tty_ldisc tty_ldiscs[];
+ extern int fg_console, last_console, want_console;
+ 
+ extern int kmsg_redirect;
+@@ -395,6 +398,17 @@
+ extern void disassociate_ctty(int priv);
+ extern void tty_flip_buffer_push(struct tty_struct *tty);
+ extern int tty_get_baud_rate(struct tty_struct *tty);
++
++extern struct tty_ldisc *tty_ldisc_ref(struct tty_struct *);
++extern void tty_ldisc_deref(struct tty_ldisc *);
++extern struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *);
++
++extern struct tty_ldisc *tty_ldisc_get(int);
++extern void tty_ldisc_put(int);
++
++extern void tty_wakeup(struct tty_struct *tty);
++extern void tty_ldisc_flush(struct tty_struct *tty);
++
+ 
+ /* n_tty.c */
+ extern struct tty_ldisc tty_ldisc_N_TTY;
+===== drivers/char/generic_serial.c 1.5 vs 1.6 =====
+--- 1.5/drivers/char/generic_serial.c	2002-07-30 02:09:00 +09:00
++++ 1.6/drivers/char/generic_serial.c	2004-12-17 00:13:15 +09:00
+@@ -439,10 +439,7 @@
+ 	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+ 	restore_flags(flags);
+ 
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ 	func_exit ();
+ }
+ 
+@@ -582,10 +579,7 @@
+ 	if (!tty) return;
+ 
+ 	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ 	func_exit ();
+ }
+@@ -729,8 +723,8 @@
+ {
+ 	unsigned long flags;
+ 	struct gs_port *port;
+-
+-	func_enter ();
++	
++	func_enter();
+ 
+ 	if (!tty) return;
+ 
+@@ -803,8 +797,7 @@
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 
+ 	port->event = 0;
+===== drivers/char/serial_tx3912.c 1.6 vs 1.7 =====
+--- 1.6/drivers/char/serial_tx3912.c	2002-06-26 10:08:07 +09:00
++++ 1.7/drivers/char/serial_tx3912.c	2004-12-16 22:57:23 +09:00
+@@ -127,12 +127,9 @@
+ 	}
+ 	
+         if (port->gs.xmit_cnt <= port->gs.wakeup_chars) {
+-                if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-                    port->gs.tty->ldisc.write_wakeup)
+-                        (port->gs.tty->ldisc.write_wakeup)(port->gs.tty);
++		tty_wakeup(port->gs.tty);
+                 rs_dprintk(TX3912_UART_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n",
+                             port->gs.wakeup_chars); 
+-                wake_up_interruptible(&port->gs.tty->write_wait);
+        	}	
+ }
+ 
+===== drivers/s390/net/ctctty.c 1.5 vs 1.6 =====
+--- 1.5/drivers/s390/net/ctctty.c	2002-12-03 11:10:12 +09:00
++++ 1.6/drivers/s390/net/ctctty.c	2004-12-16 23:59:01 +09:00
+@@ -362,9 +362,8 @@
+ 
+ 		info->flags &= ~CTC_ASYNC_TX_LINESTAT;
+ 		if (tty) {
+-			if (wake && (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-			    tty->ldisc.write_wakeup)
+-				(tty->ldisc.write_wakeup)(tty);
++			if (wake)
++				tty_wakeup(tty);
+ 			wake_up_interruptible(&tty->write_wait);
+ 		}
+ 	}
+@@ -655,10 +654,7 @@
+ 	skb_queue_purge(&info->tx_queue);
+ 	info->lsr |= UART_LSR_TEMT;
+ 	restore_flags(flags);
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup) (tty);
++	tty_wakeup(tty);
+ }
+ 
+ static void
+@@ -1168,8 +1164,7 @@
+ 	ctc_tty_shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	spin_lock_irqsave(&ctc_tty_lock, saveflags);
+ 	info->tty = 0;
+ 	spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
+===== drivers/char/epca.c 1.10 vs 1.11 =====
+--- 1.10/drivers/char/epca.c	2003-02-20 01:15:53 +09:00
++++ 1.11/drivers/char/epca.c	2004-12-17 00:13:00 +09:00
+@@ -585,9 +585,7 @@
+ 		if (tty->driver.flush_buffer)
+ 			tty->driver.flush_buffer(tty);
+ 
+-		if (tty->ldisc.flush_buffer)
+-			tty->ldisc.flush_buffer(tty);
+-
++		tty_ldisc_flush(tty);
+ 		shutdown(ch);
+ 		tty->closing = 0;
+ 		ch->event = 0;
+@@ -692,15 +690,13 @@
+ 		cli();
+ 		if (tty->driver.flush_buffer)
+ 			tty->driver.flush_buffer(tty);
+-
+-		if (tty->ldisc.flush_buffer)
+-			tty->ldisc.flush_buffer(tty);
+-
++		
++		tty_ldisc_flush(tty);
++		
+ 		shutdown(ch);
+ 
+ 		if (ch->count)
+ 			MOD_DEC_USE_COUNT;
+-		
+ 
+ 		ch->tty   = NULL;
+ 		ch->event = 0;
+@@ -1175,9 +1171,7 @@
+ 	memoff(ch);
+ 	restore_flags(flags);
+ 
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ 
+ } /* End pc_flush_buffer */
+ 
+@@ -2383,10 +2377,7 @@
+ 				{ /* Begin if LOWWAIT */
+ 
+ 					ch->statusflags &= ~LOWWAIT;
+-					if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-						  tty->ldisc.write_wakeup)
+-						(tty->ldisc.write_wakeup)(tty);
+-					wake_up_interruptible(&tty->write_wait);
++					tty_wakeup(tty);
+ 
+ 				} /* End if LOWWAIT */
+ 
+@@ -2402,11 +2393,7 @@
+ 				{ /* Begin if EMPTYWAIT */
+ 
+ 					ch->statusflags &= ~EMPTYWAIT;
+-					if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-						  tty->ldisc.write_wakeup)
+-						(tty->ldisc.write_wakeup)(tty);
+-
+-					wake_up_interruptible(&tty->write_wait);
++					tty_wakeup(tty);
+ 
+ 				} /* End if EMPTYWAIT */
+ 
+@@ -3255,8 +3242,8 @@
+ 			}
+ 			else 
+ 			{
+-				if (tty->ldisc.flush_buffer)
+-					tty->ldisc.flush_buffer(tty);
++				/* ldisc lock already held in ioctl */
++				tty_ldisc_flush(tty);
+ 			}
+ 
+ 			/* Fall Thru */
+===== drivers/char/tty_io.c 1.32 vs 1.33 =====
+--- 1.32/drivers/char/tty_io.c	2004-10-22 05:55:41 +09:00
++++ 1.33/drivers/char/tty_io.c	2004-12-16 22:57:23 +09:00
+@@ -120,7 +120,6 @@
+ 
+ struct termios tty_std_termios;		/* for the benefit of tty drivers  */
+ struct tty_driver *tty_drivers;		/* linked list of tty drivers */
+-struct tty_ldisc ldiscs[NR_LDISCS];	/* line disc dispatch table	*/
+ 
+ #ifdef CONFIG_UNIX98_PTYS
+ extern struct tty_driver ptm_driver[];	/* Unix98 pty masters; for /dev/ptmx */
+@@ -260,63 +259,315 @@
+ 	return 0;
+ }
+ 
++/*
++ *     This is probably overkill for real world processors but
++ *     they are not on hot paths so a little discipline won't do
++ *     any harm.
++ */    
++
++static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
++{
++	down(&tty->termios_sem);
++	tty->termios->c_line = num;
++	up(&tty->termios_sem);
++}
++
++/*
++ *     This guards the refcounted line discipline lists. The lock
++ *     must be taken with irqs off because there are hangup path
++ *     callers who will do ldisc lookups and cannot sleep.
++ */
++
++spinlock_t tty_ldisc_lock = SPIN_LOCK_UNLOCKED;
++DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
++struct tty_ldisc tty_ldiscs[NR_LDISCS]; /* line disc dispatch table     */
++
+ int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
+ {
++
++	unsigned long flags;
++	int ret = 0;
++
+ 	if (disc < N_TTY || disc >= NR_LDISCS)
+ 		return -EINVAL;
+-	
++
++	spin_lock_irqsave(&tty_ldisc_lock, flags);
+ 	if (new_ldisc) {
+-		ldiscs[disc] = *new_ldisc;
+-		ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
+-		ldiscs[disc].num = disc;
+-	} else
+-		memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc));
++		tty_ldiscs[disc] = *new_ldisc;
++		tty_ldiscs[disc].num = disc;
++		tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
++		tty_ldiscs[disc].refcount = 0;
++	} else {
++		if(tty_ldiscs[disc].refcount)
++			ret = -EBUSY;
++		else
++			tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;
++        }
++	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ 	
+-	return 0;
++	return ret;
++
+ }
+ 
++
+ EXPORT_SYMBOL(tty_register_ldisc);
+ 
+-/* Set the discipline of a tty line. */
++struct tty_ldisc *tty_ldisc_get(int disc)
++{
++	unsigned long flags;
++	struct tty_ldisc *ld;
++
++	if (disc < N_TTY || disc >= NR_LDISCS)
++		return NULL;
++
++	spin_lock_irqsave(&tty_ldisc_lock, flags);
++
++	ld = &tty_ldiscs[disc];
++	/* Check the entry is defined */
++	if(ld->flags & LDISC_FLAG_DEFINED)
++		ld->refcount++;
++	else
++		ld = NULL;
++	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++	return ld;
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_get);
++
++void tty_ldisc_put(int disc)
++{
++	struct tty_ldisc *ld;
++	unsigned long flags;
++
++	if (disc < N_TTY || disc >= NR_LDISCS)
++		BUG();
++
++	spin_lock_irqsave(&tty_ldisc_lock, flags);
++	ld = &tty_ldiscs[disc];
++	if(ld->refcount <= 0)
++		BUG();
++	ld->refcount--;
++	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++}
++      
++EXPORT_SYMBOL_GPL(tty_ldisc_put);
++
++void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
++{
++	tty->ldisc = *ld;
++	tty->ldisc.refcount = 0;
++}
++
++/**
++ *     tty_ldisc_try           -       internal helper
++ *     @tty: the tty
++ *
++ *     Make a single attempt to grab and bump the refcount on
++ *     the tty ldisc. Return 0 on failure or 1 on success. This is
++ *     used to implement both the waiting and non waiting versions
++ *     of tty_ldisc_ref
++ */
++
++static int tty_ldisc_try(struct tty_struct *tty)
++{
++	unsigned long flags;
++	struct tty_ldisc *ld;
++	int ret = 0;
++
++	spin_lock_irqsave(&tty_ldisc_lock, flags);
++	ld = &tty->ldisc;
++	if(test_bit(TTY_LDISC, &tty->flags))
++	{
++		ld->refcount++;
++		ret = 1;
++	}
++	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++	return ret;
++}
++
++/**
++ *     tty_ldisc_ref_wait      -       wait for the tty ldisc
++ *     @tty: tty device
++ *
++ *     Dereference the line discipline for the terminal and take a
++ *     reference to it. If the line discipline is in flux then
++ *     wait patiently until it changes.
++ *
++ *     Note: Must not be called from an IRQ/timer context. The caller
++ *     must also be careful not to hold other locks that will deadlock
++ *     against a discipline change, such as an existing ldisc reference
++ *     (which we check for)
++ */
++
++struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
++{
++	/* wait_event is a macro */
++	wait_event(tty_ldisc_wait, tty_ldisc_try(tty));
++	return &tty->ldisc;
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
++
++/**
++ *     tty_ldisc_ref           -       get the tty ldisc
++ *     @tty: tty device
++ *
++ *     Dereference the line discipline for the terminal and take a
++ *     reference to it. If the line discipline is in flux then
++ *     return NULL. Can be called from IRQ and timer functions.
++ */
++
++struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
++{
++	if(tty_ldisc_try(tty))
++		return &tty->ldisc;
++	return NULL;
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_ref);
++
++
++void tty_ldisc_deref(struct tty_ldisc *ld)
++{
++
++	unsigned long flags;
++
++	if(ld == NULL)
++		BUG();
++
++	spin_lock_irqsave(&tty_ldisc_lock, flags);
++	if(ld->refcount == 0)
++		printk(KERN_EMERG "tty_ldisc_deref: no references.\n");
++	else
++		ld->refcount--;
++	if(ld->refcount == 0)
++		wake_up(&tty_ldisc_wait);
++	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_deref);
++
++/**
++  *     tty_ldisc_enable        -       allow ldisc use
++  *     @tty: terminal to activate ldisc on
++  *
++  *     Set the TTY_LDISC flag when the line discipline can be called
++  *     again. Do neccessary wakeups for existing sleepers.
++  *
++  *     Note: nobody should set this bit except via this function. Clearing
++  *     directly is allowed.
++  */
++
++static void tty_ldisc_enable(struct tty_struct *tty)
++{
++	set_bit(TTY_LDISC, &tty->flags);
++	wake_up(&tty_ldisc_wait);
++}
++
++/**
++ *     tty_set_ldisc           -       set line discipline
++ *     @tty: the terminal to set
++ *     @ldisc: the line discipline
++ *
++ *     Set the discipline of a tty line. Must be called from a process
++ *     context.
++ */
++
+ static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
+ {
+ 	int	retval = 0;
+ 	struct	tty_ldisc o_ldisc;
+ 	char buf[64];
++	int work;
++	unsigned long flags;
++	struct tty_ldisc *ld;
+ 
+ 	if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS))
+ 		return -EINVAL;
++
++restart:
++
++	if (tty->ldisc.num == ldisc)
++		return 0; /* We are already in the desired discipline */
++
++	ld = tty_ldisc_get(ldisc);
+ 	/* Eduardo Blanco <ejbs@cs.cs.com.uy> */
+ 	/* Cyrus Durgin <cider@speakeasy.org> */
+-	if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) {
++	if (ld == NULL)
++	{
+ 		char modname [20];
+-		sprintf(modname, "tty-ldisc-%d", ldisc);
+-		request_module (modname);
++                sprintf(modname, "tty-ldisc-%d", ldisc);
++                request_module (modname);
++		ld = tty_ldisc_get(ldisc);
+ 	}
+-	if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
++	
++	if (ld == NULL)
+ 		return -EINVAL;
+ 
+-	if (tty->ldisc.num == ldisc)
+-		return 0;	/* We are already in the desired discipline */
++	
+ 	o_ldisc = tty->ldisc;
+-
+ 	tty_wait_until_sent(tty, 0);
++
++	/*
++	 *      Make sure we don't change while someone holds a
++	 *      reference to the line discipline. The TTY_LDISC bit
++	 *      prevents anyone taking a reference once it is clear.
++	 *      We need the lock to avoid racing reference takers.
++	 */
++
++	spin_lock_irqsave(&tty_ldisc_lock, flags);
++	if(tty->ldisc.refcount)
++	{
++		/* Free the new ldisc we grabbed. Must drop the lock
++		   first. */
++		spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++		tty_ldisc_put(ldisc);
++		/*
++		 * There are several reasons we may be busy, including
++		 * random momentary I/O traffic. We must therefore
++		 * retry. We could distinguish between blocking ops
++		 * and retries if we made tty_ldisc_wait() smarter. That
++		 * is up for discussion.
++		 */
++		if(wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0)
++			return -ERESTARTSYS;
++		goto restart;
++	}
++	clear_bit(TTY_LDISC, &tty->flags);
++	clear_bit(TTY_DONT_FLIP, &tty->flags);
++	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++
++	/*
++	 *      From this point on we know nobody has an ldisc
++	 *      usage reference, nor can they obtain one until
++	 *      we say so later on.
++	 */
++
++	/*
++	 * Wait for ->hangup_work and ->flip.work handlers to terminate
++	 */
++	run_task_queue(&tq_timer);
++	flush_scheduled_tasks();
+ 	
+ 	/* Shutdown the current discipline. */
+ 	if (tty->ldisc.close)
+ 		(tty->ldisc.close)(tty);
+ 
+ 	/* Now set up the new line discipline. */
+-	tty->ldisc = ldiscs[ldisc];
+-	tty->termios->c_line = ldisc;
++	tty_ldisc_assign(tty, ld);
++	tty_set_termios_ldisc(tty, ldisc);
+ 	if (tty->ldisc.open)
+ 		retval = (tty->ldisc.open)(tty);
+ 	if (retval < 0) {
+-		tty->ldisc = o_ldisc;
+-		tty->termios->c_line = tty->ldisc.num;
++		tty_ldisc_put(ldisc);
++		/* There is an outstanding reference here so this is safe */
++		tty_ldisc_assign(tty, tty_ldisc_get(o_ldisc.num));
++		tty_set_termios_ldisc(tty, tty->ldisc.num);
+ 		if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) {
+-			tty->ldisc = ldiscs[N_TTY];
+-			tty->termios->c_line = N_TTY;
++			tty_ldisc_put(o_ldisc.num);
++			/* This driver is always present */
++			tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
++			tty_set_termios_ldisc(tty, N_TTY);
+ 			if (tty->ldisc.open) {
+ 				int r = tty->ldisc.open(tty);
+ 
+@@ -327,8 +578,21 @@
+ 			}
+ 		}
+ 	}
++	/* At this point we hold a reference to the new ldisc and a
++	    reference to the old ldisc. If we ended up flipping back
++	    to the existing ldisc we have two references to it */
++
+ 	if (tty->ldisc.num != o_ldisc.num && tty->driver.set_ldisc)
+ 		tty->driver.set_ldisc(tty);
++
++	tty_ldisc_put(o_ldisc.num);
++
++	/*
++	 *      Allow ldisc referencing to occur as soon as the driver
++	 *      ldisc callback completes.
++	 */
++	tty_ldisc_enable(tty);
++
+ 	return retval;
+ }
+ 
+@@ -430,11 +694,45 @@
+ 
+ static spinlock_t redirect_lock = SPIN_LOCK_UNLOCKED;
+ static struct file *redirect;
+-/*
+- * This can be called by the "eventd" kernel thread.  That is process synchronous,
+- * but doesn't hold any locks, so we need to make sure we have the appropriate
+- * locks for what we're doing..
+- */
++
++/**
++  *     tty_wakeup      -       request more data
++  *     @tty: terminal
++  *
++  *     Internal and external helper for wakeups of tty. This function
++  *     informs the line discipline if present that the driver is ready\
++  *     to receive more output data.
++  */
++
++void tty_wakeup(struct tty_struct *tty)
++{
++	struct tty_ldisc *ld;
++
++	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {
++		ld = tty_ldisc_ref(tty);
++		if(ld) {
++			if(ld->write_wakeup)
++				ld->write_wakeup(tty);
++			tty_ldisc_deref(ld);
++		}
++	}
++	wake_up_interruptible(&tty->write_wait);
++}
++
++EXPORT_SYMBOL_GPL(tty_wakeup);
++
++void tty_ldisc_flush(struct tty_struct *tty)
++{
++	struct tty_ldisc *ld = tty_ldisc_ref(tty);
++	if(ld) {
++		if(ld->flush_buffer)
++			ld->flush_buffer(tty);
++		tty_ldisc_deref(ld);
++	}
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_flush);
++
+ void do_tty_hangup(void *data)
+ {
+ 	struct tty_struct *tty = (struct tty_struct *) data;
+@@ -442,6 +740,7 @@
+ 	struct file *f = NULL;
+ 	struct task_struct *p;
+ 	struct list_head *l;
++	struct tty_ldisc *ld;
+ 	int    closecount = 0, n;
+ 
+ 	if (!tty)
+@@ -475,20 +774,22 @@
+ 	file_list_unlock();
+ 	
+ 	/* FIXME! What are the locking issues here? This may me overdoing things.. */
++	ld = tty_ldisc_ref(tty);
++	if(ld != NULL)	
+ 	{
+-		unsigned long flags;
+-
+-		save_flags(flags); cli();
+-		if (tty->ldisc.flush_buffer)
+-			tty->ldisc.flush_buffer(tty);
++		if (ld->flush_buffer)
++			ld->flush_buffer(tty);
+ 		if (tty->driver.flush_buffer)
+ 			tty->driver.flush_buffer(tty);
+-		if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		restore_flags(flags);
++		if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && ld->write_wakeup)
++			ld->write_wakeup(tty);
++		if (ld->hangup)
++			ld->hangup(tty);
+ 	}
+ 
++	/* FIXME: Once we trust the LDISC code better we can wait here for
++	   ldisc completion and fix the driver call race */
++
+ 	wake_up_interruptible(&tty->write_wait);
+ 	wake_up_interruptible(&tty->read_wait);
+ 
+@@ -496,21 +797,19 @@
+ 	 * Shutdown the current line discipline, and reset it to
+ 	 * N_TTY.
+ 	 */
++
+ 	if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
+-		*tty->termios = tty->driver.init_termios;
+-	if (tty->ldisc.num != ldiscs[N_TTY].num) {
+-		if (tty->ldisc.close)
+-			(tty->ldisc.close)(tty);
+-		tty->ldisc = ldiscs[N_TTY];
+-		tty->termios->c_line = N_TTY;
+-		if (tty->ldisc.open) {
+-			int i = (tty->ldisc.open)(tty);
+-			if (i < 0)
+-				printk(KERN_ERR "do_tty_hangup: N_TTY open: "
+-						"error %d\n", -i);
+-		}
++	{
++		down(&tty->termios_sem);
++                *tty->termios = tty->driver.init_termios;
++		up(&tty->termios_sem);
+ 	}
+-	
++
++	/* Defer ldisc switch */
++	/* tty_deferred_ldisc_switch(N_TTY)
++	   This should get done automatically when the port closes and
++	   tty_release is called */
++
+ 	read_lock(&tasklist_lock);
+  	for_each_task(p) {
+ 		if ((tty->session > 0) && (p->session == tty->session) &&
+@@ -541,6 +840,17 @@
+ 				tty->driver.close(tty, cons_filp);
+ 	} else if (tty->driver.hangup)
+ 		(tty->driver.hangup)(tty);
++
++	/* We don't want to have driver/ldisc interactions beyond
++	   the ones we did here. The driver layer expects no
++	   calls after ->hangup() from the ldisc side. However we
++	   can't yet guarantee all that */
++
++	set_bit(TTY_HUPPED, &tty->flags);
++	if(ld) {
++		tty_ldisc_enable(tty);
++		tty_ldisc_deref(ld);
++	}
+ 	unlock_kernel();
+ 	if (f)
+ 		fput(f);
+@@ -644,10 +954,8 @@
+ 	}
+ 	if (tty->driver.start)
+ 		(tty->driver.start)(tty);
+-	if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
+-	wake_up_interruptible(&tty->write_wait);
++	/* If we have a running line discipline it may need kicking */
++	tty_wakeup(tty);
+ }
+ 
+ static ssize_t tty_read(struct file * file, char * buf, size_t count, 
+@@ -656,6 +964,7 @@
+ 	int i;
+ 	struct tty_struct * tty;
+ 	struct inode *inode;
++	struct tty_ldisc *ld;
+ 
+ 	/* Can't seek (pread) on ttys.  */
+ 	if (ppos != &file->f_pos)
+@@ -684,11 +993,15 @@
+ 			return -ERESTARTSYS;
+ 		}
+ #endif
++	/* We want to wait for the line discipline to sort out in this
++	   situation */
++	ld = tty_ldisc_ref_wait(tty);
+ 	lock_kernel();
+-	if (tty->ldisc.read)
+-		i = (tty->ldisc.read)(tty,file,buf,count);
++	if (ld->read)
++		i = (ld->read)(tty,file,buf,count);
+ 	else
+ 		i = -EIO;
++	tty_ldisc_deref(ld);
+ 	unlock_kernel();
+ 	if (i > 0)
+ 		inode->i_atime = CURRENT_TIME;
+@@ -757,6 +1070,8 @@
+ 	int is_console;
+ 	struct tty_struct * tty;
+ 	struct inode *inode = file->f_dentry->d_inode;
++	ssize_t ret;
++	struct tty_ldisc *ld;
+ 
+ 	/* Can't seek (pwrite) on ttys.  */
+ 	if (ppos != &file->f_pos)
+@@ -803,13 +1118,19 @@
+ 		}
+ 	}
+ #endif
+-	if (!tty->ldisc.write)
+-		return -EIO;
+-	return do_tty_write(tty->ldisc.write, tty, file,
+-			    (const unsigned char *)buf, count);
++
++	ld = tty_ldisc_ref_wait(tty);
++	if (!ld->write)
++		ret = -EIO;
++	else
++		ret = do_tty_write(ld->write, tty, file,
++				(const unsigned char __user *)buf, count);
++	tty_ldisc_deref(ld);
++	return ret;
+ }
+ 
+-/* Semaphore to protect creating and releasing a tty */
++/* Semaphore to protect creating and releasing a tty. This is shared with
++   vt.c for deeply disgusting hack reasons */
+ static DECLARE_MUTEX(tty_sem);
+ 
+ static void down_tty_sem(int index)
+@@ -971,7 +1292,10 @@
+ 				(tty->ldisc.close)(tty);
+ 			goto release_mem_out;
+ 		}
++		set_bit(TTY_LDISC, &o_tty->flags);
++		tty_ldisc_enable(o_tty);
+ 	}
++	tty_ldisc_enable(tty);
+ 	goto success;
+ 
+ 	/*
+@@ -999,7 +1323,9 @@
+ 	}
+ 	tty->count++;
+ 	tty->driver = *driver; /* N.B. why do this every time?? */
+-
++	/* FIXME */
++	if(!test_bit(TTY_LDISC, &tty->flags))
++		printk(KERN_ERR "init_dev but no ldisc\n");
+ success:
+ 	*ret_tty = tty;
+ 	
+@@ -1080,6 +1406,7 @@
+ 	int	pty_master, tty_closing, o_tty_closing, do_sleep;
+ 	int	idx;
+ 	char	buf[64];
++	unsigned long flags;
+ 	
+ 	tty = (struct tty_struct *)filp->private_data;
+ 	if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "release_dev"))
+@@ -1272,24 +1599,59 @@
+ #endif
+ 
+ 	/*
++	 * Prevent flush_to_ldisc() from rescheduling the work for later.  Then
++	 * kill any delayed work. As this is the final close it does not
++	 * race with the set_ldisc code path.
++	 */
++	clear_bit(TTY_LDISC, &tty->flags);
++	clear_bit(TTY_DONT_FLIP, &tty->flags);
++
++	/*
++	 * Wait for ->hangup_work and ->flip.work handlers to terminate
++	 */
++
++	run_task_queue(&tq_timer);
++	flush_scheduled_tasks();
++
++	/*
++	 * Wait for any short term users (we know they are just driver
++         * side waiters as the file is closing so user count on the file
++         * side is zero.
++	 */
++
++	spin_lock_irqsave(&tty_ldisc_lock, flags);
++	while(tty->ldisc.refcount)
++	{
++		spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++		wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0);
++		spin_lock_irqsave(&tty_ldisc_lock, flags);
++	}
++	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++
++	/*
+ 	 * Shutdown the current line discipline, and reset it to N_TTY.
+ 	 * N.B. why reset ldisc when we're releasing the memory??
++	 * FIXME: this MUST get fixed for the new reflocking
+ 	 */
+ 	if (tty->ldisc.close)
+ 		(tty->ldisc.close)(tty);
+-	tty->ldisc = ldiscs[N_TTY];
+-	tty->termios->c_line = N_TTY;
++	tty_ldisc_put(tty->ldisc.num);
++
++	/*
++	 *      Switch the line discipline back
++	 */
++	tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
++	tty_set_termios_ldisc(tty,N_TTY); 
++
+ 	if (o_tty) {
++		/* FIXME: could o_tty be in setldisc here ? */
++		clear_bit(TTY_LDISC, &o_tty->flags);
+ 		if (o_tty->ldisc.close)
+ 			(o_tty->ldisc.close)(o_tty);
+-		o_tty->ldisc = ldiscs[N_TTY];
++		tty_ldisc_put(o_tty->ldisc.num);
++		tty_ldisc_assign(o_tty, tty_ldisc_get(N_TTY));
++		tty_set_termios_ldisc(o_tty,N_TTY);
+ 	}
+-	
+-	/*
+-	 * Make sure that the tty's task queue isn't activated. 
+-	 */
+-	run_task_queue(&tq_timer);
+-	flush_scheduled_tasks();
+ 
+ 	/* 
+ 	 * The release_mem function takes care of the details of clearing
+@@ -1464,14 +1826,18 @@
+ static unsigned int tty_poll(struct file * filp, poll_table * wait)
+ {
+ 	struct tty_struct * tty;
++	struct tty_ldisc *ld;
++	int ret = 0;
+ 
+ 	tty = (struct tty_struct *)filp->private_data;
+ 	if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "tty_poll"))
+ 		return 0;
+ 
+-	if (tty->ldisc.poll)
+-		return (tty->ldisc.poll)(tty, filp, wait);
+-	return 0;
++	ld = tty_ldisc_ref_wait(tty);
++	if (ld->poll)
++		ret = (ld->poll)(tty, filp, wait);
++	tty_ldisc_deref(ld);
++	return ret;
+ }
+ 
+ static int tty_fasync(int fd, struct file * filp, int on)
+@@ -1505,12 +1871,15 @@
+ static int tiocsti(struct tty_struct *tty, char * arg)
+ {
+ 	char ch, mbz = 0;
++	struct tty_ldisc *ld;
+ 
+ 	if ((current->tty != tty) && !suser())
+ 		return -EPERM;
+ 	if (get_user(ch, arg))
+ 		return -EFAULT;
+-	tty->ldisc.receive_buf(tty, &ch, &mbz, 1);
++	ld = tty_ldisc_ref_wait(tty);
++	ld->receive_buf(tty, &ch, &mbz, 1);
++	tty_ldisc_deref(ld);
+ 	return 0;
+ }
+ 
+@@ -1718,6 +2087,7 @@
+ {
+ 	struct tty_struct *tty, *real_tty;
+ 	int retval;
++	struct tty_ldisc *ld;
+ 	
+ 	tty = (struct tty_struct *)file->private_data;
+ 	if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
+@@ -1808,6 +2178,7 @@
+ 		case TIOCGSID:
+ 			return tiocgsid(tty, real_tty, (pid_t *) arg);
+ 		case TIOCGETD:
++			/* FIXME: check this is ok */
+ 			return put_user(tty->ldisc.num, (int *) arg);
+ 		case TIOCSETD:
+ 			return tiocsetd(tty, (int *) arg);
+@@ -1841,16 +2212,19 @@
+ 			return send_break(tty, arg ? arg*(HZ/10) : HZ/4);
+ 	}
+ 	if (tty->driver.ioctl) {
+-		int retval = (tty->driver.ioctl)(tty, file, cmd, arg);
++		retval = (tty->driver.ioctl)(tty, file, cmd, arg);
+ 		if (retval != -ENOIOCTLCMD)
+ 			return retval;
+ 	}
+-	if (tty->ldisc.ioctl) {
+-		int retval = (tty->ldisc.ioctl)(tty, file, cmd, arg);
+-		if (retval != -ENOIOCTLCMD)
+-			return retval;
++	ld = tty_ldisc_ref_wait(tty);
++	retval = -EINVAL;
++	if (ld->ioctl) {
++		retval = ld->ioctl(tty, file, cmd, arg);
++		if (retval == -ENOIOCTLCMD)
++			retval = -EINVAL;
+ 	}
+-	return -EINVAL;
++	tty_ldisc_deref(ld);
++	return retval;
+ }
+ 
+ 
+@@ -1883,14 +2257,20 @@
+ 	int session;
+ 	int		i;
+ 	struct file	*filp;
++	struct tty_ldisc *disc;
+ 	
+ 	if (!tty)
+ 		return;
+ 	session  = tty->session;
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	/* We don't want an ldisc switch during this */
++	disc = tty_ldisc_ref(tty);
++	if (disc && disc->flush_buffer)
++		disc->flush_buffer(tty);
++	tty_ldisc_deref(disc);
++
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
++
+ 	read_lock(&tasklist_lock);
+ 	for_each_task(p) {
+ 		if ((p->tty == tty) ||
+@@ -1942,11 +2322,16 @@
+ 	unsigned char	*cp;
+ 	char		*fp;
+ 	int		count;
+-	unsigned long flags;
++	unsigned long 	flags;
++	struct tty_ldisc *disc;
++
++	disc = tty_ldisc_ref(tty);
++	if (disc == NULL)       /*  !TTY_LDISC */
++		return;
+ 
+ 	if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
+ 		queue_task(&tty->flip.tqueue, &tq_timer);
+-		return;
++		goto out;
+ 	}
+ 	if (tty->flip.buf_num) {
+ 		cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
+@@ -1969,7 +2354,31 @@
+ 	tty->flip.count = 0;
+ 	restore_flags(flags);
+ 	
+-	tty->ldisc.receive_buf(tty, cp, fp, count);
++	disc->receive_buf(tty, cp, fp, count);
++out:
++	tty_ldisc_deref(disc);
++}
++
++/*
++ *     Call the ldisc flush directly from a driver. This function may
++ *     return an error and need retrying by the user.
++ */
++
++int tty_push_data(struct tty_struct *tty, unsigned char *cp, unsigned char *fp, int count)
++{
++	int ret = 0;
++	struct tty_ldisc *disc;
++	
++	disc = tty_ldisc_ref(tty);
++	if(test_bit(TTY_DONT_FLIP, &tty->flags))
++		ret = -EAGAIN;
++	else if(disc == NULL)
++		ret = -EIO;
++	else
++		disc->receive_buf(tty, cp, fp, count);
++	tty_ldisc_deref(disc);
++	return ret;
++
+ }
+ 
+ /*
+@@ -2032,13 +2441,14 @@
+ {
+ 	memset(tty, 0, sizeof(struct tty_struct));
+ 	tty->magic = TTY_MAGIC;
+-	tty->ldisc = ldiscs[N_TTY];
++	tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
+ 	tty->pgrp = -1;
+ 	tty->flip.char_buf_ptr = tty->flip.char_buf;
+ 	tty->flip.flag_buf_ptr = tty->flip.flag_buf;
+ 	tty->flip.tqueue.routine = flush_to_ldisc;
+ 	tty->flip.tqueue.data = tty;
+ 	init_MUTEX(&tty->flip.pty_sem);
++	init_MUTEX(&tty->termios_sem);
+ 	init_waitqueue_head(&tty->write_wait);
+ 	init_waitqueue_head(&tty->read_wait);
+ 	tty->tq_hangup.routine = do_tty_hangup;
+@@ -2217,7 +2627,7 @@
+ void __init console_init(void)
+ {
+ 	/* Setup the default TTY line discipline. */
+-	memset(ldiscs, 0, sizeof(ldiscs));
++	memset(tty_ldiscs, 0, NR_LDISCS*sizeof(struct tty_ldisc));
+ 	(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
+ 
+ 	/*
+===== drivers/char/pcxx.c 1.6 vs 1.7 =====
+--- 1.6/drivers/char/pcxx.c	2002-08-06 23:42:07 +09:00
++++ 1.7/drivers/char/pcxx.c	2004-12-16 22:57:23 +09:00
+@@ -619,28 +619,11 @@
+ 	
+ 		if(tty->driver.flush_buffer)
+ 			tty->driver.flush_buffer(tty);
+-		if(tty->ldisc.flush_buffer)
+-			tty->ldisc.flush_buffer(tty);
++		tty_ldisc_flush(tty);
+ 		shutdown(info);
+ 		tty->closing = 0;
+ 		info->event = 0;
+ 		info->tty = NULL;
+-#ifndef MODULE
+-/* ldiscs[] is not available in a MODULE
+-** worth noting that while I'm not sure what this hunk of code is supposed
+-** to do, it is not present in the serial.c driver.  Hmmm.  If you know,
+-** please send me a note.  brian@ilinx.com
+-** Don't know either what this is supposed to do christoph@lameter.com.
+-*/
+-		if(tty->ldisc.num != ldiscs[N_TTY].num) {
+-			if(tty->ldisc.close)
+-				(tty->ldisc.close)(tty);
+-			tty->ldisc = ldiscs[N_TTY];
+-			tty->termios->c_line = N_TTY;
+-			if(tty->ldisc.open)
+-				(tty->ldisc.open)(tty);
+-		}
+-#endif
+ 		if(info->blocked_open) {
+ 			if(info->close_delay) {
+ 				current->state = TASK_INTERRUPTIBLE;
+@@ -883,9 +866,7 @@
+ 	memoff(ch);
+ 	restore_flags(flags);
+ 
+-	wake_up_interruptible(&tty->write_wait);
+-	if((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ static void pcxe_flush_chars(struct tty_struct *tty)
+@@ -1793,10 +1774,7 @@
+ 			if (event & LOWTX_IND) {
+ 				if (ch->statusflags & LOWWAIT) {
+ 					ch->statusflags &= ~LOWWAIT;
+-					if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-						tty->ldisc.write_wakeup)
+-						(tty->ldisc.write_wakeup)(tty);
+-					wake_up_interruptible(&tty->write_wait);
++					tty_wakeup(tty);
+ 				}
+ 			}
+ 
+@@ -1804,10 +1782,7 @@
+ 				ch->statusflags &= ~TXBUSY;
+ 				if (ch->statusflags & EMPTYWAIT) {
+ 					ch->statusflags &= ~EMPTYWAIT;
+-					if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-						tty->ldisc.write_wakeup)
+-						(tty->ldisc.write_wakeup)(tty);
+-					wake_up_interruptible(&tty->write_wait);
++					tty_wakeup(tty);
+ 				}
+ 			}
+ 		}
+@@ -2254,8 +2229,7 @@
+ 				tty_wait_until_sent(tty, 0);
+ 			}
+ 			else {
+-				if(tty->ldisc.flush_buffer)
+-					tty->ldisc.flush_buffer(tty);
++				tty_ldisc_flush(tty);
+ 			}
+ 
+ 			/* Fall Thru */
+===== drivers/net/wan/8253x/8253xtty.c 1.2 vs 1.3 =====
+--- 1.2/drivers/net/wan/8253x/8253xtty.c	2003-02-12 03:04:34 +09:00
++++ 1.3/drivers/net/wan/8253x/8253xtty.c	2004-12-17 00:01:04 +09:00
+@@ -691,10 +691,7 @@
+ 	port->DoingInterrupt = 1;
+ 	if (test_and_clear_bit(SAB8253X_EVENT_WRITE_WAKEUP, &port->event)) 
+ 	{
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait); /* in case tty driver waiting on write */
++		tty_wakeup(tty);
+ 	}
+ 	port->DoingInterrupt = 0;
+ }
+@@ -2001,10 +1998,7 @@
+ 	{
+ 		tty->driver.flush_buffer(tty);
+ 	}
+-	if (tty->ldisc.flush_buffer)
+-	{
+-		tty->ldisc.flush_buffer(tty);
+-	}
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	port->event = 0;
+ 	port->tty = 0;
+===== drivers/char/istallion.c 1.10 vs 1.11 =====
+--- 1.10/drivers/char/istallion.c	2004-07-13 09:39:43 +09:00
++++ 1.11/drivers/char/istallion.c	2004-12-16 23:35:07 +09:00
+@@ -1214,8 +1214,7 @@
+ 	clear_bit(ST_TXBUSY, &portp->state);
+ 	clear_bit(ST_RXSTOP, &portp->state);
+ 	set_bit(TTY_IO_ERROR, &tty->flags);
+-	if (tty->ldisc.flush_buffer)
+-		(tty->ldisc.flush_buffer)(tty);
++	tty_ldisc_flush(tty);
+ 	set_bit(ST_DOFLUSHRX, &portp->state);
+ 	stli_flushbuffer(tty);
+ 
+@@ -2477,10 +2476,7 @@
+ 	}
+ 	restore_flags(flags);
+ 
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*****************************************************************************/
+@@ -2915,6 +2911,7 @@
+ 	asynotify_t		nt;
+ 	unsigned long		oldsigs;
+ 	int			rc, donerx;
++	struct tty_ldisc	*ld;
+ 
+ #if DEBUG
+ 	printk(KERN_DEBUG "stli_hostcmd(brdp=%x,channr=%d)\n",
+@@ -3014,10 +3011,15 @@
+ 			clear_bit(ST_TXBUSY, &portp->state);
+ 		if (nt.data & (DT_TXEMPTY | DT_TXLOW)) {
+ 			if (tty != (struct tty_struct *) NULL) {
+-				if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-				    tty->ldisc.write_wakeup) {
+-					(tty->ldisc.write_wakeup)(tty);
+-					EBRDENABLE(brdp);
++				if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))) {
++					ld = tty_ldisc_ref(tty);
++					if(ld) {
++						if(ld->write_wakeup) {
++							ld->write_wakeup(tty);
++							EBRDENABLE(brdp);
++						}
++						tty_ldisc_deref(ld);
++					}
+ 				}
+ 				wake_up_interruptible(&tty->write_wait);
+ 			}
+===== drivers/usb/acm.c 1.16 vs 1.17 =====
+--- 1.16/drivers/usb/acm.c	2003-09-06 00:45:49 +09:00
++++ 1.17/drivers/usb/acm.c	2004-12-16 23:55:33 +09:00
+@@ -285,10 +285,7 @@
+ 
+ 	if (!ACM_READY(acm)) return;
+ 
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
+-
+-	wake_up_interruptible(&tty->write_wait);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+===== drivers/net/wan/8253x/8253xsyn.c 1.1 vs 1.2 =====
+--- 1.1/drivers/net/wan/8253x/8253xsyn.c	2002-04-05 04:05:10 +09:00
++++ 1.2/drivers/net/wan/8253x/8253xsyn.c	2004-12-17 00:00:20 +09:00
+@@ -1108,10 +1108,7 @@
+ 	{
+ 		tty->driver.flush_buffer(tty);
+ 	}
+-	if (tty->ldisc.flush_buffer)
+-	{
+-		tty->ldisc.flush_buffer(tty);
+-	}
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	port->event = 0;
+ 	port->tty = 0;
+===== drivers/usb/serial/keyspan_pda.c 1.11 vs 1.12 =====
+--- 1.11/drivers/usb/serial/keyspan_pda.c	2003-08-28 20:13:49 +09:00
++++ 1.12/drivers/usb/serial/keyspan_pda.c	2004-12-16 22:57:23 +09:00
+@@ -183,13 +183,7 @@
+ 	wake_up_interruptible( &port->write_wait );
+ 
+ 	/* wake up line discipline */
+-	if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+-	&& tty->ldisc.write_wakeup )
+-		(tty->ldisc.write_wakeup)(tty);
+-
+-	/* wake up other tty processes */
+-	wake_up_interruptible( &tty->write_wait );
+-	/* For 2.2.16 backport -- wake_up_interruptible( &tty->poll_wait ); */
++	tty_wakeup(tty);
+ }
+ 
+ static void keyspan_pda_request_unthrottle( struct usb_serial *serial )
+===== drivers/usb/serial/mct_u232.c 1.20 vs 1.21 =====
+--- 1.20/drivers/usb/serial/mct_u232.c	2004-06-13 06:24:26 +09:00
++++ 1.21/drivers/usb/serial/mct_u232.c	2004-12-16 22:57:23 +09:00
+@@ -569,11 +569,7 @@
+ 
+ 	if (write_blocking) {
+ 		wake_up_interruptible(&port->write_wait);
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
+-		
++		tty_wakeup(tty);
+ 	} else {
+ 		/* from generic_write_bulk_callback */
+ 		queue_task(&port->tqueue, &tq_immediate);
+===== arch/cris/drivers/serial.c 1.16 vs 1.17 =====
+--- 1.16/arch/cris/drivers/serial.c	2003-08-30 04:32:50 +09:00
++++ 1.17/arch/cris/drivers/serial.c	2004-12-16 23:23:53 +09:00
+@@ -3324,10 +3324,7 @@
+ 		return;
+ 	
+ 	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ }
+ 
+@@ -3952,11 +3949,7 @@
+ 	info->xmit.head = info->xmit.tail = 0;
+ 	restore_flags(flags);
+ 
+-	wake_up_interruptible(&tty->write_wait);
+-
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -4578,8 +4571,7 @@
+ 	shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+===== drivers/char/mxser.c 1.12 vs 1.13 =====
+--- 1.12/drivers/char/mxser.c	2004-08-17 03:17:23 +09:00
++++ 1.13/drivers/char/mxser.c	2004-12-17 00:14:49 +09:00
+@@ -725,10 +725,7 @@
+ 	tty = info->tty;
+ 	if (tty) {
+ 		if (test_and_clear_bit(MXSER_EVENT_TXLOW, &info->event)) {
+-			if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-			    tty->ldisc.write_wakeup)
+-				(tty->ldisc.write_wakeup) (tty);
+-			wake_up_interruptible(&tty->write_wait);
++			tty_wakeup(tty);
+ 		}
+ 		if (test_and_clear_bit(MXSER_EVENT_HANGUP, &info->event)) {
+ 			tty_hangup(tty);	/* FIXME: module removal race here - AKPM */
+@@ -890,8 +887,8 @@
+ 	mxser_shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
++
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+@@ -1050,10 +1047,7 @@
+ 	cli();
+ 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ 	restore_flags(flags);
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup) (tty);
++	tty_wakeup(tty);
+ }
+ 
+ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
+===== drivers/char/rocket.c 1.10 vs 1.11 =====
+--- 1.10/drivers/char/rocket.c	2003-09-12 00:22:06 +09:00
++++ 1.11/drivers/char/rocket.c	2004-12-16 22:57:23 +09:00
+@@ -241,9 +241,12 @@
+ 				   CHANNEL_t *cp, unsigned int ChanStatus)
+ {
+ 	unsigned int CharNStat;
+-	int ToRecv, wRecv, space, count;
++	int ToRecv, wRecv, space = 0, count;
+ 	unsigned char	*cbuf;
+ 	char		*fbuf;
++	struct tty_ldisc *ld;
++
++	ld = tty_ldisc_ref(tty);
+ 	
+ 	ToRecv= sGetRxCnt(cp);
+ 	space = 2*TTY_FLIPBUF_SIZE;
+@@ -348,8 +351,8 @@
+ 		fbuf += ToRecv;
+ 		count += ToRecv;
+ 	}
+-	tty->ldisc.receive_buf(tty, tty->flip.char_buf,
+-			       tty->flip.flag_buf, count);
++	ld->receive_buf(tty, tty->flip.char_buf, tty->flip.flag_buf, count);
++	tty_ldisc_deref(ld);
+ }
+ 
+ /*
+@@ -400,10 +403,7 @@
+ 	if (info->xmit_cnt == 0)
+ 		xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f));
+ 	if (info->xmit_cnt < WAKEUP_CHARS) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ #ifdef ROCKET_DEBUG_INTR
+ 	printk("(%d,%d,%d,%d)...", info->xmit_cnt, info->xmit_head,
+@@ -1128,8 +1128,7 @@
+ 	}
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 
+ 	xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f));
+ 	if (info->blocked_open) {
+@@ -1806,10 +1805,7 @@
+ 	restore_flags(flags);
+ end:
+ 	if (info->xmit_cnt < WAKEUP_CHARS) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ 	return retval;
+ }
+@@ -1868,9 +1864,7 @@
+ 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ 	sti();
+ 	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ 	
+ 	cp = &info->channel;
+ 	
+===== drivers/s390/char/con3215.c 1.7 vs 1.8 =====
+--- 1.7/drivers/s390/char/con3215.c	2003-06-27 20:39:05 +09:00
++++ 1.8/drivers/s390/char/con3215.c	2004-12-16 22:57:23 +09:00
+@@ -363,10 +363,7 @@
+ 	tty = raw->tty;
+ 	if (tty != NULL &&
+ 	    RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ }
+ 
+@@ -978,10 +975,7 @@
+ 
+ 	raw = (raw3215_info *) tty->driver_data;
+ 	raw3215_flush_buffer(raw);
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+===== drivers/char/vt.c 1.14 vs 1.15 =====
+--- 1.14/drivers/char/vt.c	2004-01-11 02:47:12 +09:00
++++ 1.15/drivers/char/vt.c	2004-12-16 22:57:23 +09:00
+@@ -588,8 +588,7 @@
+ 		  default:
+ 			return -EINVAL;
+ 		}
+-		if (tty->ldisc.flush_buffer)
+-			tty->ldisc.flush_buffer(tty);
++		tty_ldisc_flush(tty);
+ 		return 0;
+ 
+ 	case KDGKBMODE:
+===== drivers/net/strip.c 1.5 vs 1.6 =====
+--- 1.5/drivers/net/strip.c	2002-02-05 16:55:07 +09:00
++++ 1.6/drivers/net/strip.c	2004-12-17 00:02:33 +09:00
+@@ -2706,8 +2706,7 @@
+     tty->disc_data = strip_info;
+     if (tty->driver.flush_buffer)
+         tty->driver.flush_buffer(tty);
+-    if (tty->ldisc.flush_buffer)
+-        tty->ldisc.flush_buffer(tty);
++    tty_ldisc_flush(tty);
+ 
+     /*
+      * Restore default settings
+===== drivers/isdn/isdn_tty.c 1.9 vs 1.10 =====
+--- 1.9/drivers/isdn/isdn_tty.c	2002-02-05 23:07:05 +09:00
++++ 1.10/drivers/isdn/isdn_tty.c	2004-12-17 00:05:22 +09:00
+@@ -321,10 +321,7 @@
+ 		info->send_outstanding++;
+ 		info->msr &= ~UART_MSR_CTS;
+ 		info->lsr &= ~UART_LSR_TEMT;
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup) (tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 		return;
+ 	}
+ 	if (slen < 0) {
+@@ -1214,10 +1211,7 @@
+ 						/* If DLE decoding results in zero-transmit, but
+ 						 * c originally was non-zero, do a wakeup.
+ 						 */
+-						if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-						 tty->ldisc.write_wakeup)
+-							(tty->ldisc.write_wakeup) (tty);
+-						wake_up_interruptible(&tty->write_wait);
++						tty_wakeup(tty);
+ 						info->msr |= UART_MSR_CTS;
+ 						info->lsr |= UART_LSR_TEMT;
+ 					}
+@@ -1335,10 +1329,7 @@
+ 	isdn_tty_cleanup_xmit(info);
+ 	info->xmit_count = 0;
+ 	restore_flags(flags);
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup) (tty);
++	tty_wakeup(tty);
+ }
+ 
+ static void
+@@ -1867,8 +1858,7 @@
+ 	isdn_tty_shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	info->tty = 0;
+ 	info->ncarrier = 0;
+ 	tty->closing = 0;
+@@ -2791,8 +2781,7 @@
+ 			restore_flags(flags);
+ 			return;
+ 		}
+-		if (info->tty->ldisc.flush_buffer)
+-			info->tty->ldisc.flush_buffer(info->tty);
++		tty_ldisc_flush(info->tty);
+ 		if ((info->flags & ISDN_ASYNC_CHECK_CD) &&
+ 		    (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+ 		       (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) {
+===== arch/ppc/8xx_io/uart.c 1.23 vs 1.24 =====
+--- 1.23/arch/ppc/8xx_io/uart.c	2003-10-01 08:26:08 +09:00
++++ 1.24/arch/ppc/8xx_io/uart.c	2004-12-16 23:22:28 +09:00
+@@ -738,10 +738,7 @@
+ 		return;
+ 
+ 	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ }
+ 
+@@ -1209,10 +1206,7 @@
+ 	/* There is nothing to "flush", whatever we gave the CPM
+ 	 * is on its way out.
+ 	 */
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ 	info->flags &= ~TX_WAKEUP;
+ }
+ 
+@@ -1791,8 +1785,7 @@
+ 	shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+===== drivers/net/ppp_async.c 1.8 vs 1.9 =====
+--- 1.8/drivers/net/ppp_async.c	2002-03-11 23:13:28 +09:00
++++ 1.9/drivers/net/ppp_async.c	2004-12-16 22:57:23 +09:00
+@@ -117,6 +117,9 @@
+  * frees the memory that ppp_asynctty_receive is using.  The best
+  * way to fix this is to use a rwlock in the tty struct, but for now
+  * we use a single global rwlock for all ttys in ppp line discipline.
++ *
++ * FIXME: this is no longer true. The _close path for the ldisc is 
++ * now guaranteed to be sane. 
+  */
+ static rwlock_t disc_data_lock = RW_LOCK_UNLOCKED;
+ 
+@@ -139,7 +142,8 @@
+ }
+ 
+ /*
+- * Called when a tty is put into PPP line discipline.
++ * Called when a tty is put into PPP line discipline. Called in process
++ * context.
+  */
+ static int
+ ppp_asynctty_open(struct tty_struct *tty)
+@@ -227,6 +231,18 @@
+ }
+ 
+ /*
++ * Called on tty hangup in process context.
++ *
++ * Wait for I/O to driver to complete and unregister PPP channel.
++ * This is already done by the close routine, so just call that.
++ */
++static int ppp_asynctty_hangup(struct tty_struct *tty)
++{
++	ppp_asynctty_close(tty);
++	return 0;
++}
++
++/*
+  * Read does nothing - no data is ever available this way.
+  * Pppd reads and writes packets via /dev/ppp instead.
+  */
+@@ -248,6 +264,11 @@
+ 	return -EAGAIN;
+ }
+ 
++/*
++ * Called in process context only. May be re-entered by multiple
++ * ioctl calling threads.
++ */
++ 
+ static int
+ ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
+ 		   unsigned int cmd, unsigned long arg)
+@@ -359,6 +380,7 @@
+ 	write:	ppp_asynctty_write,
+ 	ioctl:	ppp_asynctty_ioctl,
+ 	poll:	ppp_asynctty_poll,
++	hangup: ppp_asynctty_hangup,
+ 	receive_room: ppp_asynctty_room,
+ 	receive_buf: ppp_asynctty_receive,
+ 	write_wakeup: ppp_asynctty_wakeup,
+@@ -714,7 +736,8 @@
+ 
+ /*
+  * Flush output from our internal buffers.
+- * Called for the TCFLSH ioctl.
++ * Called for the TCFLSH ioctl. Can be entered in parallel
++ * but this is covered by the xmit_lock.
+  */
+ static void
+ ppp_async_flush_output(struct asyncppp *ap)
+@@ -819,7 +842,9 @@
+ 	ppp_input_error(&ap->chan, code);
+ }
+ 
+-/* called when the tty driver has data for us. */
++/* Called when the tty driver has data for us. Runs parallel with the
++   other ldisc functions but will not be re-entered */
++
+ static void
+ ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
+ 		char *flags, int count)
+===== drivers/s390/char/tubtty.c 1.7 vs 1.8 =====
+--- 1.7/drivers/s390/char/tubtty.c	2002-10-07 23:42:10 +09:00
++++ 1.8/drivers/s390/char/tubtty.c	2004-12-16 23:56:31 +09:00
+@@ -445,10 +445,7 @@
+ 		ob->bc_cnt = 0;
+ 		TUBUNLOCK(tubp->irq, flags);
+ 	}
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ static int
+@@ -646,10 +643,7 @@
+ 	}
+ 
+ 	if (tty != NULL) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup != NULL)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ do_unlock:
+ 	TUBUNLOCK(tubp->irq, flags);
+===== drivers/tc/zs.c 1.7 vs 1.8 =====
+--- 1.7/drivers/tc/zs.c	2004-01-21 00:13:29 +09:00
++++ 1.8/drivers/tc/zs.c	2004-12-16 22:57:23 +09:00
+@@ -695,10 +695,7 @@
+ 		return;
+ 
+ 	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ }
+ 
+@@ -1020,10 +1017,7 @@
+ 	cli();
+ 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ 	sti();
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1429,8 +1423,7 @@
+ 	shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+===== drivers/bluetooth/hci_ldisc.c 1.7 vs 1.8 =====
+--- 1.7/drivers/bluetooth/hci_ldisc.c	2004-06-20 20:50:15 +09:00
++++ 1.8/drivers/bluetooth/hci_ldisc.c	2004-12-17 00:03:36 +09:00
+@@ -188,9 +188,7 @@
+ 		kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
+ 	}
+ 
+-	/* Flush any pending characters in the driver and discipline. */
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+@@ -283,9 +281,10 @@
+ 
+ 	spin_lock_init(&hu->rx_lock);
+ 
+-	/* Flush any pending characters in the driver and line discipline */
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	/* Flush any pending characters in the driver and line discipline. */
++	/* FIXME: why is this needed. Note don't use ldisc_ref here as the
++	   open path is before the ldisc is referencable */
++	tty_ldisc_flush(tty);
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+===== drivers/char/specialix.c 1.5 vs 1.6 =====
+--- 1.5/drivers/char/specialix.c	2002-05-31 07:24:01 +09:00
++++ 1.6/drivers/char/specialix.c	2004-12-17 00:28:34 +09:00
+@@ -1579,8 +1579,7 @@
+ 	sx_shutdown_port(bp, port);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	port->event = 0;
+ 	port->tty = 0;
+@@ -1758,10 +1757,7 @@
+ 	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+ 	restore_flags(flags);
+ 	
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ 
+@@ -2211,12 +2207,8 @@
+ 	if(!(tty = port->tty)) 
+ 		return;
+ 
+-	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
+-	}
++	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) 
++		tty_wakeup(tty);
+ }
+ 
+ 
+===== drivers/char/moxa.c 1.7 vs 1.8 =====
+--- 1.7/drivers/char/moxa.c	2002-02-05 16:53:47 +09:00
++++ 1.8/drivers/char/moxa.c	2004-12-17 00:14:38 +09:00
+@@ -677,8 +677,8 @@
+ 
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
++
+ 	tty->closing = 0;
+ 	ch->event = 0;
+ 	ch->tty = 0;
+@@ -754,10 +754,7 @@
+ 	if (ch == NULL)
+ 		return;
+ 	MoxaPortFlushData(ch->port, 1);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup) (tty);
+-	wake_up_interruptible(&tty->write_wait);
++	tty_wakeup(tty);
+ }
+ 
+ static int moxa_chars_in_buffer(struct tty_struct *tty)
+@@ -1011,10 +1008,7 @@
+ 				if (MoxaPortTxQueue(ch->port) <= WAKEUP_CHARS) {
+ 					if (!tp->stopped) {
+ 						ch->statusflags &= ~LOWWAIT;
+-						if ((tp->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-						  tp->ldisc.write_wakeup)
+-							(tp->ldisc.write_wakeup) (tp);
+-						wake_up_interruptible(&tp->write_wait);
++						tty_wakeup(tp);
+ 					}
+ 				}
+ 			}
+@@ -1203,10 +1197,7 @@
+ 	if (ch->tty && (ch->statusflags & EMPTYWAIT)) {
+ 		if (MoxaPortTxQueue(ch->port) == 0) {
+ 			ch->statusflags &= ~EMPTYWAIT;
+-			if ((ch->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-			    ch->tty->ldisc.write_wakeup)
+-				(ch->tty->ldisc.write_wakeup) (ch->tty);
+-			wake_up_interruptible(&ch->tty->write_wait);
++			tty_wakeup(ch->tty);
+ 			return;
+ 		}
+ 		moxaEmptyTimer[ch->port].expires = jiffies + HZ;
+===== drivers/char/n_r3964.c 1.8 vs 1.9 =====
+--- 1.8/drivers/char/n_r3964.c	2002-12-31 02:26:52 +09:00
++++ 1.9/drivers/char/n_r3964.c	2004-12-16 22:57:23 +09:00
+@@ -158,7 +158,8 @@
+         r3964_write,           /* write */
+         r3964_ioctl,           /* ioctl */
+         r3964_set_termios,     /* set_termios */
+-        r3964_poll,            /* poll */            
++        r3964_poll,            /* poll */
++        NULL,                  /* hangup */
+         r3964_receive_buf,     /* receive_buf */
+         r3964_receive_room,    /* receive_room */
+         0                      /* write_wakeup */
+===== drivers/char/sh-sci.c 1.14 vs 1.15 =====
+--- 1.14/drivers/char/sh-sci.c	2003-09-20 03:05:52 +09:00
++++ 1.15/drivers/char/sh-sci.c	2004-12-16 23:37:32 +09:00
+@@ -1021,10 +1021,7 @@
+ 		return;
+ 
+ 	if (test_and_clear_bit(SCI_EVENT_WRITE_WAKEUP, &port->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ }
+ 
+===== drivers/char/selection.c 1.3 vs 1.4 =====
+--- 1.3/drivers/char/selection.c	2002-02-05 16:44:55 +09:00
++++ 1.4/drivers/char/selection.c	2004-12-17 00:23:54 +09:00
+@@ -290,9 +290,11 @@
+ {
+ 	struct vt_struct *vt = (struct vt_struct *) tty->driver_data;
+ 	int	pasted = 0, count;
++	struct  tty_ldisc *ld;
+ 	DECLARE_WAITQUEUE(wait, current);
+ 
+ 	poke_blanked_console();
++	ld = tty_ldisc_ref_wait(tty);
+ 	add_wait_queue(&vt->paste_wait, &wait);
+ 	while (sel_buffer && sel_buffer_lth > pasted) {
+ 		set_current_state(TASK_INTERRUPTIBLE);
+@@ -301,12 +303,14 @@
+ 			continue;
+ 		}
+ 		count = sel_buffer_lth - pasted;
+-		count = MIN(count, tty->ldisc.receive_room(tty));
+-		tty->ldisc.receive_buf(tty, sel_buffer + pasted, 0, count);
++		count = MIN(count, ld->receive_room(tty));
++		ld->receive_buf(tty, sel_buffer + pasted, 0, count);
+ 		pasted += count;
+ 	}
+ 	remove_wait_queue(&vt->paste_wait, &wait);
+ 	current->state = TASK_RUNNING;
++
++	tty_ldisc_deref(ld);
+ 	return 0;
+ }
+ 
+===== drivers/usb/serial/io_ti.c 1.8 vs 1.9 =====
+--- 1.8/drivers/usb/serial/io_ti.c	2003-08-28 20:13:49 +09:00
++++ 1.9/drivers/usb/serial/io_ti.c	2004-12-16 22:57:23 +09:00
+@@ -1800,12 +1800,7 @@
+ 	tty = port->tty;
+ 	if (tty) {
+ 		/* let the tty driver wakeup if it has a special write_wakeup function */
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+-			(tty->ldisc.write_wakeup)(tty);
+-		}
+-
+-		/* tell the tty driver that something has changed */
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ }
+ 
+===== drivers/char/ip2/i2lib.c 1.4 vs 1.5 =====
+--- 1.4/drivers/char/ip2/i2lib.c	2002-02-28 22:57:21 +09:00
++++ 1.5/drivers/char/ip2/i2lib.c	2004-12-16 23:44:31 +09:00
+@@ -1385,15 +1385,9 @@
+ 	ip2trace (CHANN, ITRC_SICMD, 10, 2, tp->flags,
+ 			(1 << TTY_DO_WRITE_WAKEUP) );
+ 
+-	wake_up_interruptible ( &tp->write_wait );
+-	if ( ( tp->flags & (1 << TTY_DO_WRITE_WAKEUP) ) 
+-	  && tp->ldisc.write_wakeup )
+-	{
+-		(tp->ldisc.write_wakeup) ( tp );
++	tty_wakeup(tp);
++	ip2trace (CHANN, ITRC_SICMD, 11, 0 );
+ 
+-		ip2trace (CHANN, ITRC_SICMD, 11, 0 );
+-
+-	}
+ }
+ 
+ static inline void
+===== include/linux/tty_ldisc.h 1.2 vs 1.3 =====
+--- 1.2/include/linux/tty_ldisc.h	2002-02-05 16:39:25 +09:00
++++ 1.3/include/linux/tty_ldisc.h	2004-12-16 22:57:23 +09:00
+@@ -95,6 +95,13 @@
+  * 	that line discpline should try to send more characters to the
+  * 	low-level driver for transmission.  If the line discpline does
+  * 	not have any more data to send, it can just return.
++ *
++ * int (*hangup)(struct tty_struct *)
++ *
++ *	Called on a hangup. Tells the discipline that it should
++ *	cease I/O to the tty driver. Can sleep. The driver should
++ *	seek to perform this action quickly but should wait until
++ *	any pending driver I/O is completed.
+  */
+ 
+ #include <linux/fs.h>
+@@ -121,6 +128,7 @@
+ 	void	(*set_termios)(struct tty_struct *tty, struct termios * old);
+ 	unsigned int (*poll)(struct tty_struct *, struct file *,
+ 			     struct poll_table_struct *);
++	int     (*hangup)(struct tty_struct *tty);
+ 	
+ 	/*
+ 	 * The following routines are called from below.
+@@ -129,6 +137,7 @@
+ 			       char *fp, int count);
+ 	int	(*receive_room)(struct tty_struct *);
+ 	void	(*write_wakeup)(struct tty_struct *);
++	int	refcount;
+ };
+ 
+ #define TTY_LDISC_MAGIC	0x5403
+===== drivers/char/amiserial.c 1.8 vs 1.9 =====
+--- 1.8/drivers/char/amiserial.c	2002-08-05 21:48:52 +09:00
++++ 1.9/drivers/char/amiserial.c	2004-12-17 00:11:41 +09:00
+@@ -575,10 +575,7 @@
+ 		return;
+ 
+ 	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ }
+ 
+@@ -1040,10 +1037,7 @@
+ 	save_flags(flags); cli();
+ 	info->xmit.head = info->xmit.tail = 0;
+ 	restore_flags(flags);
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1608,8 +1602,7 @@
+ 	shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+===== drivers/char/tty_ioctl.c 1.4 vs 1.5 =====
+--- 1.4/drivers/char/tty_ioctl.c	2002-07-30 20:19:55 +09:00
++++ 1.5/drivers/char/tty_ioctl.c	2004-12-16 22:57:23 +09:00
+@@ -96,8 +96,16 @@
+ {
+ 	int canon_change;
+ 	struct termios old_termios = *tty->termios;
++	struct tty_ldisc *ld;
++
++	/*
++	 *      Perform the actual termios internal changes under lock.
++	 */
++
++	/* FIXME: we need to decide on some locking/ordering semantics
++	   for the set_termios notification eventually */
++	down(&tty->termios_sem);
+ 
+-	cli();
+ 	*tty->termios = *new_termios;
+ 	unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
+ 	canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON;
+@@ -107,7 +115,6 @@
+ 		tty->canon_data = 0;
+ 		tty->erasing = 0;
+ 	}
+-	sti();
+ 	if (canon_change && !L_ICANON(tty) && tty->read_cnt)
+ 		/* Get characters left over from canonical mode. */
+ 		wake_up_interruptible(&tty->read_wait);
+@@ -134,13 +141,19 @@
+ 	if (tty->driver.set_termios)
+ 		(*tty->driver.set_termios)(tty, &old_termios);
+ 
+-	if (tty->ldisc.set_termios)
+-		(*tty->ldisc.set_termios)(tty, &old_termios);
++	ld = tty_ldisc_ref(tty);
++	if (ld != NULL) {
++		if (ld->set_termios)
++			(ld->set_termios)(tty, &old_termios);
++		tty_ldisc_deref(ld);
++	}
++	up(&tty->termios_sem);
+ }
+ 
+ static int set_termios(struct tty_struct * tty, unsigned long arg, int opt)
+ {
+ 	struct termios tmp_termios;
++	struct tty_ldisc *ld;
+ 	int retval = tty_check_change(tty);
+ 
+ 	if (retval)
+@@ -157,8 +170,13 @@
+ 			return -EFAULT;
+ 	}
+ 
+-	if ((opt & TERMIOS_FLUSH) && tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	ld = tty_ldisc_ref(tty);
++
++	if (ld != NULL) {
++		if ((opt & TERMIOS_FLUSH) && ld->flush_buffer)
++			ld->flush_buffer(tty);
++		tty_ldisc_deref(ld);
++	}
+ 
+ 	if (opt & TERMIOS_WAIT) {
+ 		tty_wait_until_sent(tty, 0);
+@@ -223,12 +241,16 @@
+ static int get_sgttyb(struct tty_struct * tty, struct sgttyb * sgttyb)
+ {
+ 	struct sgttyb tmp;
++	unsigned long flags;
+ 
++	down(&tty->termios_sem);
+ 	tmp.sg_ispeed = 0;
+ 	tmp.sg_ospeed = 0;
+ 	tmp.sg_erase = tty->termios->c_cc[VERASE];
+ 	tmp.sg_kill = tty->termios->c_cc[VKILL];
+ 	tmp.sg_flags = get_sgflags(tty);
++	up(&tty->termios_sem);
++
+ 	return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0;
+ }
+ 
+@@ -267,12 +289,14 @@
+ 	retval = tty_check_change(tty);
+ 	if (retval)
+ 		return retval;
+-	termios =  *tty->termios;
+ 	if (copy_from_user(&tmp, sgttyb, sizeof(tmp)))
+ 		return -EFAULT;
++	down(&tty->termios_sem);
++	termios =  *tty->termios;
+ 	termios.c_cc[VERASE] = tmp.sg_erase;
+ 	termios.c_cc[VKILL] = tmp.sg_kill;
+ 	set_sgflags(&termios, tmp.sg_flags);
++	up(&tty->termios_sem);
+ 	change_termios(tty, &termios);
+ 	return 0;
+ }
+@@ -362,6 +386,8 @@
+ {
+ 	struct tty_struct * real_tty;
+ 	int retval;
++	struct tty_ldisc *ld;
++	unsigned long flags;
+ 
+ 	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+ 	    tty->driver.subtype == PTY_TYPE_MASTER)
+@@ -440,22 +466,26 @@
+ 			retval = tty_check_change(tty);
+ 			if (retval)
+ 				return retval;
++
++			ld = tty_ldisc_ref(tty);
+ 			switch (arg) {
+ 			case TCIFLUSH:
+-				if (tty->ldisc.flush_buffer)
+-					tty->ldisc.flush_buffer(tty);
++				if (ld->flush_buffer)
++					ld->flush_buffer(tty);
+ 				break;
+ 			case TCIOFLUSH:
+-				if (tty->ldisc.flush_buffer)
+-					tty->ldisc.flush_buffer(tty);
++				if (ld->flush_buffer)
++					ld->flush_buffer(tty);
+ 				/* fall through */
+ 			case TCOFLUSH:
+ 				if (tty->driver.flush_buffer)
+ 					tty->driver.flush_buffer(tty);
+ 				break;
+ 			default:
++				tty_ldisc_deref(ld);
+ 				return -EINVAL;
+ 			}
++			tty_ldisc_deref(ld);
+ 			return 0;
+ 		case TIOCOUTQ:
+ 			return put_user(tty->driver.chars_in_buffer ?
+@@ -501,9 +531,11 @@
+ 		case TIOCSSOFTCAR:
+ 			if (get_user(arg, (unsigned int *) arg))
+ 				return -EFAULT;
++			down(&tty->termios_sem);
+ 			tty->termios->c_cflag =
+ 				((tty->termios->c_cflag & ~CLOCAL) |
+ 				 (arg ? CLOCAL : 0));
++			up(&tty->termios_sem);
+ 			return 0;
+ 		default:
+ 			return -ENOIOCTLCMD;
+===== drivers/sbus/char/zs.c 1.11 vs 1.12 =====
+--- 1.11/drivers/sbus/char/zs.c	2002-03-31 00:45:50 +09:00
++++ 1.12/drivers/sbus/char/zs.c	2004-12-16 22:57:23 +09:00
+@@ -746,10 +746,7 @@
+ 		return;
+ 
+ 	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-		    tty->ldisc.write_wakeup)
+-			(tty->ldisc.write_wakeup)(tty);
+-		wake_up_interruptible(&tty->write_wait);
++		tty_wakeup(tty);
+ 	}
+ }
+ 
+@@ -1199,10 +1196,7 @@
+ 	cli();
+ 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ 	sti();
+-	wake_up_interruptible(&tty->write_wait);
+-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    tty->ldisc.write_wakeup)
+-		(tty->ldisc.write_wakeup)(tty);
++	tty_wakeup(tty);
+ }
+ 
+ /*
+@@ -1600,15 +1594,14 @@
+ 	shutdown(info);
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	info->event = 0;
+ 	info->tty = 0;
+-	if (tty->ldisc.num != ldiscs[N_TTY].num) {
++	if (tty->ldisc.num != N_TTY) {
+ 		if (tty->ldisc.close)
+ 			(tty->ldisc.close)(tty);
+-		tty->ldisc = ldiscs[N_TTY];
++		tty->ldisc = *(tty_ldisc_get(N_TTY));
+ 		tty->termios->c_line = N_TTY;
+ 		if (tty->ldisc.open)
+ 			(tty->ldisc.open)(tty);
+===== drivers/net/slip.c 1.7 vs 1.8 =====
+--- 1.7/drivers/net/slip.c	2002-08-16 22:59:45 +09:00
++++ 1.8/drivers/net/slip.c	2004-12-16 22:57:23 +09:00
+@@ -670,7 +670,9 @@
+  * Handle the 'receiver data ready' interrupt.
+  * This function is called by the 'tty_io' module in the kernel when
+  * a block of SLIP data has been received, which can now be decapsulated
+- * and sent on to some IP layer for further processing.
++ * and sent on to some IP layer for further processing. This will not
++ * be re-entered while running but other ldisc functions may be called
++ * in parallel
+  */
+  
+ static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
+@@ -826,9 +828,11 @@
+  * SLIP line discipline is called for.  Because we are
+  * sure the tty line exists, we only have to link it to
+  * a free SLIP channel...
++ *
++ * Called in process context serialized from other ldisc calls.
+  */
+-static int
+-slip_open(struct tty_struct *tty)
++
++static int slip_open(struct tty_struct *tty)
+ {
+ 	struct slip *sl;
+ 	int err;
+@@ -865,8 +869,6 @@
+ 	sl->pid = current->pid;
+ 	if (tty->driver.flush_buffer)
+ 		tty->driver.flush_buffer(tty);
+-	if (tty->ldisc.flush_buffer)
+-		tty->ldisc.flush_buffer(tty);
+ 
+ 	if (!test_bit(SLF_INUSE, &sl->flags)) {
+ 		/* Perform the low-level SLIP initialization. */
+@@ -910,6 +912,9 @@
+ }
+ 
+ /*
++
++  FIXME: 1,2 are fixed 3 was never true anyway.
++  
+    Let me to blame a bit.
+    1. TTY module calls this funstion on soft interrupt.
+    2. TTY module calls this function WITH MASKED INTERRUPTS!
+@@ -928,9 +933,8 @@
+ 
+ /*
+  * Close down a SLIP channel.
+- * This means flushing out any pending queues, and then restoring the
+- * TTY line discipline to what it was before it got hooked to SLIP
+- * (which usually is TTY again).
++ * This means flushing out any pending queues, and then returning. This
++ * call is serialized against other ldisc functions.
+  */
+ static void
+ slip_close(struct tty_struct *tty)
+===== drivers/char/ip2main.c 1.10 vs 1.11 =====
+--- 1.10/drivers/char/ip2main.c	2004-08-17 03:41:24 +09:00
++++ 1.11/drivers/char/ip2main.c	2004-12-16 23:27:10 +09:00
+@@ -1462,8 +1462,7 @@
+ 	if (tty->pgrp > 0)
+ 		kill_pg(tty->pgrp, sig, 1);
+ 	if (flush || !L_NOFLSH(tty)) {
+-		if ( tty->ldisc.flush_buffer )  
+-			tty->ldisc.flush_buffer(tty);
++		tty_ldisc_flush(tty);
+ 		i2InputFlush( tty->driver_data );
+ 	}
+ }
+@@ -1853,8 +1852,7 @@
+ 
+ 	if ( tty->driver.flush_buffer ) 
+ 		tty->driver.flush_buffer(tty);
+-	if ( tty->ldisc.flush_buffer )  
+-		tty->ldisc.flush_buffer(tty);
++	tty_ldisc_flush(tty);
+ 	tty->closing = 0;
+ 	
+ 	pCh->pTTY = NULL;

Deleted: trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/115_tty_lockup-3.diff
===================================================================
--- trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/115_tty_lockup-3.diff	2004-12-21 08:22:52 UTC (rev 2012)
+++ trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/115_tty_lockup-3.diff	2004-12-21 09:34:50 UTC (rev 2013)
@@ -1,18 +0,0 @@
-# origin: dann frazier <dannf@debian.org>
-# cset: n/a
-# inclusion: missing comment text backported from 2.6 bitkeeper
-# revision date: 2004-12-14
-# description: Fix unterminated comment
-diff -urN kernel-source-2.4.27.orig/drivers/char/tty_io.c kernel-source-2.4.27/drivers/char/tty_io.c
---- kernel-source-2.4.27.orig/drivers/char/tty_io.c	2004-12-01 03:07:57.000000000 -0700
-+++ kernel-source-2.4.27/drivers/char/tty_io.c	2004-12-14 19:32:06.886350385 -0700
-@@ -767,6 +767,9 @@
- 	/* Defer ldisc switch */
- 	/* tty_deferred_ldisc_switch(N_TTY);
- 
-+	  This should get done automatically when the port closes and
-+	  tty_release is called */
-+
- 	read_lock(&tasklist_lock);
-  	for_each_task(p) {
- 		if ((tty->session > 0) && (p->session == tty->session) &&

Modified: trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/series/2.4.27-7
===================================================================
--- trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/series/2.4.27-7	2004-12-21 08:22:52 UTC (rev 2012)
+++ trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/series/2.4.27-7	2004-12-21 09:34:50 UTC (rev 2013)
@@ -1,7 +1,9 @@
 + 113-unix-serialization.diff
 + 114-binfmt_aout-CAN-2004-1074.diff
-+ 115_tty_lockup-3.diff
 + 116-cmsg-validation-checks.diff
 + 117-igmp-source-filter-fixes.diff
 + 118-cmsg-validation-checks-compat.diff
 + 119-acpi_early-build.diff
+- 093_tty_lockup.diff
+- 093_tty_lockup-2.diff
++ 093-tty_lockup-3.diff