Remove Andrew Morton's old email accounts
[safe/jmp/linux-2.6] / drivers / char / vt.c
index 0fefe2a..d8f83e2 100644 (file)
@@ -59,7 +59,7 @@
  * by Martin Mares <mj@atrey.karlin.mff.cuni.cz>, July 1998
  *
  * Removed old-style timers, introduced console_timer, made timer
- * deletion SMP-safe.  17Jun00, Andrew Morton <andrewm@uow.edu.au>
+ * deletion SMP-safe.  17Jun00, Andrew Morton
  *
  * Removed console_lock, enabled interrupts across all console operations
  * 13 March 2001, Andrew Morton
@@ -86,6 +86,7 @@
 #include <linux/mm.h>
 #include <linux/console.h>
 #include <linux/init.h>
+#include <linux/mutex.h>
 #include <linux/vt_kern.h>
 #include <linux/selection.h>
 #include <linux/tiocl.h>
 #include <linux/pm.h>
 #include <linux/font.h>
 #include <linux/bitops.h>
-
-#include <asm/io.h>
+#include <linux/notifier.h>
+#include <linux/device.h>
+#include <linux/io.h>
 #include <asm/system.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 
 #define MAX_NR_CON_DRIVER 16
 
@@ -157,6 +159,8 @@ static void blank_screen_t(unsigned long dummy);
 static void set_palette(struct vc_data *vc);
 
 static int printable;          /* Is console ready for printing? */
