Merge 3.7-rc3 into tty-next
Greg Kroah-Hartman [Mon, 29 Oct 2012 16:00:57 +0000 (09:00 -0700)]
This merges the tty changes in 3.7-rc3 into tty-next

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

49 files changed:
Documentation/devicetree/bindings/tty/serial/of-serial.txt
arch/alpha/include/asm/ioctls.h
arch/arm/plat-omap/include/plat/serial.h
arch/mips/include/uapi/asm/ioctls.h
arch/parisc/include/uapi/asm/ioctls.h
arch/powerpc/include/uapi/asm/ioctls.h
arch/sh/include/uapi/asm/ioctls.h
arch/sparc/include/uapi/asm/ioctls.h
arch/x86/platform/ce4100/ce4100.c
arch/xtensa/include/uapi/asm/ioctls.h
drivers/bluetooth/hci_ldisc.c
drivers/staging/dgrp/dgrp_common.h
drivers/staging/dgrp/dgrp_driver.c
drivers/staging/dgrp/dgrp_net_ops.c
drivers/staging/dgrp/dgrp_specproc.c
drivers/staging/dgrp/dgrp_sysfs.c
drivers/tty/hvc/hvcs.c
drivers/tty/n_tty.c
drivers/tty/pty.c
drivers/tty/serial/8250/8250.c
drivers/tty/serial/8250/8250.h
drivers/tty/serial/8250/8250_dw.c
drivers/tty/serial/8250/8250_early.c
drivers/tty/serial/8250/8250_pci.c
drivers/tty/serial/amba-pl011.c
drivers/tty/serial/clps711x.c
drivers/tty/serial/ifx6x60.c
drivers/tty/serial/ifx6x60.h
drivers/tty/serial/of_serial.c
drivers/tty/serial/omap-serial.c
drivers/tty/serial/pxa.c
drivers/tty/serial/samsung.c
drivers/tty/serial/vt8500_serial.c
drivers/tty/tty_audit.c
drivers/tty/tty_buffer.c
drivers/tty/tty_io.c
drivers/tty/tty_ioctl.c
drivers/tty/tty_ldisc.c
drivers/tty/tty_mutex.c
drivers/tty/tty_port.c
drivers/tty/vt/selection.c
fs/compat_ioctl.c
fs/devpts/inode.c
include/linux/devpts_fs.h
include/linux/tty.h
include/linux/tty_flip.h
include/uapi/asm-generic/ioctls.h
kernel/auditsc.c
kernel/printk.c

index ba385f2..1e1145c 100644 (file)
@@ -14,7 +14,10 @@ Required properties:
        - "serial" if the port type is unknown.
 - reg : offset and length of the register set for the device.
 - interrupts : should contain uart interrupt.
-- clock-frequency : the input clock frequency for the UART.
+- clock-frequency : the input clock frequency for the UART
+        or
+  clocks phandle to refer to the clk used as per Documentation/devicetree
+  /bindings/clock/clock-bindings.txt
 
 Optional properties:
 - current-speed : the current active speed of the UART.
index 80e1cee..92c557b 100644 (file)
@@ -95,6 +95,9 @@
 #define TIOCGDEV       _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */
 #define TIOCSIG                _IOW('T',0x36, int)  /* Generate signal on Pty slave */
 #define TIOCVHANGUP    0x5437
+#define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
+#define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
+#define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 
 #define TIOCSERCONFIG  0x5453
 #define TIOCSERGWILD   0x5454
index 65fce44..b780470 100644 (file)
 #define OMAP5UART4             OMAP4UART4
 #define ZOOM_UART              95              /* Only on zoom2/3 */
 
-/* This is only used by 8250.c for omap1510 */
-#define is_omap_port(pt)       ({int __ret = 0;                        \
-                       if ((pt)->port.mapbase == OMAP1_UART1_BASE ||   \
-                           (pt)->port.mapbase == OMAP1_UART2_BASE ||   \
-                           (pt)->port.mapbase == OMAP1_UART3_BASE)     \
-                               __ret = 1;                              \
-                       __ret;                                          \
-                       })
-
 #ifndef __ASSEMBLER__
 
 struct omap_board_data;
index 92403c3..addd56b 100644 (file)
@@ -86,6 +86,9 @@
 #define TIOCGDEV       _IOR('T', 0x32, unsigned int) /* Get primary device node of /dev/console */
 #define TIOCSIG                _IOW('T', 0x36, int)  /* Generate signal on Pty slave */
 #define TIOCVHANGUP    0x5437
+#define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
+#define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
+#define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 
 /* I hope the range from 0x5480 on is free ... */
 #define TIOCSCTTY      0x5480          /* become controlling tty */
index 054ec06..66719c3 100644 (file)
@@ -55,6 +55,9 @@
 #define TIOCGDEV       _IOR('T',0x32, int)  /* Get primary device node of /dev/console */
 #define TIOCSIG                _IOW('T',0x36, int)  /* Generate signal on Pty slave */
 #define TIOCVHANGUP    0x5437
+#define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
+#define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
+#define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 
 #define FIONCLEX       0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX                0x5451
index e9b7887..49a2579 100644 (file)
@@ -97,6 +97,9 @@
 #define TIOCGDEV       _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */
 #define TIOCSIG                _IOW('T',0x36, int)  /* Generate signal on Pty slave */
 #define TIOCVHANGUP    0x5437
+#define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
+#define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
+#define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 
 #define TIOCSERCONFIG  0x5453
 #define TIOCSERGWILD   0x5454
index a6769f3..3422410 100644 (file)
@@ -88,6 +88,9 @@
 #define TIOCGDEV       _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */
 #define TIOCSIG                _IOW('T',0x36, int)  /* Generate signal on Pty slave */
 #define TIOCVHANGUP    _IO('T', 0x37)
+#define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
+#define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
+#define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 
 #define TIOCSERCONFIG  _IO('T', 83) /* 0x5453 */
 #define TIOCSERGWILD   _IOR('T', 84,  int) /* 0x5454 */
index 9155f70..897d172 100644 (file)
@@ -21,6 +21,9 @@
 #define TCSETSF2       _IOW('T', 15, struct termios2)
 #define TIOCGDEV       _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */
 #define TIOCVHANGUP    _IO('T', 0x37)
+#define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
+#define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
+#define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 
 /* Note that all the ioctls that are not available in Linux have a 
  * double underscore on the front to: a) avoid some programs to
index 4c61b52..0dcc30e 100644 (file)
@@ -92,8 +92,11 @@ static void ce4100_serial_fixup(int port, struct uart_port *up,
                up->membase =
                        (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
                up->membase += up->mapbase & ~PAGE_MASK;
+               up->mapbase += port * 0x100;
+               up->membase += port * 0x100;
                up->iotype   = UPIO_MEM32;
                up->regshift = 2;
+               up->irq = 4;
        }
 #endif
        up->iobase = 0;
index 2aa4cd9..b4cb110 100644 (file)
 #define TIOCGDEV       _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */
 #define TIOCSIG                _IOW('T',0x36, int)  /* Generate signal on Pty slave */
 #define TIOCVHANGUP    _IO('T', 0x37)
+#define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
+#define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
+#define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 
 #define TIOCSERCONFIG  _IO('T', 83)
 #define TIOCSERGWILD   _IOR('T', 84,  int)
index c8abce3..ed0fade 100644 (file)
@@ -270,15 +270,10 @@ static int hci_uart_send_frame(struct sk_buff *skb)
  */
 static int hci_uart_tty_open(struct tty_struct *tty)
 {
-       struct hci_uart *hu = (void *) tty->disc_data;
+       struct hci_uart *hu;
 
        BT_DBG("tty %p", tty);
 
-       /* FIXME: This btw is bogus, nothing requires the old ldisc to clear
-          the pointer */
-       if (hu)
-               return -EEXIST;
-
        /* Error if the tty has no write op instead of leaving an exploitable
           hole */
        if (tty->ops->write == NULL)
index 05ff338..0583fe9 100644 (file)
@@ -31,7 +31,6 @@
  * All global storage allocation.
  ************************************************************************/
 
-extern int dgrp_rawreadok;  /* Allow raw writing of input */
 extern int dgrp_register_cudevices; /* enable legacy cu devices */
 extern int dgrp_register_prdevices; /* enable transparent print devices */
 extern int dgrp_poll_tick;          /* Poll interval - in ms */
index 6e4a0eb..aa26258 100644 (file)
@@ -39,14 +39,10 @@ MODULE_VERSION(DIGI_VERSION);
 struct list_head nd_struct_list;
 struct dgrp_poll_data dgrp_poll_data;
 
-int dgrp_rawreadok = 1;                /* Bypass flipbuf on input */
 int dgrp_register_cudevices = 1;/* Turn on/off registering legacy cu devices */
 int dgrp_register_prdevices = 1;/* Turn on/off registering transparent print */
 int dgrp_poll_tick = 20;       /* Poll interval - in ms */
 
-module_param_named(rawreadok, dgrp_rawreadok, int, 0644);
-MODULE_PARM_DESC(rawreadok, "Bypass flip buffers on input");
-
 module_param_named(register_cudevices, dgrp_register_cudevices, int, 0644);
 MODULE_PARM_DESC(register_cudevices, "Turn on/off registering legacy cu devices");
 
index ab839ea..0788357 100644 (file)
@@ -151,20 +151,15 @@ static void dgrp_read_data_block(struct ch_struct *ch, u8 *flipbuf,
  * Copys the rbuf to the flipbuf and sends to line discipline.
  * Sends input buffer data to the line discipline.
  *
- * There are several modes to consider here:
- *    rawreadok, tty->real_raw, and IF_PARMRK
  */
 static void dgrp_input(struct ch_struct *ch)
 {
        struct nd_struct *nd;
        struct tty_struct *tty;
-       int remain;
        int data_len;
        int len;
-       int flip_len;
        int tty_count;
        ulong lock_flags;
-       struct tty_ldisc *ld;
        u8  *myflipbuf;
        u8  *myflipflagbuf;
 
@@ -212,37 +207,11 @@ static void dgrp_input(struct ch_struct *ch)
 
        spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
 
-       /* Decide how much data we can send into the tty layer */
-       if (dgrp_rawreadok && tty->real_raw)
-               flip_len = MYFLIPLEN;
-       else
-               flip_len = TTY_FLIPBUF_SIZE;
-
        /* data_len should be the number of chars that we read in */
        data_len = (ch->ch_rin - ch->ch_rout) & RBUF_MASK;
-       remain = data_len;
 
        /* len is the amount of data we are going to transfer here */
-       len = min(data_len, flip_len);
-
-       /* take into consideration length of ldisc */
-       len = min(len, (N_TTY_BUF_SIZE - 1) - tty->read_cnt);
-
-       ld = tty_ldisc_ref(tty);
-
-       /*
-        * If we were unable to get a reference to the ld,
-        * don't flush our buffer, and act like the ld doesn't
-        * have any space to put the data right now.
-        */
-       if (!ld) {
-               len = 0;
-       } else if (!ld->ops->receive_buf) {
-               spin_lock_irqsave(&nd->nd_lock, lock_flags);
-               ch->ch_rout = ch->ch_rin;
-               spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
-               len = 0;
-       }
+       len = tty_buffer_request_room(tty, data_len);
 
        /* Check DPA flow control */
        if ((nd->nd_dpa_debug) &&
@@ -254,42 +223,22 @@ static void dgrp_input(struct ch_struct *ch)
 
                dgrp_read_data_block(ch, myflipbuf, len);
 
-               /*
-                * In high performance mode, we don't have to update
-                * flag_buf or any of the counts or pointers into flip buf.
-                */
-               if (!dgrp_rawreadok || !tty->real_raw) {
-                       if (I_PARMRK(tty) || I_BRKINT(tty) || I_INPCK(tty))
-                               parity_scan(ch, myflipbuf, myflipflagbuf, &len);
-                       else
-                               memset(myflipflagbuf, TTY_NORMAL, len);
-               }
+               if (I_PARMRK(tty) || I_BRKINT(tty) || I_INPCK(tty))
+                       parity_scan(ch, myflipbuf, myflipflagbuf, &len);
+               else
+                       memset(myflipflagbuf, TTY_NORMAL, len);
 
                if ((nd->nd_dpa_debug) &&
                    (nd->nd_dpa_port == PORT_NUM(MINOR(tty_devnum(tty)))))
                        dgrp_dpa_data(nd, 1, myflipbuf, len);
 
-               /*
-                * If we're doing raw reads, jam it right into the
-                * line disc bypassing the flip buffers.
-                */
-               if (dgrp_rawreadok && tty->real_raw)
-                       ld->ops->receive_buf(tty, myflipbuf, NULL, len);
-               else {
-                       len = tty_buffer_request_room(tty, len);
-                       tty_insert_flip_string_flags(tty, myflipbuf,
-                                                    myflipflagbuf, len);
-
-                       /* Tell the tty layer its okay to "eat" the data now */
-                       tty_flip_buffer_push(tty);
-               }
+               tty_insert_flip_string_flags(tty, myflipbuf,
+                                            myflipflagbuf, len);
+               tty_flip_buffer_push(tty);
 
                ch->ch_rxcount += len;
        }
 
-       if (ld)
-               tty_ldisc_deref(ld);
-
        /*
         * Wake up any sleepers (maybe dgrp close) that might be waiting
         * for a channel flag state change.
index 24327c3..db91f67 100644 (file)
@@ -629,8 +629,6 @@ static int info_proc_show(struct seq_file *m, void *v)
 {
        seq_printf(m, "version: %s\n", DIGI_VERSION);
        seq_puts(m, "register_with_sysfs: 1\n");
-       seq_printf(m, "rawreadok: 0x%08x\t(%d)\n",
-                  dgrp_rawreadok, dgrp_rawreadok);
        seq_printf(m, "pollrate: 0x%08x\t(%d)\n",
                   dgrp_poll_tick, dgrp_poll_tick);
 
index e5a3c88..8b513e9 100644 (file)
@@ -55,23 +55,6 @@ static DEVICE_ATTR(register_with_sysfs, 0400,
                   dgrp_class_register_with_sysfs_show, NULL);
 
 
-static ssize_t dgrp_class_rawreadok_show(struct device *c,
-                                        struct device_attribute *attr,
-                                        char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "%d\n", dgrp_rawreadok);
-}
-static ssize_t dgrp_class_rawreadok_store(struct device *c,
-                                         struct device_attribute *attr,
-                                         const char *buf, size_t count)
-{
-       sscanf(buf, "0x%x\n", &dgrp_rawreadok);
-       return count;
-}
-static DEVICE_ATTR(rawreadok, 0600, dgrp_class_rawreadok_show,
-                  dgrp_class_rawreadok_store);
-
-
 static ssize_t dgrp_class_pollrate_show(struct device *c,
                                        struct device_attribute *attr,
                                        char *buf)
@@ -91,7 +74,6 @@ static DEVICE_ATTR(pollrate, 0600, dgrp_class_pollrate_show,
 
 static struct attribute *dgrp_sysfs_global_settings_entries[] = {
        &dev_attr_pollrate.attr,
-       &dev_attr_rawreadok.attr,
        &dev_attr_register_with_sysfs.attr,
        NULL
 };
index cab5c7a..744c3b8 100644 (file)
@@ -1496,8 +1496,10 @@ static int __devinit hvcs_initialize(void)
                num_ttys_to_alloc = hvcs_parm_num_devs;
 
        hvcs_tty_driver = alloc_tty_driver(num_ttys_to_alloc);
-       if (!hvcs_tty_driver)
+       if (!hvcs_tty_driver) {
+               mutex_unlock(&hvcs_init_mutex);
                return -ENOMEM;
+       }
 
        if (hvcs_alloc_index_list(num_ttys_to_alloc)) {
                rc = -ENOMEM;
index 8c0b7b4..19083ef 100644 (file)
 #define ECHO_OP_SET_CANON_COL 0x81
 #define ECHO_OP_ERASE_TAB 0x82
 
+struct n_tty_data {
+       unsigned int column;
+       unsigned long overrun_time;
+       int num_overrun;
+
+       unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
+       unsigned char echo_overrun:1;
+
+       DECLARE_BITMAP(process_char_map, 256);
+       DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE);
+
+       char *read_buf;
+       int read_head;
+       int read_tail;
+       int read_cnt;
+
+       unsigned char *echo_buf;
+       unsigned int echo_pos;
+       unsigned int echo_cnt;
+
+       int canon_data;
+       unsigned long canon_head;
+       unsigned int canon_column;
+
+       struct mutex atomic_read_lock;
+       struct mutex output_lock;
+       struct mutex echo_lock;
+       spinlock_t read_lock;
+};
+
 static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
                               unsigned char __user *ptr)
 {
-       tty_audit_add_data(tty, &x, 1);
+       struct n_tty_data *ldata = tty->disc_data;
+
+       tty_audit_add_data(tty, &x, 1, ldata->icanon);
        return put_user(x, ptr);
 }
 
@@ -92,17 +124,18 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
 
 static void n_tty_set_room(struct tty_struct *tty)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        int left;
        int old_left;
 
-       /* tty->read_cnt is not read locked ? */
+       /* ldata->read_cnt is not read locked ? */
        if (I_PARMRK(tty)) {
                /* Multiply read_cnt by 3, since each byte might take up to
                 * three times as many spaces when PARMRK is set (depending on
                 * its flags, e.g. parity error). */
-               left = N_TTY_BUF_SIZE - tty->read_cnt * 3 - 1;
+               left = N_TTY_BUF_SIZE - ldata->read_cnt * 3 - 1;
        } else
-               left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
+               left = N_TTY_BUF_SIZE - ldata->read_cnt - 1;
 
        /*
         * If we are doing input canonicalization, and there are no
@@ -111,44 +144,47 @@ static void n_tty_set_room(struct tty_struct *tty)
         * characters will be beeped.
         */
        if (left <= 0)
-               left = tty->icanon && !tty->canon_data;
+               left = ldata->icanon && !ldata->canon_data;
        old_left = tty->receive_room;
        tty->receive_room = left;
 
        /* Did this open up the receive buffer? We may need to flip */
-       if (left && !old_left)
-               schedule_work(&tty->buf.work);
+       if (left && !old_left) {
+               WARN_RATELIMIT(tty->port->itty == NULL,
+                               "scheduling with invalid itty\n");
+               schedule_work(&tty->port->buf.work);
+       }
 }
 
-static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty)
+static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata)
 {
-       if (tty->read_cnt < N_TTY_BUF_SIZE) {
-               tty->read_buf[tty->read_head] = c;
-               tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);
-               tty->read_cnt++;
+       if (ldata->read_cnt < N_TTY_BUF_SIZE) {
+               ldata->read_buf[ldata->read_head] = c;
+               ldata->read_head = (ldata->read_head + 1) & (N_TTY_BUF_SIZE-1);
+               ldata->read_cnt++;
        }
 }
 
 /**
  *     put_tty_queue           -       add character to tty
  *     @c: character
- *     @tty: tty device
+ *     @ldata: n_tty data
  *
  *     Add a character to the tty read_buf queue. This is done under the
  *     read_lock to serialize character addition and also to protect us
  *     against parallel reads or flushes
  */
 
