tty: Power: fix suspend vt regression
[safe/jmp/linux-2.6] / drivers / char / vt.c
index 4818327..0c80c68 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
 #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/smp_lock.h>
 #include <linux/tiocl.h>
 #include <linux/kbd_kern.h>
 #include <linux/consolemap.h>
 #include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/workqueue.h>
-#include <linux/bootmem.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
@@ -168,8 +172,9 @@ int do_poke_blanked_console;
 int console_blanked;
 
 static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */
-static int blankinterval = 10*60*HZ;
 static int vesa_off_interval;
+static int blankinterval = 10*60;
+core_param(consoleblank, blankinterval, int, 0444);
 
 static DECLARE_WORK(console_work, console_callback);
 
@@ -220,6 +225,34 @@ 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)
@@ -367,7 +400,7 @@ static u8 build_attr(struct vc_data *vc, u8 _color, u8 _intensity, u8 _blink,
  *  Bit 7   : blink
  */
        {
-       u8 a = vc->vc_color;
+       u8 a = _color;
        if (!vc->vc_can_do_color)
                return _intensity |
                       (_italic ? 2 : 0) |
@@ -669,6 +702,7 @@ void redraw_screen(struct vc_data *vc, int is_switch)
        if (is_switch) {
                set_leds();
                compute_shiftstate();
+               notify_update(vc);
        }
 }
 
@@ -715,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))
@@ -726,35 +761,36 @@ 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);
-           if (!vc->vc_kmalloced)
-               vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL);
+           vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL);
            if (!vc->vc_screenbuf) {
                kfree(vc);
                vc_cons[currcons].d = NULL;
                return -ENOMEM;
            }
-           vc->vc_kmalloced = 1;
            vc_init(vc, vc->vc_rows, vc->vc_cols, 1);
+           vcs_make_sysfs(currcons);
+           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;
 }
 
@@ -765,12 +801,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 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();
@@ -778,6 +832,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;
 
@@ -798,7 +855,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;
@@ -853,10 +910,8 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
        if (new_scr_end > new_origin)
                scr_memsetw((void *)new_origin, vc->vc_video_erase_char,
                            new_scr_end - new_origin);
-       if (vc->vc_kmalloced)
-               kfree(vc->vc_screenbuf);
+       kfree(vc->vc_screenbuf);
        vc->vc_screenbuf = newscreen;
-       vc->vc_kmalloced = 1;
        vc->vc_screenbuf_size = new_screen_size;
        set_origin(vc);
 
@@ -866,32 +921,61 @@ 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, &ws);
        }
 
        if (CON_IS_VISIBLE(vc))
                update_screen(vc);
+       vt_event_post(VT_EVENT_RESIZE, vc->vc_num, vc->vc_num);
        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)
+{
+       return vc_do_resize(vc->vc_tty, vc, cols, rows);
+}
+
+/**
+ *     vt_resize               -       resize a VT
+ *     @tty: tty to resize
+ *     @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.
+ */
+static int vt_resize(struct tty_struct *tty, struct winsize *ws)
 {
-       int rc;
+       struct vc_data *vc = tty->driver_data;
+       int ret;
 
        acquire_console_sem();
-       rc = vc_resize(vc, cols, lines);
+       ret = vc_do_resize(tty, vc, ws->ws_col, ws->ws_row);
        release_console_sem();
-       return rc;
+       return ret;
 }
 
 void vc_deallocate(unsigned int currcons)
@@ -900,11 +984,14 @@ 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);
+               vcs_remove_sysfs(currcons);
                vc->vc_sw->con_deinit(vc);
                put_pid(vc->vt_pid);
                module_put(vc->vc_sw->owner);
-               if (vc->vc_kmalloced)
-                       kfree(vc->vc_screenbuf);
+               kfree(vc->vc_screenbuf);
                if (currcons >= MIN_NR_CONSOLES)
                        kfree(vc);
                vc_cons[currcons].d = NULL;
@@ -1012,6 +1099,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)
@@ -1032,6 +1120,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)
@@ -1040,6 +1129,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');
        }
 }
 
@@ -1392,7 +1482,7 @@ static void setterm_command(struct vc_data *vc)
                        update_attr(vc);
                        break;
                case 9: /* set blanking interval */
-                       blankinterval = ((vc->vc_par[1] < 60) ? vc->vc_par[1] : 60) * 60 * HZ;
+                       blankinterval = ((vc->vc_par[1] < 60) ? vc->vc_par[1] : 60) * 60;
                        poke_blanked_console();
                        break;
                case 10: /* set bell frequency in Hz */
