/*
       *  linux/drivers/char/console.c
       *
       *  Copyright (C) 1991, 1992  Linus Torvalds
       */
      
      /*
       * Hopefully this will be a rather complete VT102 implementation.
       *
       * Beeping thanks to John T Kohl.
       *
       * Virtual Consoles, Screen Blanking, Screen Dumping, Color, Graphics
       *   Chars, and VT100 enhancements by Peter MacDonald.
       *
       * Copy and paste function by Andrew Haylett,
       *   some enhancements by Alessandro Rubini.
       *
       * Code to check for different video-cards mostly by Galen Hunt,
       * <g-hunt@ee.utah.edu>
       *
       * Rudimentary ISO 10646/Unicode/UTF-8 character set support by
       * Markus Kuhn, <mskuhn@immd4.informatik.uni-erlangen.de>.
       *
       * Dynamic allocation of consoles, aeb@cwi.nl, May 1994
       * Resizing of consoles, aeb, 940926
       *
       * Code for xterm like mouse click reporting by Peter Orbaek 20-Jul-94
       * <poe@daimi.aau.dk>
       *
       * User-defined bell sound, new setterm control sequences and printk
       * redirection by Martin Mares <mj@k332.feld.cvut.cz> 19-Nov-95
       *
       * APM screenblank bug fixed Takashi Manabe <manabe@roy.dsl.tutics.tut.jp>
       *
       * Merge with the abstract console driver by Geert Uytterhoeven
       * <geert@linux-m68k.org>, Jan 1997.
       *
       *   Original m68k console driver modifications by
       *
       *     - Arno Griffioen <arno@usn.nl>
       *     - David Carter <carter@cs.bris.ac.uk>
       * 
       *   Note that the abstract console driver allows all consoles to be of
       *   potentially different sizes, so the following variables depend on the
       *   current console (currcons):
       *
       *     - video_num_columns
       *     - video_num_lines
       *     - video_size_row
       *     - can_do_color
       *
       *   The abstract console driver provides a generic interface for a text
       *   console. It supports VGA text mode, frame buffer based graphical consoles
       *   and special graphics processors that are only accessible through some
       *   registers (e.g. a TMS340x0 GSP).
       *
       *   The interface to the hardware is specified using a special structure
       *   (struct consw) which contains function pointers to console operations
       *   (see <linux/console.h> for more information).
       *
       * Support for changeable cursor shape
       * by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz>, August 1997
       *
       * Ported to i386 and con_scrolldelta fixed
       * by Emmanuel Marty <core@ggi-project.org>, April 1998
       *
       * Resurrected character buffers in videoram plus lots of other trickery
       * 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>
       */
      
      #include <linux/module.h>
      #include <linux/sched.h>
      #include <linux/tty.h>
      #include <linux/tty_flip.h>
      #include <linux/kernel.h>
      #include <linux/string.h>
      #include <linux/errno.h>
      #include <linux/kd.h>
      #include <linux/malloc.h>
      #include <linux/major.h>
      #include <linux/mm.h>
      #include <linux/console.h>
      #include <linux/init.h>
      #include <linux/devfs_fs_kernel.h>
      #include <linux/vt_kern.h>
      #include <linux/selection.h>
      #include <linux/console_struct.h>
      #include <linux/kbd_kern.h>
      #include <linux/consolemap.h>
      #include <linux/timer.h>
      #include <linux/interrupt.h>
      #include <linux/config.h>
      #include <linux/version.h>
      #include <linux/tqueue.h>
      #include <linux/bootmem.h>
      #include <linux/pm.h>
      
      #include <asm/io.h>
      #include <asm/system.h>
      #include <asm/uaccess.h>
      #include <asm/bitops.h>
      
      #include <asm/linux_logo.h>
      
      #include "console_macros.h"
      
      
      const struct consw *conswitchp;
      
      /* A bitmap for codes <32. A bit of 1 indicates that the code
       * corresponding to that bit number invokes some special action
       * (such as cursor movement) and should not be displayed as a
       * glyph unless the disp_ctrl mode is explicitly enabled.
       */
      #define CTRL_ACTION 0x0d00ff81
      #define CTRL_ALWAYS 0x0800f501	/* Cannot be overridden by disp_ctrl */
      
      /*
       * Here is the default bell parameters: 750HZ, 1/8th of a second
       */
      #define DEFAULT_BELL_PITCH	750
      #define DEFAULT_BELL_DURATION	(HZ/8)
      
      extern void vcs_make_devfs (unsigned int index, int unregister);
      
      #ifndef MIN
      #define MIN(a,b)	((a) < (b) ? (a) : (b))
      #endif
      
      static struct tty_struct *console_table[MAX_NR_CONSOLES];
      static struct termios *console_termios[MAX_NR_CONSOLES];
      static struct termios *console_termios_locked[MAX_NR_CONSOLES];
      struct vc vc_cons [MAX_NR_CONSOLES];
      
      #ifndef VT_SINGLE_DRIVER
      static const struct consw *con_driver_map[MAX_NR_CONSOLES];
      #endif
      
      static int con_open(struct tty_struct *, struct file *);
      static void vc_init(unsigned int console, unsigned int rows,
      		    unsigned int cols, int do_clear);
      static void blank_screen(unsigned long dummy);
      static void gotoxy(int currcons, int new_x, int new_y);
      static void save_cur(int currcons);
      static void reset_terminal(int currcons, int do_clear);
      static void con_flush_chars(struct tty_struct *tty);
      static void set_vesa_blanking(unsigned long arg);
      static void set_cursor(int currcons);
      static void hide_cursor(int currcons);
      static void unblank_screen_t(unsigned long dummy);
      
      static int printable;		/* Is console ready for printing? */
      
      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;
      
      /*
       * fg_console is the current virtual console,
       * last_console is the last used one,
       * want_console is the console we want to switch to,
       * kmsg_redirect is the console for kernel messages,
       */
      int fg_console;
      int last_console;
      int want_console = -1;
      int kmsg_redirect;
      
      /*
       * For each existing display, we have a pointer to console currently visible
       * on that display, allowing consoles other than fg_console to be refreshed
       * appropriately. Unless the low-level driver supplies its own display_fg
       * variable, we use this one for the "master display".
       */
      static struct vc_data *master_display_fg;
      
      /*
       * Unfortunately, we need to delay tty echo when we're currently writing to the
       * console since the code is (and always was) not re-entrant, so we insert
       * all filp requests to con_task_queue instead of tq_timer and run it from
       * the console_tasklet.  The console_tasklet is protected by the IRQ
       * protected console_lock.
       */
      DECLARE_TASK_QUEUE(con_task_queue);
      
      /*
       * For the same reason, we defer scrollback to the console tasklet.
       */
      static int scrollback_delta;
      
      /*
       * Hook so that the power management routines can (un)blank
       * the console on our behalf.
       */
      int (*console_blank_hook)(int);
      
      static struct timer_list console_timer;
      
      /*
       *	Low-Level Functions
       */
      
      #define IS_FG (currcons == fg_console)
      #define IS_VISIBLE CON_IS_VISIBLE(vc_cons[currcons].d)
      
      #ifdef VT_BUF_VRAM_ONLY
      #define DO_UPDATE 0
      #else
      #define DO_UPDATE IS_VISIBLE
      #endif
      
      static int pm_con_request(struct pm_dev *dev, pm_request_t rqst, void *data);
      static struct pm_dev *pm_con;
      
 221  static inline unsigned short *screenpos(int currcons, int offset, int viewed)
      {
      	unsigned short *p;
      	
 225  	if (!viewed)
      		p = (unsigned short *)(origin + offset);
 227  	else if (!sw->con_screen_pos)
      		p = (unsigned short *)(visible_origin + offset);
 229  	else
      		p = sw->con_screen_pos(vc_cons[currcons].d, offset);
 231  	return p;
      }
      
 234  static inline void scrolldelta(int lines)
      {
      	scrollback_delta += lines;
      	tasklet_schedule(&console_tasklet);
      }
      
 240  static void scrup(int currcons, unsigned int t, unsigned int b, int nr)
      {
      	unsigned short *d, *s;
      
 244  	if (t+nr >= b)
      		nr = b - t - 1;
 246  	if (b > video_num_lines || t >= b || nr < 1)
 247  		return;
 248  	if (IS_VISIBLE && sw->con_scroll(vc_cons[currcons].d, t, b, SM_UP, nr))
 249  		return;
      	d = (unsigned short *) (origin+video_size_row*t);
      	s = (unsigned short *) (origin+video_size_row*(t+nr));
      	scr_memcpyw(d, s, (b-t-nr) * video_size_row);
      	scr_memsetw(d + (b-t-nr) * video_num_columns, video_erase_char, video_size_row*nr);
      }
      
      static void
 257  scrdown(int currcons, unsigned int t, unsigned int b, int nr)
      {
      	unsigned short *s;
      	unsigned int step;
      
 262  	if (t+nr >= b)
      		nr = b - t - 1;
 264  	if (b > video_num_lines || t >= b || nr < 1)
 265  		return;
 266  	if (IS_VISIBLE && sw->con_scroll(vc_cons[currcons].d, t, b, SM_DOWN, nr))
 267  		return;
      	s = (unsigned short *) (origin+video_size_row*t);
      	step = video_num_columns * nr;
      	scr_memmovew(s + step, s, (b-t-nr)*video_size_row);
      	scr_memsetw(s, video_erase_char, 2*step);
      }
      
 274  static void do_update_region(int currcons, unsigned long start, int count)
      {
      #ifndef VT_BUF_VRAM_ONLY
      	unsigned int xx, yy, offset;
      	u16 *p;
      
      	p = (u16 *) start;
 281  	if (!sw->con_getxy) {
      		offset = (start - origin) / 2;
      		xx = offset % video_num_columns;
      		yy = offset / video_num_columns;
 285  	} else {
      		int nxx, nyy;
      		start = sw->con_getxy(vc_cons[currcons].d, start, &nxx, &nyy);
      		xx = nxx; yy = nyy;
      	}
 290  	for(;;) {
      		u16 attrib = scr_readw(p) & 0xff00;
      		int startx = xx;
      		u16 *q = p;
 294  		while (xx < video_num_columns && count) {
 295  			if (attrib != (scr_readw(p) & 0xff00)) {
 296  				if (p > q)
      					sw->con_putcs(vc_cons[currcons].d, q, p-q, yy, startx);
      				startx = xx;
      				q = p;
      				attrib = scr_readw(p) & 0xff00;
      			}
      			p++;
      			xx++;
      			count--;
      		}
 306  		if (p > q)
      			sw->con_putcs(vc_cons[currcons].d, q, p-q, yy, startx);
 308  		if (!count)
 309  			break;
      		xx = 0;
      		yy++;
 312  		if (sw->con_getxy) {
      			p = (u16 *)start;
      			start = sw->con_getxy(vc_cons[currcons].d, start, NULL, NULL);
      		}
      	}
      #endif
      }
      
 320  void update_region(int currcons, unsigned long start, int count)
      {
 322  	if (DO_UPDATE) {
      		hide_cursor(currcons);
      		do_update_region(currcons, start, count);
      		set_cursor(currcons);
      	}
      }
      
      /* Structure of attributes is hardware-dependent */
      
 331  static u8 build_attr(int currcons, u8 _color, u8 _intensity, u8 _blink, u8 _underline, u8 _reverse)
      {
 333  	if (sw->con_build_attr)
 334  		return sw->con_build_attr(vc_cons[currcons].d, _color, _intensity, _blink, _underline, _reverse);
      
      #ifndef VT_BUF_VRAM_ONLY
      /*
       * ++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;
 349  	if (!can_do_color)
      		return _intensity |
      		       (_underline ? 4 : 0) |
      		       (_reverse ? 8 : 0) |
 353  		       (_blink ? 0x80 : 0);
 354  	if (_underline)
      		a = (a & 0xf0) | ulcolor;
 356  	else if (_intensity == 0)
      		a = (a & 0xf0) | halfcolor;
 358  	if (_reverse)
      		a = ((a) & 0x88) | ((((a) >> 4) | ((a) << 4)) & 0x77);
 360  	if (_blink)
      		a ^= 0x80;
 362  	if (_intensity == 2)
      		a ^= 0x08;
 364  	if (hi_font_mask == 0x100)
      		a <<= 1;
 366  	return a;
      	}
      #else
      	return 0;
      #endif
      }
      
 373  static void update_attr(int currcons)
      {
      	attr = build_attr(currcons, color, intensity, blink, underline, reverse ^ decscnm);
      	video_erase_char = (build_attr(currcons, color, 1, blink, 0, decscnm) << 8) | ' ';
      }
      
      /* Note: inverting the screen twice should revert to the original state */
      
 381  void invert_screen(int currcons, int offset, int count, int viewed)
      {
      	unsigned short *p;
      
      	count /= 2;
      	p = screenpos(currcons, offset, viewed);
 387  	if (sw->con_invert_region)
      		sw->con_invert_region(vc_cons[currcons].d, p, count);
      #ifndef VT_BUF_VRAM_ONLY
 390  	else {
      		u16 *q = p;
      		int cnt = count;
      
 394  		if (!can_do_color) {
 395  			while (cnt--) *q++ ^= 0x0800;
 396  		} else if (hi_font_mask == 0x100) {
 397  			while (cnt--) {
      				u16 a = *q;
      				a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) | (((a) & 0x0e00) << 4);
      				*q++ = a;
      			}
 402  		} else {
 403  			while (cnt--) {
      				u16 a = *q;
      				a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4);
      				*q++ = a;
      			}
      		}
      	}
      #endif
 411  	if (DO_UPDATE)
      		do_update_region(currcons, (unsigned long) p, count);
      }
      
      /* used by selection: complement pointer position */
 416  void complement_pos(int currcons, int offset)
      {
      	static unsigned short *p;
      	static unsigned short old;
      	static unsigned short oldx, oldy;
      
 422  	if (p) {
      		scr_writew(old, p);
 424  		if (DO_UPDATE)
      			sw->con_putc(vc_cons[currcons].d, old, oldy, oldx);
      	}
 427  	if (offset == -1)
      		p = NULL;
 429  	else {
      		unsigned short new;
      		p = screenpos(currcons, offset, 1);
      		old = scr_readw(p);
      		new = old ^ complement_mask;
      		scr_writew(new, p);
 435  		if (DO_UPDATE) {
      			oldx = (offset >> 1) % video_num_columns;
      			oldy = (offset >> 1) / video_num_columns;
      			sw->con_putc(vc_cons[currcons].d, new, oldy, oldx);
      		}
      	}
      }
      
 443  static void insert_char(int currcons, unsigned int nr)
      {
      	unsigned short *p, *q = (unsigned short *) pos;
      
      	p = q + video_num_columns - nr - x;
 448  	while (--p >= q)
      		scr_writew(scr_readw(p), p + nr);
      	scr_memsetw(q, video_erase_char, nr*2);
      	need_wrap = 0;
 452  	if (DO_UPDATE) {
      		unsigned short oldattr = attr;
      		sw->con_bmove(vc_cons[currcons].d,y,x,y,x+nr,1,
      			      video_num_columns-x-nr);
      		attr = video_erase_char >> 8;
 457  		while (nr--)
      			sw->con_putc(vc_cons[currcons].d,
      				     video_erase_char,y,x+nr);
      		attr = oldattr;
      	}
      }
      
 464  static void delete_char(int currcons, unsigned int nr)
      {
      	unsigned int i = x;
      	unsigned short *p = (unsigned short *) pos;
      
 469  	while (++i <= video_num_columns - nr) {
      		scr_writew(scr_readw(p+nr), p);
      		p++;
      	}
      	scr_memsetw(p, video_erase_char, nr*2);
      	need_wrap = 0;
 475  	if (DO_UPDATE) {
      		unsigned short oldattr = attr;
      		sw->con_bmove(vc_cons[currcons].d, y, x+nr, y, x, 1,
      			      video_num_columns-x-nr);
      		attr = video_erase_char >> 8;
 480  		while (nr--)
      			sw->con_putc(vc_cons[currcons].d,
      				     video_erase_char, y,
      				     video_num_columns-1-nr);
      		attr = oldattr;
      	}
      }
      
      static int softcursor_original;
      
 490  static void add_softcursor(int currcons)
      {
      	int i = scr_readw((u16 *) pos);
      	u32 type = cursor_type;
      
 495  	if (! (type & 0x10)) return;
 496  	if (softcursor_original != -1) return;
      	softcursor_original = i;
      	i |= ((type >> 8) & 0xff00 );
      	i ^= ((type) & 0xff00 );
 500  	if ((type & 0x20) && ((softcursor_original & 0x7000) == (i & 0x7000))) i ^= 0x7000;
 501  	if ((type & 0x40) && ((i & 0x700) == ((i & 0x7000) >> 4))) i ^= 0x0700;
      	scr_writew(i, (u16 *) pos);
 503  	if (DO_UPDATE)
      		sw->con_putc(vc_cons[currcons].d, i, y, x);
      }
      
 507  static void hide_cursor(int currcons)
      {
 509  	if (currcons == sel_cons)
      		clear_selection();
 511  	if (softcursor_original != -1) {
      		scr_writew(softcursor_original,(u16 *) pos);
 513  		if (DO_UPDATE)
      			sw->con_putc(vc_cons[currcons].d, softcursor_original, y, x);
      		softcursor_original = -1;
      	}
      	sw->con_cursor(vc_cons[currcons].d,CM_ERASE);
      }
      
 520  static void set_cursor(int currcons)
      {
 522      if (!IS_FG || console_blanked || vcmode == KD_GRAPHICS)
 523  	return;
 524      if (deccm) {
 525  	if (currcons == sel_cons)
      		clear_selection();
      	add_softcursor(currcons);
 528  	if ((cursor_type & 0x0f) != 1)
      	    sw->con_cursor(vc_cons[currcons].d,CM_DRAW);
 530      } else
      	hide_cursor(currcons);
      }
      
 534  static void set_origin(int currcons)
      {
      	if (!IS_VISIBLE ||
      	    !sw->con_set_origin ||
 538  	    !sw->con_set_origin(vc_cons[currcons].d))
      		origin = (unsigned long) screenbuf;
      	visible_origin = origin;
      	scr_end = origin + screenbuf_size;
      	pos = origin + video_size_row*y + 2*x;
      }
      
 545  static inline void save_screen(int currcons)
      {
 547  	if (sw->con_save_screen)
      		sw->con_save_screen(vc_cons[currcons].d);
      }
      
      /*
       *	Redrawing of screen
       */
      
 555  void redraw_screen(int new_console, int is_switch)
      {
      	int redraw = 1;
      	int currcons, old_console;
      
 560  	if (!vc_cons_allocated(new_console)) {
      		/* strange ... */
      		/* printk("redraw_screen: tty %d not allocated ??\n", new_console+1); */
 563  		return;
      	}
      
 566  	if (is_switch) {
      		currcons = fg_console;
      		hide_cursor(currcons);
 569  		if (fg_console != new_console) {
      			struct vc_data **display = vc_cons[new_console].d->vc_display_fg;
      			old_console = (*display) ? (*display)->vc_num : fg_console;
      			*display = vc_cons[new_console].d;
      			fg_console = new_console;
      			currcons = old_console;
 575  			if (!IS_VISIBLE) {
      				save_screen(currcons);
      				set_origin(currcons);
      			}
      			currcons = new_console;
 580  			if (old_console == new_console)
      				redraw = 0;
      		}
 583  	} else {
      		currcons = new_console;
      		hide_cursor(currcons);
      	}
      
 588  	if (redraw) {
      		int update;
      		set_origin(currcons);
      		update = sw->con_switch(vc_cons[currcons].d);
      		set_palette(currcons);
 593  		if (update && vcmode != KD_GRAPHICS)
      			do_update_region(currcons, origin, screenbuf_size/2);
      	}
      	set_cursor(currcons);
 597  	if (is_switch) {
      		set_leds();
      		compute_shiftstate();
      	}
      }
      
      /*
       *	Allocation, freeing and resizing of VTs.
       */
      
 607  int vc_cons_allocated(unsigned int i)
      {
 609  	return (i < MAX_NR_CONSOLES && vc_cons[i].d);
      }
      
 612  static void visual_init(int currcons, int init)
      {
          /* ++Geert: sw->con_init determines console size */
          sw = conswitchp;
      #ifndef VT_SINGLE_DRIVER
 617      if (con_driver_map[currcons])
      	sw = con_driver_map[currcons];
      #endif
          cons_num = currcons;
          display_fg = &master_display_fg;
          vc_cons[currcons].d->vc_uni_pagedir_loc = &vc_cons[currcons].d->vc_uni_pagedir;
          vc_cons[currcons].d->vc_uni_pagedir = 0;
          hi_font_mask = 0;
          complement_mask = 0;
          can_do_color = 0;
          sw->con_init(vc_cons[currcons].d, init);
 628      if (!complement_mask)
              complement_mask = can_do_color ? 0x7700 : 0x0800;
          s_complement_mask = complement_mask;
          video_size_row = video_num_columns<<1;
          screenbuf_size = video_num_lines*video_size_row;
      }
      
 635  int vc_allocate(unsigned int currcons)	/* return 0 on success */
      {
 637  	if (currcons >= MAX_NR_CONSOLES)
 638  		return -ENXIO;
 639  	if (!vc_cons[currcons].d) {
      	    long p, q;
      
      	    /* prevent users from taking too much memory */
 643  	    if (currcons >= MAX_NR_USER_CONSOLES && !capable(CAP_SYS_RESOURCE))
 644  	      return -EPERM;
      
      	    /* due to the granularity of kmalloc, we waste some memory here */
      	    /* the alloc is done in two steps, to optimize the common situation
      	       of a 25x80 console (structsize=216, screenbuf_size=4000) */
      	    /* 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 */
      	    p = (long) kmalloc(structsize, GFP_KERNEL);
 653  	    if (!p)
 654  		return -ENOMEM;
      	    vc_cons[currcons].d = (struct vc_data *)p;
      	    vt_cons[currcons] = (struct vt_struct *)(p+sizeof(struct vc_data));
      	    visual_init(currcons, 1);
 658  	    if (!*vc_cons[currcons].d->vc_uni_pagedir_loc)
      		con_set_default_unimap(currcons);
      	    q = (long)kmalloc(screenbuf_size, GFP_KERNEL);
 661  	    if (!q) {
      		kfree((char *) p);
      		vc_cons[currcons].d = NULL;
      		vt_cons[currcons] = NULL;
 665  		return -ENOMEM;
      	    }
      	    screenbuf = (unsigned short *) q;
      	    kmalloced = 1;
      	    vc_init(currcons, video_num_lines, video_num_columns, 1);
      
 671  	    if (!pm_con) {
      		    pm_con = pm_register(PM_SYS_DEV,
      					 PM_SYS_VGA,
      					 pm_con_request);
      	    }
      	}
 677  	return 0;
      }
      
      /*
       * Change # of rows and columns (0 means unchanged/the size of fg_console)
       * [this is to be used together with some user program
       * like resize that changes the hardware videomode]
       */
 685  int vc_resize(unsigned int lines, unsigned int cols,
      	      unsigned int first, unsigned int last)
      {
      	unsigned int cc, ll, ss, sr, todo = 0;
      	unsigned int currcons = fg_console, i;
      	unsigned short *newscreens[MAX_NR_CONSOLES];
      
      	cc = (cols ? cols : video_num_columns);
      	ll = (lines ? lines : video_num_lines);
      	sr = cc << 1;
      	ss = sr * ll;
      
 697   	for (currcons = first; currcons <= last; currcons++) {
      		if (!vc_cons_allocated(currcons) ||
 699  		    (cc == video_num_columns && ll == video_num_lines))
      			newscreens[currcons] = NULL;
 701  		else {
      			unsigned short *p = (unsigned short *) kmalloc(ss, GFP_USER);
 703  			if (!p) {
 704  				for (i = first; i < currcons; i++)
 705  					if (newscreens[i])
      						kfree(newscreens[i]);
 707  				return -ENOMEM;
      			}
      			newscreens[currcons] = p;
      			todo++;
      		}
      	}
 713  	if (!todo)
 714  		return 0;
      
 716  	for (currcons = first; currcons <= last; currcons++) {
      		unsigned int occ, oll, oss, osr;
      		unsigned long ol, nl, nlend, rlth, rrem;
 719  		if (!newscreens[currcons] || !vc_cons_allocated(currcons))
 720  			continue;
      
      		oll = video_num_lines;
      		occ = video_num_columns;
      		osr = video_size_row;
      		oss = screenbuf_size;
      
      		video_num_lines = ll;
      		video_num_columns = cc;
      		video_size_row = sr;
      		screenbuf_size = ss;
      
      		rlth = MIN(osr, sr);
      		rrem = sr - rlth;
      		ol = origin;
      		nl = (long) newscreens[currcons];
      		nlend = nl + ss;
 737  		if (ll < oll)
      			ol += (oll - ll) * osr;
      
      		update_attr(currcons);
      
 742  		while (ol < scr_end) {
      			scr_memcpyw((unsigned short *) nl, (unsigned short *) ol, rlth);
 744  			if (rrem)
      				scr_memsetw((void *)(nl + rlth), video_erase_char, rrem);
      			ol += osr;
      			nl += sr;
      		}
 749  		if (nlend > nl)
      			scr_memsetw((void *) nl, video_erase_char, nlend - nl);
 751  		if (kmalloced)
      			kfree(screenbuf);
      		screenbuf = newscreens[currcons];
      		kmalloced = 1;
      		screenbuf_size = ss;
      		set_origin(currcons);
      
      		/* do part of a reset_terminal() */
      		top = 0;
      		bottom = video_num_lines;
      		gotoxy(currcons, x, y);
      		save_cur(currcons);
      
 764  		if (console_table[currcons]) {
      			struct winsize ws, *cws = &console_table[currcons]->winsize;
      			memset(&ws, 0, sizeof(ws));
      			ws.ws_row = video_num_lines;
      			ws.ws_col = video_num_columns;
      			if ((ws.ws_row != cws->ws_row || ws.ws_col != cws->ws_col) &&
 770  			    console_table[currcons]->pgrp > 0)
      				kill_pg(console_table[currcons]->pgrp, SIGWINCH, 1);
      			*cws = ws;
      		}
      
 775  		if (IS_VISIBLE)
      			update_screen(currcons);
      	}
      
 779  	return 0;
      }
      
      
 783  void vc_disallocate(unsigned int currcons)
      {
 785  	if (vc_cons_allocated(currcons)) {
      	    sw->con_deinit(vc_cons[currcons].d);
 787  	    if (kmalloced)
      		kfree(screenbuf);
 789  	    if (currcons >= MIN_NR_CONSOLES)
      		kfree(vc_cons[currcons].d);
      	    vc_cons[currcons].d = NULL;
      	}
      }
      
      /*
       *	VT102 emulator
       */
      
      #define set_kbd(x) set_vc_kbd_mode(kbd_table+currcons,x)
      #define clr_kbd(x) clr_vc_kbd_mode(kbd_table+currcons,x)
      #define is_kbd(x) vc_kbd_mode(kbd_table+currcons,x)
      
      #define decarm		VC_REPEAT
      #define decckm		VC_CKMODE
      #define kbdapplic	VC_APPLIC
      #define lnm		VC_CRLF
      
      /*
       * this is what the terminal answers to a ESC-Z or csi0c query.
       */
      #define VT100ID "\033[?1;2c"
      #define VT102ID "\033[?6c"
      
      unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
      				       8,12,10,14, 9,13,11,15 };
      
      /* the default colour table, for VGA+ colour systems */
      int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa,
          0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff};
      int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa,
          0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff};
      int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa,
          0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff};
      
      /*
       * gotoxy() must verify all boundaries, because the arguments
       * might also be negative. If the given position is out of
       * bounds, the cursor is placed at the nearest margin.
       */
 830  static void gotoxy(int currcons, int new_x, int new_y)
      {
      	int min_y, max_y;
      
 834  	if (new_x < 0)
      		x = 0;
      	else
 837  		if (new_x >= video_num_columns)
      			x = video_num_columns - 1;
 839  		else
      			x = new_x;
 841   	if (decom) {
      		min_y = top;
      		max_y = bottom;
 844  	} else {
      		min_y = 0;
      		max_y = video_num_lines;
      	}
 848  	if (new_y < min_y)
      		y = min_y;
 850  	else if (new_y >= max_y)
      		y = max_y - 1;
 852  	else
      		y = new_y;
      	pos = origin + y*video_size_row + (x<<1);
      	need_wrap = 0;
      }
      
      /* for absolute user moves, when decom is set */
 859  static void gotoxay(int currcons, int new_x, int new_y)
      {
      	gotoxy(currcons, new_x, decom ? (top+new_y) : new_y);
      }
      
 864  void scrollback(int lines)
      {
      	int currcons = fg_console;
      
 868  	if (!lines)
      		lines = video_num_lines/2;
      	scrolldelta(-lines);
      }
      
 873  void scrollfront(int lines)
      {
      	int currcons = fg_console;
      
 877  	if (!lines)
      		lines = video_num_lines/2;
      	scrolldelta(lines);
      }
      
 882  static void lf(int currcons)
      {
          	/* don't scroll if above bottom of scrolling region, or
      	 * if below scrolling region
      	 */
 887      	if (y+1 == bottom)
      		scrup(currcons,top,bottom,1);
 889  	else if (y < video_num_lines-1) {
      	    	y++;
      		pos += video_size_row;
      	}
      	need_wrap = 0;
      }
      
 896  static void ri(int currcons)
      {
          	/* don't scroll if below top of scrolling region, or
      	 * if above scrolling region
      	 */
 901  	if (y == top)
      		scrdown(currcons,top,bottom,1);
 903  	else if (y > 0) {
      		y--;
      		pos -= video_size_row;
      	}
      	need_wrap = 0;
      }
      
 910  static inline void cr(int currcons)
      {
      	pos -= x<<1;
      	need_wrap = x = 0;
      }
      
 916  static inline void bs(int currcons)
      {
 918  	if (x) {
      		pos -= 2;
      		x--;
      		need_wrap = 0;
      	}
      }
      
 925  static inline void del(int currcons)
      {
      	/* ignored */
      }
      
 930  static void csi_J(int currcons, int vpar)
      {
      	unsigned int count;
      	unsigned short * start;
      
 935  	switch (vpar) {
 936  		case 0:	/* erase from cursor to end of display */
      			count = (scr_end-pos)>>1;
      			start = (unsigned short *) pos;
 939  			if (DO_UPDATE) {
      				/* do in two stages */
      				sw->con_clear(vc_cons[currcons].d, y, x, 1,
      					      video_num_columns-x);
      				sw->con_clear(vc_cons[currcons].d, y+1, 0,
      					      video_num_lines-y-1,
      					      video_num_columns);
      			}
 947  			break;
 948  		case 1:	/* erase from start to cursor */
      			count = ((pos-origin)>>1)+1;
      			start = (unsigned short *) origin;
 951  			if (DO_UPDATE) {
      				/* do in two stages */
      				sw->con_clear(vc_cons[currcons].d, 0, 0, y,
      					      video_num_columns);
      				sw->con_clear(vc_cons[currcons].d, y, 0, 1,
      					      x + 1);
      			}
 958  			break;
 959  		case 2: /* erase whole display */
      			count = video_num_columns * video_num_lines;
      			start = (unsigned short *) origin;
 962  			if (DO_UPDATE)
      				sw->con_clear(vc_cons[currcons].d, 0, 0,
      					      video_num_lines,
      					      video_num_columns);
 966  			break;
 967  		default:
 968  			return;
      	}
      	scr_memsetw(start, video_erase_char, 2*count);
      	need_wrap = 0;
      }
      
 974  static void csi_K(int currcons, int vpar)
      {
      	unsigned int count;
      	unsigned short * start;
      
 979  	switch (vpar) {
 980  		case 0:	/* erase from cursor to end of line */
      			count = video_num_columns-x;
      			start = (unsigned short *) pos;
 983  			if (DO_UPDATE)
      				sw->con_clear(vc_cons[currcons].d, y, x, 1,
      					      video_num_columns-x);
 986  			break;
 987  		case 1:	/* erase from start of line to cursor */
      			start = (unsigned short *) (pos - (x<<1));
      			count = x+1;
 990  			if (DO_UPDATE)
      				sw->con_clear(vc_cons[currcons].d, y, 0, 1,
      					      x + 1);
 993  			break;
 994  		case 2: /* erase whole line */
      			start = (unsigned short *) (pos - (x<<1));
      			count = video_num_columns;
 997  			if (DO_UPDATE)
      				sw->con_clear(vc_cons[currcons].d, y, 0, 1,
      					      video_num_columns);
1000  			break;
1001  		default:
1002  			return;
      	}
      	scr_memsetw(start, video_erase_char, 2 * count);
      	need_wrap = 0;
      }
      
