static void reset_terminal(struct vc_data *vc, int do_clear)
{
	vc->vc_top		= 0;
	vc->vc_bottom		= vc->vc_rows;
	vc->vc_state		= ESnormal;
	vc->vc_priv		= EPecma;
	vc->vc_translate	= set_translate(LAT1_MAP, vc);
	vc->vc_G0_charset	= LAT1_MAP;
	vc->vc_G1_charset	= GRAF_MAP;
	vc->vc_charset		= 0;
	vc->vc_need_wrap	= 0;
	vc->vc_report_mouse	= 0;
	vc->vc_utf              = default_utf8;
	vc->vc_utf_count	= 0;

	vc->vc_disp_ctrl	= 0;
	vc->vc_toggle_meta	= 0;

	vc->vc_decscnm		= 0;
	vc->vc_decom		= 0;
	vc->vc_decawm		= 1;
	vc->vc_deccm		= global_cursor_default;
	vc->vc_decim		= 0;

	vt_reset_keyboard(vc->vc_num);

	vc->vc_cursor_type = cur_default;
	vc->vc_complement_mask = vc->vc_s_complement_mask;

	default_attr(vc);
	update_attr(vc);

	vc->vc_tab_stop[0]	=
	vc->vc_tab_stop[1]	=
	vc->vc_tab_stop[2]	=
	vc->vc_tab_stop[3]	=
	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;
	vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;

	gotoxy(vc, 0, 0);
	save_cur(vc);
	if (do_clear)
	    csi_J(vc, 2);
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

#define DEFAULT_BELL_PITCH	750

#define DEFAULT_BELL_DURATION	(HZ/8)

#define DEFAULT_CURSOR_BLINK_MS	200

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

int default_utf8 = true;

int global_cursor_default = -1;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

int do_poke_blanked_console;

int console_blanked;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

int fg_console;

int last_console;

int want_console = -1;

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

int (*console_blank_hook)(int);

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

enum { EPecma = 0, EPdec, EPeq, EPgt, EPlt}

#define set_kbd(vc, x)	vt_set_kbd_mode_bit((vc)->vc_num, (x))

#define clr_kbd(vc, x)	vt_clr_kbd_mode_bit((vc)->vc_num, (x))

#define is_kbd(vc, x)	vt_get_kbd_mode_bit((vc)->vc_num, (x))

struct rgb { u8 r; u8 g; u8 b; }

enum { ESnormal, ESesc, ESsquare, ESgetpars, ESfunckey,
	EShash, ESsetG0, ESsetG1, ESpercent, EScsiignore, ESnonstd,
	ESpalette, ESosc }

struct interval {
	uint32_t first;
	uint32_t last;
}

struct tty_driver *console_driver;

static struct console vt_console_driver = {
	.name		= "tty",
	.write		= vt_console_print,
	.device		= vt_console_device,
	.unblank	= unblank_screen,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
};

static int default_color           = 7;

static int default_italic_color    = 2;

static int default_underline_color = 3;


static void set_mode(struct vc_data *vc, int on_off)
{
	int i;

	for (i = 0; i <= vc->vc_npar; i++)
		if (vc->vc_priv == EPdec) {
			switch(vc->vc_par[i]) {	/* DEC private modes set/reset */
			case 1:			/* Cursor keys send ^[Ox/^[[x */
				if (on_off)
					set_kbd(vc, decckm);
				else
					clr_kbd(vc, decckm);
				break;
			case 3:	/* 80/132 mode switch unimplemented */
#if 0
				vc_resize(deccolm ? 132 : 80, vc->vc_rows);
				/* this alone does not suffice; some user mode
				   utility has to change the hardware regs */
#endif
				break;
			case 5:			/* Inverted screen on/off */
				if (vc->vc_decscnm != on_off) {
					vc->vc_decscnm = on_off;
					invert_screen(vc, 0, vc->vc_screenbuf_size, 0);
					update_attr(vc);
				}
				break;
			case 6:			/* Origin relative/absolute */
				vc->vc_decom = on_off;
				gotoxay(vc, 0, 0);
				break;
			case 7:			/* Autowrap on/off */
				vc->vc_decawm = on_off;
				break;
			case 8:			/* Autorepeat on/off */
				if (on_off)
					set_kbd(vc, decarm);
				else
					clr_kbd(vc, decarm);
				break;
			case 9:
				vc->vc_report_mouse = on_off ? 1 : 0;
				break;
			case 25:		/* Cursor on/off */
				vc->vc_deccm = on_off;
				break;
			case 1000:
				vc->vc_report_mouse = on_off ? 2 : 0;
				break;
			}
		} else {
			switch(vc->vc_par[i]) {	/* ANSI modes set/reset */
			case 3:			/* Monitor (display ctrls) */
				vc->vc_disp_ctrl = on_off;
				break;
			case 4:			/* Insert Mode on/off */
				vc->vc_decim = on_off;
				break;
			case 20:		/* Lf, Enter == CrLf/Lf */
				if (on_off)
					set_kbd(vc, lnm);
				else
					clr_kbd(vc, lnm);
				break;
			}
		}
}

vt: vt_ioctl: fix VT_DISALLOCATE freeing in-use virtual console

The VT_DISALLOCATE ioctl can free a virtual console while tty_release()
is still running, causing a use-after-free in con_shutdown().  This
occurs because VT_DISALLOCATE considers a virtual console's
'struct vc_data' to be unused as soon as the corresponding tty's
refcount hits 0.  But actually it may be still being closed.

Fix this by making vc_data be reference-counted via the embedded
'struct tty_port'.  A newly allocated virtual console has refcount 1.
Opening it for the first time increments the refcount to 2.  Closing it
for the last time decrements the refcount (in tty_operations::cleanup()
so that it happens late enough), as does VT_DISALLOCATE.

Reproducer:
	#include <fcntl.h>
	#include <linux/vt.h>
	#include <sys/ioctl.h>
	#include <unistd.h>

	int main()
	{
		if (fork()) {
			for (;;)
				close(open("/dev/tty5", O_RDWR));
		} else {
			int fd = open("/dev/tty10", O_RDWR);

			for (;;)
				ioctl(fd, VT_DISALLOCATE, 5);
		}
	}

KASAN report:
	BUG: KASAN: use-after-free in con_shutdown+0x76/0x80 drivers/tty/vt/vt.c:3278
	Write of size 8 at addr ffff88806a4ec108 by task syz_vt/129

	CPU: 0 PID: 129 Comm: syz_vt Not tainted 5.6.0-rc2 #11
	Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS ?-20191223_100556-anatol 04/01/2014
	Call Trace:
	 [...]
	 con_shutdown+0x76/0x80 drivers/tty/vt/vt.c:3278
	 release_tty+0xa8/0x410 drivers/tty/tty_io.c:1514
	 tty_release_struct+0x34/0x50 drivers/tty/tty_io.c:1629
	 tty_release+0x984/0xed0 drivers/tty/tty_io.c:1789
	 [...]

	Allocated by task 129:
	 [...]
	 kzalloc include/linux/slab.h:669 [inline]
	 vc_allocate drivers/tty/vt/vt.c:1085 [inline]
	 vc_allocate+0x1ac/0x680 drivers/tty/vt/vt.c:1066
	 con_install+0x4d/0x3f0 drivers/tty/vt/vt.c:3229
	 tty_driver_install_tty drivers/tty/tty_io.c:1228 [inline]
	 tty_init_dev+0x94/0x350 drivers/tty/tty_io.c:1341
	 tty_open_by_driver drivers/tty/tty_io.c:1987 [inline]
	 tty_open+0x3ca/0xb30 drivers/tty/tty_io.c:2035
	 [...]

	Freed by task 130:
	 [...]
	 kfree+0xbf/0x1e0 mm/slab.c:3757
	 vt_disallocate drivers/tty/vt/vt_ioctl.c:300 [inline]
	 vt_ioctl+0x16dc/0x1e30 drivers/tty/vt/vt_ioctl.c:818
	 tty_ioctl+0x9db/0x11b0 drivers/tty/tty_io.c:2660
	 [...]

Fixes: 4001d7b7fc27 ("vt: push down the tty lock so we can see what is left to tackle")
Cc: <stable@vger.kernel.org> # v3.4+
Reported-by: syzbot+522643ab5729b0421998@syzkaller.appspotmail.com
Acked-by: Jiri Slaby <jslaby@suse.cz>
Signed-off-by: Eric Biggers <ebiggers@google.com>
Link: https://lore.kernel.org/r/20200322034305.210082-2-ebiggers@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

int default_utf8 = true;

int global_cursor_default = -1;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

int do_poke_blanked_console;

int console_blanked;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

int fg_console;

int last_console;

int want_console = -1;

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

int (*console_blank_hook)(int);

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

enum { EPecma = 0, EPdec, EPeq, EPgt, EPlt}

#define set_kbd(vc, x)	vt_set_kbd_mode_bit((vc)->vc_num, (x))

#define clr_kbd(vc, x)	vt_clr_kbd_mode_bit((vc)->vc_num, (x))

#define decarm		VC_REPEAT

#define decckm		VC_CKMODE

#define lnm		VC_CRLF

struct rgb { u8 r; u8 g; u8 b; }

struct interval {
	uint32_t first;
	uint32_t last;
}

struct tty_driver *console_driver;

static struct console vt_console_driver = {
	.name		= "tty",
	.write		= vt_console_print,
	.device		= vt_console_device,
	.unblank	= unblank_screen,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
};

static int default_color           = 7;

static int default_italic_color    = 2;

static int default_underline_color = 3;


static void csi_P(struct vc_data *vc, unsigned int nr)
{
	if (nr > vc->vc_cols - vc->vc_x)
		nr = vc->vc_cols - vc->vc_x;
	else if (!nr)
		nr = 1;
	delete_char(vc, nr);
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

int default_utf8 = true;

int global_cursor_default = -1;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

int do_poke_blanked_console;

int console_blanked;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

int fg_console;

int last_console;

int want_console = -1;

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

int (*console_blank_hook)(int);

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
				       8,12,10,14, 9,13,11,15 };

unsigned char default_red[] = {
	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
	0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
};

unsigned char default_grn[] = {
	0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa,
	0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff
};


u32 screen_glyph_unicode(struct vc_data *vc, int n)
{
	struct uni_screen *uniscr = get_vc_uniscr(vc);

	if (uniscr)
		return uniscr->lines[n / vc->vc_cols][n % vc->vc_cols];
	return inverse_translate(vc, screen_glyph(vc, n * 2), 1);
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

int default_utf8 = true;

int global_cursor_default = -1;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

int do_poke_blanked_console;

int console_blanked;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

int fg_console;

int last_console;

int want_console = -1;

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

int (*console_blank_hook)(int);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

#define get_vc_uniscr(vc) NULL

#define get_vc_uniscr(vc) vc->vc_uni_screen

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

struct rgb { u8 r; u8 g; u8 b; }

struct interval {
	uint32_t first;
	uint32_t last;
}

struct tty_driver *console_driver;

static struct console vt_console_driver = {
	.name		= "tty",
	.write		= vt_console_print,
	.device		= vt_console_device,
	.unblank	= unblank_screen,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
};

static int default_color           = 7;

static int default_italic_color    = 2;

static int default_underline_color = 3;

static const struct tty_operations con_ops = {
	.install = con_install,
	.open = con_open,
	.close = con_close,
	.write = con_write,
	.write_room = con_write_room,
	.put_char = con_put_char,
	.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,
	.cleanup = con_cleanup,
};

static struct cdev vc0_cdev;

static struct attribute *vt_dev_attrs[] = {
	&dev_attr_active.attr,
	NULL
};

static struct class *vtconsole_class;

static struct attribute *con_dev_attrs[] = {
	&dev_attr_bind.attr,
	&dev_attr_name.attr,
	NULL
};


u16 vcs_scr_readw(struct vc_data *vc, const u16 *org)
{
	if ((unsigned long)org == vc->vc_pos && softcursor_original != -1)
		return softcursor_original;
	return scr_readw(org);
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static struct vc_data *master_display_fg;

static struct device *tty0dev;

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
				       8,12,10,14, 9,13,11,15 };

unsigned char default_red[] = {
	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
	0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
};

unsigned char default_grn[] = {
	0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa,
	0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff
};

unsigned char default_blu[] = {
	0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa,
	0x55, 0x55, 0x55, 0x55, 0xff, 0xff, 0xff, 0xff
};

struct rgb { u8 r; u8 g; u8 b; }

struct interval {
	uint32_t first;
	uint32_t last;
}

struct tty_driver *console_driver;

static struct console vt_console_driver = {
	.name		= "tty",
	.write		= vt_console_print,
	.device		= vt_console_device,
	.unblank	= unblank_screen,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
};


static inline void respond_ID(struct tty_struct *tty)
{
	respond_string(VT102ID, tty->port);
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

#define VT102ID "\033[?6c"

struct rgb { u8 r; u8 g; u8 b; }

struct interval {
	uint32_t first;
	uint32_t last;
}

struct tty_driver *console_driver;

static struct console vt_console_driver = {
	.name		= "tty",
	.write		= vt_console_print,
	.device		= vt_console_device,
	.unblank	= unblank_screen,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
};

static int default_color           = 7;

static int default_italic_color    = 2;

static int default_underline_color = 3;

static const struct tty_operations con_ops = {
	.install = con_install,
	.open = con_open,
	.close = con_close,
	.write = con_write,
	.write_room = con_write_room,
	.put_char = con_put_char,
	.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,
	.cleanup = con_cleanup,
};

static struct cdev vc0_cdev;

static DEVICE_ATTR(active, S_IRUGO, show_tty_active, NULL);

static struct attribute *vt_dev_attrs[] = {
	&dev_attr_active.attr,
	NULL
};

static struct class *vtconsole_class;

static DEVICE_ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind);


static void set_origin(struct vc_data *vc)
{
	WARN_CONSOLE_UNLOCKED();

	if (!con_is_visible(vc) ||
	    !vc->vc_sw->con_set_origin ||
	    !vc->vc_sw->con_set_origin(vc))
		vc->vc_origin = (unsigned long)vc->vc_screenbuf;
	vc->vc_visible_origin = vc->vc_origin;
	vc->vc_scr_end = vc->vc_origin + vc->vc_screenbuf_size;
	vc->vc_pos = vc->vc_origin + vc->vc_size_row * vc->vc_y + 2 * vc->vc_x;
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
				       8,12,10,14, 9,13,11,15 };

unsigned char default_red[] = {
	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
	0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
};

unsigned char default_grn[] = {
	0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa,
	0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff
};

unsigned char default_blu[] = {
	0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa,
	0x55, 0x55, 0x55, 0x55, 0xff, 0xff, 0xff, 0xff
};

struct rgb { u8 r; u8 g; u8 b; }

struct interval {
	uint32_t first;
	uint32_t last;
}

struct tty_driver *console_driver;


static void rgb_background(struct vc_data *vc, const struct rgb *c)
{
	/* For backgrounds, err on the dark side. */
	vc->vc_color = (vc->vc_color & 0x0f)
		| (c->r&0x80) >> 1 | (c->g&0x80) >> 2 | (c->b&0x80) >> 3;
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
				       8,12,10,14, 9,13,11,15 };

struct rgb { u8 r; u8 g; u8 b; }

struct interval {
	uint32_t first;
	uint32_t last;
}

struct tty_driver *console_driver;

static struct console vt_console_driver = {
	.name		= "tty",
	.write		= vt_console_print,
	.device		= vt_console_device,
	.unblank	= unblank_screen,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
};

static int default_color           = 7;

static int default_italic_color    = 2;

static int default_underline_color = 3;

static const struct tty_operations con_ops = {
	.install = con_install,
	.open = con_open,
	.close = con_close,
	.write = con_write,
	.write_room = con_write_room,
	.put_char = con_put_char,
	.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,
	.cleanup = con_cleanup,
};

static struct cdev vc0_cdev;

static DEVICE_ATTR(active, S_IRUGO, show_tty_active, NULL);


static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x,
				 unsigned int nr)
{
	struct uni_screen *uniscr = get_vc_uniscr(vc);

	if (uniscr) {
		char32_t *ln = uniscr->lines[vc->vc_y];

		memset32(&ln[x], ' ', nr);
	}
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

int default_utf8 = true;

int global_cursor_default = -1;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

int do_poke_blanked_console;

int console_blanked;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

int fg_console;

int last_console;

int want_console = -1;

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

int (*console_blank_hook)(int);

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

#define get_vc_uniscr(vc) NULL

#define get_vc_uniscr(vc) vc->vc_uni_screen

typedef uint32_t char32_t;

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
				       8,12,10,14, 9,13,11,15 };

unsigned char default_red[] = {
	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
	0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
};

unsigned char default_grn[] = {
	0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa,
	0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff
};


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 long end;
	unsigned int old_rows, old_row_size, first_copied_row;
	unsigned int new_cols, new_rows, new_row_size, new_screen_size;
	unsigned int user;
	unsigned short *newscreen;
	struct uni_screen *new_uniscr = NULL;

	WARN_CONSOLE_UNLOCKED();

	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;

	new_cols = (cols ? cols : vc->vc_cols);
	new_rows = (lines ? lines : vc->vc_rows);
	new_row_size = new_cols << 1;
	new_screen_size = new_row_size * new_rows;

	if (new_cols == vc->vc_cols && new_rows == vc->vc_rows)
		return 0;

	if (new_screen_size > (4 << 20))
		return -EINVAL;
	newscreen = kzalloc(new_screen_size, GFP_USER);
	if (!newscreen)
		return -ENOMEM;

	if (get_vc_uniscr(vc)) {
		new_uniscr = vc_uniscr_alloc(new_cols, new_rows);
		if (!new_uniscr) {
			kfree(newscreen);
			return -ENOMEM;
		}
	}

	if (vc_is_sel(vc))
		clear_selection();

	old_rows = vc->vc_rows;
	old_row_size = vc->vc_size_row;

	err = resize_screen(vc, new_cols, new_rows, user);
	if (err) {
		kfree(newscreen);
		kfree(new_uniscr);
		return err;
	}

	vc->vc_rows = new_rows;
	vc->vc_cols = new_cols;
	vc->vc_size_row = new_row_size;
	vc->vc_screenbuf_size = new_screen_size;

	rlth = min(old_row_size, new_row_size);
	rrem = new_row_size - rlth;
	old_origin = vc->vc_origin;
	new_origin = (long) newscreen;
	new_scr_end = new_origin + new_screen_size;

	if (vc->vc_y > new_rows) {
		if (old_rows - vc->vc_y < new_rows) {
			/*
			 * Cursor near the bottom, copy contents from the
			 * bottom of buffer
			 */
			first_copied_row = (old_rows - new_rows);
		} else {
			/*
			 * Cursor is in no man's land, copy 1/2 screenful
			 * from the top and bottom of cursor position
			 */
			first_copied_row = (vc->vc_y - new_rows/2);
		}
		old_origin += first_copied_row * old_row_size;
	} else
		first_copied_row = 0;
	end = old_origin + old_row_size * min(old_rows, new_rows);

	vc_uniscr_copy_area(new_uniscr, new_cols, new_rows,
			    get_vc_uniscr(vc), rlth/2, first_copied_row,
			    min(old_rows, new_rows));
	vc_uniscr_set(vc, new_uniscr);

	update_attr(vc);

	while (old_origin < end) {
		scr_memcpyw((unsigned short *) new_origin,
			    (unsigned short *) old_origin, rlth);
		if (rrem)
			scr_memsetw((void *)(new_origin + rlth),
				    vc->vc_video_erase_char, rrem);
		old_origin += old_row_size;
		new_origin += new_row_size;
	}
	if (new_scr_end > new_origin)
		scr_memsetw((void *)new_origin, vc->vc_video_erase_char,
			    new_scr_end - new_origin);
	kfree(vc->vc_screenbuf);
	vc->vc_screenbuf = newscreen;
	vc->vc_screenbuf_size = new_screen_size;
	set_origin(vc);

	/* do part of a reset_terminal() */
	vc->vc_top = 0;
	vc->vc_bottom = vc->vc_rows;
	gotoxy(vc, vc->vc_x, vc->vc_y);
	save_cur(vc);

	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;
		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);
	notify_update(vc);
	return err;
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