+int default_utf8 = true;
+module_param(default_utf8, int, S_IRUGO | S_IWUSR);
 
 /*
  * ignore_poke: don't unblank the screen when things are typed.  This is
@@ -220,6 +224,35 @@ enum {
 };
 
 /*
+ * Notifier list for console events.
+ */
+static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);
+
+int register_vt_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_register(&vt_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(register_vt_notifier);
+
+int unregister_vt_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_unregister(&vt_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_vt_notifier);
+
+static void notify_write(struct vc_data *vc, unsigned int unicode)
+{
+       struct vt_notifier_param param = { .vc = vc, unicode = unicode };
+       atomic_notifier_call_chain(&vt_notifier_list, VT_WRITE, &param);
+}
+
+static void notify_update(struct vc_data *vc)
+{
+       struct vt_notifier_param param = { .vc = vc };
+       atomic_notifier_call_chain(&vt_notifier_list, VT_UPDATE, &param);
+}
+
+/*
  *     Low-Level Functions
  */
 
@@ -228,7 +261,7 @@ enum {
 #ifdef VT_BUF_VRAM_ONLY
 #define DO_UPDATE(vc)  0
 #else
-#define DO_UPDATE(vc)  CON_IS_VISIBLE(vc)
+#define DO_UPDATE(vc)  (CON_IS_VISIBLE(vc) && !console_blanked)
 #endif
 
 static inline unsigned short *screenpos(struct vc_data *vc, int offset, int viewed)
@@ -348,10 +381,12 @@ void update_region(struct vc_data *vc, unsigned long start, int count)
 
 /* Structure of attributes is hardware-dependent */
 
-static u8 build_attr(struct vc_data *vc, u8 _color, u8 _intensity, u8 _blink, u8 _underline, u8 _reverse)
+static u8 build_attr(struct vc_data *vc, u8 _color, u8 _intensity, u8 _blink,
+    u8 _underline, u8 _reverse, u8 _italic)
 {
        if (vc->vc_sw->con_build_attr)
-               return vc->vc_sw->con_build_attr(vc, _color, _intensity, _blink, _underline, _reverse);
+               return vc->vc_sw->con_build_attr(vc, _color, _intensity,
+                      _blink, _underline, _reverse, _italic);
 
 #ifndef VT_BUF_VRAM_ONLY
 /*
@@ -365,13 +400,16 @@ static u8 build_attr(struct vc_data *vc, u8 _color, u8 _intensity, u8 _blink, u8
  *  Bit 7   : blink
  */
        {
-       u8 a = vc->vc_color;
+       u8 a = _color;
        if (!vc->vc_can_do_color)
                return _intensity |
+                      (_italic ? 2 : 0) |
                       (_underline ? 4 : 0) |
                       (_reverse ? 8 : 0) |
                       (_blink ? 0x80 : 0);
-       if (_underline)
+       if (_italic)
+               a = (a & 0xF0) | vc->vc_itcolor;
+       else if (_underline)
                a = (a & 0xf0) | vc->vc_ulcolor;
        else if (_intensity == 0)
                a = (a & 0xf0) | vc->vc_ulcolor;
@@ -392,8 +430,10 @@ static u8 build_attr(struct vc_data *vc, u8 _color, u8 _intensity, u8 _blink, u8
 
 static void update_attr(struct vc_data *vc)
 {
-       vc->vc_attr = build_attr(vc, vc->vc_color, vc->vc_intensity, vc->vc_blink, vc->vc_underline, vc->vc_reverse ^ vc->vc_decscnm);
-       vc->vc_video_erase_char = (build_attr(vc, vc->vc_color, 1, vc->vc_blink, 0, vc->vc_decscnm) << 8) | ' ';
+       vc->vc_attr = build_attr(vc, vc->vc_color, vc->vc_intensity,
+                     vc->vc_blink, vc->vc_underline,
+                     vc->vc_reverse ^ vc->vc_decscnm, vc->vc_italic);
+       vc->vc_video_erase_char = (build_attr(vc, vc->vc_color, 1, vc->vc_blink, 0, vc->vc_decscnm, 0) << 8) | ' ';
 }
 
 /* Note: inverting the screen twice should revert to the original state */
@@ -662,6 +702,7 @@ void redraw_screen(struct vc_data *vc, int is_switch)
        if (is_switch) {
                set_leds();
                compute_shiftstate();
+               notify_update(vc);
        }
 }
 
@@ -708,6 +749,7 @@ int vc_allocate(unsigned int currcons)      /* return 0 on success */
                return -ENXIO;
        if (!vc_cons[currcons].d) {
            struct vc_data *vc;
+           struct vt_notifier_param param;
 
            /* prevent users from taking too much memory */
            if (currcons >= MAX_NR_USER_CONSOLES && !capable(CAP_SYS_RESOURCE))
@@ -719,11 +761,11 @@ int vc_allocate(unsigned int currcons)    /* return 0 on success */
            /* although the numbers above are not valid since long ago, the
               point is still up-to-date and the comment still has its value
               even if only as a historical artifact.  --mj, July 1998 */
-           vc = kmalloc(sizeof(struct vc_data), GFP_KERNEL);
+           param.vc = vc = kzalloc(sizeof(struct vc_data), GFP_KERNEL);
            if (!vc)
                return -ENOMEM;
-           memset(vc, 0, sizeof(*vc));
            vc_cons[currcons].d = vc;
+           INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
            visual_init(vc, currcons, 1);
            if (!*vc->vc_uni_pagedir_loc)
                con_set_default_unimap(vc);
@@ -736,17 +778,20 @@ int vc_allocate(unsigned int currcons)    /* return 0 on success */
            }
            vc->vc_kmalloced = 1;
            vc_init(vc, vc->vc_rows, vc->vc_cols, 1);
+           atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, &param);
        }
        return 0;
 }
 
-static inline int resize_screen(struct vc_data *vc, int width, int height)
+static inline int resize_screen(struct vc_data *vc, int width, int height,
+                               int user)
 {
        /* Resizes the resolution of the display adapater */
        int err = 0;
 
        if (vc->vc_mode != KD_GRAPHICS && vc->vc_sw->con_resize)
-               err = vc->vc_sw->con_resize(vc, width, height);
+               err = vc->vc_sw->con_resize(vc, width, height, user);
+
        return err;
 }
 
@@ -757,12 +802,30 @@ static inline int resize_screen(struct vc_data *vc, int width, int height)
  */
 #define VC_RESIZE_MAXCOL (32767)
 #define VC_RESIZE_MAXROW (32767)
-int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
+
+/**
+ *     vc_do_resize    -       resizing method for the tty
+ *     @tty: tty being resized
+ *     @real_tty: real tty (different to tty if a pty/tty pair)
+ *     @vc: virtual console private data
+ *     @cols: columns
+ *     @lines: lines
+ *
+ *     Resize a virtual console, clipping according to the actual constraints.
+ *     If the caller passes a tty structure then update the termios winsize
+ *     information and perform any neccessary signal handling.
+ *
+ *     Caller must hold the console semaphore. Takes the termios mutex and
+ *     ctrl_lock of the tty IFF a tty is passed.
+ */
+
+static int vc_do_resize(struct tty_struct *tty, struct tty_struct *real_tty,
+               struct vc_data *vc, unsigned int cols, unsigned int lines)
 {
        unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0;
        unsigned int old_cols, old_rows, old_row_size, old_screen_size;
        unsigned int new_cols, new_rows, new_row_size, new_screen_size;
-       unsigned int end;
+       unsigned int end, user;
        unsigned short *newscreen;
 
        WARN_CONSOLE_UNLOCKED();
@@ -770,6 +833,9 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
        if (!vc)
                return -ENXIO;
 
+       user = vc->vc_resize_user;
+       vc->vc_resize_user = 0;
+
        if (cols > VC_RESIZE_MAXCOL || lines > VC_RESIZE_MAXROW)
                return -EINVAL;
 
@@ -790,7 +856,7 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
        old_row_size = vc->vc_size_row;
        old_screen_size = vc->vc_screenbuf_size;
 
-       err = resize_screen(vc, new_cols, new_rows);
+       err = resize_screen(vc, new_cols, new_rows, user);
        if (err) {
                kfree(newscreen);
                return err;
@@ -858,17 +924,15 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
        gotoxy(vc, vc->vc_x, vc->vc_y);
        save_cur(vc);
 
-       if (vc->vc_tty) {
-               struct winsize ws, *cws = &vc->vc_tty->winsize;
-
+       if (tty) {
+               /* Rewrite the requested winsize data with the actual
+                  resulting sizes */
+               struct winsize ws;
                memset(&ws, 0, sizeof(ws));
                ws.ws_row = vc->vc_rows;
                ws.ws_col = vc->vc_cols;
                ws.ws_ypixel = vc->vc_scan_lines;
-               if ((ws.ws_row != cws->ws_row || ws.ws_col != cws->ws_col) &&
-                   vc->vc_tty->pgrp)
-                       kill_pgrp(vc->vc_tty->pgrp, SIGWINCH, 1);
-               *cws = ws;
+               tty_do_resize(tty, real_tty, &ws);
        }
 
        if (CON_IS_VISIBLE(vc))
@@ -876,14 +940,47 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
        return err;
 }
 
-int vc_lock_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
+/**
+ *     vc_resize               -       resize a VT
+ *     @vc: virtual console
+ *     @cols: columns
+ *     @rows: rows
+ *
+ *     Resize a virtual console as seen from the console end of things. We
+ *     use the common vc_do_resize methods to update the structures. The
+ *     caller must hold the console sem to protect console internals and
+ *     vc->vc_tty
+ */
+
+int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows)
 {
-       int rc;
+       return vc_do_resize(vc->vc_tty, vc->vc_tty, vc, cols, rows);
+}
+
+/**
+ *     vt_resize               -       resize a VT
+ *     @tty: tty to resize
+ *     @real_tty: tty if a pty/tty pair
+ *     @ws: winsize attributes
+ *
+ *     Resize a virtual terminal. This is called by the tty layer as we
+ *     register our own handler for resizing. The mutual helper does all
+ *     the actual work.
+ *
+ *     Takes the console sem and the called methods then take the tty
+ *     termios_mutex and the tty ctrl_lock in that order.
+ */
+
+int vt_resize(struct tty_struct *tty, struct tty_struct *real_tty,
+       struct winsize *ws)
+{
+       struct vc_data *vc = tty->driver_data;
+       int ret;
 
        acquire_console_sem();
-       rc = vc_resize(vc, cols, lines);
+       ret = vc_do_resize(tty, real_tty, vc, ws->ws_col, ws->ws_row);
        release_console_sem();
-       return rc;
+       return ret;
 }
 
 void vc_deallocate(unsigned int currcons)
@@ -892,6 +989,8 @@ void vc_deallocate(unsigned int currcons)
 
        if (vc_cons_allocated(currcons)) {
                struct vc_data *vc = vc_cons[currcons].d;
+               struct vt_notifier_param param = { .vc = vc };
+               atomic_notifier_call_chain(&vt_notifier_list, VT_DEALLOCATE, &param);
                vc->vc_sw->con_deinit(vc);
                put_pid(vc->vt_pid);
                module_put(vc->vc_sw->owner);
@@ -933,6 +1032,10 @@ int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa,
 int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa,
     0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff};
 
+module_param_array(default_red, int, NULL, S_IRUGO | S_IWUSR);
+module_param_array(default_grn, int, NULL, S_IRUGO | S_IWUSR);
+module_param_array(default_blu, int, NULL, S_IRUGO | S_IWUSR);
+
 /*
  * gotoxy() must verify all boundaries, because the arguments
  * might also be negative. If the given position is out of
@@ -1000,6 +1103,7 @@ static void lf(struct vc_data *vc)
                vc->vc_pos += vc->vc_size_row;
        }
        vc->vc_need_wrap = 0;
+       notify_write(vc, '\n');
 }
 
 static void ri(struct vc_data *vc)
@@ -1020,6 +1124,7 @@ static inline void cr(struct vc_data *vc)
 {
        vc->vc_pos -= vc->vc_x << 1;
        vc->vc_need_wrap = vc->vc_x = 0;
+       notify_write(vc, '\r');
 }
 
 static inline void bs(struct vc_data *vc)
@@ -1028,6 +1133,7 @@ static inline void bs(struct vc_data *vc)
                vc->vc_pos -= 2;
                vc->vc_x--;
                vc->vc_need_wrap = 0;
+               notify_write(vc, '\b');
        }
 }
 
@@ -1131,6 +1237,7 @@ static void csi_X(struct vc_data *vc, int vpar) /* erase the following vpar posi
 static void default_attr(struct vc_data *vc)
 {
        vc->vc_intensity = 1;
+       vc->vc_italic = 0;
        vc->vc_underline = 0;
        vc->vc_reverse = 0;
        vc->vc_blink = 0;
@@ -1153,6 +1260,9 @@ static void csi_m(struct vc_data *vc)
                        case 2:
                                vc->vc_intensity = 0;
                                break;
+                       case 3:
+                               vc->vc_italic = 1;
+                               break;
                        case 4:
                                vc->vc_underline = 1;
                                break;
@@ -1193,6 +1303,9 @@ static void csi_m(struct vc_data *vc)
                        case 22:
                                vc->vc_intensity = 1;
                                break;
+                       case 23:
+                               vc->vc_italic = 0;
+                               break;
                        case 24:
                                vc->vc_underline = 0;
                                break;
@@ -1453,6 +1566,7 @@ static void save_cur(struct vc_data *vc)
        vc->vc_saved_x          = vc->vc_x;
        vc->vc_saved_y          = vc->vc_y;
        vc->vc_s_intensity      = vc->vc_intensity;
+       vc->vc_s_italic         = vc->vc_italic;
        vc->vc_s_underline      = vc->vc_underline;
        vc->vc_s_blink          = vc->vc_blink;
        vc->vc_s_reverse        = vc->vc_reverse;
@@ -1467,6 +1581,7 @@ static void restore_cur(struct vc_data *vc)
 {
        gotoxy(vc, vc->vc_saved_x, vc->vc_saved_y);
        vc->vc_intensity        = vc->vc_s_intensity;
+       vc->vc_italic           = vc->vc_s_italic;
        vc->vc_underline        = vc->vc_s_underline;
        vc->vc_blink            = vc->vc_s_blink;
        vc->vc_reverse          = vc->vc_s_reverse;
@@ -1496,7 +1611,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear)
        vc->vc_charset          = 0;
        vc->vc_need_wrap        = 0;
        vc->vc_report_mouse     = 0;
-       vc->vc_utf              = 0;
+       vc->vc_utf              = default_utf8;
        vc->vc_utf_count        = 0;
 
        vc->vc_disp_ctrl        = 0;
@@ -1565,6 +1680,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
                                break;
                }
                vc->vc_pos += (vc->vc_x << 1);
+               notify_write(vc, '\t');
                return;
        case 10: case 11: case 12:
                lf(vc);
@@ -1929,7 +2045,46 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
  * kernel memory allocation is available.
  */
 char con_buf[CON_BUF_SIZE];
-DECLARE_MUTEX(con_buf_sem);
+DEFINE_MUTEX(con_buf_mtx);
+
+/* is_double_width() is based on the wcwidth() implementation by
+ * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
+ * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
+ */
+struct interval {
+       uint32_t first;
+       uint32_t last;
+};
+
+static int bisearch(uint32_t ucs, const struct interval *table, int max)
+{
+       int min = 0;
+       int mid;
+
+       if (ucs < table[0].first || ucs > table[max].last)
+               return 0;
+       while (max >= min) {
+               mid = (min + max) / 2;
+               if (ucs > table[mid].last)
+                       min = mid + 1;
+               else if (ucs < table[mid].first)
+                       max = mid - 1;
+               else
+                       return 1;
+       }
+       return 0;
+}
+
+static int is_double_width(uint32_t ucs)
+{
+       static const struct interval double_width[] = {
+               { 0x1100, 0x115F }, { 0x2329, 0x232A }, { 0x2E80, 0x303E },
+               { 0x3040, 0xA4CF }, { 0xAC00, 0xD7A3 }, { 0xF900, 0xFAFF },
+               { 0xFE10, 0xFE19 }, { 0xFE30, 0xFE6F }, { 0xFF00, 0xFF60 },
+               { 0xFFE0, 0xFFE6 }, { 0x20000, 0x2FFFD }, { 0x30000, 0x3FFFD }
+       };
+       return bisearch(ucs, double_width, ARRAY_SIZE(double_width) - 1);
+}
 
 /* acquires console_sem */
 static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int count)
@@ -1947,6 +2102,11 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
        unsigned int currcons;
        unsigned long draw_from = 0, draw_to = 0;
        struct vc_data *vc;
+       unsigned char vc_attr;
+       struct vt_notifier_param param;
+       uint8_t rescan;
+       uint8_t inverse;
+       uint8_t width;
        u16 himask, charmask;
        const unsigned char *orig_buf = NULL;
        int orig_count;
@@ -1975,27 +2135,9 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
            release_console_sem();
            return 0;
        }