-static void put_tty_queue(unsigned char c, struct tty_struct *tty)
+static void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
 {
        unsigned long flags;
        /*
         *      The problem of stomping on the buffers ends here.
         *      Why didn't anyone see this one coming? --AJK
        */
-       spin_lock_irqsave(&tty->read_lock, flags);
-       put_tty_queue_nolock(c, tty);
-       spin_unlock_irqrestore(&tty->read_lock, flags);
+       spin_lock_irqsave(&ldata->read_lock, flags);
+       put_tty_queue_nolock(c, ldata);
+       spin_unlock_irqrestore(&ldata->read_lock, flags);
 }
 
 /**
@@ -179,18 +215,19 @@ static void check_unthrottle(struct tty_struct *tty)
 
 static void reset_buffer_flags(struct tty_struct *tty)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        unsigned long flags;
 
-       spin_lock_irqsave(&tty->read_lock, flags);
-       tty->read_head = tty->read_tail = tty->read_cnt = 0;
-       spin_unlock_irqrestore(&tty->read_lock, flags);
+       spin_lock_irqsave(&ldata->read_lock, flags);
+       ldata->read_head = ldata->read_tail = ldata->read_cnt = 0;
+       spin_unlock_irqrestore(&ldata->read_lock, flags);
 
-       mutex_lock(&tty->echo_lock);
-       tty->echo_pos = tty->echo_cnt = tty->echo_overrun = 0;
-       mutex_unlock(&tty->echo_lock);
+       mutex_lock(&ldata->echo_lock);
+       ldata->echo_pos = ldata->echo_cnt = ldata->echo_overrun = 0;
+       mutex_unlock(&ldata->echo_lock);
 
-       tty->canon_head = tty->canon_data = tty->erasing = 0;
-       memset(&tty->read_flags, 0, sizeof tty->read_flags);
+       ldata->canon_head = ldata->canon_data = ldata->erasing = 0;
+       bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
        n_tty_set_room(tty);
 }
 
@@ -235,18 +272,19 @@ static void n_tty_flush_buffer(struct tty_struct *tty)
 
 static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        unsigned long flags;
        ssize_t n = 0;
 
-       spin_lock_irqsave(&tty->read_lock, flags);
-       if (!tty->icanon) {
-               n = tty->read_cnt;
-       } else if (tty->canon_data) {
-               n = (tty->canon_head > tty->read_tail) ?
-                       tty->canon_head - tty->read_tail :
-                       tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail);
+       spin_lock_irqsave(&ldata->read_lock, flags);
+       if (!ldata->icanon) {
+               n = ldata->read_cnt;
+       } else if (ldata->canon_data) {
+               n = (ldata->canon_head > ldata->read_tail) ?
+                       ldata->canon_head - ldata->read_tail :
+                       ldata->canon_head + (N_TTY_BUF_SIZE - ldata->read_tail);
        }
-       spin_unlock_irqrestore(&tty->read_lock, flags);
+       spin_unlock_irqrestore(&ldata->read_lock, flags);
        return n;
 }
 
@@ -301,6 +339,7 @@ static inline int is_continuation(unsigned char c, struct tty_struct *tty)
 
 static int do_output_char(unsigned char c, struct tty_struct *tty, int space)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        int     spaces;
 
        if (!space)
@@ -309,48 +348,48 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space)
        switch (c) {
        case '\n':
                if (O_ONLRET(tty))
-                       tty->column = 0;
+                       ldata->column = 0;
                if (O_ONLCR(tty)) {
                        if (space < 2)
                                return -1;
-                       tty->canon_column = tty->column = 0;
+                       ldata->canon_column = ldata->column = 0;
                        tty->ops->write(tty, "\r\n", 2);
                        return 2;
                }
-               tty->canon_column = tty->column;
+               ldata->canon_column = ldata->column;
                break;
        case '\r':
-               if (O_ONOCR(tty) && tty->column == 0)
+               if (O_ONOCR(tty) && ldata->column == 0)
                        return 0;
                if (O_OCRNL(tty)) {
                        c = '\n';
                        if (O_ONLRET(tty))
-                               tty->canon_column = tty->column = 0;
+                               ldata->canon_column = ldata->column = 0;
                        break;
                }
-               tty->canon_column = tty->column = 0;
+               ldata->canon_column = ldata->column = 0;
                break;
        case '\t':
-               spaces = 8 - (tty->column & 7);
+               spaces = 8 - (ldata->column & 7);
                if (O_TABDLY(tty) == XTABS) {
                        if (space < spaces)
                                return -1;
-                       tty->column += spaces;
+                       ldata->column += spaces;
                        tty->ops->write(tty, "        ", spaces);
                        return spaces;
                }
-               tty->column += spaces;
+               ldata->column += spaces;
                break;
        case '\b':
-               if (tty->column > 0)
-                       tty->column--;
+               if (ldata->column > 0)
+                       ldata->column--;
                break;
        default:
                if (!iscntrl(c)) {
                        if (O_OLCUC(tty))
                                c = toupper(c);
                        if (!is_continuation(c, tty))
-                               tty->column++;
+                               ldata->column++;
                }
                break;
        }
@@ -375,14 +414,15 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space)
 
 static int process_output(unsigned char c, struct tty_struct *tty)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        int     space, retval;
 
-       mutex_lock(&tty->output_lock);
+       mutex_lock(&ldata->output_lock);
 
        space = tty_write_room(tty);
        retval = do_output_char(c, tty, space);
 
-       mutex_unlock(&tty->output_lock);
+       mutex_unlock(&ldata->output_lock);
        if (retval < 0)
                return -1;
        else
@@ -411,15 +451,16 @@ static int process_output(unsigned char c, struct tty_struct *tty)
 static ssize_t process_output_block(struct tty_struct *tty,
                                    const unsigned char *buf, unsigned int nr)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        int     space;
        int     i;
        const unsigned char *cp;
 
-       mutex_lock(&tty->output_lock);
+       mutex_lock(&ldata->output_lock);
 
        space = tty_write_room(tty);
        if (!space) {
-               mutex_unlock(&tty->output_lock);
+               mutex_unlock(&ldata->output_lock);
                return 0;
        }
        if (nr > space)
@@ -431,30 +472,30 @@ static ssize_t process_output_block(struct tty_struct *tty,
                switch (c) {
                case '\n':
                        if (O_ONLRET(tty))
-                               tty->column = 0;
+                               ldata->column = 0;
                        if (O_ONLCR(tty))
                                goto break_out;
-                       tty->canon_column = tty->column;
+                       ldata->canon_column = ldata->column;
                        break;
                case '\r':
-                       if (O_ONOCR(tty) && tty->column == 0)
+                       if (O_ONOCR(tty) && ldata->column == 0)
                                goto break_out;
                        if (O_OCRNL(tty))
                                goto break_out;
-                       tty->canon_column = tty->column = 0;
+                       ldata->canon_column = ldata->column = 0;
                        break;
                case '\t':
                        goto break_out;
                case '\b':
-                       if (tty->column > 0)
-                               tty->column--;
+                       if (ldata->column > 0)
+                               ldata->column--;
                        break;
                default:
                        if (!iscntrl(c)) {
                                if (O_OLCUC(tty))
                                        goto break_out;
                                if (!is_continuation(c, tty))
-                                       tty->column++;
+                                       ldata->column++;
                        }
                        break;
                }
@@ -462,7 +503,7 @@ static ssize_t process_output_block(struct tty_struct *tty,
 break_out:
        i = tty->ops->write(tty, buf, i);
 
-       mutex_unlock(&tty->output_lock);
+       mutex_unlock(&ldata->output_lock);
        return i;
 }
 
@@ -494,21 +535,22 @@ break_out:
 
 static void process_echoes(struct tty_struct *tty)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        int     space, nr;
        unsigned char c;
        unsigned char *cp, *buf_end;
 
-       if (!tty->echo_cnt)
+       if (!ldata->echo_cnt)
                return;
 
-       mutex_lock(&tty->output_lock);
-       mutex_lock(&tty->echo_lock);
+       mutex_lock(&ldata->output_lock);
+       mutex_lock(&ldata->echo_lock);
 
        space = tty_write_room(tty);
 
-       buf_end = tty->echo_buf + N_TTY_BUF_SIZE;
-       cp = tty->echo_buf + tty->echo_pos;
-       nr = tty->echo_cnt;
+       buf_end = ldata->echo_buf + N_TTY_BUF_SIZE;
+       cp = ldata->echo_buf + ldata->echo_pos;
+       nr = ldata->echo_cnt;
        while (nr > 0) {
                c = *cp;
                if (c == ECHO_OP_START) {
@@ -545,7 +587,7 @@ static void process_echoes(struct tty_struct *tty)
                                 * Otherwise, tab spacing is normal.
                                 */
                                if (!(num_chars & 0x80))
-                                       num_chars += tty->canon_column;
+                                       num_chars += ldata->canon_column;
                                num_bs = 8 - (num_chars & 7);
 
                                if (num_bs > space) {
@@ -555,22 +597,22 @@ static void process_echoes(struct tty_struct *tty)
                                space -= num_bs;
                                while (num_bs--) {
                                        tty_put_char(tty, '\b');
-                                       if (tty->column > 0)
-                                               tty->column--;
+                                       if (ldata->column > 0)
+                                               ldata->column--;
                                }
                                cp += 3;
                                nr -= 3;
                                break;
 
                        case ECHO_OP_SET_CANON_COL:
-                               tty->canon_column = tty->column;
+                               ldata->canon_column = ldata->column;
                                cp += 2;
                                nr -= 2;
                                break;
 
                        case ECHO_OP_MOVE_BACK_COL:
-                               if (tty->column > 0)
-                                       tty->column--;
+                               if (ldata->column > 0)
+                                       ldata->column--;
                                cp += 2;
                                nr -= 2;
                                break;
@@ -582,7 +624,7 @@ static void process_echoes(struct tty_struct *tty)
                                        break;
                                }
                                tty_put_char(tty, ECHO_OP_START);
-                               tty->column++;
+                               ldata->column++;
                                space--;
                                cp += 2;
                                nr -= 2;
@@ -604,7 +646,7 @@ static void process_echoes(struct tty_struct *tty)
                                }
                                tty_put_char(tty, '^');
                                tty_put_char(tty, op ^ 0100);
-                               tty->column += 2;
+                               ldata->column += 2;
                                space -= 2;
                                cp += 2;
                                nr -= 2;
@@ -635,20 +677,20 @@ static void process_echoes(struct tty_struct *tty)
        }
 
        if (nr == 0) {
-               tty->echo_pos = 0;
-               tty->echo_cnt = 0;
-               tty->echo_overrun = 0;
+               ldata->echo_pos = 0;
+               ldata->echo_cnt = 0;
+               ldata->echo_overrun = 0;
        } else {
-               int num_processed = tty->echo_cnt - nr;
-               tty->echo_pos += num_processed;
-               tty->echo_pos &= N_TTY_BUF_SIZE - 1;
-               tty->echo_cnt = nr;
+               int num_processed = ldata->echo_cnt - nr;
+               ldata->echo_pos += num_processed;
+               ldata->echo_pos &= N_TTY_BUF_SIZE - 1;
+               ldata->echo_cnt = nr;
                if (num_processed > 0)
-                       tty->echo_overrun = 0;
+                       ldata->echo_overrun = 0;
        }
 
-       mutex_unlock(&tty->echo_lock);
-       mutex_unlock(&tty->output_lock);
+       mutex_unlock(&ldata->echo_lock);
+       mutex_unlock(&ldata->output_lock);
 
        if (tty->ops->flush_chars)
                tty->ops->flush_chars(tty);
@@ -657,72 +699,70 @@ static void process_echoes(struct tty_struct *tty)
 /**
  *     add_echo_byte   -       add a byte to the echo buffer
  *     @c: unicode byte to echo
- *     @tty: terminal device
+ *     @ldata: n_tty data
  *
  *     Add a character or operation byte to the echo buffer.
  *
  *     Should be called under the echo lock to protect the echo buffer.
  */
 
-static void add_echo_byte(unsigned char c, struct tty_struct *tty)
+static void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
 {
        int     new_byte_pos;
 
-       if (tty->echo_cnt == N_TTY_BUF_SIZE) {
+       if (ldata->echo_cnt == N_TTY_BUF_SIZE) {
                /* Circular buffer is already at capacity */
-               new_byte_pos = tty->echo_pos;
+               new_byte_pos = ldata->echo_pos;
 
                /*
                 * Since the buffer start position needs to be advanced,
                 * be sure to step by a whole operation byte group.
                 */
-               if (tty->echo_buf[tty->echo_pos] == ECHO_OP_START) {
-                       if (tty->echo_buf[(tty->echo_pos + 1) &
+               if (ldata->echo_buf[ldata->echo_pos] == ECHO_OP_START) {
+                       if (ldata->echo_buf[(ldata->echo_pos + 1) &
                                          (N_TTY_BUF_SIZE - 1)] ==
                                                ECHO_OP_ERASE_TAB) {
-                               tty->echo_pos += 3;
-                               tty->echo_cnt -= 2;
+                               ldata->echo_pos += 3;
+                               ldata->echo_cnt -= 2;
                        } else {
-                               tty->echo_pos += 2;
-                               tty->echo_cnt -= 1;
+                               ldata->echo_pos += 2;
+                               ldata->echo_cnt -= 1;
                        }
                } else {
-                       tty->echo_pos++;
+                       ldata->echo_pos++;
                }
-               tty->echo_pos &= N_TTY_BUF_SIZE - 1;
+               ldata->echo_pos &= N_TTY_BUF_SIZE - 1;
 
-               tty->echo_overrun = 1;
+               ldata->echo_overrun = 1;
        } else {
-               new_byte_pos = tty->echo_pos + tty->echo_cnt;
+               new_byte_pos = ldata->echo_pos + ldata->echo_cnt;
                new_byte_pos &= N_TTY_BUF_SIZE - 1;
-               tty->echo_cnt++;
+               ldata->echo_cnt++;
        }
 
-       tty->echo_buf[new_byte_pos] = c;
+       ldata->echo_buf[new_byte_pos] = c;
 }
 
 /**
  *     echo_move_back_col      -       add operation to move back a column
- *     @tty: terminal device
+ *     @ldata: n_tty data
  *
  *     Add an operation to the echo buffer to move back one column.
  *
  *     Locking: echo_lock to protect the echo buffer
  */
 
-static void echo_move_back_col(struct tty_struct *tty)
+static void echo_move_back_col(struct n_tty_data *ldata)
 {
-       mutex_lock(&tty->echo_lock);
-
-       add_echo_byte(ECHO_OP_START, tty);
-       add_echo_byte(ECHO_OP_MOVE_BACK_COL, tty);
-
-       mutex_unlock(&tty->echo_lock);
+       mutex_lock(&ldata->echo_lock);
+       add_echo_byte(ECHO_OP_START, ldata);
+       add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata);
+       mutex_unlock(&ldata->echo_lock);
 }
 
 /**
  *     echo_set_canon_col      -       add operation to set the canon column
- *     @tty: terminal device
+ *     @ldata: n_tty data
  *
  *     Add an operation to the echo buffer to set the canon column
  *     to the current column.
@@ -730,21 +770,19 @@ static void echo_move_back_col(struct tty_struct *tty)
  *     Locking: echo_lock to protect the echo buffer
  */
 
-static void echo_set_canon_col(struct tty_struct *tty)
+static void echo_set_canon_col(struct n_tty_data *ldata)
 {
-       mutex_lock(&tty->echo_lock);
-
-       add_echo_byte(ECHO_OP_START, tty);
-       add_echo_byte(ECHO_OP_SET_CANON_COL, tty);
-
-       mutex_unlock(&tty->echo_lock);
+       mutex_lock(&ldata->echo_lock);
+       add_echo_byte(ECHO_OP_START, ldata);
+       add_echo_byte(ECHO_OP_SET_CANON_COL, ldata);
+       mutex_unlock(&ldata->echo_lock);
 }
 
 /**
  *     echo_erase_tab  -       add operation to erase a tab
  *     @num_chars: number of character columns already used
  *     @after_tab: true if num_chars starts after a previous tab
- *     @tty: terminal device
+ *     @ldata: n_tty data
  *
  *     Add an operation to the echo buffer to erase a tab.
  *
@@ -758,12 +796,12 @@ static void echo_set_canon_col(struct tty_struct *tty)
  */
 
 static void echo_erase_tab(unsigned int num_chars, int after_tab,
-                          struct tty_struct *tty)
+                          struct n_tty_data *ldata)
 {
-       mutex_lock(&tty->echo_lock);
+       mutex_lock(&ldata->echo_lock);
 
-       add_echo_byte(ECHO_OP_START, tty);
-       add_echo_byte(ECHO_OP_ERASE_TAB, tty);
+       add_echo_byte(ECHO_OP_START, ldata);
+       add_echo_byte(ECHO_OP_ERASE_TAB, ldata);
 
        /* We only need to know this modulo 8 (tab spacing) */
        num_chars &= 7;
@@ -772,9 +810,9 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab,
        if (after_tab)
                num_chars |= 0x80;
 
-       add_echo_byte(num_chars, tty);
+       add_echo_byte(num_chars, ldata);
 
-       mutex_unlock(&tty->echo_lock);
+       mutex_unlock(&ldata->echo_lock);
 }
 
 /**
@@ -790,18 +828,16 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab,
  *     Locking: echo_lock to protect the echo buffer
  */
 
-static void echo_char_raw(unsigned char c, struct tty_struct *tty)
+static void echo_char_raw(unsigned char c, struct n_tty_data *ldata)
 {
-       mutex_lock(&tty->echo_lock);
-
+       mutex_lock(&ldata->echo_lock);
        if (c == ECHO_OP_START) {
-               add_echo_byte(ECHO_OP_START, tty);
-               add_echo_byte(ECHO_OP_START, tty);
+               add_echo_byte(ECHO_OP_START, ldata);
+               add_echo_byte(ECHO_OP_START, ldata);
        } else {
-               add_echo_byte(c, tty);
+               add_echo_byte(c, ldata);
        }
-
-       mutex_unlock(&tty->echo_lock);
+       mutex_unlock(&ldata->echo_lock);
 }
 
 /**
@@ -820,30 +856,32 @@ static void echo_char_raw(unsigned char c, struct tty_struct *tty)
 
 static void echo_char(unsigned char c, struct tty_struct *tty)
 {
-       mutex_lock(&tty->echo_lock);
+       struct n_tty_data *ldata = tty->disc_data;
+
+       mutex_lock(&ldata->echo_lock);
 
        if (c == ECHO_OP_START) {
-               add_echo_byte(ECHO_OP_START, tty);
-               add_echo_byte(ECHO_OP_START, tty);
+               add_echo_byte(ECHO_OP_START, ldata);
+               add_echo_byte(ECHO_OP_START, ldata);
        } else {
                if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t')
-                       add_echo_byte(ECHO_OP_START, tty);
-               add_echo_byte(c, tty);
+                       add_echo_byte(ECHO_OP_START, ldata);
+               add_echo_byte(c, ldata);
        }
 
-       mutex_unlock(&tty->echo_lock);
+       mutex_unlock(&ldata->echo_lock);
 }
 
 /**
  *     finish_erasing          -       complete erase
- *     @tty: tty doing the erase
+ *     @ldata: n_tty data
  */
 
-static inline void finish_erasing(struct tty_struct *tty)
+static inline void finish_erasing(struct n_tty_data *ldata)
 {
-       if (tty->erasing) {
-               echo_char_raw('/', tty);
-               tty->erasing = 0;
+       if (ldata->erasing) {
+               echo_char_raw('/', ldata);
+               ldata->erasing = 0;
        }
 }
 
@@ -861,12 +899,13 @@ static inline void finish_erasing(struct tty_struct *tty)
 
 static void eraser(unsigned char c, struct tty_struct *tty)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        enum { ERASE, WERASE, KILL } kill_type;
        int head, seen_alnums, cnt;
        unsigned long flags;
 
        /* FIXME: locking needed ? */
-       if (tty->read_head == tty->canon_head) {
+       if (ldata->read_head == ldata->canon_head) {
                /* process_output('\a', tty); */ /* what do you think? */
                return;
        }
@@ -876,24 +915,24 @@ static void eraser(unsigned char c, struct tty_struct *tty)
                kill_type = WERASE;
        else {
                if (!L_ECHO(tty)) {
-                       spin_lock_irqsave(&tty->read_lock, flags);
-                       tty->read_cnt -= ((tty->read_head - tty->canon_head) &
+                       spin_lock_irqsave(&ldata->read_lock, flags);
+                       ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) &
                                          (N_TTY_BUF_SIZE - 1));
-                       tty->read_head = tty->canon_head;
-                       spin_unlock_irqrestore(&tty->read_lock, flags);
+                       ldata->read_head = ldata->canon_head;
+                       spin_unlock_irqrestore(&ldata->read_lock, flags);
                        return;
                }
                if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
-                       spin_lock_irqsave(&tty->read_lock, flags);
-                       tty->read_cnt -= ((tty->read_head - tty->canon_head) &
+                       spin_lock_irqsave(&ldata->read_lock, flags);
+                       ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) &
                                          (N_TTY_BUF_SIZE - 1));
-                       tty->read_head = tty->canon_head;
-                       spin_unlock_irqrestore(&tty->read_lock, flags);
-                       finish_erasing(tty);
+                       ldata->read_head = ldata->canon_head;
+                       spin_unlock_irqrestore(&ldata->read_lock, flags);
+                       finish_erasing(ldata);
                        echo_char(KILL_CHAR(tty), tty);
                        /* Add a newline if ECHOK is on and ECHOKE is off. */
                        if (L_ECHOK(tty))
-                               echo_char_raw('\n', tty);
+                               echo_char_raw('\n', ldata);
                        return;
                }
                kill_type = KILL;
@@ -901,14 +940,14 @@ static void eraser(unsigned char c, struct tty_struct *tty)
 
        seen_alnums = 0;
        /* FIXME: Locking ?? */
-       while (tty->read_head != tty->canon_head) {
-               head = tty->read_head;
+       while (ldata->read_head != ldata->canon_head) {
+               head = ldata->read_head;
 
                /* erase a single possibly multibyte character */
                do {
                        head = (head - 1) & (N_TTY_BUF_SIZE-1);
-                       c = tty->read_buf[head];
-               } while (is_continuation(c, tty) && head != tty->canon_head);
+                       c = ldata->read_buf[head];
+               } while (is_continuation(c, tty) && head != ldata->canon_head);
 
                /* do not partially erase */
                if (is_continuation(c, tty))
@@ -921,30 +960,31 @@ static void eraser(unsigned char c, struct tty_struct *tty)
                        else if (seen_alnums)
                                break;
                }
-               cnt = (tty->read_head - head) & (N_TTY_BUF_SIZE-1);
-               spin_lock_irqsave(&tty->read_lock, flags);
-               tty->read_head = head;
-               tty->read_cnt -= cnt;
-               spin_unlock_irqrestore(&tty->read_lock, flags);
+               cnt = (ldata->read_head - head) & (N_TTY_BUF_SIZE-1);
+               spin_lock_irqsave(&ldata->read_lock, flags);
+               ldata->read_head = head;
+               ldata->read_cnt -= cnt;
+               spin_unlock_irqrestore(&ldata->read_lock, flags);
                if (L_ECHO(tty)) {
                        if (L_ECHOPRT(tty)) {
-                               if (!tty->erasing) {
-                                       echo_char_raw('\\', tty);
-                                       tty->erasing = 1;
+                               if (!ldata->erasing) {
+                                       echo_char_raw('\\', ldata);
+                                       ldata->erasing = 1;
                                }
                                /* if cnt > 1, output a multi-byte character */
                                echo_char(c, tty);
                                while (--cnt > 0) {
                                        head = (head+1) & (N_TTY_BUF_SIZE-1);
-                                       echo_char_raw(tty->read_buf[head], tty);
-                                       echo_move_back_col(tty);
+                                       echo_char_raw(ldata->read_buf[head],
+                                                       ldata);
+                                       echo_move_back_col(ldata);
                                }
                        } else if (kill_type == ERASE && !L_ECHOE(tty)) {
                                echo_char(ERASE_CHAR(tty), tty);
                        } else if (c == '\t') {
                                unsigned int num_chars = 0;
                                int after_tab = 0;
-                               unsigned long tail = tty->read_head;
+                               unsigned long tail = ldata->read_head;
 
                                /*
                                 * Count the columns used for characters
@@ -953,9 +993,9 @@ static void eraser(unsigned char c, struct tty_struct *tty)
                                 * This info is used to go back the correct
                                 * number of columns.
                                 */
-                               while (tail != tty->canon_head) {
+                               while (tail != ldata->canon_head) {
                                        tail = (tail-1) & (N_TTY_BUF_SIZE-1);
-                                       c = tty->read_buf[tail];
+                                       c = ldata->read_buf[tail];
                                        if (c == '\t') {
                                                after_tab = 1;
                                                break;
@@ -966,25 +1006,25 @@ static void eraser(unsigned char c, struct tty_struct *tty)
                                                num_chars++;
                                        }
                                }
-                               echo_erase_tab(num_chars, after_tab, tty);
+                               echo_erase_tab(num_chars, after_tab, ldata);
                        } else {
                                if (iscntrl(c) && L_ECHOCTL(tty)) {
-                                       echo_char_raw('\b', tty);
-                                       echo_char_raw(' ', tty);
-                                       echo_char_raw('\b', tty);
+                                       echo_char_raw('\b', ldata);
+                                       echo_char_raw(' ', ldata);
+                                       echo_char_raw('\b', ldata);
                                }
                                if (!iscntrl(c) || L_ECHOCTL(tty)) {
-                                       echo_char_raw('\b', tty);
-                                       echo_char_raw(' ', tty);
-                                       echo_char_raw('\b', tty);
+                                       echo_char_raw('\b', ldata);
+                                       echo_char_raw(' ', ldata);
+                                       echo_char_raw('\b', ldata);
                                }
                        }
                }
                if (kill_type == ERASE)
                        break;
        }
-       if (tty->read_head == tty->canon_head && L_ECHO(tty))
-               finish_erasing(tty);
+       if (ldata->read_head == ldata->canon_head && L_ECHO(tty))
+               finish_erasing(ldata);
 }
 
 /**
@@ -1023,6 +1063,8 @@ static inline void isig(int sig, struct tty_struct *tty, int flush)
 
 static inline void n_tty_receive_break(struct tty_struct *tty)
 {
+       struct n_tty_data *ldata = tty->disc_data;
+
        if (I_IGNBRK(tty))
                return;
        if (I_BRKINT(tty)) {
@@ -1030,10 +1072,10 @@ static inline void n_tty_receive_break(struct tty_struct *tty)
                return;
        }
        if (I_PARMRK(tty)) {
-               put_tty_queue('\377', tty);
-               put_tty_queue('\0', tty);
+               put_tty_queue('\377', ldata);
+               put_tty_queue('\0', ldata);
        }
-       put_tty_queue('\0', tty);
+       put_tty_queue('\0', ldata);
        wake_up_interruptible(&tty->read_wait);
 }
 
@@ -1052,16 +1094,17 @@ static inline void n_tty_receive_break(struct tty_struct *tty)
 
 static inline void n_tty_receive_overrun(struct tty_struct *tty)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        char buf[64];
 
-       tty->num_overrun++;
-       if (time_before(tty->overrun_time, jiffies - HZ) ||
-                       time_after(tty->overrun_time, jiffies)) {
+       ldata->num_overrun++;
+       if (time_after(jiffies, ldata->overrun_time + HZ) ||
+                       time_after(ldata->overrun_time, jiffies)) {
                printk(KERN_WARNING "%s: %d input overrun(s)\n",
                        tty_name(tty, buf),
-                       tty->num_overrun);
-               tty->overrun_time = jiffies;
-               tty->num_overrun = 0;
+                       ldata->num_overrun);
+               ldata->overrun_time = jiffies;
+               ldata->num_overrun = 0;
        }
 }
 
@@ -1076,16 +1119,18 @@ static inline void n_tty_receive_overrun(struct tty_struct *tty)
 static inline void n_tty_receive_parity_error(struct tty_struct *tty,
                                              unsigned char c)
 {
+       struct n_tty_data *ldata = tty->disc_data;
+
        if (I_IGNPAR(tty))
                return;
        if (I_PARMRK(tty)) {
-               put_tty_queue('\377', tty);
-               put_tty_queue('\0', tty);
-               put_tty_queue(c, tty);
+               put_tty_queue('\377', ldata);
+               put_tty_queue('\0', ldata);
+               put_tty_queue(c, ldata);
        } else  if (I_INPCK(tty))
-               put_tty_queue('\0', tty);
+               put_tty_queue('\0', ldata);
        else
-               put_tty_queue(c, tty);
+               put_tty_queue(c, ldata);
        wake_up_interruptible(&tty->read_wait);
 }
 
@@ -1101,11 +1146,12 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty,
 
 static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        unsigned long flags;
        int parmrk;
 
-       if (tty->raw) {
-               put_tty_queue(c, tty);
+       if (ldata->raw) {
+               put_tty_queue(c, ldata);
                return;
        }
 
@@ -1115,7 +1161,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
                c = tolower(c);
 
        if (L_EXTPROC(tty)) {
-               put_tty_queue(c, tty);
+               put_tty_queue(c, ldata);
                return;
        }
 
@@ -1143,26 +1189,26 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
         * handle specially, do shortcut processing to speed things
         * up.
         */
-       if (!test_bit(c, tty->process_char_map) || tty->lnext) {
-               tty->lnext = 0;
+       if (!test_bit(c, ldata->process_char_map) || ldata->lnext) {
+               ldata->lnext = 0;
                parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
-               if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) {
+               if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) {
                        /* beep if no space */
                        if (L_ECHO(tty))
                                process_output('\a', tty);
                        return;
                }
                if (L_ECHO(tty)) {
-                       finish_erasing(tty);
+                       finish_erasing(ldata);
                        /* Record the column of first canon char. */
-                       if (tty->canon_head == tty->read_head)
-                               echo_set_canon_col(tty);
+                       if (ldata->canon_head == ldata->read_head)
+                               echo_set_canon_col(ldata);
                        echo_char(c, tty);
                        process_echoes(tty);
                }
                if (parmrk)
-                       put_tty_queue(c, tty);
-               put_tty_queue(c, tty);
+                       put_tty_queue(c, ldata);
+               put_tty_queue(c, ldata);
                return;
        }
 
@@ -1218,7 +1264,7 @@ send_signal:
        } else if (c == '\n' && I_INLCR(tty))
                c = '\r';
 
-       if (tty->icanon) {
+       if (ldata->icanon) {
                if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
                    (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
                        eraser(c, tty);
@@ -1226,12 +1272,12 @@ send_signal:
                        return;
                }
                if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
-                       tty->lnext = 1;
+                       ldata->lnext = 1;
                        if (L_ECHO(tty)) {
-                               finish_erasing(tty);
+                               finish_erasing(ldata);
                                if (L_ECHOCTL(tty)) {
-                                       echo_char_raw('^', tty);
-                                       echo_char_raw('\b', tty);
+                                       echo_char_raw('^', ldata);
+                                       echo_char_raw('\b', ldata);
                                        process_echoes(tty);
                                }
                        }
@@ -1239,34 +1285,34 @@ send_signal:
                }
                if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&
                    L_IEXTEN(tty)) {
-                       unsigned long tail = tty->canon_head;
+                       unsigned long tail = ldata->canon_head;
 
-                       finish_erasing(tty);
+                       finish_erasing(ldata);
                        echo_char(c, tty);
-                       echo_char_raw('\n', tty);
-                       while (tail != tty->read_head) {
-                               echo_char(tty->read_buf[tail], tty);
+                       echo_char_raw('\n', ldata);
+                       while (tail != ldata->read_head) {
+                               echo_char(ldata->read_buf[tail], tty);
                                tail = (tail+1) & (N_TTY_BUF_SIZE-1);
                        }
                        process_echoes(tty);
                        return;
                }
                if (c == '\n') {
-                       if (tty->read_cnt >= N_TTY_BUF_SIZE) {
+                       if (ldata->read_cnt >= N_TTY_BUF_SIZE) {
                                if (L_ECHO(tty))
                                        process_output('\a', tty);
                                return;
                        }
                        if (L_ECHO(tty) || L_ECHONL(tty)) {
-                               echo_char_raw('\n', tty);
+                               echo_char_raw('\n', ldata);
                                process_echoes(tty);
                        }
                        goto handle_newline;
                }
                if (c == EOF_CHAR(tty)) {
-                       if (tty->read_cnt >= N_TTY_BUF_SIZE)
+                       if (ldata->read_cnt >= N_TTY_BUF_SIZE)
                                return;
-                       if (tty->canon_head != tty->read_head)
+                       if (ldata->canon_head != ldata->read_head)
                                set_bit(TTY_PUSH, &tty->flags);
                        c = __DISABLED_CHAR;
                        goto handle_newline;
@@ -1275,7 +1321,7 @@ send_signal:
                    (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {
                        parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty))
                                 ? 1 : 0;
-                       if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) {
+                       if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) {
                                if (L_ECHO(tty))
                                        process_output('\a', tty);
                                return;
@@ -1285,8 +1331,8 @@ send_signal:
                         */
                        if (L_ECHO(tty)) {
                                /* Record the column of first canon char. */
-                               if (tty->canon_head == tty->read_head)
-                                       echo_set_canon_col(tty);
+                               if (ldata->canon_head == ldata->read_head)
+                                       echo_set_canon_col(ldata);
                                echo_char(c, tty);
                                process_echoes(tty);
                        }
@@ -1295,15 +1341,15 @@ send_signal:
                         * EOL_CHAR and EOL2_CHAR?
                         */
                        if (parmrk)
-                               put_tty_queue(c, tty);
+                               put_tty_queue(c, ldata);
 
 handle_newline:
-                       spin_lock_irqsave(&tty->read_lock, flags);
-                       set_bit(tty->read_head, tty->read_flags);
-                       put_tty_queue_nolock(c, tty);
-                       tty->canon_head = tty->read_head;
-                       tty->canon_data++;
-                       spin_unlock_irqrestore(&tty->read_lock, flags);
+                       spin_lock_irqsave(&ldata->read_lock, flags);
+                       set_bit(ldata->read_head, ldata->read_flags);
+                       put_tty_queue_nolock(c, ldata);
+                       ldata->canon_head = ldata->read_head;
+                       ldata->canon_data++;
+                       spin_unlock_irqrestore(&ldata->read_lock, flags);
                        kill_fasync(&tty->fasync, SIGIO, POLL_IN);
                        if (waitqueue_active(&tty->read_wait))
                                wake_up_interruptible(&tty->read_wait);
@@ -1312,29 +1358,29 @@ handle_newline:
        }
 
        parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
-       if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) {
+       if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) {
                /* beep if no space */
                if (L_ECHO(tty))
                        process_output('\a', tty);
                return;
        }
        if (L_ECHO(tty)) {
-               finish_erasing(tty);
+               finish_erasing(ldata);
                if (c == '\n')
-                       echo_char_raw('\n', tty);
+                       echo_char_raw('\n', ldata);
                else {
                        /* Record the column of first canon char. */
-                       if (tty->canon_head == tty->read_head)
-                               echo_set_canon_col(tty);
+                       if (ldata->canon_head == ldata->read_head)
+                               echo_set_canon_col(ldata);
                        echo_char(c, tty);
                }
                process_echoes(tty);
        }
 
        if (parmrk)
-               put_tty_queue(c, tty);
+               put_tty_queue(c, ldata);
 
-       put_tty_queue(c, tty);
+       put_tty_queue(c, ldata);
 }
 
 
@@ -1369,33 +1415,31 @@ static void n_tty_write_wakeup(struct tty_struct *tty)
 static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
                              char *fp, int count)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        const unsigned char *p;
        char *f, flags = TTY_NORMAL;
        int     i;
        char    buf[64];
        unsigned long cpuflags;
 