int default_utf8 = true;

int global_cursor_default = -1;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

int do_poke_blanked_console;

int console_blanked;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

int fg_console;

int last_console;

int want_console = -1;

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

int (*console_blank_hook)(int);

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

#define get_vc_uniscr(vc) NULL

#define get_vc_uniscr(vc) vc->vc_uni_screen

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

#define VC_RESIZE_MAXCOL (32767)

#define VC_RESIZE_MAXROW (32767)

#define set_kbd(vc, x)	vt_set_kbd_mode_bit((vc)->vc_num, (x))

#define clr_kbd(vc, x)	vt_clr_kbd_mode_bit((vc)->vc_num, (x))

#define is_kbd(vc, x)	vt_get_kbd_mode_bit((vc)->vc_num, (x))

const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
				       8,12,10,14, 9,13,11,15 };

unsigned char default_red[] = {
	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
	0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
};


static void gotoxy(struct vc_data *vc, int new_x, int new_y)
{
	int min_y, max_y;

	if (new_x < 0)
		vc->vc_x = 0;
	else {
		if (new_x >= vc->vc_cols)
			vc->vc_x = vc->vc_cols - 1;
		else
			vc->vc_x = new_x;
	}

 	if (vc->vc_decom) {
		min_y = vc->vc_top;
		max_y = vc->vc_bottom;
	} else {
		min_y = 0;
		max_y = vc->vc_rows;
	}
	if (new_y < min_y)
		vc->vc_y = min_y;
	else if (new_y >= max_y)
		vc->vc_y = max_y - 1;
	else
		vc->vc_y = new_y;
	vc->vc_pos = vc->vc_origin + vc->vc_y * vc->vc_size_row + (vc->vc_x<<1);
	vc->vc_need_wrap = 0;
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

int default_utf8 = true;

int global_cursor_default = -1;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

int do_poke_blanked_console;

int console_blanked;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

int fg_console;

int last_console;

int want_console = -1;

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

int (*console_blank_hook)(int);

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

struct rgb { u8 r; u8 g; u8 b; }

struct interval {
	uint32_t first;
	uint32_t last;
}

struct tty_driver *console_driver;

static struct console vt_console_driver = {
	.name		= "tty",
	.write		= vt_console_print,
	.device		= vt_console_device,
	.unblank	= unblank_screen,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
};

static int default_color           = 7;

static int default_italic_color    = 2;

static int default_underline_color = 3;

static const struct tty_operations con_ops = {
	.install = con_install,
	.open = con_open,
	.close = con_close,
	.write = con_write,
	.write_room = con_write_room,
	.put_char = con_put_char,
	.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,
	.cleanup = con_cleanup,
};

static struct cdev vc0_cdev;

static DEVICE_ATTR(active, S_IRUGO, show_tty_active, NULL);

static struct attribute *vt_dev_attrs[] = {
	&dev_attr_active.attr,
	NULL
};


static void ri(struct vc_data *vc)
{
    	/* don't scroll if below top of scrolling region, or
	 * if above scrolling region
	 */
	if (vc->vc_y == vc->vc_top)
		con_scroll(vc, vc->vc_top, vc->vc_bottom, SM_DOWN, 1);
	else if (vc->vc_y > 0) {
		vc->vc_y--;
		vc->vc_pos -= vc->vc_size_row;
	}
	vc->vc_need_wrap = 0;
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

struct rgb { u8 r; u8 g; u8 b; }

struct interval {
	uint32_t first;
	uint32_t last;
}

struct tty_driver *console_driver;

static struct console vt_console_driver = {
	.name		= "tty",
	.write		= vt_console_print,
	.device		= vt_console_device,
	.unblank	= unblank_screen,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
};

static int default_color           = 7;

static int default_italic_color    = 2;

static int default_underline_color = 3;

static const struct tty_operations con_ops = {
	.install = con_install,
	.open = con_open,
	.close = con_close,
	.write = con_write,
	.write_room = con_write_room,
	.put_char = con_put_char,
	.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,
	.cleanup = con_cleanup,
};

static struct cdev vc0_cdev;

static DEVICE_ATTR(active, S_IRUGO, show_tty_active, NULL);

static struct attribute *vt_dev_attrs[] = {
	&dev_attr_active.attr,
	NULL
};

static struct class *vtconsole_class;

static DEVICE_ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind);

static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);