-       release_console_sem();
-
        orig_buf = buf;
        orig_count = count;
 
-       /* At this point 'buf' is guaranteed to be a kernel buffer
-        * and therefore no access to userspace (and therefore sleeping)
-        * will be needed.  The con_buf_sem serializes all tty based
-        * console rendering and vcs write/read operations.  We hold
-        * the console spinlock during the entire write.
-        */
-
-       acquire_console_sem();
-
-       vc = tty->driver_data;
-       if (vc == NULL) {
-               printk(KERN_ERR "vt: argh, driver_data _became_ NULL !\n");
-               release_console_sem();
-               goto out;
-       }
-
        himask = vc->vc_hi_font_mask;
        charmask = himask ? 0x1ff : 0xff;
 
@@ -2003,61 +2145,101 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
        if (IS_FG(vc))
                hide_cursor(vc);
 
+       param.vc = vc;
+
        while (!tty->stopped && count) {
                int orig = *buf;
                c = orig;
                buf++;
                n++;
                count--;
+               rescan = 0;
+               inverse = 0;
+               width = 1;
 
                /* Do no translation at all in control states */
                if (vc->vc_state != ESnormal) {
                        tc = c;
                } else if (vc->vc_utf && !vc->vc_disp_ctrl) {
-                   /* Combine UTF-8 into Unicode */
-                   /* Malformed sequences as sequences of replacement glyphs */
+                   /* Combine UTF-8 into Unicode in vc_utf_char.
+                    * vc_utf_count is the number of continuation bytes still
+                    * expected to arrive.
+                    * vc_npar is the number of continuation bytes arrived so
+                    * far
+                    */
 rescan_last_byte:
-                   if(c > 0x7f) {
+                   if ((c & 0xc0) == 0x80) {
+                       /* Continuation byte received */
+                       static const uint32_t utf8_length_changes[] = { 0x0000007f, 0x000007ff, 0x0000ffff, 0x001fffff, 0x03ffffff, 0x7fffffff };
                        if (vc->vc_utf_count) {
-                              if ((c & 0xc0) == 0x80) {
-                                      vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f);
-                                              if (--vc->vc_utf_count) {
-                                              vc->vc_npar++;
-                                              continue;
-                                              }
-                                      tc = c = vc->vc_utf_char;
-                              } else
-                                      goto replacement_glyph;
-                       } else {
-                               vc->vc_npar = 0;
-                               if ((c & 0xe0) == 0xc0) {
-                                   vc->vc_utf_count = 1;
-                                   vc->vc_utf_char = (c & 0x1f);
-                               } else if ((c & 0xf0) == 0xe0) {
-                                   vc->vc_utf_count = 2;
-                                   vc->vc_utf_char = (c & 0x0f);
-                               } else if ((c & 0xf8) == 0xf0) {
-                                   vc->vc_utf_count = 3;
-                                   vc->vc_utf_char = (c & 0x07);
-                               } else if ((c & 0xfc) == 0xf8) {
-                                   vc->vc_utf_count = 4;
-                                   vc->vc_utf_char = (c & 0x03);
-                               } else if ((c & 0xfe) == 0xfc) {
-                                   vc->vc_utf_count = 5;
-                                   vc->vc_utf_char = (c & 0x01);
-                               } else
-                                   goto replacement_glyph;
+                           vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f);
+                           vc->vc_npar++;
+                           if (--vc->vc_utf_count) {
+                               /* Still need some bytes */
                                continue;
-                             }
+                           }
+                           /* Got a whole character */
+                           c = vc->vc_utf_char;
+                           /* Reject overlong sequences */
+                           if (c <= utf8_length_changes[vc->vc_npar - 1] ||
+                                       c > utf8_length_changes[vc->vc_npar])
+                               c = 0xfffd;
+                       } else {
+                           /* Unexpected continuation byte */
+                           vc->vc_utf_count = 0;
+                           c = 0xfffd;
+                       }
                    } else {
-                     if (vc->vc_utf_count)
-                             goto replacement_glyph;
-                     tc = c;
+                       /* Single ASCII byte or first byte of a sequence received */
+                       if (vc->vc_utf_count) {
+                           /* Continuation byte expected */
+                           rescan = 1;
+                           vc->vc_utf_count = 0;
+                           c = 0xfffd;
+                       } else if (c > 0x7f) {
+                           /* First byte of a multibyte sequence received */
+                           vc->vc_npar = 0;
+                           if ((c & 0xe0) == 0xc0) {
+                               vc->vc_utf_count = 1;
+                               vc->vc_utf_char = (c & 0x1f);
+                           } else if ((c & 0xf0) == 0xe0) {
+                               vc->vc_utf_count = 2;
+                               vc->vc_utf_char = (c & 0x0f);
+                           } else if ((c & 0xf8) == 0xf0) {
+                               vc->vc_utf_count = 3;
+                               vc->vc_utf_char = (c & 0x07);
+                           } else if ((c & 0xfc) == 0xf8) {
+                               vc->vc_utf_count = 4;
+                               vc->vc_utf_char = (c & 0x03);
+                           } else if ((c & 0xfe) == 0xfc) {
+                               vc->vc_utf_count = 5;
+                               vc->vc_utf_char = (c & 0x01);
+                           } else {
+                               /* 254 and 255 are invalid */
+                               c = 0xfffd;
+                           }
+                           if (vc->vc_utf_count) {
+                               /* Still need some bytes */
+                               continue;
+                           }
+                       }
+                       /* Nothing to do if an ASCII byte was received */
                    }