-       if (!tty->read_buf)
-               return;
-
-       if (tty->real_raw) {
-               spin_lock_irqsave(&tty->read_lock, cpuflags);
-               i = min(N_TTY_BUF_SIZE - tty->read_cnt,
-                       N_TTY_BUF_SIZE - tty->read_head);
+       if (ldata->real_raw) {
+               spin_lock_irqsave(&ldata->read_lock, cpuflags);
+               i = min(N_TTY_BUF_SIZE - ldata->read_cnt,
+                       N_TTY_BUF_SIZE - ldata->read_head);
                i = min(count, i);
-               memcpy(tty->read_buf + tty->read_head, cp, i);
-               tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
-               tty->read_cnt += i;
+               memcpy(ldata->read_buf + ldata->read_head, cp, i);
+               ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
+               ldata->read_cnt += i;
                cp += i;
                count -= i;
 
-               i = min(N_TTY_BUF_SIZE - tty->read_cnt,
-                       N_TTY_BUF_SIZE - tty->read_head);
+               i = min(N_TTY_BUF_SIZE - ldata->read_cnt,
+                       N_TTY_BUF_SIZE - ldata->read_head);
                i = min(count, i);
-               memcpy(tty->read_buf + tty->read_head, cp, i);
-               tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
-               tty->read_cnt += i;
-               spin_unlock_irqrestore(&tty->read_lock, cpuflags);
+               memcpy(ldata->read_buf + ldata->read_head, cp, i);
+               ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
+               ldata->read_cnt += i;
+               spin_unlock_irqrestore(&ldata->read_lock, cpuflags);
        } else {
                for (i = count, p = cp, f = fp; i; i--, p++) {
                        if (f)
@@ -1426,7 +1470,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
 
        n_tty_set_room(tty);
 
-       if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) ||
+       if ((!ldata->icanon && (ldata->read_cnt >= tty->minimum_to_wake)) ||
                L_EXTPROC(tty)) {
                kill_fasync(&tty->fasync, SIGIO, POLL_IN);
                if (waitqueue_active(&tty->read_wait))
@@ -1470,25 +1514,25 @@ int is_ignored(int sig)
 
 static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        int canon_change = 1;
-       BUG_ON(!tty);
 
        if (old)
                canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON;
        if (canon_change) {
-               memset(&tty->read_flags, 0, sizeof tty->read_flags);
-               tty->canon_head = tty->read_tail;
-               tty->canon_data = 0;
-               tty->erasing = 0;
+               bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
+               ldata->canon_head = ldata->read_tail;
+               ldata->canon_data = 0;
+               ldata->erasing = 0;
        }
 
-       if (canon_change && !L_ICANON(tty) && tty->read_cnt)
+       if (canon_change && !L_ICANON(tty) && ldata->read_cnt)
                wake_up_interruptible(&tty->read_wait);
 
-       tty->icanon = (L_ICANON(tty) != 0);
+       ldata->icanon = (L_ICANON(tty) != 0);
        if (test_bit(TTY_HW_COOK_IN, &tty->flags)) {
-               tty->raw = 1;
-               tty->real_raw = 1;
+               ldata->raw = 1;
+               ldata->real_raw = 1;
                n_tty_set_room(tty);
                return;
        }
@@ -1496,51 +1540,51 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
            I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
            I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
            I_PARMRK(tty)) {
-               memset(tty->process_char_map, 0, 256/8);
+               bitmap_zero(ldata->process_char_map, 256);
 
                if (I_IGNCR(tty) || I_ICRNL(tty))
-                       set_bit('\r', tty->process_char_map);
+                       set_bit('\r', ldata->process_char_map);
                if (I_INLCR(tty))
-                       set_bit('\n', tty->process_char_map);
+                       set_bit('\n', ldata->process_char_map);
 
                if (L_ICANON(tty)) {
-                       set_bit(ERASE_CHAR(tty), tty->process_char_map);
-                       set_bit(KILL_CHAR(tty), tty->process_char_map);
-                       set_bit(EOF_CHAR(tty), tty->process_char_map);
-                       set_bit('\n', tty->process_char_map);
-                       set_bit(EOL_CHAR(tty), tty->process_char_map);
+                       set_bit(ERASE_CHAR(tty), ldata->process_char_map);
+                       set_bit(KILL_CHAR(tty), ldata->process_char_map);
+                       set_bit(EOF_CHAR(tty), ldata->process_char_map);
+                       set_bit('\n', ldata->process_char_map);
+                       set_bit(EOL_CHAR(tty), ldata->process_char_map);
                        if (L_IEXTEN(tty)) {
                                set_bit(WERASE_CHAR(tty),
-                                       tty->process_char_map);
+                                       ldata->process_char_map);
                                set_bit(LNEXT_CHAR(tty),
-                                       tty->process_char_map);
+                                       ldata->process_char_map);
                                set_bit(EOL2_CHAR(tty),
-                                       tty->process_char_map);
+                                       ldata->process_char_map);
                                if (L_ECHO(tty))
                                        set_bit(REPRINT_CHAR(tty),
-                                               tty->process_char_map);
+                                               ldata->process_char_map);
                        }
                }
                if (I_IXON(tty)) {
-                       set_bit(START_CHAR(tty), tty->process_char_map);
-                       set_bit(STOP_CHAR(tty), tty->process_char_map);
+                       set_bit(START_CHAR(tty), ldata->process_char_map);
+                       set_bit(STOP_CHAR(tty), ldata->process_char_map);
                }
                if (L_ISIG(tty)) {
-                       set_bit(INTR_CHAR(tty), tty->process_char_map);
-                       set_bit(QUIT_CHAR(tty), tty->process_char_map);
-                       set_bit(SUSP_CHAR(tty), tty->process_char_map);
+                       set_bit(INTR_CHAR(tty), ldata->process_char_map);
+                       set_bit(QUIT_CHAR(tty), ldata->process_char_map);
+                       set_bit(SUSP_CHAR(tty), ldata->process_char_map);
                }
-               clear_bit(__DISABLED_CHAR, tty->process_char_map);
-               tty->raw = 0;
-               tty->real_raw = 0;
+               clear_bit(__DISABLED_CHAR, ldata->process_char_map);
+               ldata->raw = 0;
+               ldata->real_raw = 0;
        } else {
-               tty->raw = 1;
+               ldata->raw = 1;
                if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) &&
                    (I_IGNPAR(tty) || !I_INPCK(tty)) &&
                    (tty->driver->flags & TTY_DRIVER_REAL_RAW))
-                       tty->real_raw = 1;
+                       ldata->real_raw = 1;
                else
-                       tty->real_raw = 0;
+                       ldata->real_raw = 0;
        }
        n_tty_set_room(tty);
        /* The termios change make the tty ready for I/O */
@@ -1560,15 +1604,13 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
 
 static void n_tty_close(struct tty_struct *tty)
 {
+       struct n_tty_data *ldata = tty->disc_data;
+
        n_tty_flush_buffer(tty);
-       if (tty->read_buf) {
-               kfree(tty->read_buf);
-               tty->read_buf = NULL;
-       }
-       if (tty->echo_buf) {
-               kfree(tty->echo_buf);
-               tty->echo_buf = NULL;
-       }
+       kfree(ldata->read_buf);
+       kfree(ldata->echo_buf);
+       kfree(ldata);
+       tty->disc_data = NULL;
 }
 
 /**
@@ -1583,37 +1625,50 @@ static void n_tty_close(struct tty_struct *tty)
 
 static int n_tty_open(struct tty_struct *tty)
 {
-       if (!tty)
-               return -EINVAL;
+       struct n_tty_data *ldata;
+
+       ldata = kzalloc(sizeof(*ldata), GFP_KERNEL);
+       if (!ldata)
+               goto err;
+
+       ldata->overrun_time = jiffies;
+       mutex_init(&ldata->atomic_read_lock);
+       mutex_init(&ldata->output_lock);
+       mutex_init(&ldata->echo_lock);
+       spin_lock_init(&ldata->read_lock);
 
        /* These are ugly. Currently a malloc failure here can panic */
-       if (!tty->read_buf) {
-               tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
-               if (!tty->read_buf)
-                       return -ENOMEM;
-       }
-       if (!tty->echo_buf) {
-               tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
+       ldata->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
+       ldata->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
+       if (!ldata->read_buf || !ldata->echo_buf)
+               goto err_free_bufs;
 
-               if (!tty->echo_buf)
-                       return -ENOMEM;
-       }
+       tty->disc_data = ldata;
        reset_buffer_flags(tty);
        tty_unthrottle(tty);
-       tty->column = 0;
+       ldata->column = 0;
        n_tty_set_termios(tty, NULL);
        tty->minimum_to_wake = 1;
        tty->closing = 0;
+
        return 0;
+err_free_bufs:
+       kfree(ldata->read_buf);
+       kfree(ldata->echo_buf);
+       kfree(ldata);
+err:
+       return -ENOMEM;
 }
 
 static inline int input_available_p(struct tty_struct *tty, int amt)
 {
+       struct n_tty_data *ldata = tty->disc_data;
+
        tty_flush_to_ldisc(tty);
-       if (tty->icanon && !L_EXTPROC(tty)) {
-               if (tty->canon_data)
+       if (ldata->icanon && !L_EXTPROC(tty)) {
+               if (ldata->canon_data)
                        return 1;
-       } else if (tty->read_cnt >= (amt ? amt : 1))
+       } else if (ldata->read_cnt >= (amt ? amt : 1))
                return 1;
 
        return 0;
@@ -1632,7 +1687,7 @@ static inline int input_available_p(struct tty_struct *tty, int amt)
  *     buffer, and once to drain the space from the (physical) beginning of
  *     the buffer to head pointer.
  *
- *     Called under the tty->atomic_read_lock sem
+ *     Called under the ldata->atomic_read_lock sem
  *
  */
 
@@ -1641,29 +1696,31 @@ static int copy_from_read_buf(struct tty_struct *tty,
                                      size_t *nr)
 
 {
+       struct n_tty_data *ldata = tty->disc_data;
        int retval;
        size_t n;
        unsigned long flags;
        bool is_eof;
 
        retval = 0;
-       spin_lock_irqsave(&tty->read_lock, flags);
-       n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail);
+       spin_lock_irqsave(&ldata->read_lock, flags);
+       n = min(ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_tail);
        n = min(*nr, n);
-       spin_unlock_irqrestore(&tty->read_lock, flags);
+       spin_unlock_irqrestore(&ldata->read_lock, flags);
        if (n) {
-               retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
+               retval = copy_to_user(*b, &ldata->read_buf[ldata->read_tail], n);
                n -= retval;
                is_eof = n == 1 &&
-                       tty->read_buf[tty->read_tail] == EOF_CHAR(tty);
-               tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n);
-               spin_lock_irqsave(&tty->read_lock, flags);
-               tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
-               tty->read_cnt -= n;
+                       ldata->read_buf[ldata->read_tail] == EOF_CHAR(tty);
+               tty_audit_add_data(tty, &ldata->read_buf[ldata->read_tail], n,
+                               ldata->icanon);
+               spin_lock_irqsave(&ldata->read_lock, flags);
+               ldata->read_tail = (ldata->read_tail + n) & (N_TTY_BUF_SIZE-1);
+               ldata->read_cnt -= n;
                /* Turn single EOF into zero-length read */
-               if (L_EXTPROC(tty) && tty->icanon && is_eof && !tty->read_cnt)
+               if (L_EXTPROC(tty) && ldata->icanon && is_eof && !ldata->read_cnt)
                        n = 0;
-               spin_unlock_irqrestore(&tty->read_lock, flags);
+               spin_unlock_irqrestore(&ldata->read_lock, flags);
                *b += n;
                *nr -= n;
        }