1008  static void csi_X(int currcons, int vpar) /* erase the following vpar positions */
      {					  /* not vt100? */
      	int count;
      
1012  	if (!vpar)
      		vpar++;
      	count = (vpar > video_num_columns-x) ? (video_num_columns-x) : vpar;
      
      	scr_memsetw((unsigned short *) pos, video_erase_char, 2 * count);
1017  	if (DO_UPDATE)
      		sw->con_clear(vc_cons[currcons].d, y, x, 1, count);
      	need_wrap = 0;
      }
      
1022  static void default_attr(int currcons)
      {
      	intensity = 1;
      	underline = 0;
      	reverse = 0;
      	blink = 0;
      	color = def_color;
      }
      
1031  static void csi_m(int currcons)
      {
      	int i;
      
1035  	for (i=0;i<=npar;i++)
1036  		switch (par[i]) {
1037  			case 0:	/* all attributes off */
      				default_attr(currcons);
1039  				break;
1040  			case 1:
      				intensity = 2;
1042  				break;
1043  			case 2:
      				intensity = 0;
1045  				break;
1046  			case 4:
      				underline = 1;
1048  				break;
1049  			case 5:
      				blink = 1;
1051  				break;
1052  			case 7:
      				reverse = 1;
1054  				break;
1055  			case 10: /* ANSI X3.64-1979 (SCO-ish?)
      				  * Select primary font, don't display
      				  * control chars if defined, don't set
      				  * bit 8 on output.
      				  */
      				translate = set_translate(charset == 0
      						? G0_charset
      						: G1_charset,currcons);
      				disp_ctrl = 0;
      				toggle_meta = 0;
1065  				break;
1066  			case 11: /* ANSI X3.64-1979 (SCO-ish?)
      				  * Select first alternate font, lets
      				  * chars < 32 be displayed as ROM chars.
      				  */
      				translate = set_translate(IBMPC_MAP,currcons);
      				disp_ctrl = 1;
      				toggle_meta = 0;
1073  				break;
1074  			case 12: /* ANSI X3.64-1979 (SCO-ish?)
      				  * Select second alternate font, toggle
      				  * high bit before displaying as ROM char.
      				  */
      				translate = set_translate(IBMPC_MAP,currcons);
      				disp_ctrl = 1;
      				toggle_meta = 1;
1081  				break;
1082  			case 21:
1083  			case 22:
      				intensity = 1;
1085  				break;
1086  			case 24:
      				underline = 0;
1088  				break;
1089  			case 25:
      				blink = 0;
1091  				break;
1092  			case 27:
      				reverse = 0;
1094  				break;
1095  			case 38: /* ANSI X3.64-1979 (SCO-ish?)
      				  * Enables underscore, white foreground
      				  * with white underscore (Linux - use
      				  * default foreground).
      				  */
      				color = (def_color & 0x0f) | background;
      				underline = 1;
1102  				break;
1103  			case 39: /* ANSI X3.64-1979 (SCO-ish?)
      				  * Disable underline option.
      				  * Reset colour to default? It did this
      				  * before...
      				  */
      				color = (def_color & 0x0f) | background;
      				underline = 0;
1110  				break;
1111  			case 49:
      				color = (def_color & 0xf0) | foreground;
1113  				break;
1114  			default:
1115  				if (par[i] >= 30 && par[i] <= 37)
      					color = color_table[par[i]-30]
      						| background;
1118  				else if (par[i] >= 40 && par[i] <= 47)
      					color = (color_table[par[i]-40]<<4)
      						| foreground;
1121  				break;
      		}
      	update_attr(currcons);
      }
      