+                   /* End of UTF-8 decoding. */
+                   /* c is the received character, or U+FFFD for invalid sequences. */
+                   /* Replace invalid Unicode code points with U+FFFD too */
+                   if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff)
+                       c = 0xfffd;
+                   tc = c;
                } else {        /* no utf or alternate charset mode */
-                 tc = vc->vc_translate[vc->vc_toggle_meta ? (c | 0x80) : c];
+                   tc = vc_translate(vc, c);
                }
 
+               param.c = tc;
+               if (atomic_notifier_call_chain(&vt_notifier_list, VT_PREWRITE,
+                                       &param) == NOTIFY_STOP)
+                       continue;
+
                 /* If the original code was a control character we
                  * only allow a glyph to be displayed if the code is
                  * not normally used (such as for cursor movement) or
@@ -2075,56 +2257,89 @@ rescan_last_byte:
                        && (c != 128+27);
 
                if (vc->vc_state == ESnormal && ok) {
+                       if (vc->vc_utf && !vc->vc_disp_ctrl) {
+                               if (is_double_width(c))
+                                       width = 2;
+                       }
                        /* Now try to find out how to display it */
                        tc = conv_uni_to_pc(vc, tc);
                        if (tc & ~charmask) {
-                               if ( tc == -4 ) {
-                                /* If we got -4 (not found) then see if we have
-                                   defined a replacement character (U+FFFD) */
-replacement_glyph:
-                                       tc = conv_uni_to_pc(vc, 0xfffd);
-                                       if (!(tc & ~charmask))
-                                               goto display_glyph;
-                               } else if ( tc != -3 )
-                                       continue; /* nothing to display */
-                                /* no hash table or no replacement --
-                                * hope for the best */
-                               if ( c & ~charmask )
-                                       tc = '?';
-                               else
-                                       tc = c;
+                               if (tc == -1 || tc == -2) {
+                                   continue; /* nothing to display */
+                               }
+                               /* Glyph not found */
+                               if ((!(vc->vc_utf && !vc->vc_disp_ctrl) || c < 128) && !(c & ~charmask)) {
+                                   /* In legacy mode use the glyph we get by a 1:1 mapping.
+                                      This would make absolutely no sense with Unicode in mind,
+                                      but do this for ASCII characters since a font may lack
+                                      Unicode mapping info and we don't want to end up with
+                                      having question marks only. */
+                                   tc = c;
+                               } else {
+                                   /* Display U+FFFD. If it's not found, display an inverse question mark. */
+                                   tc = conv_uni_to_pc(vc, 0xfffd);
+                                   if (tc < 0) {
+                                       inverse = 1;
+                                       tc = conv_uni_to_pc(vc, '?');
+                                       if (tc < 0) tc = '?';
+                                   }
+                               }
                        }
 