@@ -1730,6 +1787,7 @@ static int job_control(struct tty_struct *tty, struct file *file)
 static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
                         unsigned char __user *buf, size_t nr)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        unsigned char __user *b = buf;
        DECLARE_WAITQUEUE(wait, current);
        int c;
@@ -1741,17 +1799,13 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
        int packet;
 
 do_it_again:
-
-       if (WARN_ON(!tty->read_buf))
-               return -EAGAIN;
-
        c = job_control(tty, file);
        if (c < 0)
                return c;
 
        minimum = time = 0;
        timeout = MAX_SCHEDULE_TIMEOUT;
-       if (!tty->icanon) {
+       if (!ldata->icanon) {
                time = (HZ / 10) * TIME_CHAR(tty);
                minimum = MIN_CHAR(tty);
                if (minimum) {
@@ -1774,10 +1828,10 @@ do_it_again:
         *      Internal serialization of reads.
         */
        if (file->f_flags & O_NONBLOCK) {
-               if (!mutex_trylock(&tty->atomic_read_lock))
+               if (!mutex_trylock(&ldata->atomic_read_lock))
                        return -EAGAIN;
        } else {
-               if (mutex_lock_interruptible(&tty->atomic_read_lock))
+               if (mutex_lock_interruptible(&ldata->atomic_read_lock))
                        return -ERESTARTSYS;
        }
        packet = tty->packet;
@@ -1830,7 +1884,6 @@ do_it_again:
                        /* FIXME: does n_tty_set_room need locking ? */
                        n_tty_set_room(tty);
                        timeout = schedule_timeout(timeout);
-                       BUG_ON(!tty->read_buf);
                        continue;
                }
                __set_current_state(TASK_RUNNING);
@@ -1845,45 +1898,45 @@ do_it_again:
                        nr--;
                }
 
-               if (tty->icanon && !L_EXTPROC(tty)) {
+               if (ldata->icanon && !L_EXTPROC(tty)) {
                        /* N.B. avoid overrun if nr == 0 */
-                       spin_lock_irqsave(&tty->read_lock, flags);
-                       while (nr && tty->read_cnt) {
+                       spin_lock_irqsave(&ldata->read_lock, flags);
+                       while (nr && ldata->read_cnt) {
                                int eol;
 
-                               eol = test_and_clear_bit(tty->read_tail,
-                                               tty->read_flags);
-                               c = tty->read_buf[tty->read_tail];
-                               tty->read_tail = ((tty->read_tail+1) &
+                               eol = test_and_clear_bit(ldata->read_tail,
+                                               ldata->read_flags);
+                               c = ldata->read_buf[ldata->read_tail];
+                               ldata->read_tail = ((ldata->read_tail+1) &
                                                  (N_TTY_BUF_SIZE-1));
-                               tty->read_cnt--;
+                               ldata->read_cnt--;
                                if (eol) {
                                        /* this test should be redundant:
                                         * we shouldn't be reading data if
                                         * canon_data is 0
                                         */
-                                       if (--tty->canon_data < 0)
-                                               tty->canon_data = 0;
+                                       if (--ldata->canon_data < 0)
+                                               ldata->canon_data = 0;
                                }
-                               spin_unlock_irqrestore(&tty->read_lock, flags);
+                               spin_unlock_irqrestore(&ldata->read_lock, flags);
 
                                if (!eol || (c != __DISABLED_CHAR)) {
                                        if (tty_put_user(tty, c, b++)) {
                                                retval = -EFAULT;
                                                b--;
-                                               spin_lock_irqsave(&tty->read_lock, flags);
+                                               spin_lock_irqsave(&ldata->read_lock, flags);
                                                break;
                                        }
                                        nr--;
                                }
                                if (eol) {
                                        tty_audit_push(tty);
-                                       spin_lock_irqsave(&tty->read_lock, flags);
+                                       spin_lock_irqsave(&ldata->read_lock, flags);
                                        break;
                                }
-                               spin_lock_irqsave(&tty->read_lock, flags);
+                               spin_lock_irqsave(&ldata->read_lock, flags);
                        }
-                       spin_unlock_irqrestore(&tty->read_lock, flags);
+                       spin_unlock_irqrestore(&ldata->read_lock, flags);
                        if (retval)
                                break;
                } else {
@@ -1915,7 +1968,7 @@ do_it_again:
                if (time)
                        timeout = time;
        }
-       mutex_unlock(&tty->atomic_read_lock);
+       mutex_unlock(&ldata->atomic_read_lock);
        remove_wait_queue(&tty->read_wait, &wait);
 
        if (!waitqueue_active(&tty->read_wait))
@@ -2076,19 +2129,19 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
        return mask;
 }
 
-static unsigned long inq_canon(struct tty_struct *tty)
+static unsigned long inq_canon(struct n_tty_data *ldata)
 {
        int nr, head, tail;
 
-       if (!tty->canon_data)
+       if (!ldata->canon_data)
                return 0;
-       head = tty->canon_head;
-       tail = tty->read_tail;
+       head = ldata->canon_head;
+       tail = ldata->read_tail;
        nr = (head - tail) & (N_TTY_BUF_SIZE-1);
        /* Skip EOF-chars.. */
        while (head != tail) {
-               if (test_bit(tail, tty->read_flags) &&
-                   tty->read_buf[tail] == __DISABLED_CHAR)
+               if (test_bit(tail, ldata->read_flags) &&
+                   ldata->read_buf[tail] == __DISABLED_CHAR)
                        nr--;
                tail = (tail+1) & (N_TTY_BUF_SIZE-1);
        }
@@ -2098,6 +2151,7 @@ static unsigned long inq_canon(struct tty_struct *tty)
 static int n_tty_ioctl(struct tty_struct *tty, struct file *file,
                       unsigned int cmd, unsigned long arg)
 {
+       struct n_tty_data *ldata = tty->disc_data;
        int retval;
 
        switch (cmd) {
@@ -2105,9 +2159,9 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file,
                return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
        case TIOCINQ:
                /* FIXME: Locking */
-               retval = tty->read_cnt;
+               retval = ldata->read_cnt;
                if (L_ICANON(tty))
-                       retval = inq_canon(tty);
+                       retval = inq_canon(ldata);
                return put_user(retval, (unsigned int __user *) arg);
        default:
                return n_tty_ioctl_helper(tty, file, cmd, arg);
index a82b399..0ce0b3e 100644 (file)
@@ -4,9 +4,6 @@
  *  Added support for a Unix98-style ptmx device.
  *    -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
  *
- *  When reading this code see also fs/devpts. In particular note that the
- *  driver_data field is used by the devpts side as a binding to the devpts
- *  inode.
  */
 
 #include <linux/module.h>
@@ -59,7 +56,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
 #ifdef CONFIG_UNIX98_PTYS
                if (tty->driver == ptm_driver) {
                        mutex_lock(&devpts_mutex);
-                       devpts_pty_kill(tty->link);
+                       devpts_pty_kill(tty->link->driver_data);
                        mutex_unlock(&devpts_mutex);
                }
 #endif
@@ -96,7 +93,7 @@ static void pty_unthrottle(struct tty_struct *tty)
 
 static int pty_space(struct tty_struct *to)
 {
-       int n = 8192 - to->buf.memory_used;
+       int n = 8192 - to->port->buf.memory_used;
        if (n < 0)
                return 0;
        return n;
@@ -174,6 +171,41 @@ static int pty_set_lock(struct tty_struct *tty, int __user *arg)
        return 0;
 }
 
+static int pty_get_lock(struct tty_struct *tty, int __user *arg)
+{
+       int locked = test_bit(TTY_PTY_LOCK, &tty->flags);
+       return put_user(locked, arg);
+}
+
+/* Set the packet mode on a pty */
+static int pty_set_pktmode(struct tty_struct *tty, int __user *arg)
+{
+       unsigned long flags;
+       int pktmode;
+
+       if (get_user(pktmode, arg))
+               return -EFAULT;
+
+       spin_lock_irqsave(&tty->ctrl_lock, flags);
+       if (pktmode) {
+               if (!tty->packet) {
+                       tty->packet = 1;
+                       tty->link->ctrl_status = 0;
+               }
+       } else
+               tty->packet = 0;
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+
+       return 0;
+}
+
+/* Get the packet mode of a pty */
+static int pty_get_pktmode(struct tty_struct *tty, int __user *arg)
+{
+       int pktmode = tty->packet;
+       return put_user(pktmode, arg);
+}
+
 /* Send a signal to the slave */
 static int pty_signal(struct tty_struct *tty, int sig)
 {
@@ -348,6 +380,7 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,
        tty_port_init(ports[1]);
        o_tty->port = ports[0];
        tty->port = ports[1];
+       o_tty->port->itty = o_tty;
 
        tty_driver_kref_get(driver);
        tty->count++;
@@ -366,8 +399,15 @@ err:
        return retval;
 }
 
+/* this is called once with whichever end is closed last */
+static void pty_unix98_shutdown(struct tty_struct *tty)
+{
+       devpts_kill_index(tty->driver_data, tty->index);
+}
+
 static void pty_cleanup(struct tty_struct *tty)
 {
+       tty->port->itty = NULL;
        kfree(tty->port);
 }
 
@@ -393,6 +433,12 @@ static int pty_bsd_ioctl(struct tty_struct *tty,
        switch (cmd) {
        case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
                return pty_set_lock(tty, (int __user *) arg);
+       case TIOCGPTLCK: /* Get PT Lock status */
+               return pty_get_lock(tty, (int __user *)arg);
+       case TIOCPKT: /* Set PT packet mode */
+               return pty_set_pktmode(tty, (int __user *)arg);
+       case TIOCGPKT: /* Get PT packet mode */
+               return pty_get_pktmode(tty, (int __user *)arg);
        case TIOCSIG:    /* Send signal to other side of pty */
                return pty_signal(tty, (int) arg);
        }
@@ -507,6 +553,12 @@ static int pty_unix98_ioctl(struct tty_struct *tty,
        switch (cmd) {
        case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
                return pty_set_lock(tty, (int __user *)arg);
+       case TIOCGPTLCK: /* Get PT Lock status */
+               return pty_get_lock(tty, (int __user *)arg);
+       case TIOCPKT: /* Set PT packet mode */
+               return pty_set_pktmode(tty, (int __user *)arg);
+       case TIOCGPKT: /* Get PT packet mode */
+               return pty_get_pktmode(tty, (int __user *)arg);
        case TIOCGPTN: /* Get PT Number */
                return put_user(tty->index, (unsigned int __user *)arg);
        case TIOCSIG:    /* Send signal to other side of pty */
@@ -547,7 +599,7 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
        struct tty_struct *tty;
 
        mutex_lock(&devpts_mutex);
-       tty = devpts_get_tty(pts_inode, idx);
+       tty = devpts_get_priv(pts_inode);
        mutex_unlock(&devpts_mutex);
        /* Master must be open before slave */
        if (!tty)
@@ -581,6 +633,7 @@ static const struct tty_operations ptm_unix98_ops = {
        .set_termios = pty_set_termios,
        .ioctl = pty_unix98_ioctl,
        .resize = pty_resize,
+       .shutdown = pty_unix98_shutdown,
        .cleanup = pty_cleanup
 };
 
@@ -596,6 +649,7 @@ static const struct tty_operations pty_unix98_ops = {
        .chars_in_buffer = pty_chars_in_buffer,
        .unthrottle = pty_unthrottle,
        .set_termios = pty_set_termios,
+       .shutdown = pty_unix98_shutdown,
        .cleanup = pty_cleanup,
 };
 
@@ -614,6 +668,7 @@ static const struct tty_operations pty_unix98_ops = {
 static int ptmx_open(struct inode *inode, struct file *filp)
 {
        struct tty_struct *tty;
+       struct inode *slave_inode;
        int retval;
        int index;
 
@@ -650,15 +705,21 @@ static int ptmx_open(struct inode *inode, struct file *filp)
 
        tty_add_file(tty, filp);
 
-       retval = devpts_pty_new(inode, tty->link);
-       if (retval)
+       slave_inode = devpts_pty_new(inode,
+                       MKDEV(UNIX98_PTY_SLAVE_MAJOR, index), index,
+                       tty->link);
+       if (IS_ERR(slave_inode)) {
+               retval = PTR_ERR(slave_inode);
                goto err_release;
+       }
 
        retval = ptm_driver->ops->open(tty, filp);
        if (retval)
                goto err_release;
 
        tty_unlock(tty);
+       tty->driver_data = inode;
+       tty->link->driver_data = slave_inode;
        return 0;
 err_release:
        tty_unlock(tty);
index 3ba4234..5ccbd90 100644 (file)
@@ -2349,16 +2349,14 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
                        serial_port_out(port, UART_EFR, efr);
        }
 
-#ifdef CONFIG_ARCH_OMAP1
        /* Workaround to enable 115200 baud on OMAP1510 internal ports */
-       if (cpu_is_omap1510() && is_omap_port(up)) {
+       if (is_omap1510_8250(up)) {
                if (baud == 115200) {
                        quot = 1;
                        serial_port_out(port, UART_OMAP_OSC_12M_SEL, 1);
                } else
                        serial_port_out(port, UART_OMAP_OSC_12M_SEL, 0);
        }
-#endif
 
        /*
         * For NatSemi, switch to bank 2 not bank 1, to avoid resetting EXCR2,
@@ -2439,10 +2437,9 @@ static unsigned int serial8250_port_size(struct uart_8250_port *pt)
 {
        if (pt->port.iotype == UPIO_AU)
                return 0x1000;
-#ifdef CONFIG_ARCH_OMAP1
-       if (is_omap_port(pt))
+       if (is_omap1_8250(pt))
                return 0x16 << pt->port.regshift;
-#endif
+
        return 8 << pt->port.regshift;
 }
 
index 5a76f9c..3b4ea84 100644 (file)
@@ -106,3 +106,39 @@ static inline int serial8250_pnp_init(void) { return 0; }
 static inline void serial8250_pnp_exit(void) { }
 #endif
 
+#ifdef CONFIG_ARCH_OMAP1
+static inline int is_omap1_8250(struct uart_8250_port *pt)
+{
+       int res;
+
+       switch (pt->port.mapbase) {
+       case OMAP1_UART1_BASE:
+       case OMAP1_UART2_BASE:
+       case OMAP1_UART3_BASE:
+               res = 1;
+               break;
+       default:
+               res = 0;
+               break;
+       }
+
+       return res;
+}
+
+static inline int is_omap1510_8250(struct uart_8250_port *pt)
+{
+       if (!cpu_is_omap1510())
+               return 0;
+
+       return is_omap1_8250(pt);
+}
+#else
+static inline int is_omap1_8250(struct uart_8250_port *pt)
+{
+       return 0;
+}
+static inline int is_omap1510_8250(struct uart_8250_port *pt)
+{
+       return 0;
+}
+#endif
index c3b2ec0..b19b8c5 100644 (file)
@@ -161,6 +161,29 @@ static int __devexit dw8250_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int dw8250_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct dw8250_data *data = platform_get_drvdata(pdev);
+
+       serial8250_suspend_port(data->line);
+
+       return 0;
+}
+
+static int dw8250_resume(struct platform_device *pdev)
+{
+       struct dw8250_data *data = platform_get_drvdata(pdev);
+
+       serial8250_resume_port(data->line);
+
+       return 0;
+}
+#else
+#define dw8250_suspend NULL
+#define dw8250_resume NULL
+#endif /* CONFIG_PM */
+
 static const struct of_device_id dw8250_match[] = {
        { .compatible = "snps,dw-apb-uart" },
        { /* Sentinel */ }
@@ -175,6 +198,8 @@ static struct platform_driver dw8250_platform_driver = {
        },
        .probe                  = dw8250_probe,
        .remove                 = __devexit_p(dw8250_remove),
+       .suspend                = dw8250_suspend,
+       .resume                 = dw8250_resume,
 };
 
 module_platform_driver(dw8250_platform_driver);
index eaafb98..843a150 100644 (file)
@@ -140,7 +140,7 @@ static void __init init_port(struct early_serial8250_device *device)
        serial_out(port, UART_FCR, 0);          /* no fifo */
        serial_out(port, UART_MCR, 0x3);        /* DTR + RTS */
 
-       divisor = port->uartclk / (16 * device->baud);
+       divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud);
        c = serial_in(port, UART_LCR);
        serial_out(port, UART_LCR, c | UART_LCR_DLAB);
        serial_out(port, UART_DLL, divisor & 0xff);
index 17b7d26..cec8852 100644 (file)
@@ -1068,7 +1068,7 @@ ce4100_serial_setup(struct serial_private *priv,
 {
        int ret;
 
-       ret = setup_port(priv, port, 0, 0, board->reg_shift);
+       ret = setup_port(priv, port, idx, 0, board->reg_shift);
        port->port.iotype = UPIO_MEM32;
        port->port.type = PORT_XSCALE;
        port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
@@ -2658,8 +2658,8 @@ static struct pciserial_board pci_boards[] __devinitdata = {
                .first_offset   = 0x1000,
        },
        [pbn_ce4100_1_115200] = {
-               .flags          = FL_BASE0,
-               .num_ports      = 1,
+               .flags          = FL_BASE_BARS,
+               .num_ports      = 2,
                .base_baud      = 921600,
                .reg_shift      = 2,
        },
index d7e1ede..7fca402 100644 (file)
@@ -56,8 +56,7 @@
 #include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/sizes.h>
-
-#include <asm/io.h>
+#include <linux/io.h>
 
 #define UART_NR                        14
 
@@ -1973,7 +1972,8 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
                goto out;
        }
 
-       uap = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL);
+       uap = devm_kzalloc(&dev->dev, sizeof(struct uart_amba_port),
+                          GFP_KERNEL);
        if (uap == NULL) {
                ret = -ENOMEM;
                goto out;
@@ -1981,16 +1981,17 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
 
        i = pl011_probe_dt_alias(i, &dev->dev);
 
-       base = ioremap(dev->res.start, resource_size(&dev->res));
+       base = devm_ioremap(&dev->dev, dev->res.start,
+                           resource_size(&dev->res));
        if (!base) {
                ret = -ENOMEM;
-               goto free;
+               goto out;
        }
 
        uap->pinctrl = devm_pinctrl_get(&dev->dev);
        if (IS_ERR(uap->pinctrl)) {
                ret = PTR_ERR(uap->pinctrl);
-               goto unmap;
+               goto out;
        }
        uap->pins_default = pinctrl_lookup_state(uap->pinctrl,
                                                 PINCTRL_STATE_DEFAULT);
@@ -2002,10 +2003,10 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
        if (IS_ERR(uap->pins_sleep))
                dev_dbg(&dev->dev, "could not get sleep pinstate\n");
 
-       uap->clk = clk_get(&dev->dev, NULL);
+       uap->clk = devm_clk_get(&dev->dev, NULL);
        if (IS_ERR(uap->clk)) {
                ret = PTR_ERR(uap->clk);
-               goto unmap;
+               goto out;
        }
 
        uap->vendor = vendor;
@@ -2038,11 +2039,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
                amba_set_drvdata(dev, NULL);
                amba_ports[i] = NULL;
                pl011_dma_remove(uap);
-               clk_put(uap->clk);
- unmap:
-               iounmap(base);
- free:
-               kfree(uap);
        }
  out:
        return ret;
@@ -2062,9 +2058,6 @@ static int pl011_remove(struct amba_device *dev)
                        amba_ports[i] = NULL;
 
        pl011_dma_remove(uap);
-       iounmap(uap->port.membase);
-       clk_put(uap->clk);
-       kfree(uap);
        return 0;
 }
 
index d0f719f..a0a6db5 100644 (file)
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
 #endif
 
 #include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/sysrq.h>
-#include <linux/spinlock.h>
 #include <linux/device.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
+#include <linux/console.h>
 #include <linux/serial_core.h>
 #include <linux/serial.h>
 #include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
 
 #include <mach/hardware.h>
-#include <asm/irq.h>
-
-#define UART_NR                2
-
-#define SERIAL_CLPS711X_MAJOR  204
-#define SERIAL_CLPS711X_MINOR  40
-#define SERIAL_CLPS711X_NR     UART_NR
-
-/*
- * We use the relevant SYSCON register as a base address for these ports.
- */
-#define UBRLCR(port)           ((port)->iobase + UBRLCR1 - SYSCON1)
-#define UARTDR(port)           ((port)->iobase + UARTDR1 - SYSCON1)
-#define SYSFLG(port)           ((port)->iobase + SYSFLG1 - SYSCON1)
-#define SYSCON(port)           ((port)->iobase + SYSCON1 - SYSCON1)
-
-#define TX_IRQ(port)           ((port)->irq)
-#define RX_IRQ(port)           ((port)->irq + 1)
 
-#define UART_ANY_ERR           (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR)
-
-#define tx_enabled(port)       ((port)->unused[0])
+#define UART_CLPS711X_NAME     "uart-clps711x"
+#define UART_CLPS711X_NR       2
+#define UART_CLPS711X_MAJOR    204
+#define UART_CLPS711X_MINOR    40
+
+#define UBRLCR(port)           ((port)->line ? UBRLCR2 : UBRLCR1)
+#define UARTDR(port)           ((port)->line ? UARTDR2 : UARTDR1)
+#define SYSFLG(port)           ((port)->line ? SYSFLG2 : SYSFLG1)
+#define SYSCON(port)           ((port)->line ? SYSCON2 : SYSCON1)
+#define TX_IRQ(port)           ((port)->line ? IRQ_UTXINT2 : IRQ_UTXINT1)
+#define RX_IRQ(port)           ((port)->line ? IRQ_URXINT2 : IRQ_URXINT1)
+
+struct clps711x_port {
+       struct uart_driver      uart;
+       struct clk              *uart_clk;
+       struct uart_port        port[UART_CLPS711X_NR];
+       int                     tx_enabled[UART_CLPS711X_NR];
+#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
+       struct console          console;
+#endif
+};
 
-static void clps711xuart_stop_tx(struct uart_port *port)
+static void uart_clps711x_stop_tx(struct uart_port *port)
 {
-       if (tx_enabled(port)) {
+       struct clps711x_port *s = dev_get_drvdata(port->dev);
+
+       if (s->tx_enabled[port->line]) {
                disable_irq(TX_IRQ(port));
-               tx_enabled(port) = 0;
+               s->tx_enabled[port->line] = 0;
        }
 }
 
-static void clps711xuart_start_tx(struct uart_port *port)
+static void uart_clps711x_start_tx(struct uart_port *port)
 {
-       if (!tx_enabled(port)) {
+       struct clps711x_port *s = dev_get_drvdata(port->dev);
+
+       if (!s->tx_enabled[port->line]) {
                enable_irq(TX_IRQ(port));
-               tx_enabled(port) = 1;
+               s->tx_enabled[port->line] = 1;
        }
 }
 
-static void clps711xuart_stop_rx(struct uart_port *port)
+static void uart_clps711x_stop_rx(struct uart_port *port)
 {
        disable_irq(RX_IRQ(port));
 }
 
-static void clps711xuart_enable_ms(struct uart_port *port)
+static void uart_clps711x_enable_ms(struct uart_port *port)
 {
+       /* Do nothing */
 }
 
-static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id)
+static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id)
 {
        struct uart_port *port = dev_id;
-       struct tty_struct *tty = port->state->port.tty;
+       struct tty_struct *tty = tty_port_tty_get(&port->state->port);
        unsigned int status, ch, flg;
 
-       status = clps_readl(SYSFLG(port));
-       while (!(status & SYSFLG_URXFE)) {
-               ch = clps_readl(UARTDR(port));
+       if (!tty)
+               return IRQ_HANDLED;
 
-               port->icount.rx++;
+       for (;;) {
+               status = clps_readl(SYSFLG(port));
+               if (status & SYSFLG_URXFE)
+                       break;
+
+               ch = clps_readw(UARTDR(port));
+               status = ch & (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR);
+               ch &= 0xff;
 
+               port->icount.rx++;
                flg = TTY_NORMAL;
 
-               /*
-                * Note that the error handling code is
-                * out of the main execution path
-                */
-               if (unlikely(ch & UART_ANY_ERR)) {
-                       if (ch & UARTDR_PARERR)
+               if (unlikely(status)) {
+                       if (status & UARTDR_PARERR)
                                port->icount.parity++;
-                       else if (ch & UARTDR_FRMERR)
+                       else if (status & UARTDR_FRMERR)
                                port->icount.frame++;
-                       if (ch & UARTDR_OVERR)
+                       else if (status & UARTDR_OVERR)
                                port->icount.overrun++;
 
-                       ch &= port->read_status_mask;
+                       status &= port->read_status_mask;
 
-                       if (ch & UARTDR_PARERR)
+                       if (status & UARTDR_PARERR)
                                flg = TTY_PARITY;
-                       else if (ch & UARTDR_FRMERR)
+                       else if (status & UARTDR_FRMERR)
                                flg = TTY_FRAME;
-
-#ifdef SUPPORT_SYSRQ
-                       port->sysrq = 0;
-#endif
+                       else if (status & UARTDR_OVERR)
+                               flg = TTY_OVERRUN;
                }
 
                if (uart_handle_sysrq_char(port, ch))
-                       goto ignore_char;
+                       continue;
 
-               /*
-                * CHECK: does overrun affect the current character?
-                * ASSUMPTION: it does not.
-                */
-               uart_insert_char(port, ch, UARTDR_OVERR, ch, flg);
+               if (status & port->ignore_status_mask)
+                       continue;
 
-       ignore_char:
-               status = clps_readl(SYSFLG(port));
+               uart_insert_char(port, status, UARTDR_OVERR, ch, flg);
        }
+
        tty_flip_buffer_push(tty);
+
+       tty_kref_put(tty);
+
        return IRQ_HANDLED;
 }
 
-static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id)
+static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
 {
        struct uart_port *port = dev_id;
+       struct clps711x_port *s = dev_get_drvdata(port->dev);
        struct circ_buf *xmit = &port->state->xmit;
-       int count;
 
        if (port->x_char) {
-               clps_writel(port->x_char, UARTDR(port));
+               clps_writew(port->x_char, UARTDR(port));
                port->icount.tx++;
                port->x_char = 0;
                return IRQ_HANDLED;
        }
 
-       if (uart_circ_empty(xmit) || uart_tx_stopped(port))
-               goto disable_tx_irq;
+       if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+               disable_irq_nosync(TX_IRQ(port));
+               s->tx_enabled[port->line] = 0;
+               return IRQ_HANDLED;
+       }
 
-       count = port->fifosize >> 1;
-       do {
-               clps_writel(xmit->buf[xmit->tail], UARTDR(port));
+       while (!uart_circ_empty(xmit)) {
+               clps_writew(xmit->buf[xmit->tail], UARTDR(port));
                xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
                port->icount.tx++;
-               if (uart_circ_empty(xmit))
+               if (clps_readl(SYSFLG(port) & SYSFLG_UTXFF))
                        break;
-       } while (--count > 0);
+       }
 
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
                uart_write_wakeup(port);
 
-       if (uart_circ_empty(xmit)) {
-       disable_tx_irq:
-               disable_irq_nosync(TX_IRQ(port));
-               tx_enabled(port) = 0;
-       }
-
        return IRQ_HANDLED;
 }
 
-static unsigned int clps711xuart_tx_empty(struct uart_port *port)
+static unsigned int uart_clps711x_tx_empty(struct uart_port *port)
 {
-       unsigned int status = clps_readl(SYSFLG(port));
-       return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT;
+       return (clps_readl(SYSFLG(port) & SYSFLG_UBUSY)) ? 0 : TIOCSER_TEMT;
 }
 
-static unsigned int clps711xuart_get_mctrl(struct uart_port *port)
+static unsigned int uart_clps711x_get_mctrl(struct uart_port *port)
 {
-       unsigned int port_addr;
-       unsigned int result = 0;
-       unsigned int status;
+       unsigned int status, result = 0;
 
-       port_addr = SYSFLG(port);
-       if (port_addr == SYSFLG1) {
+       if (port->line == 0) {
                status = clps_readl(SYSFLG1);
                if (status & SYSFLG1_DCD)
                        result |= TIOCM_CAR;
@@ -199,104 +187,86 @@ static unsigned int clps711xuart_get_mctrl(struct uart_port *port)
                        result |= TIOCM_DSR;
                if (status & SYSFLG1_CTS)
                        result |= TIOCM_CTS;
-       }
+       } else
+               result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
 
        return result;
 }
 
-static void
-clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl)
+static void uart_clps711x_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
+       /* Do nothing */
 }
 
-static void clps711xuart_break_ctl(struct uart_port *port, int break_state)
+static void uart_clps711x_break_ctl(struct uart_port *port, int break_state)
 {
        unsigned long flags;
        unsigned int ubrlcr;
 
        spin_lock_irqsave(&port->lock, flags);
+
        ubrlcr = clps_readl(UBRLCR(port));
-       if (break_state == -1)
+       if (break_state)
                ubrlcr |= UBRLCR_BREAK;
        else
                ubrlcr &= ~UBRLCR_BREAK;
        clps_writel(ubrlcr, UBRLCR(port));
+
        spin_unlock_irqrestore(&port->lock, flags);
 }
 
-static int clps711xuart_startup(struct uart_port *port)
+static int uart_clps711x_startup(struct uart_port *port)
 {
-       unsigned int syscon;
-       int retval;
-
-       tx_enabled(port) = 1;
-
-       /*
-        * Allocate the IRQs
-        */
-       retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0,
-                            "clps711xuart_tx", port);
-       if (retval)
-               return retval;
-
-       retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0,
-                            "clps711xuart_rx", port);
-       if (retval) {
-               free_irq(TX_IRQ(port), port);
-               return retval;
+       struct clps711x_port *s = dev_get_drvdata(port->dev);
+       int ret;
+
+       s->tx_enabled[port->line] = 1;
+       /* Allocate the IRQs */
+       ret = devm_request_irq(port->dev, TX_IRQ(port), uart_clps711x_int_tx,
+                              0, UART_CLPS711X_NAME " TX", port);
+       if (ret)
+               return ret;
+
+       ret = devm_request_irq(port->dev, RX_IRQ(port), uart_clps711x_int_rx,
+                              0, UART_CLPS711X_NAME " RX", port);
+       if (ret) {
+               devm_free_irq(port->dev, TX_IRQ(port), port);
+               return ret;
        }
 
-       /*
-        * enable the port
-        */
-       syscon = clps_readl(SYSCON(port));
-       syscon |= SYSCON_UARTEN;
-       clps_writel(syscon, SYSCON(port));
+       /* Disable break */
+       clps_writel(clps_readl(UBRLCR(port)) & ~UBRLCR_BREAK, UBRLCR(port));
+
+       /* Enable the port */
+       clps_writel(clps_readl(SYSCON(port)) | SYSCON_UARTEN, SYSCON(port));
 
        return 0;
 }
 
-static void clps711xuart_shutdown(struct uart_port *port)
+static void uart_clps711x_shutdown(struct uart_port *port)
 {
-       unsigned int ubrlcr, syscon;
+       /* Free the interrupts */
+       devm_free_irq(port->dev, TX_IRQ(port), port);
+       devm_free_irq(port->dev, RX_IRQ(port), port);
 
-       /*
-        * Free the interrupt
-        */
-       free_irq(TX_IRQ(port), port);   /* TX interrupt */
-       free_irq(RX_IRQ(port), port);   /* RX interrupt */
-
-       /*
-        * disable the port
-        */
-       syscon = clps_readl(SYSCON(port));
-       syscon &= ~SYSCON_UARTEN;
-       clps_writel(syscon, SYSCON(port));
-
-       /*
-        * disable break condition and fifos
-        */
-       ubrlcr = clps_readl(UBRLCR(port));
-       ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK);
-       clps_writel(ubrlcr, UBRLCR(port));
+       /* Disable the port */
+       clps_writel(clps_readl(SYSCON(port)) & ~SYSCON_UARTEN, SYSCON(port));
 }
 
-static void
-clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios,
-                        struct ktermios *old)
+static void uart_clps711x_set_termios(struct uart_port *port,
+                                     struct ktermios *termios,
+                                     struct ktermios *old)
 {
        unsigned int ubrlcr, baud, quot;
        unsigned long flags;
 
-       /*
-        * We don't implement CREAD.
-        */
-       termios->c_cflag |= CREAD;
+       /* Mask termios capabilities we don't support */
+       termios->c_cflag &= ~CMSPAR;
+       termios->c_iflag &= ~(BRKINT | IGNBRK);
 
-       /*
-        * Ask the core to calculate the divisor for us.
-        */
-       baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
+       /* Ask the core to calculate the divisor for us */
+       baud = uart_get_baud_rate(port, termios, old, port->uartclk / 4096,
+                                                     port->uartclk / 16);
        quot = uart_get_divisor(port, baud);
 
        switch (termios->c_cflag & CSIZE) {
@@ -309,160 +279,117 @@ clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios,
        case CS7:
                ubrlcr = UBRLCR_WRDLEN7;
                break;
-       default: // CS8
+       case CS8:
+       default:
                ubrlcr = UBRLCR_WRDLEN8;
                break;
        }
+
        if (termios->c_cflag & CSTOPB)
                ubrlcr |= UBRLCR_XSTOP;
+
        if (termios->c_cflag & PARENB) {
                ubrlcr |= UBRLCR_PRTEN;
                if (!(termios->c_cflag & PARODD))
                        ubrlcr |= UBRLCR_EVENPRT;
        }
-       if (port->fifosize > 1)
-               ubrlcr |= UBRLCR_FIFOEN;
 
-       spin_lock_irqsave(&port->lock, flags);
+       /* Enable FIFO */
+       ubrlcr |= UBRLCR_FIFOEN;
 
-       /*
-        * Update the per-port timeout.
-        */
-       uart_update_timeout(port, termios->c_cflag, baud);
+       spin_lock_irqsave(&port->lock, flags);
 
+       /* Set read status mask */
        port->read_status_mask = UARTDR_OVERR;
        if (termios->c_iflag & INPCK)
                port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR;
 
-       /*
-        * Characters to ignore
-        */
+       /* Set status ignore mask */
        port->ignore_status_mask = 0;
-       if (termios->c_iflag & IGNPAR)
-               port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR;
-       if (termios->c_iflag & IGNBRK) {
-               /*
-                * If we're ignoring parity and break indicators,
-                * ignore overruns to (for real raw support).
-                */
-               if (termios->c_iflag & IGNPAR)
-                       port->ignore_status_mask |= UARTDR_OVERR;
-       }
+       if (!(termios->c_cflag & CREAD))
+               port->ignore_status_mask |= UARTDR_OVERR | UARTDR_PARERR |
+                                           UARTDR_FRMERR;
 
-       quot -= 1;
+       uart_update_timeout(port, termios->c_cflag, baud);
 
-       clps_writel(ubrlcr | quot, UBRLCR(port));
+       clps_writel(ubrlcr | (quot - 1), UBRLCR(port));
 
        spin_unlock_irqrestore(&port->lock, flags);
 }
 
-static const char *clps711xuart_type(struct uart_port *port)
+static const char *uart_clps711x_type(struct uart_port *port)
 {
-       return port->type == PORT_CLPS711X ? "CLPS711x" : NULL;
+       return (port->type == PORT_CLPS711X) ? "CLPS711X" : NULL;
 }
 
-/*
- * Configure/autoconfigure the port.
- */
-static void clps711xuart_config_port(struct uart_port *port, int flags)
+static void uart_clps711x_config_port(struct uart_port *port, int flags)
 {
        if (flags & UART_CONFIG_TYPE)
                port->type = PORT_CLPS711X;
 }
 
-static void clps711xuart_release_port(struct uart_port *port)
+static void uart_clps711x_release_port(struct uart_port *port)
 {
+       /* Do nothing */
 }
 
-static int clps711xuart_request_port(struct uart_port *port)
+static int uart_clps711x_request_port(struct uart_port *port)
 {
+       /* Do nothing */
        return 0;
 }
 
-static struct uart_ops clps711x_pops = {
-       .tx_empty       = clps711xuart_tx_empty,
-       .set_mctrl      = clps711xuart_set_mctrl_null,
-       .get_mctrl      = clps711xuart_get_mctrl,
-       .stop_tx        = clps711xuart_stop_tx,
-       .start_tx       = clps711xuart_start_tx,
-       .stop_rx        = clps711xuart_stop_rx,
-       .enable_ms      = clps711xuart_enable_ms,
-       .break_ctl      = clps711xuart_break_ctl,
-       .startup        = clps711xuart_startup,
-       .shutdown       = clps711xuart_shutdown,
-       .set_termios    = clps711xuart_set_termios,
-       .type           = clps711xuart_type,
-       .config_port    = clps711xuart_config_port,
-       .release_port   = clps711xuart_release_port,
-       .request_port   = clps711xuart_request_port,
-};
-
-static struct uart_port clps711x_ports[UART_NR] = {
-       {
-               .iobase         = SYSCON1,
-               .irq            = IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */
-               .uartclk        = 3686400,
-               .fifosize       = 16,
-               .ops            = &clps711x_pops,
-               .line           = 0,
-               .flags          = UPF_BOOT_AUTOCONF,
-       },
-       {
-               .iobase         = SYSCON2,
-               .irq            = IRQ_UTXINT2, /* IRQ_URXINT2 */
-               .uartclk        = 3686400,
-               .fifosize       = 16,
-               .ops            = &clps711x_pops,
-               .line           = 1,
-               .flags          = UPF_BOOT_AUTOCONF,
-       }
+static const struct uart_ops uart_clps711x_ops = {
+       .tx_empty       = uart_clps711x_tx_empty,
+       .set_mctrl      = uart_clps711x_set_mctrl,
+       .get_mctrl      = uart_clps711x_get_mctrl,
+       .stop_tx        = uart_clps711x_stop_tx,
+       .start_tx       = uart_clps711x_start_tx,
+       .stop_rx        = uart_clps711x_stop_rx,
+       .enable_ms      = uart_clps711x_enable_ms,
+       .break_ctl      = uart_clps711x_break_ctl,
+       .startup        = uart_clps711x_startup,
+       .shutdown       = uart_clps711x_shutdown,
+       .set_termios    = uart_clps711x_set_termios,
+       .type           = uart_clps711x_type,
+       .config_port    = uart_clps711x_config_port,
+       .release_port   = uart_clps711x_release_port,
+       .request_port   = uart_clps711x_request_port,
 };
 
 #ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
-static void clps711xuart_console_putchar(struct uart_port *port, int ch)
+static void uart_clps711x_console_putchar(struct uart_port *port, int ch)
 {
        while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF)
                barrier();
-       clps_writel(ch, UARTDR(port));
+
+       clps_writew(ch, UARTDR(port));
 }
 
-/*
- *     Print a string to the serial port trying not to disturb
- *     any possible real use of the port...
- *
- *     The console_lock must be held when we get here.
- *
- *     Note that this is called with interrupts already disabled
- */
-static void
-clps711xuart_console_write(struct console *co, const char *s,
-                          unsigned int count)
+static void uart_clps711x_console_write(struct console *co, const char *c,
+                                       unsigned n)
 {
-       struct uart_port *port = clps711x_ports + co->index;
-       unsigned int status, syscon;
+       struct clps711x_port *s = (struct clps711x_port *)co->data;
+       struct uart_port *port = &s->port[co->index];
+       u32 syscon;
 
-       /*
-        *      Ensure that the port is enabled.
-        */
+       /* Ensure that the port is enabled */
        syscon = clps_readl(SYSCON(port));
        clps_writel(syscon | SYSCON_UARTEN, SYSCON(port));
 
-       uart_console_write(port, s, count, clps711xuart_console_putchar);
+       uart_console_write(port, c, n, uart_clps711x_console_putchar);
 
-       /*
-        *      Finally, wait for transmitter to become empty
-        *      and restore the uart state.
-        */
-       do {
-               status = clps_readl(SYSFLG(port));
-       } while (status & SYSFLG_UBUSY);
+       /* Wait for transmitter to become empty */
+       while (clps_readl(SYSFLG(port)) & SYSFLG_UBUSY)
+               barrier();
 
+       /* Restore the uart state */
        clps_writel(syscon, SYSCON(port));
 }
 
-static void __init
-clps711xuart_console_get_options(struct uart_port *port, int *baud,
-                                int *parity, int *bits)
+static void uart_clps711x_console_get_options(struct uart_port *port,
+                                             int *baud, int *parity,
+                                             int *bits)
 {
        if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) {
                unsigned int ubrlcr, quot;
@@ -487,92 +414,124 @@ clps711xuart_console_get_options(struct uart_port *port, int *baud,
        }
 }
 
-static int __init clps711xuart_console_setup(struct console *co, char *options)
+static int uart_clps711x_console_setup(struct console *co, char *options)
 {
-       struct uart_port *port;
-       int baud = 38400;
-       int bits = 8;
-       int parity = 'n';
-       int flow = 'n';
-
-       /*
-        * Check whether an invalid uart number has been specified, and
-        * if so, search for the first available port that does have
-        * console support.
-        */
-       port = uart_get_console(clps711x_ports, UART_NR, co);
+       int baud = 38400, bits = 8, parity = 'n', flow = 'n';
+       struct clps711x_port *s = (struct clps711x_port *)co->data;
+       struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0];
 
        if (options)
                uart_parse_options(options, &baud, &parity, &bits, &flow);
        else
-               clps711xuart_console_get_options(port, &baud, &parity, &bits);
+               uart_clps711x_console_get_options(port, &baud, &parity, &bits);
 
        return uart_set_options(port, co, baud, parity, bits, flow);
 }
+#endif
 
-static struct uart_driver clps711x_reg;
-static struct console clps711x_console = {
-       .name           = "ttyCL",
-       .write          = clps711xuart_console_write,
-       .device         = uart_console_device,
-       .setup          = clps711xuart_console_setup,
-       .flags          = CON_PRINTBUFFER,
-       .index          = -1,
-       .data           = &clps711x_reg,
-};
-
-static int __init clps711xuart_console_init(void)
+static int __devinit uart_clps711x_probe(struct platform_device *pdev)
 {
-       register_console(&clps711x_console);
-       return 0;
-}
-console_initcall(clps711xuart_console_init);
+       struct clps711x_port *s;
+       int ret, i;
+
+       s = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_port), GFP_KERNEL);
+       if (!s) {
+               dev_err(&pdev->dev, "Error allocating port structure\n");
+               return -ENOMEM;
+       }
+       platform_set_drvdata(pdev, s);
 
-#define CLPS711X_CONSOLE       &clps711x_console
-#else
-#define CLPS711X_CONSOLE       NULL
+       s->uart_clk = devm_clk_get(&pdev->dev, "uart");
+       if (IS_ERR(s->uart_clk)) {
+               dev_err(&pdev->dev, "Can't get UART clocks\n");
+               ret = PTR_ERR(s->uart_clk);
+               goto err_out;
+       }
+
+       s->uart.owner           = THIS_MODULE;
+       s->uart.dev_name        = "ttyCL";
+       s->uart.major           = UART_CLPS711X_MAJOR;
+       s->uart.minor           = UART_CLPS711X_MINOR;
+       s->uart.nr              = UART_CLPS711X_NR;
+#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
+       s->uart.cons            = &s->console;
+       s->uart.cons->device    = uart_console_device;
+       s->uart.cons->write     = uart_clps711x_console_write;
+       s->uart.cons->setup     = uart_clps711x_console_setup;
+       s->uart.cons->flags     = CON_PRINTBUFFER;
+       s->uart.cons->index     = -1;
+       s->uart.cons->data      = s;
+       strcpy(s->uart.cons->name, "ttyCL");
 #endif
+       ret = uart_register_driver(&s->uart);
+       if (ret) {
+               dev_err(&pdev->dev, "Registering UART driver failed\n");
+               devm_clk_put(&pdev->dev, s->uart_clk);
+               goto err_out;
+       }
 
-static struct uart_driver clps711x_reg = {
-       .driver_name            = "ttyCL",
-       .dev_name               = "ttyCL",
-       .major                  = SERIAL_CLPS711X_MAJOR,
-       .minor                  = SERIAL_CLPS711X_MINOR,
-       .nr                     = UART_NR,
+       for (i = 0; i < UART_CLPS711X_NR; i++) {
+               s->port[i].line         = i;
+               s->port[i].dev          = &pdev->dev;
+               s->port[i].irq          = TX_IRQ(&s->port[i]);
+               s->port[i].iobase       = SYSCON(&s->port[i]);
+               s->port[i].type         = PORT_CLPS711X;
+               s->port[i].fifosize     = 16;
+               s->port[i].flags        = UPF_SKIP_TEST | UPF_FIXED_TYPE;
+               s->port[i].uartclk      = clk_get_rate(s->uart_clk);
+               s->port[i].ops          = &uart_clps711x_ops;
+               WARN_ON(uart_add_one_port(&s->uart, &s->port[i]));
+       }
 
-       .cons                   = CLPS711X_CONSOLE,
-};
+       return 0;
 
-static int __init clps711xuart_init(void)
-{
-       int ret, i;
+err_out:
+       platform_set_drvdata(pdev, NULL);
 
-       printk(KERN_INFO "Serial: CLPS711x driver\n");
+       return ret;
+}
 
-       ret = uart_register_driver(&clps711x_reg);
-       if (ret)
-               return ret;
+static int __devexit uart_clps711x_remove(struct platform_device *pdev)
+{
+       struct clps711x_port *s = platform_get_drvdata(pdev);
+       int i;
 
-       for (i = 0; i < UART_NR; i++)
-               uart_add_one_port(&clps711x_reg, &clps711x_ports[i]);
+       for (i = 0; i < UART_CLPS711X_NR; i++)
+               uart_remove_one_port(&s->uart, &s->port[i]);
+
+       devm_clk_put(&pdev->dev, s->uart_clk);
+       uart_unregister_driver(&s->uart);
+       platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
 
-static void __exit clps711xuart_exit(void)
-{
-       int i;
+static struct platform_driver clps711x_uart_driver = {
+       .driver = {
+               .name   = UART_CLPS711X_NAME,
+               .owner  = THIS_MODULE,
+       },
+       .probe  = uart_clps711x_probe,
+       .remove = __devexit_p(uart_clps711x_remove),
+};
+module_platform_driver(clps711x_uart_driver);
 
-       for (i = 0; i < UART_NR; i++)
-               uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]);
+static struct platform_device clps711x_uart_device = {
+       .name   = UART_CLPS711X_NAME,
+};
 
-       uart_unregister_driver(&clps711x_reg);
+static int __init uart_clps711x_init(void)
+{
+       return platform_device_register(&clps711x_uart_device);
 }
+module_init(uart_clps711x_init);
 
-module_init(clps711xuart_init);
-module_exit(clps711xuart_exit);
+static void __exit uart_clps711x_exit(void)
+{
+       platform_device_unregister(&clps711x_uart_device);
+}
+module_exit(uart_clps711x_exit);
 
 MODULE_AUTHOR("Deep Blue Solutions Ltd");
-MODULE_DESCRIPTION("CLPS-711x generic serial driver");
+MODULE_DESCRIPTION("CLPS711X serial driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR);
index 5b9bc19..e595c83 100644 (file)
@@ -152,26 +152,67 @@ ifx_spi_power_state_clear(struct ifx_spi_device *ifx_dev, unsigned char val)
 }
 
 /**
- *     swap_buf
+ *     swap_buf_8
  *     @buf: our buffer
  *     @len : number of bytes (not words) in the buffer
  *     @end: end of buffer
  *
  *     Swap the contents of a buffer into big endian format
  */
-static inline void swap_buf(u16 *buf, int len, void *end)
+static inline void swap_buf_8(unsigned char *buf, int len, void *end)
+{
+       /* don't swap buffer if SPI word width is 8 bits */
+       return;
+}
+
+/**
+ *     swap_buf_16
+ *     @buf: our buffer
+ *     @len : number of bytes (not words) in the buffer
+ *     @end: end of buffer
+ *
+ *     Swap the contents of a buffer into big endian format
+ */
+static inline void swap_buf_16(unsigned char *buf, int len, void *end)
 {
        int n;
 
+       u16 *buf_16 = (u16 *)buf;
        len = ((len + 1) >> 1);
-       if ((void *)&buf[len] > end) {
-               pr_err("swap_buf: swap exceeds boundary (%p > %p)!",
-                      &buf[len], end);
+       if ((void *)&buf_16[len] > end) {
+               pr_err("swap_buf_16: swap exceeds boundary (%p > %p)!",
+                      &buf_16[len], end);
                return;
        }
        for (n = 0; n < len; n++) {
-               *buf = cpu_to_be16(*buf);
-               buf++;
+               *buf_16 = cpu_to_be16(*buf_16);
+               buf_16++;
+       }
+}
+
+/**
+ *     swap_buf_32
+ *     @buf: our buffer
+ *     @len : number of bytes (not words) in the buffer
+ *     @end: end of buffer
+ *
+ *     Swap the contents of a buffer into big endian format
+ */
+static inline void swap_buf_32(unsigned char *buf, int len, void *end)
+{
+       int n;
+
+       u32 *buf_32 = (u32 *)buf;
+       len = (len + 3) >> 2;
+
+       if ((void *)&buf_32[len] > end) {
+               pr_err("swap_buf_32: swap exceeds boundary (%p > %p)!\n",
+                      &buf_32[len], end);
+               return;
+       }
+       for (n = 0; n < len; n++) {
+               *buf_32 = cpu_to_be32(*buf_32);
+               buf_32++;
        }
 }
 
@@ -190,9 +231,7 @@ static void mrdy_assert(struct ifx_spi_device *ifx_dev)
        if (!val) {
                if (!test_and_set_bit(IFX_SPI_STATE_TIMER_PENDING,
                                      &ifx_dev->flags)) {
-                       ifx_dev->spi_timer.expires =
-                               jiffies + IFX_SPI_TIMEOUT_SEC*HZ;
-                       add_timer(&ifx_dev->spi_timer);
+                       mod_timer(&ifx_dev->spi_timer,jiffies + IFX_SPI_TIMEOUT_SEC*HZ);
 
                }
        }
@@ -449,7 +488,7 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev)
                                        tx_count-IFX_SPI_HEADER_OVERHEAD,
                                        ifx_dev->spi_more);
        /* swap actual data in the buffer */