1126  static void respond_string(const char * p, struct tty_struct * tty)
      {
1128  	while (*p) {
      		tty_insert_flip_char(tty, *p, 0);
      		p++;
      	}
      	con_schedule_flip(tty);
      }
      
1135  static void cursor_report(int currcons, struct tty_struct * tty)
      {
      	char buf[40];
      
      	sprintf(buf, "\033[%d;%dR", y + (decom ? top+1 : 1), x+1);
      	respond_string(buf, tty);
      }
      
1143  static inline void status_report(struct tty_struct * tty)
      {
      	respond_string("\033[0n", tty);	/* Terminal ok */
      }
      
1148  static inline void respond_ID(struct tty_struct * tty)
      {
      	respond_string(VT102ID, tty);
      }
      
1153  void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry)
      {
      	char buf[8];
      
      	sprintf(buf, "\033[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
      		(char)('!' + mry));
      	respond_string(buf, tty);
      }
      
      /* invoked via ioctl(TIOCLINUX) and through set_selection */
1163  int mouse_reporting(void)
      {
      	int currcons = fg_console;
      
1167  	return report_mouse;
      }
      
1170  static void set_mode(int currcons, int on_off)
      {
      	int i;
      
1174  	for (i=0; i<=npar; i++)
1175  		if (ques) switch(par[i]) {	/* DEC private modes set/reset */
1176  			case 1:			/* Cursor keys send ^[Ox/^[[x */
1177  				if (on_off)
      					set_kbd(decckm);
1179  				else
      					clr_kbd(decckm);
1181  				break;
1182  			case 3:	/* 80/132 mode switch unimplemented */
      				deccolm = on_off;
      #if 0
      				(void) vc_resize(video_num_lines, deccolm ? 132 : 80);
      				/* this alone does not suffice; some user mode
      				   utility has to change the hardware regs */
      #endif
1189  				break;
1190  			case 5:			/* Inverted screen on/off */
1191  				if (decscnm != on_off) {
      					decscnm = on_off;
      					invert_screen(currcons, 0, screenbuf_size, 0);
      					update_attr(currcons);
      				}
1196  				break;
1197  			case 6:			/* Origin relative/absolute */
      				decom = on_off;
      				gotoxay(currcons,0,0);
1200  				break;
1201  			case 7:			/* Autowrap on/off */
      				decawm = on_off;
1203  				break;
1204  			case 8:			/* Autorepeat on/off */
1205  				if (on_off)
      					set_kbd(decarm);
1207  				else
      					clr_kbd(decarm);
1209  				break;
1210  			case 9:
      				report_mouse = on_off ? 1 : 0;
1212  				break;
1213  			case 25:		/* Cursor on/off */
      				deccm = on_off;
1215  				break;
1216  			case 1000:
      				report_mouse = on_off ? 2 : 0;
1218  				break;
1219  		} else switch(par[i]) {		/* ANSI modes set/reset */
1220  			case 3:			/* Monitor (display ctrls) */
      				disp_ctrl = on_off;
1222  				break;
1223  			case 4:			/* Insert Mode on/off */
      				decim = on_off;
1225  				break;
1226  			case 20:		/* Lf, Enter == CrLf/Lf */
1227  				if (on_off)
      					set_kbd(lnm);
1229  				else
      					clr_kbd(lnm);
1231  				break;
      		}
      }
      