-display_glyph:
-                       if (vc->vc_need_wrap || vc->vc_decim)
+                       if (!inverse) {
+                               vc_attr = vc->vc_attr;
+                       } else {
+                               /* invert vc_attr */
+                               if (!vc->vc_can_do_color) {
+                                       vc_attr = (vc->vc_attr) ^ 0x08;
+                               } else if (vc->vc_hi_font_mask == 0x100) {
+                                       vc_attr = ((vc->vc_attr) & 0x11) | (((vc->vc_attr) & 0xe0) >> 4) | (((vc->vc_attr) & 0x0e) << 4);
+                               } else {
+                                       vc_attr = ((vc->vc_attr) & 0x88) | (((vc->vc_attr) & 0x70) >> 4) | (((vc->vc_attr) & 0x07) << 4);
+                               }
                                FLUSH
-                       if (vc->vc_need_wrap) {
-                               cr(vc);
-                               lf(vc);
                        }
-                       if (vc->vc_decim)
-                               insert_char(vc, 1);
-                       scr_writew(himask ?
-                                    ((vc->vc_attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) :
-                                    (vc->vc_attr << 8) + tc,
-                                  (u16 *) vc->vc_pos);
-                       if (DO_UPDATE(vc) && draw_x < 0) {
-                               draw_x = vc->vc_x;
-                               draw_from = vc->vc_pos;
+
+                       while (1) {
+                               if (vc->vc_need_wrap || vc->vc_decim)
+                                       FLUSH
+                               if (vc->vc_need_wrap) {
+                                       cr(vc);
+                                       lf(vc);
+                               }
+                               if (vc->vc_decim)
+                                       insert_char(vc, 1);
+                               scr_writew(himask ?
+                                            ((vc_attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) :
+                                            (vc_attr << 8) + tc,
+                                          (u16 *) vc->vc_pos);
+                               if (DO_UPDATE(vc) && draw_x < 0) {
+                                       draw_x = vc->vc_x;
+                                       draw_from = vc->vc_pos;
+                               }
+                               if (vc->vc_x == vc->vc_cols - 1) {
+                                       vc->vc_need_wrap = vc->vc_decawm;
+                                       draw_to = vc->vc_pos + 2;
+                               } else {
+                                       vc->vc_x++;
+                                       draw_to = (vc->vc_pos += 2);
+                               }
+
+                               if (!--width) break;
+
+                               tc = conv_uni_to_pc(vc, ' '); /* A space is printed in the second column */
+                               if (tc < 0) tc = ' ';
                        }
-                       if (vc->vc_x == vc->vc_cols - 1) {
-                               vc->vc_need_wrap = vc->vc_decawm;
-                               draw_to = vc->vc_pos + 2;
-                       } else {
-                               vc->vc_x++;
-                               draw_to = (vc->vc_pos += 2);
+                       notify_write(vc, c);
+
+                       if (inverse) {
+                               FLUSH
                        }
-                       if (vc->vc_utf_count) {
-                               if (vc->vc_npar) {
-                                       vc->vc_npar--;
-                                       goto display_glyph;
-                               }
-                               vc->vc_utf_count = 0;
+
+                       if (rescan) {
+                               rescan = 0;
+                               inverse = 0;
+                               width = 1;
                                c = orig;
                                goto rescan_last_byte;
                        }
@@ -2136,8 +2351,7 @@ display_glyph:
        FLUSH
        console_conditional_schedule();
        release_console_sem();
-
-out:
+       notify_update(vc);
        return n;
 #undef FLUSH
 }
@@ -2181,6 +2395,7 @@ static void console_callback(struct work_struct *ignored)
                do_blank_screen(0);
                blank_timer_expired = 0;
        }
+       notify_update(vc_cons[fg_console].d);
 
        release_console_sem();
 }
@@ -2223,13 +2438,15 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
 {
        struct vc_data *vc = vc_cons[fg_console].d;
        unsigned char c;
-       static unsigned long printing;
+       static DEFINE_SPINLOCK(printing_lock);
        const ushort *start;
        ushort cnt = 0;
        ushort myx;
 
        /* console busy or not yet initialized */
-       if (!printable || test_and_set_bit(0, &printing))
+       if (!printable)
+               return;
+       if (!spin_trylock(&printing_lock))
                return;
 
        if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1))
@@ -2282,6 +2499,7 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
                                continue;
                }
                scr_writew((vc->vc_attr << 8) + c, (unsigned short *)vc->vc_pos);
+               notify_write(vc, c);
                cnt++;
                if (myx == vc->vc_cols - 1) {
                        vc->vc_need_wrap = 1;
@@ -2300,9 +2518,10 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
                }
        }
        set_cursor(vc);