static void rgb_foreground(struct vc_data *vc, const struct rgb *c)
{
	u8 hue = 0, max = max3(c->r, c->g, c->b);

	if (c->r > max / 2)
		hue |= 4;
	if (c->g > max / 2)
		hue |= 2;
	if (c->b > max / 2)
		hue |= 1;

	if (hue == 7 && max <= 0x55) {
		hue = 0;
		vc->vc_intensity = 2;
	} else if (max > 0xaa)
		vc->vc_intensity = 2;
	else
		vc->vc_intensity = 1;

	vc->vc_color = (vc->vc_color & 0xf0) | hue;
}


static inline bool vt_in_use(unsigned int i)
{
	extern struct tty_driver *console_driver;

	return console_driver->ttys[i] && console_driver->ttys[i]->count;
}

bool vt_dont_switch;

static void complete_change_console(struct vc_data *vc);

struct vt_event_wait {
	struct list_head list;
	struct vt_event event;
	int done;
}

static LIST_HEAD(vt_events);

static DEFINE_SPINLOCK(vt_event_lock);

static DECLARE_WAIT_QUEUE_HEAD(vt_event_waitqueue);

struct compat_consolefontdesc {
	unsigned short charcount;       /* characters in font (256 or 512) */
	unsigned short charheight;      /* scan lines per character (1-32) */
	compat_caddr_t chardata;	/* font data in expanded form */
}