-       swap_buf((u16 *)(ifx_dev->tx_buffer), tx_count,
+       ifx_dev->swap_buf((ifx_dev->tx_buffer), tx_count,
                &ifx_dev->tx_buffer[IFX_SPI_TRANSFER_SIZE]);
        return tx_count;
 }
@@ -617,7 +656,7 @@ static void ifx_spi_complete(void *ctx)
 
        if (!ifx_dev->spi_msg.status) {
                /* check header validity, get comm flags */
-               swap_buf((u16 *)ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD,
+               ifx_dev->swap_buf(ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD,
                        &ifx_dev->rx_buffer[IFX_SPI_HEADER_OVERHEAD]);
                decode_result = ifx_spi_decode_spi_header(ifx_dev->rx_buffer,
                                &length, &more, &cts);
@@ -636,7 +675,8 @@ static void ifx_spi_complete(void *ctx)
 
                actual_length = min((unsigned int)length,
                                        ifx_dev->spi_msg.actual_length);
-               swap_buf((u16 *)(ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD),
+               ifx_dev->swap_buf(
+                       (ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD),
                         actual_length,
                         &ifx_dev->rx_buffer[IFX_SPI_TRANSFER_SIZE]);
                ifx_spi_insert_flip_string(
@@ -826,7 +866,7 @@ error_ret:
 static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev)
 {
        if (test_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags)) {
-               del_timer_sync(&ifx_dev->spi_timer);
+               del_timer(&ifx_dev->spi_timer);
                clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags);
        }
 
@@ -1001,6 +1041,14 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
                return -ENODEV;
        }
 
+       /* init swap_buf function according to word width configuration */
+       if (spi->bits_per_word == 32)
+               ifx_dev->swap_buf = swap_buf_32;
+       else if (spi->bits_per_word == 16)
+               ifx_dev->swap_buf = swap_buf_16;
+       else
+               ifx_dev->swap_buf = swap_buf_8;
+
        /* ensure SPI protocol flags are initialized to enable transfer */
        ifx_dev->spi_more = 0;
        ifx_dev->spi_slave_cts = 0;
index e8464ba..d8869f5 100644 (file)
@@ -124,6 +124,7 @@ struct ifx_spi_device {
 #define MR_INPROGRESS  1
 #define MR_COMPLETE    2
        wait_queue_head_t mdm_reset_wait;
+       void (*swap_buf)(unsigned char *buf, int len, void *end);
 };
 
 #endif /* _IFX6X60_H */
index df443b9..533ccfe 100644 (file)
 #include <linux/of_serial.h>
 #include <linux/of_platform.h>
 #include <linux/nwpserial.h>
+#include <linux/clk.h>
 
 struct of_serial_info {
+       struct clk *clk;
        int type;
        int line;
 };
@@ -51,7 +53,8 @@ EXPORT_SYMBOL_GPL(tegra_serial_handle_break);
  * Fill a struct uart_port for a given device node
  */
 static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
-                                       int type, struct uart_port *port)
+                       int type, struct uart_port *port,
+                       struct of_serial_info *info)
 {
        struct resource resource;
        struct device_node *np = ofdev->dev.of_node;
@@ -60,8 +63,17 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
 
        memset(port, 0, sizeof *port);
        if (of_property_read_u32(np, "clock-frequency", &clk)) {
-               dev_warn(&ofdev->dev, "no clock-frequency property set\n");
-               return -ENODEV;
+
+               /* Get clk rate through clk driver if present */
+               info->clk = clk_get(&ofdev->dev, NULL);
+               if (info->clk == NULL) {
+                       dev_warn(&ofdev->dev,
+                               "clk or clock-frequency not defined\n");
+                       return -ENODEV;
+               }
+
+               clk_prepare_enable(info->clk);
+               clk = clk_get_rate(info->clk);
        }
        /* If current-speed was set, then try not to change it. */
        if (of_property_read_u32(np, "current-speed", &spd) == 0)
@@ -70,7 +82,7 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
        ret = of_address_to_resource(np, 0, &resource);
        if (ret) {
                dev_warn(&ofdev->dev, "invalid address\n");
-               return ret;
+               goto out;
        }
 
        spin_lock_init(&port->lock);
@@ -97,7 +109,8 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
                default:
                        dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n",
                                 prop);
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       goto out;
                }
        }
 