+       notify_update(vc);
 
 quit:
-       clear_bit(0, &printing);
+       spin_unlock(&printing_lock);
 }
 
 static struct tty_driver *vt_console_device(struct console *c, int *index)
@@ -2343,13 +2562,14 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
        int lines;
        int ret;
 
-       if (tty->driver->type != TTY_DRIVER_TYPE_CONSOLE)
-               return -EINVAL;
        if (current->signal->tty != tty && !capable(CAP_SYS_ADMIN))
                return -EPERM;
        if (get_user(type, p))
                return -EFAULT;
        ret = 0;
+
+       lock_kernel();
+
        switch (type)
        {
                case TIOCL_SETSEL:
@@ -2369,7 +2589,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
                        ret = sel_loadlut(p);
                        break;
                case TIOCL_GETSHIFTSTATE:
-                       
+
        /*
         * Make it possible to react to Shift+Mousebutton.
         * Note that 'shift_state' is an undocumented
@@ -2424,6 +2644,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
                        ret = -EINVAL;
                        break;
        }
+       unlock_kernel();
        return ret;
 }
 
@@ -2441,11 +2662,11 @@ static int con_write(struct tty_struct *tty, const unsigned char *buf, int count
        return retval;
 }
 
-static void con_put_char(struct tty_struct *tty, unsigned char ch)
+static int con_put_char(struct tty_struct *tty, unsigned char ch)
 {
        if (in_interrupt())
-               return; /* n_r3964 calls put_char() from interrupt context */
-       do_con_write(tty, &ch, 1);
+               return 0;       /* n_r3964 calls put_char() from interrupt context */
+       return do_con_write(tty, &ch, 1);
 }
 
 static int con_write_room(struct tty_struct *tty)