1235  static void setterm_command(int currcons)
      {
1237  	switch(par[0]) {
1238  		case 1:	/* set color for underline mode */
1239  			if (can_do_color && par[1] < 16) {
      				ulcolor = color_table[par[1]];
1241  				if (underline)
      					update_attr(currcons);
      			}
1244  			break;
1245  		case 2:	/* set color for half intensity mode */
1246  			if (can_do_color && par[1] < 16) {
      				halfcolor = color_table[par[1]];
1248  				if (intensity == 0)
      					update_attr(currcons);
      			}
1251  			break;
1252  		case 8:	/* store colors as defaults */
      			def_color = attr;
1254  			if (hi_font_mask == 0x100)
      				def_color >>= 1;
      			default_attr(currcons);
      			update_attr(currcons);
1258  			break;
1259  		case 9:	/* set blanking interval */
      			blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
      			poke_blanked_console();
1262  			break;
1263  		case 10: /* set bell frequency in Hz */
1264  			if (npar >= 1)
      				bell_pitch = par[1];
1266  			else
      				bell_pitch = DEFAULT_BELL_PITCH;
1268  			break;
1269  		case 11: /* set bell duration in msec */
1270  			if (npar >= 1)
      				bell_duration = (par[1] < 2000) ?
      					par[1]*HZ/1000 : 0;
1273  			else
      				bell_duration = DEFAULT_BELL_DURATION;
1275  			break;
1276  		case 12: /* bring specified console to the front */
1277  			if (par[1] >= 1 && vc_cons_allocated(par[1]-1))
      				set_console(par[1] - 1);
1279  			break;
1280  		case 13: /* unblank the screen */
      			poke_blanked_console();
1282  			break;
1283  		case 14: /* set vesa powerdown interval */
      			vesa_off_interval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
1285  			break;
      	}
      }
      
1289  static void insert_line(int currcons, unsigned int nr)
      {
      	scrdown(currcons,y,bottom,nr);
      	need_wrap = 0;
      }
      
      
1296  static void delete_line(int currcons, unsigned int nr)
      {
      	scrup(currcons,y,bottom,nr);
      	need_wrap = 0;
      }
      
1302  static void csi_at(int currcons, unsigned int nr)
      {
1304  	if (nr > video_num_columns - x)
      		nr = video_num_columns - x;
1306  	else if (!nr)
      		nr = 1;
      	insert_char(currcons, nr);
      }
      
1311  static void csi_L(int currcons, unsigned int nr)
      {
1313  	if (nr > video_num_lines - y)
      		nr = video_num_lines - y;
1315  	else if (!nr)
      		nr = 1;
      	insert_line(currcons, nr);
      }
      
1320  static void csi_P(int currcons, unsigned int nr)
      {
1322  	if (nr > video_num_columns - x)
      		nr = video_num_columns - x;
1324  	else if (!nr)
      		nr = 1;
      	delete_char(currcons, nr);
      }
      
1329  static void csi_M(int currcons, unsigned int nr)
      {
1331  	if (nr > video_num_lines - y)
      		nr = video_num_lines - y;
1333  	else if (!nr)
      		nr=1;
      	delete_line(currcons, nr);
      }
      
1338  static void save_cur(int currcons)
      {
      	saved_x		= x;
      	saved_y		= y;
      	s_intensity	= intensity;
      	s_underline	= underline;
      	s_blink		= blink;
      	s_reverse	= reverse;
      	s_charset	= charset;
      	s_color		= color;
      	saved_G0	= G0_charset;
      	saved_G1	= G1_charset;
      }
      
1352  static void restore_cur(int currcons)
      {
      	gotoxy(currcons,saved_x,saved_y);
      	intensity	= s_intensity;
      	underline	= s_underline;
      	blink		= s_blink;
      	reverse		= s_reverse;
      	charset		= s_charset;
      	color		= s_color;
      	G0_charset	= saved_G0;
      	G1_charset	= saved_G1;
      	translate	= set_translate(charset ? G1_charset : G0_charset,currcons);
      	update_attr(currcons);
      	need_wrap = 0;
      }
      
      enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
      	EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
      	ESpalette };
      
1372  static void reset_terminal(int currcons, int do_clear)
      {
      	top		= 0;
      	bottom		= video_num_lines;
      	vc_state	= ESnormal;
      	ques		= 0;
      	translate	= set_translate(LAT1_MAP,currcons);
      	G0_charset	= LAT1_MAP;
      	G1_charset	= GRAF_MAP;
      	charset		= 0;
      	need_wrap	= 0;
      	report_mouse	= 0;
      	utf             = 0;
      	utf_count       = 0;
      
      	disp_ctrl	= 0;
      	toggle_meta	= 0;
      
      	decscnm		= 0;
      	decom		= 0;
      	decawm		= 1;
      	deccm		= 1;
      	decim		= 0;
      
      	set_kbd(decarm);
      	clr_kbd(decckm);
      	clr_kbd(kbdapplic);
      	clr_kbd(lnm);
      	kbd_table[currcons].lockstate = 0;
      	kbd_table[currcons].slockstate = 0;
      	kbd_table[currcons].ledmode = LED_SHOW_FLAGS;
      	kbd_table[currcons].ledflagstate = kbd_table[currcons].default_ledflagstate;
      	set_leds();
      
      	cursor_type = CUR_DEFAULT;
      	complement_mask = s_complement_mask;
      
      	default_attr(currcons);
      	update_attr(currcons);
      
      	tab_stop[0]	= 0x01010100;
      	tab_stop[1]	=
      	tab_stop[2]	=
      	tab_stop[3]	=
      	tab_stop[4]	= 0x01010101;
      
      	bell_pitch = DEFAULT_BELL_PITCH;
      	bell_duration = DEFAULT_BELL_DURATION;
      
      	gotoxy(currcons,0,0);
      	save_cur(currcons);
1423  	if (do_clear)
      	    csi_J(currcons,2);
      }
      