@@ -1517,7 +1607,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;
@@ -1550,7 +1640,10 @@ static void reset_terminal(struct vc_data *vc, int do_clear)
        vc->vc_tab_stop[1]      =
        vc->vc_tab_stop[2]      =
        vc->vc_tab_stop[3]      =
-       vc->vc_tab_stop[4]      = 0x01010101;
+       vc->vc_tab_stop[4]      =
+       vc->vc_tab_stop[5]      =
+       vc->vc_tab_stop[6]      =
+       vc->vc_tab_stop[7]      = 0x01010101;
 
        vc->vc_bell_pitch = DEFAULT_BELL_PITCH;
        vc->vc_bell_duration = DEFAULT_BELL_DURATION;
@@ -1586,6 +1679,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);
@@ -1840,7 +1934,10 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
                                        vc->vc_tab_stop[1] =
                                        vc->vc_tab_stop[2] =
                                        vc->vc_tab_stop[3] =
-                                       vc->vc_tab_stop[4] = 0;
+                                       vc->vc_tab_stop[4] =
+                                       vc->vc_tab_stop[5] =
+                                       vc->vc_tab_stop[6] =
+                                       vc->vc_tab_stop[7] = 0;
                        }
                        return;
                case 'm':
@@ -1950,10 +2047,10 @@ 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 -- 2003-05-20 (Unicode 4.0)
+ * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
  * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
  */
 struct interval {
@@ -1985,11 +2082,10 @@ 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 },
-               { 0xFE30, 0xFE6F }, { 0xFF00, 0xFF60 }, { 0xFFE0, 0xFFE6 },
-               { 0x20000, 0x2FFFD }, { 0x30000, 0x3FFFD }
+               { 0xFE10, 0xFE19 }, { 0xFE30, 0xFE6F }, { 0xFF00, 0xFF60 },
+               { 0xFFE0, 0xFFE6 }, { 0x20000, 0x2FFFD }, { 0x30000, 0x3FFFD }
        };
-       return bisearch(ucs, double_width,
-               sizeof(double_width) / sizeof(*double_width) - 1);
+       return bisearch(ucs, double_width, ARRAY_SIZE(double_width) - 1);
 }
 
 /* acquires console_sem */
@@ -2009,6 +2105,7 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
        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;
@@ -2032,35 +2129,13 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
        currcons = vc->vc_num;
        if (!vc_cons_allocated(currcons)) {
            /* could this happen? */
-           static int error = 0;
-           if (!error) {
-               error = 1;
-               printk("con_write: tty %d not allocated\n", currcons+1);
-           }
+               printk_once("con_write: tty %d not allocated\n", currcons+1);
            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;
 
@@ -2068,6 +2143,8 @@ 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;
@@ -2153,9 +2230,14 @@ rescan_last_byte:
                        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
@@ -2184,9 +2266,12 @@ rescan_last_byte:
                                    continue; /* nothing to display */
                                }
                                /* Glyph not found */
-                               if (!(vc->vc_utf && !vc->vc_disp_ctrl) && !(c & ~charmask)) {
+                               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. */
+                                      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. */
@@ -2210,6 +2295,7 @@ rescan_last_byte:
                                } else {
                                        vc_attr = ((vc->vc_attr) & 0x88) | (((vc->vc_attr) & 0x70) >> 4) | (((vc->vc_attr) & 0x07) << 4);
                                }
+                               FLUSH
                        }
 
                        while (1) {
@@ -2242,6 +2328,11 @@ rescan_last_byte:
                                tc = conv_uni_to_pc(vc, ' '); /* A space is printed in the second column */
                                if (tc < 0) tc = ' ';
                        }
+                       notify_write(vc, c);
+
+                       if (inverse) {
+                               FLUSH
+                       }
 
                        if (rescan) {
                                rescan = 0;
@@ -2258,8 +2349,7 @@ rescan_last_byte:
        FLUSH
        console_conditional_schedule();
        release_console_sem();
-
-out:
+       notify_update(vc);
        return n;
 #undef FLUSH
 }
@@ -2303,6 +2393,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();
 }
@@ -2345,13 +2436,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))
@@ -2404,6 +2497,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;
@@ -2422,9 +2516,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)
@@ -2465,13 +2560,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:
@@ -2491,7 +2587,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
@@ -2546,6 +2642,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
                        ret = -EINVAL;
                        break;
        }
+       unlock_kernel();
        return ret;
 }
 
@@ -2563,18 +2660,18 @@ 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)
 {
        if (tty->stopped)
                return 0;
-       return 4096;            /* No limit, really; we're not buffering */
+       return 32768;           /* No limit, really; we're not buffering */
 }
 
 static int con_chars_in_buffer(struct tty_struct *tty)