@@ -2534,6 +2755,12 @@ static int con_open(struct tty_struct *tty, struct file *filp)
                ret = vc_allocate(currcons);
                if (ret == 0) {
                        struct vc_data *vc = vc_cons[currcons].d;
+
+                       /* Still being freed */
+                       if (vc->vc_tty) {
+                               release_console_sem();
+                               return -ERESTARTSYS;
+                       }
                        tty->driver_data = vc;
                        vc->vc_tty = tty;
 
@@ -2541,8 +2768,12 @@ static int con_open(struct tty_struct *tty, struct file *filp)
                                tty->winsize.ws_row = vc_cons[currcons].d->vc_rows;
                                tty->winsize.ws_col = vc_cons[currcons].d->vc_cols;
                        }
-                       release_console_sem();
+                       if (vc->vc_utf)
+                               tty->termios->c_iflag |= IUTF8;
+                       else
+                               tty->termios->c_iflag &= ~IUTF8;
                        vcs_make_sysfs(tty);
+                       release_console_sem();
                        return ret;
                }
        }
@@ -2550,36 +2781,27 @@ static int con_open(struct tty_struct *tty, struct file *filp)
        return ret;
 }
 
-/*
- * We take tty_mutex in here to prevent another thread from coming in via init_dev
- * and taking a ref against the tty while we're in the process of forgetting
- * about it and cleaning things up.
- *
- * This is because vcs_remove_sysfs() can sleep and will drop the BKL.
- */
 static void con_close(struct tty_struct *tty, struct file *filp)
 {
-       mutex_lock(&tty_mutex);
-       acquire_console_sem();
-       if (tty && tty->count == 1) {
-               struct vc_data *vc = tty->driver_data;
+       /* Nothing to do - we defer to shutdown */
+}
 
-               if (vc)
-                       vc->vc_tty = NULL;
-               tty->driver_data = NULL;
-               release_console_sem();
-               vcs_remove_sysfs(tty);
-               mutex_unlock(&tty_mutex);
-               /*
-                * tty_mutex is released, but we still hold BKL, so there is
-                * still exclusion against init_dev()
-                */
-               return;
-       }
+static void con_shutdown(struct tty_struct *tty)
+{
+       struct vc_data *vc = tty->driver_data;
+       BUG_ON(vc == NULL);
+       acquire_console_sem();
+       vc->vc_tty = NULL;
+       vcs_remove_sysfs(tty);
        release_console_sem();
-       mutex_unlock(&tty_mutex);
+       tty_shutdown(tty);
 }
 