1427  static void do_con_trol(struct tty_struct *tty, unsigned int currcons, int c)
      {
      	/*
      	 *  Control characters can be used in the _middle_
      	 *  of an escape sequence.
      	 */
1433  	switch (c) {
1434  	case 0:
1435  		return;
1436  	case 7:
1437  		if (bell_duration)
      			kd_mksound(bell_pitch, bell_duration);
1439  		return;
1440  	case 8:
      		bs(currcons);
1442  		return;
1443  	case 9:
      		pos -= (x << 1);
1445  		while (x < video_num_columns - 1) {
      			x++;
1447  			if (tab_stop[x >> 5] & (1 << (x & 31)))
1448  				break;
      		}
      		pos += (x << 1);
1451  		return;
1452  	case 10: case 11: case 12:
      		lf(currcons);
1454  		if (!is_kbd(lnm))
1455  			return;
1456  	case 13:
      		cr(currcons);
1458  		return;
1459  	case 14:
      		charset = 1;
      		translate = set_translate(G1_charset,currcons);
      		disp_ctrl = 1;
1463  		return;
1464  	case 15:
      		charset = 0;
      		translate = set_translate(G0_charset,currcons);
      		disp_ctrl = 0;
1468  		return;
1469  	case 24: case 26:
      		vc_state = ESnormal;
1471  		return;
1472  	case 27:
      		vc_state = ESesc;
1474  		return;
1475  	case 127:
      		del(currcons);
1477  		return;
1478  	case 128+27:
      		vc_state = ESsquare;
1480  		return;
      	}
1482  	switch(vc_state) {
1483  	case ESesc:
      		vc_state = ESnormal;
1485  		switch (c) {
1486  		case '[':
      			vc_state = ESsquare;
1488  			return;
1489  		case ']':
      			vc_state = ESnonstd;
1491  			return;
1492  		case '%':
      			vc_state = ESpercent;
1494  			return;
1495  		case 'E':
      			cr(currcons);
      			lf(currcons);
1498  			return;
1499  		case 'M':
      			ri(currcons);
1501  			return;
1502  		case 'D':
      			lf(currcons);
1504  			return;
1505  		case 'H':
      			tab_stop[x >> 5] |= (1 << (x & 31));
1507  			return;
1508  		case 'Z':
      			respond_ID(tty);
1510  			return;
1511  		case '7':
      			save_cur(currcons);
1513  			return;
1514  		case '8':
      			restore_cur(currcons);
1516  			return;
1517  		case '(':
      			vc_state = ESsetG0;
1519  			return;
1520  		case ')':
      			vc_state = ESsetG1;
1522  			return;
1523  		case '#':
      			vc_state = EShash;
1525  			return;
1526  		case 'c':
      			reset_terminal(currcons,1);
1528  			return;
1529  		case '>':  /* Numeric keypad */
      			clr_kbd(kbdapplic);
1531  			return;
1532  		case '=':  /* Appl. keypad */
      			set_kbd(kbdapplic);
1534  			return;
      		}
1536  		return;
1537  	case ESnonstd:
1538  		if (c=='P') {   /* palette escape sequence */
1539  			for (npar=0; npar<NPAR; npar++)
      				par[npar] = 0 ;
      			npar = 0 ;
      			vc_state = ESpalette;
1543  			return;
1544  		} else if (c=='R') {   /* reset palette */
      			reset_palette(currcons);
      			vc_state = ESnormal;
1547  		} else
      			vc_state = ESnormal;
1549  		return;
1550  	case ESpalette:
1551  		if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) {
      			par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ;
1553  			if (npar==7) {
      				int i = par[0]*3, j = 1;
      				palette[i] = 16*par[j++];
      				palette[i++] += par[j++];
      				palette[i] = 16*par[j++];
      				palette[i++] += par[j++];
      				palette[i] = 16*par[j++];
      				palette[i] += par[j];
      				set_palette(currcons);
      				vc_state = ESnormal;
      			}
1564  		} else
      			vc_state = ESnormal;
1566  		return;
1567  	case ESsquare:
1568  		for(npar = 0 ; npar < NPAR ; npar++)
      			par[npar] = 0;
      		npar = 0;
      		vc_state = ESgetpars;
1572  		if (c == '[') { /* Function key */
      			vc_state=ESfunckey;
1574  			return;
      		}
      		ques = (c=='?');
1577  		if (ques)
1578  			return;
1579  	case ESgetpars:
1580  		if (c==';' && npar<NPAR-1) {
      			npar++;
1582  			return;
1583  		} else if (c>='0' && c<='9') {
      			par[npar] *= 10;
      			par[npar] += c-'0';
1586  			return;
1587  		} else vc_state=ESgotpars;
1588  	case ESgotpars:
      		vc_state = ESnormal;
1590  		switch(c) {
1591  		case 'h':
      			set_mode(currcons,1);
1593  			return;
1594  		case 'l':
      			set_mode(currcons,0);
1596  			return;
1597  		case 'c':
1598  			if (ques) {
1599  				if (par[0])
      					cursor_type = par[0] | (par[1]<<8) | (par[2]<<16);
1601  				else
      					cursor_type = CUR_DEFAULT;
1603  				return;
      			}
1605  			break;
1606  		case 'm':
1607  			if (ques) {
      				clear_selection();
1609  				if (par[0])
      					complement_mask = par[0]<<8 | par[1];
1611  				else
      					complement_mask = s_complement_mask;
1613  				return;
      			}
1615  			break;
1616  		case 'n':
1617  			if (!ques) {
1618  				if (par[0] == 5)
      					status_report(tty);
1620  				else if (par[0] == 6)
      					cursor_report(currcons,tty);
      			}
1623  			return;
      		}
1625  		if (ques) {
      			ques = 0;
1627  			return;
      		}
1629  		switch(c) {
1630  		case 'G': case '`':
1631  			if (par[0]) par[0]--;
      			gotoxy(currcons,par[0],y);
1633  			return;
1634  		case 'A':
1635  			if (!par[0]) par[0]++;
      			gotoxy(currcons,x,y-par[0]);
1637  			return;
1638  		case 'B': case 'e':
1639  			if (!par[0]) par[0]++;
      			gotoxy(currcons,x,y+par[0]);
1641  			return;
1642  		case 'C': case 'a':
1643  			if (!par[0]) par[0]++;
      			gotoxy(currcons,x+par[0],y);
1645  			return;
1646  		case 'D':
1647  			if (!par[0]) par[0]++;
      			gotoxy(currcons,x-par[0],y);
1649  			return;
1650  		case 'E':
1651  			if (!par[0]) par[0]++;
      			gotoxy(currcons,0,y+par[0]);
1653  			return;
1654  		case 'F':
1655  			if (!par[0]) par[0]++;
      			gotoxy(currcons,0,y-par[0]);
1657  			return;
1658  		case 'd':
1659  			if (par[0]) par[0]--;
      			gotoxay(currcons,x,par[0]);
1661  			return;
1662  		case 'H': case 'f':
1663  			if (par[0]) par[0]--;
1664  			if (par[1]) par[1]--;
      			gotoxay(currcons,par[1],par[0]);
1666  			return;
1667  		case 'J':
      			csi_J(currcons,par[0]);
1669  			return;
1670  		case 'K':
      			csi_K(currcons,par[0]);
1672  			return;
1673  		case 'L':
      			csi_L(currcons,par[0]);
1675  			return;
1676  		case 'M':
      			csi_M(currcons,par[0]);
1678  			return;
1679  		case 'P':
      			csi_P(currcons,par[0]);
1681  			return;
1682  		case 'c':
1683  			if (!par[0])
      				respond_ID(tty);
1685  			return;
1686  		case 'g':
1687  			if (!par[0])
      				tab_stop[x >> 5] &= ~(1 << (x & 31));
1689  			else if (par[0] == 3) {
      				tab_stop[0] =
      					tab_stop[1] =
      					tab_stop[2] =
      					tab_stop[3] =
      					tab_stop[4] = 0;
      			}
1696  			return;
1697  		case 'm':
      			csi_m(currcons);
1699  			return;
1700  		case 'q': /* DECLL - but only 3 leds */
      			/* map 0,1,2,3 to 0,1,2,4 */
1702  			if (par[0] < 4)
      				setledstate(kbd_table + currcons,
      					    (par[0] < 3) ? par[0] : 4);
1705  			return;
1706  		case 'r':
1707  			if (!par[0])
      				par[0]++;
1709  			if (!par[1])
      				par[1] = video_num_lines;
      			/* Minimum allowed region is 2 lines */
      			if (par[0] < par[1] &&
1713  			    par[1] <= video_num_lines) {
      				top=par[0]-1;
      				bottom=par[1];
      				gotoxay(currcons,0,0);
      			}
1718  			return;
1719  		case 's':
      			save_cur(currcons);
1721  			return;
1722  		case 'u':
      			restore_cur(currcons);
1724  			return;
1725  		case 'X':
      			csi_X(currcons, par[0]);
1727  			return;
1728  		case '@':
      			csi_at(currcons,par[0]);
1730  			return;
1731  		case ']': /* setterm functions */
      			setterm_command(currcons);
1733  			return;
      		}
1735  		return;
1736  	case ESpercent:
      		vc_state = ESnormal;
1738  		switch (c) {
1739  		case '@':  /* defined in ISO 2022 */
      			utf = 0;
1741  			return;
1742  		case 'G':  /* prelim official escape code */
1743  		case '8':  /* retained for compatibility */
      			utf = 1;
1745  			return;
      		}
1747  		return;
1748  	case ESfunckey:
      		vc_state = ESnormal;
1750  		return;
1751  	case EShash:
      		vc_state = ESnormal;
1753  		if (c == '8') {
      			/* DEC screen alignment test. kludge :-) */
      			video_erase_char =
      				(video_erase_char & 0xff00) | 'E';
      			csi_J(currcons, 2);
      			video_erase_char =
      				(video_erase_char & 0xff00) | ' ';
      			do_update_region(currcons, origin, screenbuf_size/2);
      		}
1762  		return;
1763  	case ESsetG0:
1764  		if (c == '0')
      			G0_charset = GRAF_MAP;
1766  		else if (c == 'B')
      			G0_charset = LAT1_MAP;
1768  		else if (c == 'U')
      			G0_charset = IBMPC_MAP;
1770  		else if (c == 'K')
      			G0_charset = USER_MAP;
1772  		if (charset == 0)
      			translate = set_translate(G0_charset,currcons);
      		vc_state = ESnormal;
1775  		return;
1776  	case ESsetG1:
1777  		if (c == '0')
      			G1_charset = GRAF_MAP;
1779  		else if (c == 'B')
      			G1_charset = LAT1_MAP;
1781  		else if (c == 'U')
      			G1_charset = IBMPC_MAP;
1783  		else if (c == 'K')
      			G1_charset = USER_MAP;
1785  		if (charset == 1)
      			translate = set_translate(G1_charset,currcons);
      		vc_state = ESnormal;
1788  		return;
1789  	default:
      		vc_state = ESnormal;
      	}
      }
      
      /* This is a temporary buffer used to prepare a tty console write
       * so that we can easily avoid touching user space while holding the
       * console spinlock.  It is allocated in con_init and is shared by
       * this code and the vc_screen read/write tty calls.
       *
       * We have to allocate this statically in the kernel data section
       * since console_init (and thus con_init) are called before any
       * kernel memory allocation is available.
       */
      char con_buf[PAGE_SIZE];
      #define CON_BUF_SIZE	PAGE_SIZE
      DECLARE_MUTEX(con_buf_sem);
      
1807  static int do_con_write(struct tty_struct * tty, int from_user,
      			const unsigned char *buf, int count)
      {
      #ifdef VT_BUF_VRAM_ONLY
      #define FLUSH do { } while(0);
      #else
      #define FLUSH if (draw_x >= 0) { \
      	sw->con_putcs(vc_cons[currcons].d, (u16 *)draw_from, (u16 *)draw_to-(u16 *)draw_from, y, draw_x); \
      	draw_x = -1; \
      	}
      #endif
      
      	int c, tc, ok, n = 0, draw_x = -1;
      	unsigned int currcons;
      	unsigned long draw_from = 0, draw_to = 0;
      	struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
      	u16 himask, charmask;
      	const unsigned char *orig_buf = NULL;
      	int orig_count;
      
      	currcons = vt->vc_num;