@@ -115,6 +128,10 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
                port->handle_break = tegra_serial_handle_break;
 
        return 0;
+out:
+       if (info->clk)
+               clk_disable_unprepare(info->clk);
+       return ret;
 }
 
 /*
@@ -141,7 +158,7 @@ static int __devinit of_platform_serial_probe(struct platform_device *ofdev)
                return -ENOMEM;
 
        port_type = (unsigned long)match->data;
-       ret = of_platform_serial_setup(ofdev, port_type, &port);
+       ret = of_platform_serial_setup(ofdev, port_type, &port, info);
        if (ret)
                goto out;
 
@@ -204,6 +221,9 @@ static int of_platform_serial_remove(struct platform_device *ofdev)
                /* need to add code for these */
                break;
        }
+
+       if (info->clk)
+               clk_disable_unprepare(info->clk);
        kfree(info);
        return 0;
 }
index 6d3d26a..c34735c 100644 (file)
@@ -96,7 +96,7 @@ struct uart_omap_port {
        unsigned char           msr_saved_flags;
        char                    name[20];
        unsigned long           port_activity;
-       u32                     context_loss_cnt;
+       int                     context_loss_cnt;
        u32                     errata;
        u8                      wakeups_enabled;
        unsigned int            irq_pending:1;
@@ -702,11 +702,7 @@ serial_omap_configure_xonxoff
        serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
        serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
        serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
-       /* Enable special char function UARTi.EFR_REG[5] and
-        * load the new software flow control mode IXON or IXOFF
-        * and restore the UARTi.EFR_REG[4] ENHANCED_EN value.
-        */
-       serial_out(up, UART_EFR, up->efr | UART_EFR_SCD);
+
        serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
 
        serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR);
@@ -1081,7 +1077,7 @@ out:
 
 #ifdef CONFIG_SERIAL_OMAP_CONSOLE
 
-static struct uart_omap_port *serial_omap_console_ports[4];
+static struct uart_omap_port *serial_omap_console_ports[OMAP_MAX_HSUART_PORTS];
 
 static struct uart_driver serial_omap_reg;
 
@@ -1556,11 +1552,15 @@ static int serial_omap_runtime_resume(struct device *dev)
 {
        struct uart_omap_port *up = dev_get_drvdata(dev);
 
-       u32 loss_cnt = serial_omap_get_context_loss_count(up);
+       int loss_cnt = serial_omap_get_context_loss_count(up);
 
-       if (up->context_loss_cnt != loss_cnt)
+       if (loss_cnt < 0) {
+               dev_err(dev, "serial_omap_get_context_loss_count failed : %d\n",
+                       loss_cnt);
                serial_omap_restore_context(up);
-
+       } else if (up->context_loss_cnt != loss_cnt) {
+               serial_omap_restore_context(up);
+       }
        up->latency = up->calc_latency;
        schedule_work(&up->qos_work);
 
index 9033fc6..2764828 100644 (file)
@@ -705,6 +705,57 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count)
        clk_disable_unprepare(up->clk);
 }
 
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context.
+ */
+
+static int serial_pxa_get_poll_char(struct uart_port *port)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+       unsigned char lsr = serial_in(up, UART_LSR);
+
+       while (!(lsr & UART_LSR_DR))
+               lsr = serial_in(up, UART_LSR);
+
+       return serial_in(up, UART_RX);
+}
+
+
+static void serial_pxa_put_poll_char(struct uart_port *port,
+                        unsigned char c)
+{
+       unsigned int ier;
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+       /*
+        *      First save the IER then disable the interrupts
+        */
+       ier = serial_in(up, UART_IER);
+       serial_out(up, UART_IER, UART_IER_UUE);
+
+       wait_for_xmitr(up);
+       /*
+        *      Send the character out.
+        *      If a LF, also do CR...
+        */
+       serial_out(up, UART_TX, c);
+       if (c == 10) {
+               wait_for_xmitr(up);
+               serial_out(up, UART_TX, 13);
+       }
+
+       /*
+        *      Finally, wait for transmitter to become empty
+        *      and restore the IER
+        */
+       wait_for_xmitr(up);
+       serial_out(up, UART_IER, ier);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
 static int __init
 serial_pxa_console_setup(struct console *co, char *options)
 {
@@ -759,6 +810,10 @@ struct uart_ops serial_pxa_pops = {
        .request_port   = serial_pxa_request_port,
        .config_port    = serial_pxa_config_port,
        .verify_port    = serial_pxa_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+       .poll_get_char = serial_pxa_get_poll_char,
+       .poll_put_char = serial_pxa_put_poll_char,
+#endif
 };
 
 static struct uart_driver serial_pxa_reg = {
index 7f04717..740458c 100644 (file)
@@ -530,16 +530,16 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
        switch (level) {
        case 3:
                if (!IS_ERR(ourport->baudclk))
-                       clk_disable(ourport->baudclk);
+                       clk_disable_unprepare(ourport->baudclk);
 
-               clk_disable(ourport->clk);
+               clk_disable_unprepare(ourport->clk);
                break;
 
        case 0:
-               clk_enable(ourport->clk);
+               clk_prepare_enable(ourport->clk);
 
                if (!IS_ERR(ourport->baudclk))
-                       clk_enable(ourport->baudclk);
+                       clk_prepare_enable(ourport->baudclk);
 
                break;
        default:
@@ -713,11 +713,11 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
                s3c24xx_serial_setsource(port, clk_sel);
 
                if (!IS_ERR(ourport->baudclk)) {
-                       clk_disable(ourport->baudclk);
+                       clk_disable_unprepare(ourport->baudclk);
                        ourport->baudclk = ERR_PTR(-EINVAL);
                }
 
-               clk_enable(clk);
+               clk_prepare_enable(clk);
 
                ourport->baudclk = clk;
                ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
@@ -1287,9 +1287,9 @@ static int s3c24xx_serial_resume(struct device *dev)
        struct s3c24xx_uart_port *ourport = to_ourport(port);
 
        if (port) {
-               clk_enable(ourport->clk);
+               clk_prepare_enable(ourport->clk);
                s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));
-               clk_disable(ourport->clk);
+               clk_disable_unprepare(ourport->clk);
 
                uart_resume_port(&s3c24xx_uart_drv, port);
        }
index 205d4cf..4354fe5 100644 (file)
@@ -567,10 +567,6 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
        if (!mmres || !irqres)
                return -ENODEV;
 
-       vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL);
-       if (!vt8500_port)
-               return -ENOMEM;
-
        if (np)
                port = of_alias_get_id(np, "serial");
                if (port > VT8500_MAX_PORTS)
@@ -593,6 +589,10 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
                return -EBUSY;
        }
 
+       vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL);
+       if (!vt8500_port)
+               return -ENOMEM;
+
        vt8500_port->uart.type = PORT_VT8500;
        vt8500_port->uart.iotype = UPIO_MEM;
        vt8500_port->uart.mapbase = mmres->start;
index b0b39b8..6953dc8 100644 (file)
@@ -23,7 +23,7 @@ struct tty_audit_buf {
 };
 
 static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor,
-                                                int icanon)
+                                                unsigned icanon)
 {
        struct tty_audit_buf *buf;
 
@@ -239,7 +239,8 @@ int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid)
  *     if TTY auditing is disabled or out of memory.  Otherwise, return a new
  *     reference to the buffer.
  */
-static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty)
+static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty,
+               unsigned icanon)
 {
        struct tty_audit_buf *buf, *buf2;
 
@@ -257,7 +258,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty)
 
        buf2 = tty_audit_buf_alloc(tty->driver->major,
                                   tty->driver->minor_start + tty->index,
-                                  tty->icanon);
+                                  icanon);
        if (buf2 == NULL) {
                audit_log_lost("out of memory in TTY auditing");
                return NULL;
@@ -287,7 +288,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty)
  *     Audit @data of @size from @tty, if necessary.
  */
 void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
-                       size_t size)
+                       size_t size, unsigned icanon)
 {
        struct tty_audit_buf *buf;
        int major, minor;
@@ -299,7 +300,7 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
            && tty->driver->subtype == PTY_TYPE_MASTER)
                return;
 
-       buf = tty_audit_buf_get(tty);
+       buf = tty_audit_buf_get(tty, icanon);
        if (!buf)
                return;
 
@@ -307,11 +308,11 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
        major = tty->driver->major;
        minor = tty->driver->minor_start + tty->index;
        if (buf->major != major || buf->minor != minor
-           || buf->icanon != tty->icanon) {
+           || buf->icanon != icanon) {
                tty_audit_buf_push_current(buf);
                buf->major = major;
                buf->minor = minor;
-               buf->icanon = tty->icanon;
+               buf->icanon = icanon;
        }
        do {
                size_t run;
index 91e326f..45d9161 100644 (file)
  *     Locking: none
  */
 
-void tty_buffer_free_all(struct tty_struct *tty)
+void tty_buffer_free_all(struct tty_port *port)
 {
+       struct tty_bufhead *buf = &port->buf;
        struct tty_buffer *thead;
-       while ((thead = tty->buf.head) != NULL) {
-               tty->buf.head = thead->next;
+
+       while ((thead = buf->head) != NULL) {
+               buf->head = thead->next;
                kfree(thead);
        }
-       while ((thead = tty->buf.free) != NULL) {
-               tty->buf.free = thead->next;
+       while ((thead = buf->free) != NULL) {
+               buf->free = thead->next;
                kfree(thead);
        }
-       tty->buf.tail = NULL;
-       tty->buf.memory_used = 0;
+       buf->tail = NULL;
+       buf->memory_used = 0;
 }
 
 /**
@@ -54,11 +56,11 @@ void tty_buffer_free_all(struct tty_struct *tty)
  *     Locking: Caller must hold tty->buf.lock
  */
 
-static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size)
+static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size)
 {
        struct tty_buffer *p;
 
-       if (tty->buf.memory_used + size > 65536)
+       if (port->buf.memory_used + size > 65536)
                return NULL;
        p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC);
        if (p == NULL)
@@ -70,7 +72,7 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size)
        p->read = 0;
        p->char_buf_ptr = (char *)(p->data);
        p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size;