+static int default_italic_color    = 2; // green (ASCII)
+static int default_underline_color = 3; // cyan (ASCII)
+module_param_named(italic, default_italic_color, int, S_IRUGO | S_IWUSR);
+module_param_named(underline, default_underline_color, int, S_IRUGO | S_IWUSR);
+
 static void vc_init(struct vc_data *vc, unsigned int rows,
                    unsigned int cols, int do_clear)
 {
@@ -2599,7 +2821,8 @@ static void vc_init(struct vc_data *vc, unsigned int rows,
                vc->vc_palette[k++] = default_blu[j] ;
        }
        vc->vc_def_color       = 0x07;   /* white */
-       vc->vc_ulcolor          = 0x0f;   /* bold white */
+       vc->vc_ulcolor         = default_underline_color;
+       vc->vc_itcolor         = default_italic_color;
        vc->vc_halfcolor       = 0x08;   /* grey */
        init_waitqueue_head(&vc->paste_wait);
        reset_terminal(vc, do_clear);
@@ -2695,10 +2918,20 @@ static const struct tty_operations con_ops = {
        .start = con_start,
        .throttle = con_throttle,
        .unthrottle = con_unthrottle,
+       .resize = vt_resize,
+       .shutdown = con_shutdown
 };
 
-int __init vty_init(void)
+static struct cdev vc0_cdev;
+
+int __init vty_init(const struct file_operations *console_fops)
 {
+       cdev_init(&vc0_cdev, console_fops);
+       if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
+           register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
+               panic("Couldn't register /dev/tty0 driver\n");
+       device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
+
        vcs_init();
 
        console_driver = alloc_tty_driver(MAX_NR_CONSOLES);
@@ -2711,11 +2944,12 @@ int __init vty_init(void)
        console_driver->minor_start = 1;
        console_driver->type = TTY_DRIVER_TYPE_CONSOLE;
        console_driver->init_termios = tty_std_termios;
+       if (default_utf8)
+               console_driver->init_termios.c_iflag |= IUTF8;
        console_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
        tty_set_operations(console_driver, &con_ops);
        if (tty_register_driver(console_driver))
                panic("Couldn't register console driver\n");
-
        kbd_init();
        console_map_init();
 #ifdef CONFIG_PROM_CONSOLE
@@ -2850,8 +3084,24 @@ static int con_is_graphics(const struct consw *csw, int first, int last)
        return retval;
 }
 
-static int unbind_con_driver(const struct consw *csw, int first, int last,
-                            int deflt)
+/**
+ * unbind_con_driver - unbind a console driver
+ * @csw: pointer to console driver to unregister
+ * @first: first in range of consoles that @csw should be unbound from
+ * @last: last in range of consoles that @csw should be unbound from
+ * @deflt: should next bound console driver be default after @csw is unbound?
+ *
+ * To unbind a driver from all possible consoles, pass 0 as @first and
+ * %MAX_NR_CONSOLES as @last.
+ *
+ * @deflt controls whether the console that ends up replacing @csw should be
+ * the default console.
+ *
+ * RETURNS:
+ * -ENODEV if @csw isn't a registered console driver or can't be unregistered
+ * or 0 on success.
+ */
+int unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
 {
        struct module *owner = csw->owner;
        const struct consw *defcsw = NULL;
@@ -2936,6 +3186,7 @@ err:
        return retval;
 
 }
+EXPORT_SYMBOL(unbind_con_driver);
 
 static int vt_bind(struct con_driver *con)
 {
@@ -3193,8 +3444,9 @@ int register_con_driver(const struct consw *csw, int first, int last)
                goto err;
 
        con_driver->dev = device_create(vtconsole_class, NULL,
-                                       MKDEV(0, con_driver->node),
-                                       "vtcon%i", con_driver->node);
+                                               MKDEV(0, con_driver->node),
+                                               NULL, "vtcon%i",
+                                               con_driver->node);
 
        if (IS_ERR(con_driver->dev)) {
                printk(KERN_WARNING "Unable to create device for %s; "
@@ -3303,8 +3555,9 @@ static int __init vtconsole_class_init(void)
 
                if (con->con && !con->dev) {
                        con->dev = device_create(vtconsole_class, NULL,
-                                                MKDEV(0, con->node),
-                                                "vtcon%i", con->node);
+                                                        MKDEV(0, con->node),
+                                                        NULL, "vtcon%i",
+                                                        con->node);
 
                        if (IS_ERR(con->dev)) {
                                printk(KERN_WARNING "Unable to create "
@@ -3352,9 +3605,6 @@ void do_blank_screen(int entering_gfx)
                }
                return;
        }
-       if (blank_state != blank_normal_wait)
-               return;
-       blank_state = blank_off;
 
        /* entering graphics mode? */
        if (entering_gfx) {
@@ -3362,10 +3612,15 @@ void do_blank_screen(int entering_gfx)
                save_screen(vc);
                vc->vc_sw->con_blank(vc, -1, 1);
                console_blanked = fg_console + 1;
+               blank_state = blank_off;
                set_origin(vc);
                return;
        }
 
+       if (blank_state != blank_normal_wait)
+               return;
+       blank_state = blank_off;
+
        /* don't blank graphics */
        if (vc->vc_mode != KD_TEXT) {
                console_blanked = fg_console + 1;
@@ -3613,7 +3868,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op)
                goto out;
 
        c = (font.width+7)/8 * 32 * font.charcount;
-       
+
        if (op->data && font.charcount > op->charcount)
                rc = -ENOSPC;
        if (!(op->flags & KD_FONT_FLAG_OLD)) {
@@ -3778,6 +4033,7 @@ u16 screen_glyph(struct vc_data *vc, int offset)
                c |= 0x100;
        return c;
 }
+EXPORT_SYMBOL_GPL(screen_glyph);
 
 /* used by vcs - note the word offset */
 unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed)
@@ -3825,7 +4081,6 @@ EXPORT_SYMBOL(default_blu);
 EXPORT_SYMBOL(update_region);
 EXPORT_SYMBOL(redraw_screen);
 EXPORT_SYMBOL(vc_resize);
-EXPORT_SYMBOL(vc_lock_resize);
 EXPORT_SYMBOL(fg_console);
 EXPORT_SYMBOL(console_blank_hook);
 EXPORT_SYMBOL(console_blanked);