1828  	if (!vc_cons_allocated(currcons)) {
      	    /* could this happen? */
      	    static int error = 0;
1831  	    if (!error) {
      		error = 1;
      		printk("con_write: tty %d not allocated\n", currcons+1);
      	    }
1835  	    return 0;
      	}
      
      	orig_buf = buf;
      	orig_count = count;
      
1841  	if (from_user) {
      		down(&con_buf_sem);
      
      again:
1845  		if (count > CON_BUF_SIZE)
      			count = CON_BUF_SIZE;
1847  		if (copy_from_user(con_buf, buf, count)) {
      			n = 0; /* ?? are error codes legal here ?? */
1849  			goto out;
      		}
      
      		buf = con_buf;
      	}
      
      	/* At this point 'buf' is guarenteed 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.
      	 */
      
1862  	spin_lock_irq(&console_lock);
      
      	himask = hi_font_mask;
      	charmask = himask ? 0x1ff : 0xff;
      
      	/* undraw cursor first */
1868  	if (IS_FG)
      		hide_cursor(currcons);
      
1871  	while (!tty->stopped && count) {
      		c = *buf;
      		buf++;
      		n++;
      		count--;
      
1877  		if (utf) {
      		    /* Combine UTF-8 into Unicode */
      		    /* Incomplete characters silently ignored */
1880  		    if(c > 0x7f) {
1881  			if (utf_count > 0 && (c & 0xc0) == 0x80) {
      				utf_char = (utf_char << 6) | (c & 0x3f);
      				utf_count--;
1884  				if (utf_count == 0)
      				    tc = c = utf_char;
1886  				else continue;
1887  			} else {
1888  				if ((c & 0xe0) == 0xc0) {
      				    utf_count = 1;
      				    utf_char = (c & 0x1f);
1891  				} else if ((c & 0xf0) == 0xe0) {
      				    utf_count = 2;
      				    utf_char = (c & 0x0f);
1894  				} else if ((c & 0xf8) == 0xf0) {
      				    utf_count = 3;
      				    utf_char = (c & 0x07);
1897  				} else if ((c & 0xfc) == 0xf8) {
      				    utf_count = 4;
      				    utf_char = (c & 0x03);
1900  				} else if ((c & 0xfe) == 0xfc) {
      				    utf_count = 5;
      				    utf_char = (c & 0x01);
1903  				} else
      				    utf_count = 0;
1905  				continue;
      			      }
1907  		    } else {
      		      tc = c;
      		      utf_count = 0;
      		    }
1911  		} else {	/* no utf */
      		  tc = translate[toggle_meta ? (c|0x80) : c];
      		}
      
                      /* 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
                       * if the disp_ctrl mode has been explicitly enabled.
                       * Certain characters (as given by the CTRL_ALWAYS
                       * bitmap) are always displayed as control characters,
                       * as the console would be pretty useless without
                       * them; to display an arbitrary font position use the
                       * direct-to-font zone in UTF-8 mode.
                       */
                      ok = tc && (c >= 32 ||
                                  (!utf && !(((disp_ctrl ? CTRL_ALWAYS
                                               : CTRL_ACTION) >> c) & 1)))
                              && (c != 127 || disp_ctrl)
      			&& (c != 128+27);
      
1931  		if (vc_state == ESnormal && ok) {
      			/* Now try to find out how to display it */
      			tc = conv_uni_to_pc(vc_cons[currcons].d, tc);
1934  			if ( tc == -4 ) {
                                      /* If we got -4 (not found) then see if we have
                                         defined a replacement character (U+FFFD) */
                                      tc = conv_uni_to_pc(vc_cons[currcons].d, 0xfffd);
      
      				/* One reason for the -4 can be that we just
      				   did a clear_unimap();
      				   try at least to show something. */
1942  				if (tc == -4)
      				     tc = c;
1944                          } else if ( tc == -3 ) {
                                      /* Bad hash table -- hope for the best */
                                      tc = c;
                              }
1948  			if (tc & ~charmask)
1949                                  continue; /* Conversion failed */
      
1951  			if (need_wrap || decim)
1952  				FLUSH
1953  			if (need_wrap) {
      				cr(currcons);
      				lf(currcons);
      			}
1957  			if (decim)
      				insert_char(currcons, 1);
      			scr_writew(himask ?
      				     ((attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) :
      				     (attr << 8) + tc,
      				   (u16 *) pos);
1963  			if (DO_UPDATE && draw_x < 0) {
      				draw_x = x;
      				draw_from = pos;
      			}
1967  			if (x == video_num_columns - 1) {
      				need_wrap = decawm;
      				draw_to = pos+2;
1970  			} else {
      				x++;
      				draw_to = (pos+=2);
      			}
1974  			continue;
      		}
1976  		FLUSH
      		do_con_trol(tty, currcons, c);
      	}
1979  	FLUSH
1980  	spin_unlock_irq(&console_lock);
      
      out:
1983  	if (from_user) {
      		/* If the user requested something larger than
      		 * the CON_BUF_SIZE, and the tty is not stopped,
      		 * keep going.
      		 */
1988  		if ((orig_count > CON_BUF_SIZE) && !tty->stopped) {
      			orig_count -= CON_BUF_SIZE;
      			orig_buf += CON_BUF_SIZE;
      			count = orig_count;
      			buf = orig_buf;
1993  			goto again;
      		}
      
      		up(&con_buf_sem);
      	}
      
1999  	return n;
      #undef FLUSH
      }
      
      /*
       * This is the console switching tasklet.
       *
       * Doing console switching in a tasklet allows
       * us to do the switches asynchronously (needed when we want
       * to switch due to a keyboard interrupt).  Synchronization
       * with other console code and prevention of re-entrancy is
       * ensured with console_lock.
       */
2012  static void console_softint(unsigned long ignored)
      {
      	/* Runs the task queue outside of the console lock.  These
      	 * callbacks can come back into the console code and thus
      	 * will perform their own locking.
      	 */
      	run_task_queue(&con_task_queue);
      
2020  	spin_lock_irq(&console_lock);
      
2022  	if (want_console >= 0) {
2023  		if (want_console != fg_console && vc_cons_allocated(want_console)) {
      			hide_cursor(fg_console);
      			change_console(want_console);
      			/* we only changed when the console had already
      			   been allocated - a new console is not created
      			   in an interrupt routine */
      		}
      		want_console = -1;
      	}
2032  	if (do_poke_blanked_console) { /* do not unblank for a LED change */
      		do_poke_blanked_console = 0;
      		poke_blanked_console();
      	}
2036  	if (scrollback_delta) {
      		int currcons = fg_console;
      		clear_selection();
2039  		if (vcmode == KD_TEXT)
      			sw->con_scrolldelta(vc_cons[currcons].d, scrollback_delta);
      		scrollback_delta = 0;
      	}
      
2044  	spin_unlock_irq(&console_lock);
      }
      
      #ifdef CONFIG_VT_CONSOLE
      
      /*
       *	Console on virtual terminal
       *
       * The console_lock must be held when we get here.
       */
      
2055  void vt_console_print(struct console *co, const char * b, unsigned count)
      {
      	int currcons = fg_console;
      	unsigned char c;
      	static unsigned long printing;
      	const ushort *start;
      	ushort cnt = 0;
      	ushort myx;
      
      	/* console busy or not yet initialized */
2065  	if (!printable || test_and_set_bit(0, &printing))
2066  		return;
      
      	pm_access(pm_con);
      
2070  	if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1))
      		currcons = kmsg_redirect - 1;
      
      	/* read `x' only after setting currecons properly (otherwise
      	   the `x' macro will read the x of the foreground console). */
      	myx = x;
      
2077  	if (!vc_cons_allocated(currcons)) {
      		/* impossible */
      		/* printk("vt_console_print: tty %d not allocated ??\n", currcons+1); */
2080  		goto quit;
      	}
      
2083  	if (vcmode != KD_TEXT)
2084  		goto quit;
      
      	/* undraw cursor first */
2087  	if (IS_FG)
      		hide_cursor(currcons);
      
      	start = (ushort *)pos;
      
      	/* Contrived structure to try to emulate original need_wrap behaviour
      	 * Problems caused when we have need_wrap set on '\n' character */
2094  	while (count--) {
      		c = *b++;
2096  		if (c == 10 || c == 13 || c == 8 || need_wrap) {
2097  			if (cnt > 0) {
2098  				if (IS_VISIBLE)
      					sw->con_putcs(vc_cons[currcons].d, start, cnt, y, x);
      				x += cnt;
2101  				if (need_wrap)
      					x--;
      				cnt = 0;
      			}
2105  			if (c == 8) {		/* backspace */
      				bs(currcons);
      				start = (ushort *)pos;
      				myx = x;
2109  				continue;
      			}
2111  			if (c != 13)
      				lf(currcons);
      			cr(currcons);
      			start = (ushort *)pos;
      			myx = x;
2116  			if (c == 10 || c == 13)
2117  				continue;
      		}
      		scr_writew((attr << 8) + c, (unsigned short *) pos);
      		cnt++;
2121  		if (myx == video_num_columns - 1) {
      			need_wrap = 1;
2123  			continue;
      		}
      		pos+=2;
      		myx++;
      	}
2128  	if (cnt > 0) {
2129  		if (IS_VISIBLE)
      			sw->con_putcs(vc_cons[currcons].d, start, cnt, y, x);
      		x += cnt;
2132  		if (x == video_num_columns) {
      			x--;
      			need_wrap = 1;
      		}
      	}
      	set_cursor(currcons);
      
      quit:
      	clear_bit(0, &printing);
      }
      
2143  static kdev_t vt_console_device(struct console *c)
      {
2145  	return MKDEV(TTY_MAJOR, c->index ? c->index : fg_console + 1);
      }
      
      struct console vt_console_driver = {
      	name:		"tty",
      	write:		vt_console_print,
      	device:		vt_console_device,
      	wait_key:	keyboard_wait_for_keypress,
      	unblank:	unblank_screen,
      	flags:		CON_PRINTBUFFER,
      	index:		-1,
      };
      #endif
      
      /*
       *	Handling of Linux-specific VC ioctls
       */
      
2163  int tioclinux(struct tty_struct *tty, unsigned long arg)
      {
      	char type, data;
      
2167  	if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
2168  		return -EINVAL;
2169  	if (current->tty != tty && !suser())
2170  		return -EPERM;
2171  	if (get_user(type, (char *)arg))
2172  		return -EFAULT;
2173  	switch (type)
      	{
2175  		case 2:
2176  			return set_selection(arg, tty, 1);
2177  		case 3:
2178  			return paste_selection(tty);
2179  		case 4:
      			unblank_screen();
2181  			return 0;
2182  		case 5:
2183  			return sel_loadlut(arg);
2184  		case 6:
      			
      	/*
      	 * Make it possible to react to Shift+Mousebutton.
      	 * Note that 'shift_state' is an undocumented
      	 * kernel-internal variable; programs not closely
      	 * related to the kernel should not use this.
      	 */
      	 		data = shift_state;
2193  			return __put_user(data, (char *) arg);
2194  		case 7:
      			data = mouse_reporting();
2196  			return __put_user(data, (char *) arg);
2197  		case 10:
      			set_vesa_blanking(arg);
2199  			return 0;
2200  		case 11:	/* set kmsg redirect */
2201  			if (!suser())
2202  				return -EPERM;
2203  			if (get_user(data, (char *)arg+1))
2204  					return -EFAULT;
      			kmsg_redirect = data;
2206  			return 0;
2207  		case 12:	/* get fg_console */
2208  			return fg_console;
      	}
2210  	return -EINVAL;
      }
      
      /*
       *	/dev/ttyN handling
       */
      