struct compat_console_font_op {
	compat_uint_t op;        /* operation code KD_FONT_OP_* */
	compat_uint_t flags;     /* KD_FONT_FLAG_* */
	compat_uint_t width, height;     /* font size */
	compat_uint_t charcount;
	compat_caddr_t data;    /* font data with height fixed to 32 */
}

struct compat_unimapdesc {
	unsigned short entry_ct;
	compat_caddr_t entries;
}

static int disable_vt_switch;


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, _italic);

/*
 * ++roman: I completely changed the attribute format for monochrome
 * mode (!can_do_color). The formerly used MDA (monochrome display
 * adapter) format didn't allow the combination of certain effects.
 * Now the attribute is just a bit vector:
 *  Bit 0..1: intensity (0..2)
 *  Bit 2   : underline
 *  Bit 3   : reverse
 *  Bit 7   : blink
 */
	{
	u8 a = _color;
	if (!vc->vc_can_do_color)
		return _intensity |
		       (_italic ? 2 : 0) |
		       (_underline ? 4 : 0) |
		       (_reverse ? 8 : 0) |
		       (_blink ? 0x80 : 0);
	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_halfcolor;
	if (_reverse)
		a = ((a) & 0x88) | ((((a) >> 4) | ((a) << 4)) & 0x77);
	if (_blink)
		a ^= 0x80;
	if (_intensity == 2)
		a ^= 0x08;
	if (vc->vc_hi_font_mask == 0x100)
		a <<= 1;
	return a;
	}
}