-       tty->buf.memory_used += size;
+       port->buf.memory_used += size;
        return p;
 }
 
@@ -85,17 +87,19 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size)
  *     Locking: Caller must hold tty->buf.lock
  */
 
-static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b)
+static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b)
 {
+       struct tty_bufhead *buf = &port->buf;
+
        /* Dumb strategy for now - should keep some stats */
-       tty->buf.memory_used -= b->size;
-       WARN_ON(tty->buf.memory_used < 0);
+       buf->memory_used -= b->size;
+       WARN_ON(buf->memory_used < 0);
 
        if (b->size >= 512)
                kfree(b);
        else {
-               b->next = tty->buf.free;
-               tty->buf.free = b;
+               b->next = buf->free;
+               buf->free = b;
        }
 }
 
@@ -110,15 +114,16 @@ static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b)
  *     Locking: Caller must hold tty->buf.lock
  */
 
-static void __tty_buffer_flush(struct tty_struct *tty)
+static void __tty_buffer_flush(struct tty_port *port)
 {
+       struct tty_bufhead *buf = &port->buf;
        struct tty_buffer *thead;
 
-       while ((thead = tty->buf.head) != NULL) {
-               tty->buf.head = thead->next;
-               tty_buffer_free(tty, thead);
+       while ((thead = buf->head) != NULL) {
+               buf->head = thead->next;
+               tty_buffer_free(port, thead);
        }
-       tty->buf.tail = NULL;
+       buf->tail = NULL;
 }
 
 /**
@@ -134,21 +139,24 @@ static void __tty_buffer_flush(struct tty_struct *tty)
 
 void tty_buffer_flush(struct tty_struct *tty)
 {
+       struct tty_port *port = tty->port;
+       struct tty_bufhead *buf = &port->buf;
        unsigned long flags;
-       spin_lock_irqsave(&tty->buf.lock, flags);
+
+       spin_lock_irqsave(&buf->lock, flags);
 
        /* If the data is being pushed to the tty layer then we can't
           process it here. Instead set a flag and the flush_to_ldisc
           path will process the flush request before it exits */
-       if (test_bit(TTY_FLUSHING, &tty->flags)) {
-               set_bit(TTY_FLUSHPENDING, &tty->flags);
-               spin_unlock_irqrestore(&tty->buf.lock, flags);
+       if (test_bit(TTYP_FLUSHING, &port->iflags)) {
+               set_bit(TTYP_FLUSHPENDING, &port->iflags);
+               spin_unlock_irqrestore(&buf->lock, flags);
                wait_event(tty->read_wait,
-                               test_bit(TTY_FLUSHPENDING, &tty->flags) == 0);
+                               test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0);
                return;
        } else
-               __tty_buffer_flush(tty);
-       spin_unlock_irqrestore(&tty->buf.lock, flags);
+               __tty_buffer_flush(port);
+       spin_unlock_irqrestore(&buf->lock, flags);
 }
 
 /**
@@ -163,9 +171,9 @@ void tty_buffer_flush(struct tty_struct *tty)
  *     Locking: Caller must hold tty->buf.lock
  */
 
-static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
+static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size)
 {
-       struct tty_buffer **tbh = &tty->buf.free;
+       struct tty_buffer **tbh = &port->buf.free;
        while ((*tbh) != NULL) {
                struct tty_buffer *t = *tbh;
                if (t->size >= size) {
@@ -174,14 +182,14 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
                        t->used = 0;
                        t->commit = 0;
                        t->read = 0;
-                       tty->buf.memory_used += t->size;
+                       port->buf.memory_used += t->size;
                        return t;
                }
                tbh = &((*tbh)->next);
        }
        /* Round the buffer size out */
        size = (size + 0xFF) & ~0xFF;
-       return tty_buffer_alloc(tty, size);
+       return tty_buffer_alloc(port, size);
        /* Should possibly check if this fails for the largest buffer we
           have queued and recycle that ? */
 }
@@ -192,29 +200,31 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
  *
  *     Make at least size bytes of linear space available for the tty
  *     buffer. If we fail return the size we managed to find.
- *      Locking: Caller must hold tty->buf.lock
+ *      Locking: Caller must hold port->buf.lock
  */
-static int __tty_buffer_request_room(struct tty_struct *tty, size_t size)
+static int __tty_buffer_request_room(struct tty_port *port, size_t size)
 {
+       struct tty_bufhead *buf = &port->buf;
        struct tty_buffer *b, *n;
        int left;
        /* OPTIMISATION: We could keep a per tty "zero" sized buffer to
           remove this conditional if its worth it. This would be invisible
           to the callers */
-       if ((b = tty->buf.tail) != NULL)
+       b = buf->tail;
+       if (b != NULL)
                left = b->size - b->used;
        else
                left = 0;
 
        if (left < size) {
                /* This is the slow path - looking for new buffers to use */
-               if ((n = tty_buffer_find(tty, size)) != NULL) {
+               if ((n = tty_buffer_find(port, size)) != NULL) {
                        if (b != NULL) {
                                b->next = n;
                                b->commit = b->used;
                        } else
-                               tty->buf.head = n;
-                       tty->buf.tail = n;
+                               buf->head = n;
+                       buf->tail = n;
                } else
                        size = left;
        }
@@ -231,16 +241,17 @@ static int __tty_buffer_request_room(struct tty_struct *tty, size_t size)
  *     Make at least size bytes of linear space available for the tty
  *     buffer. If we fail return the size we managed to find.
  *
- *     Locking: Takes tty->buf.lock
+ *     Locking: Takes port->buf.lock
  */
 int tty_buffer_request_room(struct tty_struct *tty, size_t size)
 {
+       struct tty_port *port = tty->port;
        unsigned long flags;
        int length;
 
-       spin_lock_irqsave(&tty->buf.lock, flags);
-       length = __tty_buffer_request_room(tty, size);
-       spin_unlock_irqrestore(&tty->buf.lock, flags);
+       spin_lock_irqsave(&port->buf.lock, flags);
+       length = __tty_buffer_request_room(port, size);
+       spin_unlock_irqrestore(&port->buf.lock, flags);
        return length;
 }
 EXPORT_SYMBOL_GPL(tty_buffer_request_room);
@@ -255,12 +266,13 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room);
  *     Queue a series of bytes to the tty buffering. All the characters
  *     passed are marked with the supplied flag. Returns the number added.
  *
- *     Locking: Called functions may take tty->buf.lock
+ *     Locking: Called functions may take port->buf.lock
  */
 
 int tty_insert_flip_string_fixed_flag(struct tty_struct *tty,
                const unsigned char *chars, char flag, size_t size)
 {
+       struct tty_bufhead *buf = &tty->port->buf;
        int copied = 0;
        do {
                int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
@@ -268,18 +280,18 @@ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty,
                unsigned long flags;
                struct tty_buffer *tb;
 
-               spin_lock_irqsave(&tty->buf.lock, flags);
-               space = __tty_buffer_request_room(tty, goal);
-               tb = tty->buf.tail;
+               spin_lock_irqsave(&buf->lock, flags);
+               space = __tty_buffer_request_room(tty->port, goal);
+               tb = buf->tail;
                /* If there is no space then tb may be NULL */
                if (unlikely(space == 0)) {
-                       spin_unlock_irqrestore(&tty->buf.lock, flags);
+                       spin_unlock_irqrestore(&buf->lock, flags);
                        break;
                }
                memcpy(tb->char_buf_ptr + tb->used, chars, space);
                memset(tb->flag_buf_ptr + tb->used, flag, space);
                tb->used += space;
-               spin_unlock_irqrestore(&tty->buf.lock, flags);
+               spin_unlock_irqrestore(&buf->lock, flags);
                copied += space;
                chars += space;
                /* There is a small chance that we need to split the data over
@@ -300,12 +312,13 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag);
  *     the flags array indicates the status of the character. Returns the
  *     number added.
  *
- *     Locking: Called functions may take tty->buf.lock
+ *     Locking: Called functions may take port->buf.lock
  */
 
 int tty_insert_flip_string_flags(struct tty_struct *tty,
                const unsigned char *chars, const char *flags, size_t size)
 {
+       struct tty_bufhead *buf = &tty->port->buf;
        int copied = 0;
        do {
                int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
@@ -313,18 +326,18 @@ int tty_insert_flip_string_flags(struct tty_struct *tty,
                unsigned long __flags;
                struct tty_buffer *tb;
 
-               spin_lock_irqsave(&tty->buf.lock, __flags);
-               space = __tty_buffer_request_room(tty, goal);
-               tb = tty->buf.tail;
+               spin_lock_irqsave(&buf->lock, __flags);
+               space = __tty_buffer_request_room(tty->port, goal);
+               tb = buf->tail;
                /* If there is no space then tb may be NULL */
                if (unlikely(space == 0)) {
-                       spin_unlock_irqrestore(&tty->buf.lock, __flags);
+                       spin_unlock_irqrestore(&buf->lock, __flags);
                        break;
                }
                memcpy(tb->char_buf_ptr + tb->used, chars, space);
                memcpy(tb->flag_buf_ptr + tb->used, flags, space);
                tb->used += space;
-               spin_unlock_irqrestore(&tty->buf.lock, __flags);
+               spin_unlock_irqrestore(&buf->lock, __flags);
                copied += space;
                chars += space;
                flags += space;
@@ -342,18 +355,23 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags);
  *     Takes any pending buffers and transfers their ownership to the
  *     ldisc side of the queue. It then schedules those characters for
  *     processing by the line discipline.
+ *     Note that this function can only be used when the low_latency flag
+ *     is unset. Otherwise the workqueue won't be flushed.
  *
- *     Locking: Takes tty->buf.lock
+ *     Locking: Takes port->buf.lock
  */
 
 void tty_schedule_flip(struct tty_struct *tty)
 {
+       struct tty_bufhead *buf = &tty->port->buf;
        unsigned long flags;
-       spin_lock_irqsave(&tty->buf.lock, flags);
-       if (tty->buf.tail != NULL)
-               tty->buf.tail->commit = tty->buf.tail->used;
-       spin_unlock_irqrestore(&tty->buf.lock, flags);
-       schedule_work(&tty->buf.work);
+       WARN_ON(tty->low_latency);
+
+       spin_lock_irqsave(&buf->lock, flags);
+       if (buf->tail != NULL)
+               buf->tail->commit = buf->tail->used;
+       spin_unlock_irqrestore(&buf->lock, flags);
+       schedule_work(&buf->work);
 }
 EXPORT_SYMBOL(tty_schedule_flip);
 
@@ -369,26 +387,27 @@ EXPORT_SYMBOL(tty_schedule_flip);
  *     that need their own block copy routines into the buffer. There is no
  *     guarantee the buffer is a DMA target!
  *
- *     Locking: May call functions taking tty->buf.lock
+ *     Locking: May call functions taking port->buf.lock
  */
 
 int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars,
-                                                               size_t size)
+               size_t size)
 {
+       struct tty_bufhead *buf = &tty->port->buf;
        int space;
        unsigned long flags;
        struct tty_buffer *tb;
 
-       spin_lock_irqsave(&tty->buf.lock, flags);
-       space = __tty_buffer_request_room(tty, size);
+       spin_lock_irqsave(&buf->lock, flags);
+       space = __tty_buffer_request_room(tty->port, size);
 
-       tb = tty->buf.tail;
+       tb = buf->tail;
        if (likely(space)) {
                *chars = tb->char_buf_ptr + tb->used;
                memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
                tb->used += space;
        }
-       spin_unlock_irqrestore(&tty->buf.lock, flags);
+       spin_unlock_irqrestore(&buf->lock, flags);
        return space;
 }
 EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
@@ -406,26 +425,27 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
  *     that need their own block copy routines into the buffer. There is no
  *     guarantee the buffer is a DMA target!
  *
- *     Locking: May call functions taking tty->buf.lock
+ *     Locking: May call functions taking port->buf.lock
  */
 
 int tty_prepare_flip_string_flags(struct tty_struct *tty,
                        unsigned char **chars, char **flags, size_t size)
 {
+       struct tty_bufhead *buf = &tty->port->buf;
        int space;
        unsigned long __flags;
        struct tty_buffer *tb;
 
-       spin_lock_irqsave(&tty->buf.lock, __flags);
-       space = __tty_buffer_request_room(tty, size);
+       spin_lock_irqsave(&buf->lock, __flags);
+       space = __tty_buffer_request_room(tty->port, size);
 
-       tb = tty->buf.tail;
+       tb = buf->tail;
        if (likely(space)) {
                *chars = tb->char_buf_ptr + tb->used;
                *flags = tb->flag_buf_ptr + tb->used;
                tb->used += space;
        }
-       spin_unlock_irqrestore(&tty->buf.lock, __flags);
+       spin_unlock_irqrestore(&buf->lock, __flags);
        return space;
 }
 EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
@@ -446,20 +466,25 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
 
 static void flush_to_ldisc(struct work_struct *work)
 {
-       struct tty_struct *tty =
-               container_of(work, struct tty_struct, buf.work);
+       struct tty_port *port = container_of(work, struct tty_port, buf.work);
+       struct tty_bufhead *buf = &port->buf;
+       struct tty_struct *tty;
        unsigned long   flags;
        struct tty_ldisc *disc;
 
+       tty = port->itty;
+       if (WARN_RATELIMIT(tty == NULL, "tty is NULL\n"))
+               return;
+
        disc = tty_ldisc_ref(tty);
        if (disc == NULL)       /*  !TTY_LDISC */
                return;
 
-       spin_lock_irqsave(&tty->buf.lock, flags);
+       spin_lock_irqsave(&buf->lock, flags);
 
-       if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) {
+       if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) {
                struct tty_buffer *head;
-               while ((head = tty->buf.head) != NULL) {
+               while ((head = buf->head) != NULL) {
                        int count;
                        char *char_buf;
                        unsigned char *flag_buf;
@@ -468,14 +493,14 @@ static void flush_to_ldisc(struct work_struct *work)
                        if (!count) {
                                if (head->next == NULL)
                                        break;
-                               tty->buf.head = head->next;
-                               tty_buffer_free(tty, head);
+                               buf->head = head->next;
+                               tty_buffer_free(port, head);
                                continue;
                        }
                        /* Ldisc or user is trying to flush the buffers
                           we are feeding to the ldisc, stop feeding the
                           line discipline as we want to empty the queue */
-                       if (test_bit(TTY_FLUSHPENDING, &tty->flags))
+                       if (test_bit(TTYP_FLUSHPENDING, &port->iflags))
                                break;
                        if (!tty->receive_room)
                                break;
@@ -484,22 +509,22 @@ static void flush_to_ldisc(struct work_struct *work)
                        char_buf = head->char_buf_ptr + head->read;
                        flag_buf = head->flag_buf_ptr + head->read;
                        head->read += count;
-                       spin_unlock_irqrestore(&tty->buf.lock, flags);
+                       spin_unlock_irqrestore(&buf->lock, flags);
                        disc->ops->receive_buf(tty, char_buf,
                                                        flag_buf, count);
-                       spin_lock_irqsave(&tty->buf.lock, flags);
+                       spin_lock_irqsave(&buf->lock, flags);
                }
-               clear_bit(TTY_FLUSHING, &tty->flags);
+               clear_bit(TTYP_FLUSHING, &port->iflags);
        }
 
        /* We may have a deferred request to flush the input buffer,
           if so pull the chain under the lock and empty the queue */
-       if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {
-               __tty_buffer_flush(tty);
-               clear_bit(TTY_FLUSHPENDING, &tty->flags);
+       if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) {
+               __tty_buffer_flush(port);
+               clear_bit(TTYP_FLUSHPENDING, &port->iflags);
                wake_up(&tty->read_wait);
        }
-       spin_unlock_irqrestore(&tty->buf.lock, flags);
+       spin_unlock_irqrestore(&buf->lock, flags);
 
        tty_ldisc_deref(disc);
 }
@@ -514,7 +539,8 @@ static void flush_to_ldisc(struct work_struct *work)
  */
 void tty_flush_to_ldisc(struct tty_struct *tty)
 {
-       flush_work(&tty->buf.work);
+       if (!tty->low_latency)
+               flush_work(&tty->port->buf.work);
 }
 
 /**
@@ -532,16 +558,18 @@ void tty_flush_to_ldisc(struct tty_struct *tty)
 
 void tty_flip_buffer_push(struct tty_struct *tty)
 {
+       struct tty_bufhead *buf = &tty->port->buf;
        unsigned long flags;
-       spin_lock_irqsave(&tty->buf.lock, flags);
-       if (tty->buf.tail != NULL)
-               tty->buf.tail->commit = tty->buf.tail->used;
-       spin_unlock_irqrestore(&tty->buf.lock, flags);
+
+       spin_lock_irqsave(&buf->lock, flags);
+       if (buf->tail != NULL)
+               buf->tail->commit = buf->tail->used;
+       spin_unlock_irqrestore(&buf->lock, flags);
 
        if (tty->low_latency)
-               flush_to_ldisc(&tty->buf.work);
+               flush_to_ldisc(&buf->work);
        else
-               schedule_work(&tty->buf.work);
+               schedule_work(&buf->work);
 }
 EXPORT_SYMBOL(tty_flip_buffer_push);
 
@@ -555,13 +583,15 @@ EXPORT_SYMBOL(tty_flip_buffer_push);
  *     Locking: none
  */
 
-void tty_buffer_init(struct tty_struct *tty)
+void tty_buffer_init(struct tty_port *port)
 {
-       spin_lock_init(&tty->buf.lock);
-       tty->buf.head = NULL;
-       tty->buf.tail = NULL;
-       tty->buf.free = NULL;
-       tty->buf.memory_used = 0;
-       INIT_WORK(&tty->buf.work, flush_to_ldisc);
+       struct tty_bufhead *buf = &port->buf;
+
+       spin_lock_init(&buf->lock);
+       buf->head = NULL;
+       buf->tail = NULL;
+       buf->free = NULL;
+       buf->memory_used = 0;
+       INIT_WORK(&buf->work, flush_to_ldisc);
 }
 
index 2ea176b..739ea86 100644 (file)
@@ -186,7 +186,6 @@ void free_tty_struct(struct tty_struct *tty)
        if (tty->dev)
                put_device(tty->dev);
        kfree(tty->write_buf);
-       tty_buffer_free_all(tty);
        tty->magic = 0xDEADDEAD;
        kfree(tty);
 }
@@ -1417,6 +1416,8 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
                        "%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n",
                        __func__, tty->driver->name);
 
+       tty->port->itty = tty;
+
        /*
         * Structures all installed ... call the ldisc open routines.
         * If we fail here just call release_tty to clean up.  No need
@@ -1552,6 +1553,7 @@ static void release_tty(struct tty_struct *tty, int idx)
                tty->ops->shutdown(tty);
        tty_free_termios(tty);
        tty_driver_remove_tty(tty->driver, tty);
+       tty->port->itty = NULL;
 
        if (tty->link)
                tty_kref_put(tty->link);
@@ -1625,7 +1627,6 @@ int tty_release(struct inode *inode, struct file *filp)
        struct tty_struct *tty = file_tty(filp);
        struct tty_struct *o_tty;
        int     pty_master, tty_closing, o_tty_closing, do_sleep;
-       int     devpts;
        int     idx;
        char    buf[64];
 
@@ -1640,7 +1641,6 @@ int tty_release(struct inode *inode, struct file *filp)
        idx = tty->index;
        pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
                      tty->driver->subtype == PTY_TYPE_MASTER);
-       devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0;
        /* Review: parallel close */
        o_tty = tty->link;
 
@@ -1799,9 +1799,6 @@ int tty_release(struct inode *inode, struct file *filp)
        release_tty(tty, idx);
        mutex_unlock(&tty_mutex);
 
-       /* Make this pty number available for reallocation */
-       if (devpts)
-               devpts_kill_index(inode, idx);
        return 0;
 }
 
@@ -2690,6 +2687,11 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case TIOCNXCL:
                clear_bit(TTY_EXCLUSIVE, &tty->flags);
                return 0;
+       case TIOCGEXCL:
+       {
+               int excl = test_bit(TTY_EXCLUSIVE, &tty->flags);
+               return put_user(excl, (int __user *)p);
+       }
        case TIOCNOTTY:
                if (current->signal->tty != tty)
                        return -ENOTTY;