2217  static int con_write(struct tty_struct * tty, int from_user,
      		     const unsigned char *buf, int count)
      {
      	int	retval;
      
      	pm_access(pm_con);
      	retval = do_con_write(tty, from_user, buf, count);
      	con_flush_chars(tty);
      
2226  	return retval;
      }
      
2229  static void con_put_char(struct tty_struct *tty, unsigned char ch)
      {
      	pm_access(pm_con);
      	do_con_write(tty, 0, &ch, 1);
      }
      
2235  static int con_write_room(struct tty_struct *tty)
      {
2237  	if (tty->stopped)
2238  		return 0;
2239  	return 4096;		/* No limit, really; we're not buffering */
      }
      
2242  static int con_chars_in_buffer(struct tty_struct *tty)
      {
2244  	return 0;		/* we're not buffering */
      }
      
      /*
       * con_throttle and con_unthrottle are only used for
       * paste_selection(), which has to stuff in a large number of
       * characters...
       */
2252  static void con_throttle(struct tty_struct *tty)
      {
      }
      
2256  static void con_unthrottle(struct tty_struct *tty)
      {
      	struct vt_struct *vt = (struct vt_struct *) tty->driver_data;
      
      	wake_up_interruptible(&vt->paste_wait);
      }
      
      /*
       * Turn the Scroll-Lock LED on when the tty is stopped
       */
2266  static void con_stop(struct tty_struct *tty)
      {
      	int console_num;
2269  	if (!tty)
2270  		return;
      	console_num = MINOR(tty->device) - (tty->driver.minor_start);
2272  	if (!vc_cons_allocated(console_num))
2273  		return;
      	set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
      	set_leds();
      }
      
      /*
       * Turn the Scroll-Lock LED off when the console is started
       */
2281  static void con_start(struct tty_struct *tty)
      {
      	int console_num;
2284  	if (!tty)
2285  		return;
      	console_num = MINOR(tty->device) - (tty->driver.minor_start);
2287  	if (!vc_cons_allocated(console_num))
2288  		return;
      	clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
      	set_leds();
      }
      
2293  static void con_flush_chars(struct tty_struct *tty)
      {
      	unsigned long flags;
      	struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
      
      	pm_access(pm_con);
2299  	spin_lock_irqsave(&console_lock, flags);
      	set_cursor(vt->vc_num);
2301  	spin_unlock_irqrestore(&console_lock, flags);
      }
      
      /*
       * Allocate the console screen memory.
       */
2307  static int con_open(struct tty_struct *tty, struct file * filp)
      {
      	unsigned int	currcons;
      	int i;
      
      	currcons = MINOR(tty->device) - tty->driver.minor_start;
      
      	i = vc_allocate(currcons);
2315  	if (i)
2316  		return i;
      
      	vt_cons[currcons]->vc_num = currcons;
      	tty->driver_data = vt_cons[currcons];
      
2321  	if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
      		tty->winsize.ws_row = video_num_lines;
      		tty->winsize.ws_col = video_num_columns;
      	}
2325  	if (tty->count == 1)
      		vcs_make_devfs (currcons, 0);
2327  	return 0;
      }
      
2330  static void con_close(struct tty_struct *tty, struct file * filp)
      {
2332  	if (!tty)
2333  		return;
2334  	if (tty->count != 1) return;
      	vcs_make_devfs (MINOR (tty->device) - tty->driver.minor_start, 1);
      	tty->driver_data = 0;
      }
      
2339  static void vc_init(unsigned int currcons, unsigned int rows, unsigned int cols, int do_clear)
      {
      	int j, k ;
      
      	video_num_columns = cols;
      	video_num_lines = rows;
      	video_size_row = cols<<1;
      	screenbuf_size = video_num_lines * video_size_row;
      
      	set_origin(currcons);
      	pos = origin;
      	reset_vc(currcons);
2351  	for (j=k=0; j<16; j++) {
      		vc_cons[currcons].d->vc_palette[k++] = default_red[j] ;
      		vc_cons[currcons].d->vc_palette[k++] = default_grn[j] ;
      		vc_cons[currcons].d->vc_palette[k++] = default_blu[j] ;
      	}
      	def_color       = 0x07;   /* white */
      	ulcolor		= 0x0f;   /* bold white */
      	halfcolor       = 0x08;   /* grey */
      	init_waitqueue_head(&vt_cons[currcons]->paste_wait);
      	reset_terminal(currcons, do_clear);
      }
      
      /*
       * This routine initializes console interrupts, and does nothing
       * else. If you want the screen to clear, call tty_write with
       * the appropriate escape-sequence.
       */
      
      struct tty_driver console_driver;
      static int console_refcount;
      
      DECLARE_TASKLET_DISABLED(console_tasklet, console_softint, 0);
      
2374  void __init con_init(void)
      {
      	const char *display_desc = NULL;
      	unsigned int currcons = 0;
      
2379  	if (conswitchp)
      		display_desc = conswitchp->con_startup();
2381  	if (!display_desc) {
      		fg_console = 0;
2383  		return;
      	}
      
      	memset(&console_driver, 0, sizeof(struct tty_driver));
      	console_driver.magic = TTY_DRIVER_MAGIC;
      	console_driver.name = "vc/%d";
      	console_driver.name_base = 1;
      	console_driver.major = TTY_MAJOR;
      	console_driver.minor_start = 1;
      	console_driver.num = MAX_NR_CONSOLES;
      	console_driver.type = TTY_DRIVER_TYPE_CONSOLE;
      	console_driver.init_termios = tty_std_termios;
      	console_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
      	/* Tell tty_register_driver() to skip consoles because they are
      	 * registered before kmalloc() is ready. We'll patch them in later. 
      	 * See comments at console_init(); see also con_init_devfs(). 
      	 */
      	console_driver.flags |= TTY_DRIVER_NO_DEVFS;
      	console_driver.refcount = &console_refcount;
      	console_driver.table = console_table;
      	console_driver.termios = console_termios;
      	console_driver.termios_locked = console_termios_locked;
      
      	console_driver.open = con_open;
      	console_driver.close = con_close;
      	console_driver.write = con_write;
      	console_driver.write_room = con_write_room;
      	console_driver.put_char = con_put_char;
      	console_driver.flush_chars = con_flush_chars;
      	console_driver.chars_in_buffer = con_chars_in_buffer;
      	console_driver.ioctl = vt_ioctl;
      	console_driver.stop = con_stop;
      	console_driver.start = con_start;
      	console_driver.throttle = con_throttle;
      	console_driver.unthrottle = con_unthrottle;
      
2419  	if (tty_register_driver(&console_driver))
      		panic("Couldn't register console driver\n");
      
      	init_timer(&console_timer);
      	console_timer.function = blank_screen;
2424  	if (blankinterval) {
      		mod_timer(&console_timer, jiffies + blankinterval);
      	}
      
      	/*
      	 * kmalloc is not running yet - we use the bootmem allocator.
      	 */
2431  	for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
      		vc_cons[currcons].d = (struct vc_data *)
      				alloc_bootmem(sizeof(struct vc_data));
      		vt_cons[currcons] = (struct vt_struct *)
      				alloc_bootmem(sizeof(struct vt_struct));
      		visual_init(currcons, 1);
      		screenbuf = (unsigned short *) alloc_bootmem(screenbuf_size);
      		kmalloced = 0;
      		vc_init(currcons, video_num_lines, video_num_columns, 
      			currcons || !sw->con_save_screen);
      	}
      	currcons = fg_console = 0;
      	master_display_fg = vc_cons[currcons].d;
      	set_origin(currcons);
      	save_screen(currcons);
      	gotoxy(currcons,x,y);
      	csi_J(currcons, 0);
      	update_screen(fg_console);
      	printk("Console: %s %s %dx%d",
      		can_do_color ? "colour" : "mono",
      		display_desc, video_num_columns, video_num_lines);
      	printable = 1;
      	printk("\n");
      
      #ifdef CONFIG_VT_CONSOLE
      	register_console(&vt_console_driver);
      #endif
      
      	tasklet_enable(&console_tasklet);
      	tasklet_schedule(&console_tasklet);
      }
      
      #ifndef VT_SINGLE_DRIVER
      
2465  static void clear_buffer_attributes(int currcons)
      {
      	unsigned short *p = (unsigned short *) origin;
      	int count = screenbuf_size/2;
      	int mask = hi_font_mask | 0xff;
      
2471  	for (; count > 0; count--, p++) {
      		scr_writew((scr_readw(p)&mask) | (video_erase_char&~mask), p);
      	}
      }
      
      /*
       *	If we support more console drivers, this function is used
       *	when a driver wants to take over some existing consoles
       *	and become default driver for newly opened ones.
       */
      
2482  void take_over_console(const struct consw *csw, int first, int last, int deflt)
      {
      	int i, j = -1;
      	const char *desc;
      
      	desc = csw->con_startup();
2488  	if (!desc) return;
2489  	if (deflt)
      		conswitchp = csw;
      
2492  	for (i = first; i <= last; i++) {
      		int old_was_color;
      		int currcons = i;
      
      		con_driver_map[i] = csw;
      
2498  		if (!vc_cons[i].d || !vc_cons[i].d->vc_sw)
2499  			continue;
      
      		j = i;
2502  		if (IS_VISIBLE)
      			save_screen(i);
      		old_was_color = vc_cons[i].d->vc_can_do_color;
      		vc_cons[i].d->vc_sw->con_deinit(vc_cons[i].d);
      		visual_init(i, 0);
      		update_attr(i);
      
      		/* If the console changed between mono <-> color, then
      		 * the attributes in the screenbuf will be wrong.  The
      		 * following resets all attributes to something sane.
      		 */
2513  		if (old_was_color != vc_cons[i].d->vc_can_do_color)
      			clear_buffer_attributes(i);
      
2516  		if (IS_VISIBLE)
      			update_screen(i);
      	}
      	printk("Console: switching ");
2520  	if (!deflt)
      		printk("consoles %d-%d ", first+1, last+1);
2522  	if (j >= 0)
      		printk("to %s %s %dx%d\n",
      		       vc_cons[j].d->vc_can_do_color ? "colour" : "mono",
      		       desc, vc_cons[j].d->vc_cols, vc_cons[j].d->vc_rows);
2526  	else
      		printk("to %s\n", desc);
      }
      
2530  void give_up_console(const struct consw *csw)
      {
      	int i;
      
2534  	for(i = 0; i < MAX_NR_CONSOLES; i++)
2535  		if (con_driver_map[i] == csw)
      			con_driver_map[i] = NULL;
      }
      
      #endif
      
      /*
       *	Screen blanking
       */
      
2545  static void set_vesa_blanking(unsigned long arg)
      {
          char *argp = (char *)arg + 1;
          unsigned int mode;
          get_user(mode, argp);
          vesa_blank_mode = (mode < 4) ? mode : 0;
      }
      
      /* We can't register the console with devfs during con_init(), because it
       * is called before kmalloc() works.  This function is called later to
       * do the registration.
       */
2557  void __init con_init_devfs (void)
      {
      	int i;
      
2561  	for (i = 0; i < console_driver.num; i++)
      		tty_register_devfs (&console_driver, DEVFS_FL_AOPEN_NOTIFY,
      				    console_driver.minor_start + i);
      }
      
2566  static void vesa_powerdown(void)
      {
          struct vc_data *c = vc_cons[fg_console].d;
          /*
           *  Power down if currently suspended (1 or 2),
           *  suspend if currently blanked (0),
           *  else do nothing (i.e. already powered down (3)).
           *  Called only if powerdown features are allowed.
           */
2575      switch (vesa_blank_mode) {
2576  	case VESA_NO_BLANKING:
      	    c->vc_sw->con_blank(c, VESA_VSYNC_SUSPEND+1);
2578  	    break;
2579  	case VESA_VSYNC_SUSPEND:
2580  	case VESA_HSYNC_SUSPEND:
      	    c->vc_sw->con_blank(c, VESA_POWERDOWN+1);
2582  	    break;
          }
      }
      