static void csi_L(struct vc_data *vc, unsigned int nr)
{
	if (nr > vc->vc_rows - vc->vc_y)
		nr = vc->vc_rows - vc->vc_y;
	else if (!nr)
		nr = 1;
	con_scroll(vc, vc->vc_y, vc->vc_bottom, SM_DOWN, nr);
	vc->vc_need_wrap = 0;
}


static void set_cursor(struct vc_data *vc)
{
	if (!con_is_fg(vc) || console_blanked || vc->vc_mode == KD_GRAPHICS)
		return;
	if (vc->vc_deccm) {
		if (vc_is_sel(vc))
			clear_selection();
		add_softcursor(vc);
		if ((vc->vc_cursor_type & 0x0f) != 1)
			vc->vc_sw->con_cursor(vc, CM_DRAW);
	} else
		hide_cursor(vc);
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

int console_blanked;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

struct rgb { u8 r; u8 g; u8 b; }

struct interval {
	uint32_t first;
	uint32_t last;
}

struct tty_driver *console_driver;

static struct console vt_console_driver = {
	.name		= "tty",
	.write		= vt_console_print,
	.device		= vt_console_device,
	.unblank	= unblank_screen,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
};

static int default_color           = 7;

static int default_italic_color    = 2;

static int default_underline_color = 3;

static const struct tty_operations con_ops = {
	.install = con_install,
	.open = con_open,
	.close = con_close,
	.write = con_write,
	.write_room = con_write_room,
	.put_char = con_put_char,
	.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,
	.cleanup = con_cleanup,
};

static struct cdev vc0_cdev;

static DEVICE_ATTR(active, S_IRUGO, show_tty_active, NULL);

static struct attribute *vt_dev_attrs[] = {
	&dev_attr_active.attr,
	NULL
};

static struct class *vtconsole_class;

static DEVICE_ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind);

static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);


void putconsxy(struct vc_data *vc, unsigned char *p)
{
	hide_cursor(vc);
	gotoxy(vc, p[0], p[1]);
	set_cursor(vc);
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static struct vc_data *master_display_fg;

static struct device *tty0dev;

struct uni_screen {
	char32_t *lines[0];
}

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
				       8,12,10,14, 9,13,11,15 };

unsigned char default_red[] = {
	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
	0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
};

unsigned char default_grn[] = {
	0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa,
	0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff
};

unsigned char default_blu[] = {
	0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa,
	0x55, 0x55, 0x55, 0x55, 0xff, 0xff, 0xff, 0xff
};

struct rgb { u8 r; u8 g; u8 b; }

struct interval {
	uint32_t first;
	uint32_t last;
}

struct tty_driver *console_driver;

static struct console vt_console_driver = {
	.name		= "tty",
	.write		= vt_console_print,
	.device		= vt_console_device,
	.unblank	= unblank_screen,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
};


static inline unsigned short *screenpos(struct vc_data *vc, int offset, int viewed)
{
	unsigned short *p;

	if (!viewed)
		p = (unsigned short *)(vc->vc_origin + offset);
	else if (!vc->vc_sw->con_screen_pos)
		p = (unsigned short *)(vc->vc_visible_origin + offset);
	else
		p = vc->vc_sw->con_screen_pos(vc, offset);
	return p;
}


static void add_softcursor(struct vc_data *vc)
{
	int i = scr_readw((u16 *) vc->vc_pos);
	u32 type = vc->vc_cursor_type;

	if (! (type & 0x10)) return;
	if (softcursor_original != -1) return;
	softcursor_original = i;
	i |= ((type >> 8) & 0xff00 );
	i ^= ((type) & 0xff00 );
	if ((type & 0x20) && ((softcursor_original & 0x7000) == (i & 0x7000))) i ^= 0x7000;
	if ((type & 0x40) && ((i & 0x700) == ((i & 0x7000) >> 4))) i ^= 0x0700;
	scr_writew(i, (u16 *) vc->vc_pos);
	if (con_should_update(vc))
		vc->vc_sw->con_putc(vc, i, vc->vc_y, vc->vc_x);
}


do_fontx_ioctl(int cmd, struct consolefontdesc __user *user_cfd, int perm, struct console_font_op *op)
{
	struct consolefontdesc cfdarg;
	int i;

	if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc)))
		return -EFAULT;

	switch (cmd) {
	case PIO_FONTX:
		if (!perm)
			return -EPERM;
		op->op = KD_FONT_OP_SET;
		op->flags = KD_FONT_FLAG_OLD;
		op->width = 8;
		op->height = cfdarg.charheight;
		op->charcount = cfdarg.charcount;
		op->data = cfdarg.chardata;
		return con_font_op(vc_cons[fg_console].d, op);
	case GIO_FONTX: {
		op->op = KD_FONT_OP_GET;
		op->flags = KD_FONT_FLAG_OLD;
		op->width = 8;
		op->height = cfdarg.charheight;
		op->charcount = cfdarg.charcount;
		op->data = cfdarg.chardata;
		i = con_font_op(vc_cons[fg_console].d, op);
		if (i)
			return i;
		cfdarg.charheight = op->height;
		cfdarg.charcount = op->charcount;
		if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc)))
			return -EFAULT;
		return 0;
		}
	}
	return -EINVAL;
}