@@ -2937,19 +2939,13 @@ void initialize_tty_struct(struct tty_struct *tty,
        tty_ldisc_init(tty);
        tty->session = NULL;
        tty->pgrp = NULL;
-       tty->overrun_time = jiffies;
-       tty_buffer_init(tty);
        mutex_init(&tty->legacy_mutex);
        mutex_init(&tty->termios_mutex);
        mutex_init(&tty->ldisc_mutex);
        init_waitqueue_head(&tty->write_wait);
        init_waitqueue_head(&tty->read_wait);
        INIT_WORK(&tty->hangup_work, do_tty_hangup);
-       mutex_init(&tty->atomic_read_lock);
        mutex_init(&tty->atomic_write_lock);
-       mutex_init(&tty->output_lock);
-       mutex_init(&tty->echo_lock);
-       spin_lock_init(&tty->read_lock);
        spin_lock_init(&tty->ctrl_lock);
        INIT_LIST_HEAD(&tty->tty_files);
        INIT_WORK(&tty->SAK_work, do_SAK_work);
index 12b1fa0..8481b29 100644 (file)
@@ -1118,7 +1118,6 @@ EXPORT_SYMBOL_GPL(tty_perform_flush);
 int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
                       unsigned int cmd, unsigned long arg)
 {
-       unsigned long flags;
        int retval;
 
        switch (cmd) {
@@ -1153,26 +1152,6 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
                return 0;
        case TCFLSH:
                return tty_perform_flush(tty, arg);
-       case TIOCPKT:
-       {
-               int pktmode;
-
-               if (tty->driver->type != TTY_DRIVER_TYPE_PTY ||
-                   tty->driver->subtype != PTY_TYPE_MASTER)
-                       return -ENOTTY;
-               if (get_user(pktmode, (int __user *) arg))
-                       return -EFAULT;
-               spin_lock_irqsave(&tty->ctrl_lock, flags);
-               if (pktmode) {
-                       if (!tty->packet) {
-                               tty->packet = 1;
-                               tty->link->ctrl_status = 0;
-                       }
-               } else
-                       tty->packet = 0;
-               spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-               return 0;
-       }
        default:
                /* Try the mode commands */
                return tty_mode_ioctl(tty, file, cmd, arg);
index 0f2a2c5..c578229 100644 (file)
@@ -26,7 +26,7 @@
  *     callers who will do ldisc lookups and cannot sleep.
  */
 
-static DEFINE_SPINLOCK(tty_ldisc_lock);
+static DEFINE_RAW_SPINLOCK(tty_ldisc_lock);
 static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
 /* Line disc dispatch table */
 static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
@@ -49,21 +49,21 @@ static void put_ldisc(struct tty_ldisc *ld)
         * If this is the last user, free the ldisc, and
         * release the ldisc ops.
         *
-        * We really want an "atomic_dec_and_lock_irqsave()",
+        * We really want an "atomic_dec_and_raw_lock_irqsave()",
         * but we don't have it, so this does it by hand.
         */
-       local_irq_save(flags);
-       if (atomic_dec_and_lock(&ld->users, &tty_ldisc_lock)) {
+       raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
+       if (atomic_dec_and_test(&ld->users)) {
                struct tty_ldisc_ops *ldo = ld->ops;
 
                ldo->refcount--;
                module_put(ldo->owner);
-               spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+               raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
 
                kfree(ld);
                return;
        }
-       local_irq_restore(flags);
+       raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
        wake_up(&ld->wq_idle);
 }
 
@@ -88,11 +88,11 @@ int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
        if (disc < N_TTY || disc >= NR_LDISCS)
                return -EINVAL;
 
-       spin_lock_irqsave(&tty_ldisc_lock, flags);
+       raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
        tty_ldiscs[disc] = new_ldisc;
        new_ldisc->num = disc;
        new_ldisc->refcount = 0;
-       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+       raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
 
        return ret;
 }
@@ -118,12 +118,12 @@ int tty_unregister_ldisc(int disc)
        if (disc < N_TTY || disc >= NR_LDISCS)
                return -EINVAL;
 
-       spin_lock_irqsave(&tty_ldisc_lock, flags);
+       raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
        if (tty_ldiscs[disc]->refcount)
                ret = -EBUSY;
        else
                tty_ldiscs[disc] = NULL;
-       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+       raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
 
        return ret;
 }
@@ -134,7 +134,7 @@ static struct tty_ldisc_ops *get_ldops(int disc)
        unsigned long flags;
        struct tty_ldisc_ops *ldops, *ret;
 
-       spin_lock_irqsave(&tty_ldisc_lock, flags);
+       raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
        ret = ERR_PTR(-EINVAL);
        ldops = tty_ldiscs[disc];
        if (ldops) {
@@ -144,7 +144,7 @@ static struct tty_ldisc_ops *get_ldops(int disc)
                        ret = ldops;
                }
        }
-       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+       raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
        return ret;
 }
 
@@ -152,10 +152,10 @@ static void put_ldops(struct tty_ldisc_ops *ldops)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&tty_ldisc_lock, flags);
+       raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
        ldops->refcount--;
        module_put(ldops->owner);
-       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+       raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
 }
 
 /**
@@ -287,11 +287,11 @@ static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty)
        unsigned long flags;
        struct tty_ldisc *ld;
 
-       spin_lock_irqsave(&tty_ldisc_lock, flags);
+       raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
        ld = NULL;
        if (test_bit(TTY_LDISC, &tty->flags))
                ld = get_ldisc(tty->ldisc);
-       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+       raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
        return ld;
 }
 
@@ -512,7 +512,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
 static int tty_ldisc_halt(struct tty_struct *tty)
 {
        clear_bit(TTY_LDISC, &tty->flags);
-       return cancel_work_sync(&tty->buf.work);
+       return cancel_work_sync(&tty->port->buf.work);
 }
 
 /**
@@ -525,7 +525,7 @@ static void tty_ldisc_flush_works(struct tty_struct *tty)
 {
        flush_work(&tty->hangup_work);
        flush_work(&tty->SAK_work);
-       flush_work(&tty->buf.work);
+       flush_work(&tty->port->buf.work);
 }
 
 /**
@@ -704,9 +704,9 @@ enable:
        /* Restart the work queue in case no characters kick it off. Safe if
           already running */
        if (work)
-               schedule_work(&tty->buf.work);
+               schedule_work(&tty->port->buf.work);
        if (o_work)
-               schedule_work(&o_tty->buf.work);
+               schedule_work(&o_tty->port->buf.work);
        mutex_unlock(&tty->ldisc_mutex);
        tty_unlock(tty);
        return retval;
@@ -817,7 +817,7 @@ void tty_ldisc_hangup(struct tty_struct *tty)
         */
        clear_bit(TTY_LDISC, &tty->flags);
        tty_unlock(tty);
-       cancel_work_sync(&tty->buf.work);
+       cancel_work_sync(&tty->port->buf.work);
        mutex_unlock(&tty->ldisc_mutex);
 retry:
        tty_lock(tty);
@@ -897,6 +897,11 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
 
 static void tty_ldisc_kill(struct tty_struct *tty)
 {
+       /* There cannot be users from userspace now. But there still might be
+        * drivers holding a reference via tty_ldisc_ref. Do not steal them the
+        * ldisc until they are done. */
+       tty_ldisc_wait_idle(tty, MAX_SCHEDULE_TIMEOUT);
+
        mutex_lock(&tty->ldisc_mutex);
        /*
         * Now kill off the ldisc
index 67feac9..2e41abe 100644 (file)
@@ -19,7 +19,7 @@ static void __lockfunc tty_lock_nested(struct tty_struct *tty,
                                       unsigned int subclass)
 {
        if (tty->magic != TTY_MAGIC) {
-               printk(KERN_ERR "L Bad %p\n", tty);
+               pr_err("L Bad %p\n", tty);
                WARN_ON(1);
                return;
        }
@@ -36,7 +36,7 @@ EXPORT_SYMBOL(tty_lock);
 void __lockfunc tty_unlock(struct tty_struct *tty)
 {
        if (tty->magic != TTY_MAGIC) {
-               printk(KERN_ERR "U Bad %p\n", tty);
+               pr_err("U Bad %p\n", tty);
                WARN_ON(1);
                return;
        }
index d7bdd8d..416b42f 100644 (file)
@@ -21,6 +21,7 @@
 void tty_port_init(struct tty_port *port)
 {
        memset(port, 0, sizeof(*port));
+       tty_buffer_init(port);
        init_waitqueue_head(&port->open_wait);
        init_waitqueue_head(&port->close_wait);
        init_waitqueue_head(&port->delta_msr_wait);
@@ -126,6 +127,7 @@ static void tty_port_destructor(struct kref *kref)
        struct tty_port *port = container_of(kref, struct tty_port, kref);
        if (port->xmit_buf)
                free_page((unsigned long)port->xmit_buf);
+       tty_buffer_free_all(port);
        if (port->ops->destruct)
                port->ops->destruct(port);
        else
index 8e9b4be..60b7b69 100644 (file)
@@ -341,15 +341,11 @@ int paste_selection(struct tty_struct *tty)
        struct  tty_ldisc *ld;
        DECLARE_WAITQUEUE(wait, current);
 
-
        console_lock();
        poke_blanked_console();
        console_unlock();
 
-       /* FIXME: wtf is this supposed to achieve ? */
-       ld = tty_ldisc_ref(tty);
-       if (!ld)
-               ld = tty_ldisc_ref_wait(tty);
+       ld = tty_ldisc_ref_wait(tty);
 
        /* FIXME: this is completely unsafe */
        add_wait_queue(&vc->paste_wait, &wait);
@@ -361,8 +357,7 @@ int paste_selection(struct tty_struct *tty)
                }
                count = sel_buffer_lth - pasted;
                count = min(count, tty->receive_room);
-               tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted,
-                                                               NULL, count);
+               ld->ops->receive_buf(tty, sel_buffer + pasted, NULL, count);
                pasted += count;
        }
        remove_wait_queue(&vc->paste_wait, &wait);
index 4c6285f..e2f57a0 100644 (file)
@@ -844,6 +844,9 @@ COMPATIBLE_IOCTL(TIOCGDEV)
 COMPATIBLE_IOCTL(TIOCCBRK)
 COMPATIBLE_IOCTL(TIOCGSID)
 COMPATIBLE_IOCTL(TIOCGICOUNT)
+COMPATIBLE_IOCTL(TIOCGPKT)
+COMPATIBLE_IOCTL(TIOCGPTLCK)
+COMPATIBLE_IOCTL(TIOCGEXCL)
 /* Little t */
 COMPATIBLE_IOCTL(TIOCGETD)
 COMPATIBLE_IOCTL(TIOCSETD)
index 14afbab..472e6be 100644 (file)
@@ -545,37 +545,38 @@ void devpts_kill_index(struct inode *ptmx_inode, int idx)
        mutex_unlock(&allocated_ptys_lock);
 }
 
-int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty)
+/**
+ * devpts_pty_new -- create a new inode in /dev/pts/
+ * @ptmx_inode: inode of the master
+ * @device: major+minor of the node to be created
+ * @index: used as a name of the node
+ * @priv: what's given back by devpts_get_priv
+ *
+ * The created inode is returned. Remove it from /dev/pts/ by devpts_pty_kill.
+ */
+struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index,
+               void *priv)
 {
-       /* tty layer puts index from devpts_new_index() in here */
-       int number = tty->index;
-       struct tty_driver *driver = tty->driver;
-       dev_t device = MKDEV(driver->major, driver->minor_start+number);
        struct dentry *dentry;
        struct super_block *sb = pts_sb_from_inode(ptmx_inode);
-       struct inode *inode = new_inode(sb);
+       struct inode *inode;
        struct dentry *root = sb->s_root;
        struct pts_fs_info *fsi = DEVPTS_SB(sb);
        struct pts_mount_opts *opts = &fsi->mount_opts;
-       int ret = 0;
        char s[12];
 
-       /* We're supposed to be given the slave end of a pty */
-       BUG_ON(driver->type != TTY_DRIVER_TYPE_PTY);
-       BUG_ON(driver->subtype != PTY_TYPE_SLAVE);
-
+       inode = new_inode(sb);
        if (!inode)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
-       inode->i_ino = number + 3;
+       inode->i_ino = index + 3;
        inode->i_uid = opts->setuid ? opts->uid : current_fsuid();
        inode->i_gid = opts->setgid ? opts->gid : current_fsgid();
        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
        init_special_inode(inode, S_IFCHR|opts->mode, device);
-       inode->i_private = tty;
-       tty->driver_data = inode;
+       inode->i_private = priv;
 
-       sprintf(s, "%d", number);
+       sprintf(s, "%d", index);
 
        mutex_lock(&root->d_inode->i_mutex);
 
@@ -585,18 +586,24 @@ int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty)
                fsnotify_create(root->d_inode, dentry);
        } else {
                iput(inode);
-               ret = -ENOMEM;
+               inode = ERR_PTR(-ENOMEM);
        }
 
        mutex_unlock(&root->d_inode->i_mutex);
 
-       return ret;
+       return inode;
 }
 
-struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number)
+/**
+ * devpts_get_priv -- get private data for a slave
+ * @pts_inode: inode of the slave
+ *
+ * Returns whatever was passed as priv in devpts_pty_new for a given inode.
+ */
+void *devpts_get_priv(struct inode *pts_inode)
 {
        struct dentry *dentry;
-       struct tty_struct *tty;
+       void *priv = NULL;
 
        BUG_ON(pts_inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR));
 
@@ -605,18 +612,22 @@ struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number)
        if (!dentry)
                return NULL;
 
-       tty = NULL;
        if (pts_inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)
-               tty = (struct tty_struct *)pts_inode->i_private;
+               priv = pts_inode->i_private;
 
        dput(dentry);
 
-       return tty;
+       return priv;
 }
 
-void devpts_pty_kill(struct tty_struct *tty)
+/**
+ * devpts_pty_kill -- remove inode form /dev/pts/
+ * @inode: inode of the slave to be removed
+ *
+ * This is an inverse operation of devpts_pty_new.
+ */
+void devpts_pty_kill(struct inode *inode)
 {
-       struct inode *inode = tty->driver_data;
        struct super_block *sb = pts_sb_from_inode(inode);
        struct dentry *root = sb->s_root;
        struct dentry *dentry;
index 5ce0e5f..251a209 100644 (file)
 int devpts_new_index(struct inode *ptmx_inode);
 void devpts_kill_index(struct inode *ptmx_inode, int idx);
 /* mknod in devpts */
-int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty);
-/* get tty structure */
-struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number);
+struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index,
+               void *priv);
+/* get private structure */
+void *devpts_get_priv(struct inode *pts_inode);
 /* unlink */
-void devpts_pty_kill(struct tty_struct *tty);
+void devpts_pty_kill(struct inode *inode);
 
 #else
 
 /* Dummy stubs in the no-pty case */
 static inline int devpts_new_index(struct inode *ptmx_inode) { return -EINVAL; }
 static inline void devpts_kill_index(struct inode *ptmx_inode, int idx) { }
-static inline int devpts_pty_new(struct inode *ptmx_inode,
-                               struct tty_struct *tty)
+static inline struct inode *devpts_pty_new(struct inode *ptmx_inode,
+               dev_t device, int index, void *priv)
 {
-       return -EINVAL;
+       return ERR_PTR(-EINVAL);
 }
-static inline struct tty_struct *devpts_get_tty(struct inode *pts_inode,
-               int number)
+static inline void *devpts_get_priv(struct inode *pts_inode)
 {
        return NULL;
 }
-static inline void devpts_pty_kill(struct tty_struct *tty) { }
+static inline void devpts_pty_kill(struct inode *inode) { }
 
 #endif
 
index f0b4eb4..d7ff88f 100644 (file)
@@ -188,7 +188,9 @@ struct tty_port_operations {
 };
        
 struct tty_port {
+       struct tty_bufhead      buf;            /* Locked internally */
        struct tty_struct       *tty;           /* Back pointer */
+       struct tty_struct       *itty;          /* internal back ptr */
        const struct tty_port_operations *ops;  /* Port operations */
        spinlock_t              lock;           /* Lock protecting tty field */
        int                     blocked_open;   /* Waiting to open */
@@ -197,6 +199,9 @@ struct tty_port {
        wait_queue_head_t       close_wait;     /* Close waiters */
        wait_queue_head_t       delta_msr_wait; /* Modem status change */
        unsigned long           flags;          /* TTY flags ASY_*/
+       unsigned long           iflags;         /* TTYP_ internal flags */
+#define TTYP_FLUSHING                  1  /* Flushing to ldisc in progress */
+#define TTYP_FLUSHPENDING              2  /* Queued buffer flush pending */
        unsigned char           console:1;      /* port is a console */
        struct mutex            mutex;          /* Locking */
        struct mutex            buf_mutex;      /* Buffer alloc lock */
@@ -235,6 +240,7 @@ struct tty_struct {
        struct mutex ldisc_mutex;
        struct tty_ldisc *ldisc;
 
+       struct mutex atomic_write_lock;
        struct mutex legacy_mutex;
        struct mutex termios_mutex;
        spinlock_t ctrl_lock;
@@ -254,7 +260,6 @@ struct tty_struct {
 
        struct tty_struct *link;
        struct fasync_struct *fasync;
-       struct tty_bufhead buf;         /* Locked internally */
        int alt_speed;          /* For magic substitution of 38400 bps */
        wait_queue_head_t write_wait;
        wait_queue_head_t read_wait;
@@ -265,37 +270,10 @@ struct tty_struct {
 
 #define N_TTY_BUF_SIZE 4096
 
-       /*
-        * The following is data for the N_TTY line discipline.  For
-        * historical reasons, this is included in the tty structure.
-        * Mostly locked by the BKL.
-        */
-       unsigned int column;
-       unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
        unsigned char closing:1;
-       unsigned char echo_overrun:1;
        unsigned short minimum_to_wake;
-       unsigned long overrun_time;
-       int num_overrun;
-       unsigned long process_char_map[256/(8*sizeof(unsigned long))];
-       char *read_buf;
-       int read_head;
-       int read_tail;
-       int read_cnt;
-       unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];
-       unsigned char *echo_buf;
-       unsigned int echo_pos;
-       unsigned int echo_cnt;
-       int canon_data;
-       unsigned long canon_head;
-       unsigned int canon_column;
-       struct mutex atomic_read_lock;
-       struct mutex atomic_write_lock;
-       struct mutex output_lock;
-       struct mutex echo_lock;
        unsigned char *write_buf;
        int write_cnt;
-       spinlock_t read_lock;
        /* If the tty has a pending do_SAK, queue it here - akpm */
        struct work_struct SAK_work;
        struct tty_port *port;
@@ -335,8 +313,6 @@ struct tty_file_private {
 #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_FLUSHING           19      /* Flushing to ldisc in progress */
-#define TTY_FLUSHPENDING       20      /* Queued buffer flush pending */
 #define TTY_HUPPING            21      /* ->hangup() in progress */
 
 #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
@@ -412,9 +388,9 @@ extern void disassociate_ctty(int priv);
 extern void no_tty(void);
 extern void tty_flip_buffer_push(struct tty_struct *tty);
 extern void tty_flush_to_ldisc(struct tty_struct *tty);
-extern void tty_buffer_free_all(struct tty_struct *tty);
+extern void tty_buffer_free_all(struct tty_port *port);
 extern void tty_buffer_flush(struct tty_struct *tty);
-extern void tty_buffer_init(struct tty_struct *tty);
+extern void tty_buffer_init(struct tty_port *port);
 extern speed_t tty_get_baud_rate(struct tty_struct *tty);
 extern speed_t tty_termios_baud_rate(struct ktermios *termios);
 extern speed_t tty_termios_input_baud_rate(struct ktermios *termios);
@@ -535,7 +511,7 @@ extern void n_tty_inherit_ops(struct tty_ldisc_ops *ops);
 /* tty_audit.c */
 #ifdef CONFIG_AUDIT
 extern void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
-                              size_t size);
+                              size_t size, unsigned icanon);
 extern void tty_audit_exit(void);
 extern void tty_audit_fork(struct signal_struct *sig);
 extern void tty_audit_tiocsti(struct tty_struct *tty, char ch);
@@ -544,7 +520,7 @@ extern int tty_audit_push_task(struct task_struct *tsk,
                               kuid_t loginuid, u32 sessionid);
 #else
 static inline void tty_audit_add_data(struct tty_struct *tty,
-                                     unsigned char *data, size_t size)
+               unsigned char *data, size_t size, unsigned icanon)
 {
 }
 static inline void tty_audit_tiocsti(struct tty_struct *tty, char ch)
index 9239d03..2002344 100644 (file)
@@ -11,7 +11,7 @@ void tty_schedule_flip(struct tty_struct *tty);
 static inline int tty_insert_flip_char(struct tty_struct *tty,
                                        unsigned char ch, char flag)
 {
-       struct tty_buffer *tb = tty->buf.tail;
+       struct tty_buffer *tb = tty->port->buf.tail;
        if (tb && tb->used < tb->size) {
                tb->flag_buf_ptr[tb->used] = flag;
                tb->char_buf_ptr[tb->used++] = ch;
index 199975f..143dacb 100644 (file)
@@ -74,6 +74,9 @@
 #define TCSETXW                0x5435
 #define TIOCSIG                _IOW('T', 0x36, int)  /* pty: generate signal */
 #define TIOCVHANGUP    0x5437
+#define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
+#define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
+#define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
 
 #define FIONCLEX       0x5450
 #define FIOCLEX                0x5451
index 2f186ed..fc7376b 100644 (file)
@@ -1159,7 +1159,7 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
        cred = current_cred();
 
        spin_lock_irq(&tsk->sighand->siglock);
-       if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name)
+       if (tsk->signal && tsk->signal->tty)
                tty = tsk->signal->tty->name;
        else
                tty = "(none)";
index 2d607f4..22e070f 100644 (file)
@@ -87,6 +87,12 @@ static DEFINE_SEMAPHORE(console_sem);
 struct console *console_drivers;
 EXPORT_SYMBOL_GPL(console_drivers);
 
+#ifdef CONFIG_LOCKDEP
+static struct lockdep_map console_lock_dep_map = {
+       .name = "console_lock"
+};
+#endif
+
 /*
  * This is used for debugging the mess that is the VT code by
  * keeping track if we have the console semaphore held. It's
@@ -1908,12 +1914,14 @@ static int __cpuinit console_cpu_notify(struct notifier_block *self,
  */
 void console_lock(void)
 {
-       BUG_ON(in_interrupt());
+       might_sleep();
+
        down(&console_sem);
        if (console_suspended)
                return;
        console_locked = 1;
        console_may_schedule = 1;
+       mutex_acquire(&console_lock_dep_map, 0, 0, _RET_IP_);
 }
 EXPORT_SYMBOL(console_lock);
 
@@ -1935,6 +1943,7 @@ int console_trylock(void)
        }
        console_locked = 1;
        console_may_schedule = 0;
+       mutex_acquire(&console_lock_dep_map, 0, 1, _RET_IP_);
        return 1;
 }
 EXPORT_SYMBOL(console_trylock);
@@ -2095,6 +2104,7 @@ skip:
                local_irq_restore(flags);
        }
        console_locked = 0;
+       mutex_release(&console_lock_dep_map, 1, _RET_IP_);
 
        /* Release the exclusive_console once it is used */
        if (unlikely(exclusive_console))