@@ -2656,6 +2753,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;
 
@@ -2663,8 +2766,11 @@ 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;
                        }
+                       if (vc->vc_utf)
+                               tty->termios->c_iflag |= IUTF8;
+                       else
+                               tty->termios->c_iflag &= ~IUTF8;
                        release_console_sem();
-                       vcs_make_sysfs(tty);
                        return ret;
                }
        }
@@ -2672,34 +2778,19 @@ 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;
        release_console_sem();
-       mutex_unlock(&tty_mutex);
+       tty_shutdown(tty);
 }
 
 static int default_italic_color    = 2; // green (ASCII)
@@ -2773,18 +2864,14 @@ static int __init con_init(void)
 
        if (blankinterval) {
                blank_state = blank_normal_wait;
-               mod_timer(&console_timer, jiffies + blankinterval);
+               mod_timer(&console_timer, jiffies + (blankinterval * HZ));
        }
 
-       /*
-        * kmalloc is not running yet - we use the bootmem allocator.
-        */
        for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
-               vc_cons[currcons].d = vc = alloc_bootmem(sizeof(struct vc_data));
+               vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT);
                INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
                visual_init(vc, currcons, 1);
-               vc->vc_screenbuf = (unsigned short *)alloc_bootmem(vc->vc_screenbuf_size);
-               vc->vc_kmalloced = 0;
+               vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
                vc_init(vc, vc->vc_rows, vc->vc_cols,
                        currcons || !vc->vc_sw->con_save_screen);
        }
@@ -2819,14 +2906,27 @@ static const struct tty_operations con_ops = {
        .flush_chars = con_flush_chars,
        .chars_in_buffer = con_chars_in_buffer,
        .ioctl = vt_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = vt_compat_ioctl,
+#endif
        .stop = con_stop,
        .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);
@@ -2839,16 +2939,14 @@ 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
-       prom_con_init();
-#endif
 #ifdef CONFIG_MDA_CONSOLE
        mda_console_init();
 #endif
@@ -2856,7 +2954,6 @@ int __init vty_init(void)
 }
 
 #ifndef VT_SINGLE_DRIVER
-#include <linux/device.h>
 
 static struct class *vtconsole_class;
 
@@ -2978,8 +3075,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;
@@ -3064,6 +3177,7 @@ err:
        return retval;
 
 }
+EXPORT_SYMBOL(unbind_con_driver);
 
 static int vt_bind(struct con_driver *con)
 {
@@ -3321,8 +3435,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; "
@@ -3431,8 +3546,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 "
@@ -3480,9 +3596,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) {
@@ -3490,10 +3603,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;
@@ -3518,6 +3636,7 @@ void do_blank_screen(int entering_gfx)
                blank_state = blank_vesa_wait;
                mod_timer(&console_timer, jiffies + vesa_off_interval);
        }
+       vt_event_post(VT_EVENT_BLANK, vc->vc_num, vc->vc_num);
 }
 EXPORT_SYMBOL(do_blank_screen);
 
@@ -3550,7 +3669,7 @@ void do_unblank_screen(int leaving_gfx)
                return; /* but leave console_blanked != 0 */
 
        if (blankinterval) {
-               mod_timer(&console_timer, jiffies + blankinterval);
+               mod_timer(&console_timer, jiffies + (blankinterval * HZ));
                blank_state = blank_normal_wait;
        }
 
@@ -3562,6 +3681,7 @@ void do_unblank_screen(int leaving_gfx)
                console_blank_hook(0);
        set_palette(vc);
        set_cursor(vc);
+       vt_event_post(VT_EVENT_UNBLANK, vc->vc_num, vc->vc_num);
 }
 EXPORT_SYMBOL(do_unblank_screen);
 
@@ -3584,7 +3704,7 @@ void unblank_screen(void)
 static void blank_screen_t(unsigned long dummy)
 {
        if (unlikely(!keventd_up())) {
-               mod_timer(&console_timer, jiffies + blankinterval);
+               mod_timer(&console_timer, jiffies + (blankinterval * HZ));
                return;
        }
        blank_timer_expired = 1;
@@ -3614,7 +3734,7 @@ void poke_blanked_console(void)
        if (console_blanked)
                unblank_screen();
        else if (blankinterval) {
-               mod_timer(&console_timer, jiffies + blankinterval);
+               mod_timer(&console_timer, jiffies + (blankinterval * HZ));
                blank_state = blank_normal_wait;
        }
 }
@@ -3741,7 +3861,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)) {
@@ -3906,6 +4026,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)
@@ -3953,7 +4074,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);