static void complete_change_console(struct vc_data *vc);

struct vt_event_wait {
	struct list_head list;
	struct vt_event event;
	int done;
}

struct compat_consolefontdesc {
	unsigned short charcount;       /* characters in font (256 or 512) */
	unsigned short charheight;      /* scan lines per character (1-32) */
	compat_caddr_t chardata;	/* font data in expanded form */
}

struct compat_console_font_op {
	compat_uint_t op;        /* operation code KD_FONT_OP_* */
	compat_uint_t flags;     /* KD_FONT_FLAG_* */
	compat_uint_t width, height;     /* font size */
	compat_uint_t charcount;
	compat_caddr_t data;    /* font data with height fixed to 32 */
}

struct compat_unimapdesc {
	unsigned short entry_ct;
	compat_caddr_t entries;
}

static int disable_vt_switch;


static inline void status_report(struct tty_struct *tty)
{
	respond_string("\033[0n", tty->port);	/* Terminal ok */
}


void invert_screen(struct vc_data *vc, int offset, int count, int viewed)
{
	unsigned short *p;

	WARN_CONSOLE_UNLOCKED();

	count /= 2;
	p = screenpos(vc, offset, viewed);
	if (vc->vc_sw->con_invert_region) {
		vc->vc_sw->con_invert_region(vc, p, count);
	} else {
		u16 *q = p;
		int cnt = count;
		u16 a;

		if (!vc->vc_can_do_color) {
			while (cnt--) {
			    a = scr_readw(q);
			    a ^= 0x0800;
			    scr_writew(a, q);
			    q++;
			}
		} else if (vc->vc_hi_font_mask == 0x100) {
			while (cnt--) {
				a = scr_readw(q);
				a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) | (((a) & 0x0e00) << 4);
				scr_writew(a, q);
				q++;
			}
		} else {
			while (cnt--) {
				a = scr_readw(q);
				a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4);
				scr_writew(a, q);
				q++;
			}
		}
	}

	if (con_should_update(vc))
		do_update_region(vc, (unsigned long) p, count);
	notify_update(vc);
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

int default_utf8 = true;

int global_cursor_default = -1;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

int do_poke_blanked_console;

int console_blanked;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

int fg_console;

int last_console;

int want_console = -1;

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

int (*console_blank_hook)(int);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
				       8,12,10,14, 9,13,11,15 };

unsigned char default_red[] = {
	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
	0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
};

unsigned char default_grn[] = {
	0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa,
	0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff
};

unsigned char default_blu[] = {
	0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa,
	0x55, 0x55, 0x55, 0x55, 0xff, 0xff, 0xff, 0xff
};

struct rgb { u8 r; u8 g; u8 b; }

struct interval {
	uint32_t first;
	uint32_t last;
}


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;
	vc->vc_charset		= vc->vc_s_charset;
	vc->vc_color		= vc->vc_s_color;
	vc->vc_G0_charset	= vc->vc_saved_G0;
	vc->vc_G1_charset	= vc->vc_saved_G1;
	vc->vc_translate	= set_translate(vc->vc_charset ? vc->vc_G1_charset : vc->vc_G0_charset, vc);
	update_attr(vc);
	vc->vc_need_wrap = 0;
}


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_italic);
	vc->vc_video_erase_char = (build_attr(vc, vc->vc_color, 1, vc->vc_blink, 0, vc->vc_decscnm, 0) << 8) | ' ';
}


static void csi_X(struct vc_data *vc, int vpar) /* erase the following vpar positions */
{					  /* not vt100? */
	int count;

	if (!vpar)
		vpar++;
	count = (vpar > vc->vc_cols - vc->vc_x) ? (vc->vc_cols - vc->vc_x) : vpar;

	vc_uniscr_clear_line(vc, vc->vc_x, count);
	scr_memsetw((unsigned short *)vc->vc_pos, vc->vc_video_erase_char, 2 * count);
	if (con_should_update(vc))
		vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1, count);
	vc->vc_need_wrap = 0;
}


static void csi_K(struct vc_data *vc, int vpar)
{
	unsigned int count;
	unsigned short *start = (unsigned short *)vc->vc_pos;
	int offset;

	switch (vpar) {
		case 0:	/* erase from cursor to end of line */
			offset = 0;
			count = vc->vc_cols - vc->vc_x;
			break;
		case 1:	/* erase from start of line to cursor */
			offset = -vc->vc_x;
			count = vc->vc_x + 1;
			break;
		case 2: /* erase whole line */
			offset = -vc->vc_x;
			count = vc->vc_cols;
			break;
		default:
			return;
	}
	vc_uniscr_clear_line(vc, vc->vc_x + offset, count);
	scr_memsetw(start + offset, vc->vc_video_erase_char, 2 * count);
	vc->vc_need_wrap = 0;
	if (con_should_update(vc))
		do_update_region(vc, (unsigned long)(start + offset), count);
}


static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr)
{
	struct uni_screen *uniscr = get_vc_uniscr(vc);

	if (uniscr) {
		char32_t *ln = uniscr->lines[vc->vc_y];
		unsigned int x = vc->vc_x, cols = vc->vc_cols;

		memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln));
		memset32(&ln[cols - nr], ' ', nr);
	}
}


static void cursor_report(struct vc_data *vc, struct tty_struct *tty)
{
	char buf[40];

	sprintf(buf, "\033[%d;%dR", vc->vc_y + (vc->vc_decom ? vc->vc_top + 1 : 1), vc->vc_x + 1);
	respond_string(buf, tty->port);
}


static void con_flush(struct vc_data *vc, unsigned long draw_from,
		unsigned long draw_to, int *draw_x)
{
	if (*draw_x < 0)
		return;

	vc->vc_sw->con_putcs(vc, (u16 *)draw_from,
			(u16 *)draw_to - (u16 *)draw_from, vc->vc_y, *draw_x);
	*draw_x = -1;
}


static inline void bs(struct vc_data *vc)
{
	if (vc->vc_x) {
		vc->vc_pos -= 2;
		vc->vc_x--;
		vc->vc_need_wrap = 0;
		notify_write(vc, '\b');
	}
}


unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed)
{
	return screenpos(vc, 2 * w_offset, viewed);
}