2586  static void vesa_powerdown_screen(unsigned long dummy)
      {
      	console_timer.function = unblank_screen_t;	/* I don't have a clue why this is necessary */
      
      	vesa_powerdown();
      }
      
2593  static void timer_do_blank_screen(int entering_gfx, int from_timer_handler)
      {
      	int currcons = fg_console;
      	int i;
      
2598  	if (console_blanked)
2599  		return;
      
      	/* entering graphics mode? */
2602  	if (entering_gfx) {
      		hide_cursor(currcons);
      		save_screen(currcons);
      		sw->con_blank(vc_cons[currcons].d, -1);
      		console_blanked = fg_console + 1;
      		set_origin(currcons);
2608  		return;
      	}
      
      	/* don't blank graphics */
2612  	if (vcmode != KD_TEXT) {
      		console_blanked = fg_console + 1;
2614  		return;
      	}
      
      	hide_cursor(currcons);
2618  	if (!from_timer_handler)
      		del_timer_sync(&console_timer);
2620  	if (vesa_off_interval) {
      		console_timer.function = vesa_powerdown_screen;
      		mod_timer(&console_timer, jiffies + vesa_off_interval);
2623  	} else {
2624  		if (!from_timer_handler)
      			del_timer_sync(&console_timer);
      		console_timer.function = unblank_screen_t;
      	}
      
      	save_screen(currcons);
      	/* In case we need to reset origin, blanking hook returns 1 */
      	i = sw->con_blank(vc_cons[currcons].d, 1);
      	console_blanked = fg_console + 1;
2633  	if (i)
      		set_origin(currcons);
      
2636  	if (console_blank_hook && console_blank_hook(1))
2637  		return;
2638      	if (vesa_blank_mode)
      		sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1);
      }
      
2642  void do_blank_screen(int entering_gfx)
      {
      	timer_do_blank_screen(entering_gfx, 0);
      }
      
2647  static void unblank_screen_t(unsigned long dummy)
      {
      	unblank_screen();
      }
      
2652  void unblank_screen(void)
      {
      	int currcons;
      
2656  	if (!console_blanked)
2657  		return;
2658  	if (!vc_cons_allocated(fg_console)) {
      		/* impossible */
      		printk("unblank_screen: tty %d not allocated ??\n", fg_console+1);
2661  		return;
      	}
      	console_timer.function = blank_screen;
2664  	if (blankinterval) {
      		mod_timer(&console_timer, jiffies + blankinterval);
      	}
      
      	currcons = fg_console;
      	console_blanked = 0;
2670  	if (console_blank_hook)
      		console_blank_hook(0);
      	set_palette(currcons);
2673  	if (sw->con_blank(vc_cons[currcons].d, 0))
      		/* Low-level driver cannot restore -> do it ourselves */
      		update_screen(fg_console);
      	set_cursor(fg_console);
      }
      
2679  static void blank_screen(unsigned long dummy)
      {
      	timer_do_blank_screen(0, 1);
      }
      
2684  void poke_blanked_console(void)
      {
      	del_timer(&console_timer);	/* Can't use _sync here: called from tasklet */
2687  	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
2688  		return;
2689  	if (console_blanked) {
      		console_timer.function = unblank_screen_t;
      		mod_timer(&console_timer, jiffies);	/* Now */
2692  	} else if (blankinterval) {
      		mod_timer(&console_timer, jiffies + blankinterval);
      	}
      }
      
      /*
       *	Palettes
       */
      
2701  void set_palette(int currcons)
      {
2703  	if (vcmode != KD_GRAPHICS)
      		sw->con_set_palette(vc_cons[currcons].d, color_table);
      }
      
2707  static int set_get_cmap(unsigned char *arg, int set)
      {
          int i, j, k;
      
2711      for (i = 0; i < 16; i++)
2712  	if (set) {
      	    get_user(default_red[i], arg++);
      	    get_user(default_grn[i], arg++);
      	    get_user(default_blu[i], arg++);
2716  	} else {
      	    put_user(default_red[i], arg++);
      	    put_user(default_grn[i], arg++);
      	    put_user(default_blu[i], arg++);
      	}
2721      if (set) {
2722  	for (i = 0; i < MAX_NR_CONSOLES; i++)
2723  	    if (vc_cons_allocated(i)) {
2724  		for (j = k = 0; j < 16; j++) {
      		    vc_cons[i].d->vc_palette[k++] = default_red[j];
      		    vc_cons[i].d->vc_palette[k++] = default_grn[j];
      		    vc_cons[i].d->vc_palette[k++] = default_blu[j];
      		}
      		set_palette(i);
      	    }
          }
2732      return 0;
      }
      
      /*
       * Load palette into the DAC registers. arg points to a colour
       * map, 3 bytes per colour, 16 colours, range from 0 to 255.
       */
      
2740  int con_set_cmap(unsigned char *arg)
      {
2742  	return set_get_cmap (arg,1);
      }
      
2745  int con_get_cmap(unsigned char *arg)
      {
2747  	return set_get_cmap (arg,0);
      }
      
2750  void reset_palette(int currcons)
      {
      	int j, k;
2753  	for (j=k=0; j<16; j++) {
      		palette[k++] = default_red[j];
      		palette[k++] = default_grn[j];
      		palette[k++] = default_blu[j];
      	}
      	set_palette(currcons);
      }
      
      /*
       *  Font switching
       *
       *  Currently we only support fonts up to 32 pixels wide, at a maximum height
       *  of 32 pixels. Userspace fontdata is stored with 32 bytes (shorts/ints, 
       *  depending on width) reserved for each character which is kinda wasty, but 
       *  this is done in order to maintain compatibility with the EGA/VGA fonts. It 
       *  is upto the actual low-level console-driver convert data into its favorite
       *  format (maybe we should add a `fontoffset' field to the `display'
       *  structure so we wont have to convert the fontdata all the time.
       *  /Jes
       */
      
      #define max_font_size 65536
      
2776  int con_font_op(int currcons, struct console_font_op *op)
      {
      	int rc = -EINVAL;
      	int size = max_font_size, set;
      	u8 *temp = NULL;
      	struct console_font_op old_op;
      
2783  	if (vt_cons[currcons]->vc_mode != KD_TEXT)
2784  		goto quit;
      	memcpy(&old_op, op, sizeof(old_op));
2786  	if (op->op == KD_FONT_OP_SET) {
2787  		if (!op->data)
2788  			return -EINVAL;
2789  		if (op->charcount > 512)
2790  			goto quit;
2791  		if (!op->height) {		/* Need to guess font height [compat] */
      			int h, i;
      			u8 *charmap = op->data, tmp;
      			
      			/* If from KDFONTOP ioctl, don't allow things which can be done in userland,
      			   so that we can get rid of this soon */
2797  			if (!(op->flags & KD_FONT_FLAG_OLD))
2798  				goto quit;
      			rc = -EFAULT;
2800  			for (h = 32; h > 0; h--)
2801  				for (i = 0; i < op->charcount; i++) {
2802  					if (get_user(tmp, &charmap[32*i+h-1]))
2803  						goto quit;
2804  					if (tmp)
2805  						goto nonzero;
      				}
      			rc = -EINVAL;
2808  			goto quit;
      		nonzero:
      			rc = -EINVAL;
      			op->height = h;
      		}
2813  		if (op->width > 32 || op->height > 32)
2814  			goto quit;
      		size = (op->width+7)/8 * 32 * op->charcount;
2816  		if (size > max_font_size)
2817  			return -ENOSPC;
      		set = 1;
2819  	} else if (op->op == KD_FONT_OP_GET)
      		set = 0;
2821  	else
2822  		return sw->con_font_op(vc_cons[currcons].d, op);
2823  	if (op->data) {
      		temp = kmalloc(size, GFP_KERNEL);
2825  		if (!temp)
2826  			return -ENOMEM;
2827  		if (set && copy_from_user(temp, op->data, size)) {
      			rc = -EFAULT;
2829  			goto quit;
      		}
      		op->data = temp;
      	}
      
2834  	spin_lock_irq(&console_lock);
      	rc = sw->con_font_op(vc_cons[currcons].d, op);
2836  	spin_unlock_irq(&console_lock);
      
      	op->data = old_op.data;
2839  	if (!rc && !set) {
      		int c = (op->width+7)/8 * 32 * op->charcount;
      		
2842  		if (op->data && op->charcount > old_op.charcount)
      			rc = -ENOSPC;
2844  		if (!(op->flags & KD_FONT_FLAG_OLD)) {
      			if (op->width > old_op.width || 
2846  			    op->height > old_op.height)
      				rc = -ENOSPC;
2848  		} else {
2849  			if (op->width != 8)
      				rc = -EIO;
      			else if ((old_op.height && op->height > old_op.height) ||
2852  			         op->height > 32)
      				rc = -ENOSPC;
      		}
2855  		if (!rc && op->data && copy_to_user(op->data, temp, c))
      			rc = -EFAULT;
      	}
2858  quit:	if (temp)
      		kfree(temp);
2860  	return rc;
      }
      
      /*
       *	Interface exported to selection and vcs.
       */
      
      /* used by selection */
2868  u16 screen_glyph(int currcons, int offset)
      {
      	u16 w = scr_readw(screenpos(currcons, offset, 1));
      	u16 c = w & 0xff;
      
2873  	if (w & hi_font_mask)
      		c |= 0x100;
2875  	return c;
      }
      
      /* used by vcs - note the word offset */
2879  unsigned short *screen_pos(int currcons, int w_offset, int viewed)
      {
2881  	return screenpos(currcons, 2 * w_offset, viewed);
      }
      
2884  void getconsxy(int currcons, char *p)
      {
      	p[0] = x;
      	p[1] = y;
      }
      
2890  void putconsxy(int currcons, char *p)
      {
      	gotoxy(currcons, p[0], p[1]);
      	set_cursor(currcons);
      }
      
2896  u16 vcs_scr_readw(int currcons, const u16 *org)
      {
2898  	if ((unsigned long)org == pos && softcursor_original != -1)
2899  		return softcursor_original;
2900  	return scr_readw(org);
      }
      
2903  void vcs_scr_writew(int currcons, u16 val, u16 *org)
      {
      	scr_writew(val, org);
2906  	if ((unsigned long)org == pos) {
      		softcursor_original = -1;
      		add_softcursor(currcons);
      	}
      }
      
2912  static int pm_con_request(struct pm_dev *dev, pm_request_t rqst, void *data)
      {
2914  	switch (rqst)
      	{
2916  	case PM_RESUME:
      		unblank_screen();
2918  		break;
2919  	case PM_SUSPEND:
      		do_blank_screen(0);
2921  		break;
      	}
2923  	return 0;
      }
      
      /*
       *	Visible symbols for modules
       */
      
      EXPORT_SYMBOL(color_table);
      EXPORT_SYMBOL(default_red);
      EXPORT_SYMBOL(default_grn);
      EXPORT_SYMBOL(default_blu);
      EXPORT_SYMBOL(video_font_height);
      EXPORT_SYMBOL(video_scan_lines);
      EXPORT_SYMBOL(vc_resize);
      EXPORT_SYMBOL(fg_console);
      EXPORT_SYMBOL(console_blank_hook);
      #ifdef CONFIG_VT
      EXPORT_SYMBOL(vt_cons);
      #endif
      #ifndef VT_SINGLE_DRIVER
      EXPORT_SYMBOL(take_over_console);
      EXPORT_SYMBOL(give_up_console);
      #endif