static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows)
{
	struct uni_screen *uniscr;
	void *p;
	unsigned int memsize, i;

	/* allocate everything in one go */
	memsize = cols * rows * sizeof(char32_t);
	memsize += rows * sizeof(char32_t *);
	p = kmalloc(memsize, GFP_KERNEL);
	if (!p)
		return NULL;

	/* initial line pointers */
	uniscr = p;
	p = uniscr->lines + rows;
	for (i = 0; i < rows; i++) {
		uniscr->lines[i] = p;
		p += cols * sizeof(char32_t);
	}
	return uniscr;
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

int default_utf8 = true;

int global_cursor_default = -1;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

int do_poke_blanked_console;

int console_blanked;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

int fg_console;

int last_console;

int want_console = -1;

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

int (*console_blank_hook)(int);

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

#define get_vc_uniscr(vc) NULL

typedef uint32_t char32_t;

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
				       8,12,10,14, 9,13,11,15 };

unsigned char default_red[] = {
	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
	0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
};

unsigned char default_grn[] = {
	0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa,
	0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff
};


static void hide_softcursor(struct vc_data *vc)
{
	if (softcursor_original != -1) {
		scr_writew(softcursor_original, (u16 *)vc->vc_pos);
		if (con_should_update(vc))
			vc->vc_sw->con_putc(vc, softcursor_original,
					vc->vc_y, vc->vc_x);
		softcursor_original = -1;
	}
}


void complement_pos(struct vc_data *vc, int offset)
{
	static int old_offset = -1;
	static unsigned short old;
	static unsigned short oldx, oldy;

	WARN_CONSOLE_UNLOCKED();

	if (old_offset != -1 && old_offset >= 0 &&
	    old_offset < vc->vc_screenbuf_size) {
		scr_writew(old, screenpos(vc, old_offset, 1));
		if (con_should_update(vc))
			vc->vc_sw->con_putc(vc, old, oldy, oldx);
		notify_update(vc);
	}

	old_offset = offset;

	if (offset != -1 && offset >= 0 &&
	    offset < vc->vc_screenbuf_size) {
		unsigned short new;
		unsigned short *p;
		p = screenpos(vc, offset, 1);
		old = scr_readw(p);
		new = old ^ vc->vc_complement_mask;
		scr_writew(new, p);
		if (con_should_update(vc)) {
			oldx = (offset >> 1) % vc->vc_cols;
			oldy = (offset >> 1) / vc->vc_cols;
			vc->vc_sw->con_putc(vc, new, oldy, oldx);
		}
		notify_update(vc);
	}
}


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;
	vc->vc_s_charset	= vc->vc_charset;
	vc->vc_s_color		= vc->vc_color;
	vc->vc_saved_G0		= vc->vc_G0_charset;
	vc->vc_saved_G1		= vc->vc_G1_charset;
}


static void lf(struct vc_data *vc)
{
    	/* don't scroll if above bottom of scrolling region, or
	 * if below scrolling region
	 */
    	if (vc->vc_y + 1 == vc->vc_bottom)
		con_scroll(vc, vc->vc_top, vc->vc_bottom, SM_UP, 1);
	else if (vc->vc_y < vc->vc_rows - 1) {
	    	vc->vc_y++;
		vc->vc_pos += vc->vc_size_row;
	}
	vc->vc_need_wrap = 0;
	notify_write(vc, '\n');
}


static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr)
{
	struct uni_screen *uniscr = get_vc_uniscr(vc);

	if (uniscr) {
		char32_t *ln = uniscr->lines[vc->vc_y];
		unsigned int x = vc->vc_x, cols = vc->vc_cols;

		memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln));
		memset32(&ln[x], ' ', nr);
	}
}


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 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 DEFINE_SPINLOCK(printing_lock);
	const ushort *start;
	ushort start_x, cnt;
	int kmsg_console;

	/* console busy or not yet initialized */
	if (!printable)
		return;
	if (!spin_trylock(&printing_lock))
		return;

	kmsg_console = vt_get_kmsg_redirect();
	if (kmsg_console && vc_cons_allocated(kmsg_console - 1))
		vc = vc_cons[kmsg_console - 1].d;

	if (!vc_cons_allocated(fg_console)) {
		/* impossible */
		/* printk("vt_console_print: tty %d not allocated ??\n", currcons+1); */
		goto quit;
	}

	if (vc->vc_mode != KD_TEXT)
		goto quit;

	/* undraw cursor first */
	if (con_is_fg(vc))
		hide_cursor(vc);

	start = (ushort *)vc->vc_pos;
	start_x = vc->vc_x;
	cnt = 0;
	while (count--) {
		c = *b++;
		if (c == 10 || c == 13 || c == 8 || vc->vc_need_wrap) {
			if (cnt && con_is_visible(vc))
				vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, start_x);
			cnt = 0;
			if (c == 8) {		/* backspace */
				bs(vc);
				start = (ushort *)vc->vc_pos;
				start_x = vc->vc_x;
				continue;
			}
			if (c != 13)
				lf(vc);
			cr(vc);
			start = (ushort *)vc->vc_pos;
			start_x = vc->vc_x;
			if (c == 10 || c == 13)
				continue;
		}
		vc_uniscr_putc(vc, c);
		scr_writew((vc->vc_attr << 8) + c, (unsigned short *)vc->vc_pos);
		notify_write(vc, c);
		cnt++;
		if (vc->vc_x == vc->vc_cols - 1) {
			vc->vc_need_wrap = 1;
		} else {
			vc->vc_pos += 2;
			vc->vc_x++;
		}
	}
	if (cnt && con_is_visible(vc))
		vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, start_x);
	set_cursor(vc);
	notify_update(vc);

quit:
	spin_unlock(&printing_lock);
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

#define vt_get_kmsg_redirect() vt_kmsg_redirect(-1)

static int printable;

int default_utf8 = true;

int global_cursor_default = -1;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

int do_poke_blanked_console;

int console_blanked;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

int fg_console;

int last_console;

int want_console = -1;

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

int (*console_blank_hook)(int);

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
				       8,12,10,14, 9,13,11,15 };

unsigned char default_red[] = {
	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
	0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
};

unsigned char default_grn[] = {
	0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa,
	0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff
};


static int __init con_init(void)
{
	const char *display_desc = NULL;
	struct vc_data *vc;
	unsigned int currcons = 0, i;

	console_lock();

	if (!conswitchp)
		conswitchp = &dummy_con;
	display_desc = conswitchp->con_startup();
	if (!display_desc) {
		fg_console = 0;
		console_unlock();
		return 0;
	}

	for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
		struct con_driver *con_driver = &registered_con_driver[i];

		if (con_driver->con == NULL) {
			con_driver->con = conswitchp;
			con_driver->desc = display_desc;
			con_driver->flag = CON_DRIVER_FLAG_INIT;
			con_driver->first = 0;
			con_driver->last = MAX_NR_CONSOLES - 1;
			break;
		}
	}

	for (i = 0; i < MAX_NR_CONSOLES; i++)
		con_driver_map[i] = conswitchp;

	if (blankinterval) {
		blank_state = blank_normal_wait;
		mod_timer(&console_timer, jiffies + (blankinterval * HZ));
	}

	for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
		vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT);
		INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
		tty_port_init(&vc->port);
		visual_init(vc, currcons, 1);
		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);
	}
	currcons = fg_console = 0;
	master_display_fg = vc = vc_cons[currcons].d;
	set_origin(vc);
	save_screen(vc);
	gotoxy(vc, vc->vc_x, vc->vc_y);
	csi_J(vc, 0);
	update_screen(vc);
	pr_info("Console: %s %s %dx%d\n",
		vc->vc_can_do_color ? "colour" : "mono",
		display_desc, vc->vc_cols, vc->vc_rows);
	printable = 1;

	console_unlock();

#ifdef CONFIG_VT_CONSOLE
	register_console(&vt_console_driver);
#endif
	return 0;
}

#define MAX_NR_CON_DRIVER 16

#define CON_DRIVER_FLAG_INIT   2

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

int default_utf8 = true;

int global_cursor_default = -1;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

int do_poke_blanked_console;

int console_blanked;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

int fg_console;

int last_console;

int want_console = -1;

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

int (*console_blank_hook)(int);

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

enum {
	blank_off = 0,
	blank_normal_wait,
	blank_vesa_wait,
}

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

#define get_vc_uniscr(vc) NULL

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
				       8,12,10,14, 9,13,11,15 };

unsigned char default_red[] = {
	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
	0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
};

unsigned char default_grn[] = {
	0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa,
	0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff
};


do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_data *vc)
{
	struct unimapdesc tmp;

	if (copy_from_user(&tmp, user_ud, sizeof tmp))
		return -EFAULT;
	switch (cmd) {
	case PIO_UNIMAP:
		if (!perm)
			return -EPERM;
		return con_set_unimap(vc, tmp.entry_ct, tmp.entries);
	case GIO_UNIMAP:
		if (!perm && fg_console != vc->vc_num)
			return -EPERM;
		return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp.entries);
	}
	return 0;
}


static void delete_char(struct vc_data *vc, unsigned int nr)
{
	unsigned short *p = (unsigned short *) vc->vc_pos;

	vc_uniscr_delete(vc, nr);
	scr_memcpyw(p, p + nr, (vc->vc_cols - vc->vc_x - nr) * 2);
	scr_memsetw(p + vc->vc_cols - vc->vc_x - nr, vc->vc_video_erase_char,
			nr * 2);
	vc->vc_need_wrap = 0;
	if (con_should_update(vc))
		do_update_region(vc, (unsigned long) p,
			vc->vc_cols - vc->vc_x);
}

struct con_driver {
	const struct consw *con;
	const char *desc;
	struct device *dev;
	int node;
	int first;
	int last;
	int flag;
}

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];

const struct consw *conswitchp;

struct vc vc_cons [MAX_NR_CONSOLES];

static const struct consw *con_driver_map[MAX_NR_CONSOLES];

static int con_open(struct tty_struct *, struct file *);

static void vc_init(struct vc_data *vc, unsigned int rows,
		    unsigned int cols, int do_clear);

static void gotoxy(struct vc_data *vc, int new_x, int new_y);

static void save_cur(struct vc_data *vc);

static void reset_terminal(struct vc_data *vc, int do_clear);

static void con_flush_chars(struct tty_struct *tty);

static int set_vesa_blanking(char __user *p);

static void set_cursor(struct vc_data *vc);

static void hide_cursor(struct vc_data *vc);

static void console_callback(struct work_struct *ignored);

static void con_driver_unregister_callback(struct work_struct *ignored);

static void blank_screen_t(struct timer_list *unused);

static void set_palette(struct vc_data *vc);

static int printable;

int default_utf8 = true;

int global_cursor_default = -1;

static int cur_default = CUR_DEFAULT;

static int ignore_poke;

int do_poke_blanked_console;

int console_blanked;

static int vesa_blank_mode;

static int vesa_off_interval;

static int blankinterval;

static DECLARE_WORK(console_work, console_callback);

static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);

int fg_console;

int last_console;

int want_console = -1;

static int saved_fg_console;

static int saved_last_console;

static int saved_want_console;

static int saved_vc_mode;

static int saved_console_blanked;

static struct vc_data *master_display_fg;

static int scrollback_delta;

int (*console_blank_hook)(int);

static DEFINE_TIMER(console_timer, blank_screen_t);

static int blank_state;

static int blank_timer_expired;

static struct device *tty0dev;

static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

struct uni_screen {
	char32_t *lines[0];
}

static int softcursor_original = -1;

static const struct tty_port_operations vc_port_ops = {
	.destruct = vc_port_destruct,
};

const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
				       8,12,10,14, 9,13,11,15 };

unsigned char default_red[] = {
	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
	0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
};

unsigned char default_grn[] = {
	0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa,
	0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff
};


ModuleExport size_t RegisterVIFFImage(void)
{
  MagickInfo
    *entry;

  entry=SetMagickInfo("VIFF");
  entry->decoder=(DecodeImageHandler *) ReadVIFFImage;
  entry->encoder=(EncodeImageHandler *) WriteVIFFImage;
  entry->magick=(IsImageFormatHandler *) IsVIFF;
  entry->seekable_stream=MagickTrue;
  entry->description=ConstantString("Khoros Visualization image");
  entry->module=ConstantString("VIFF");
  (void) RegisterMagickInfo(entry);
  entry=SetMagickInfo("XV");
  entry->decoder=(DecodeImageHandler *) ReadVIFFImage;
  entry->encoder=(EncodeImageHandler *) WriteVIFFImage;
  entry->seekable_stream=MagickTrue;
  entry->description=ConstantString("Khoros Visualization image");
  entry->module=ConstantString("VIFF");
  (void) RegisterMagickInfo(entry);
  return(MagickImageCoderSignature);
}

static MagickBooleanType
  WriteVIFFImage(const ImageInfo *,Image *);