vt.c 104 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/*
 *  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>
 * 
 *   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
60
 * deletion SMP-safe.  17Jun00, Andrew Morton
Linus Torvalds's avatar
Linus Torvalds committed
61
62
63
 *
 * Removed console_lock, enabled interrupts across all console operations
 * 13 March 2001, Andrew Morton
Adam Tlalka's avatar
Adam Tlalka committed
64
65
66
67
68
69
70
 *
 * Fixed UTF-8 mode so alternate charset modes always work according
 * to control sequences interpreted in do_con_trol function
 * preserving backward VT100 semigraphics compatibility,
 * malformed UTF sequences represented as sequences of replacement glyphs,
 * original codes or '?' as a last resort if replacement glyph is undefined
 * by Adam Tla/lka <atlka@pg.gda.pl>, Aug 2006
Linus Torvalds's avatar
Linus Torvalds committed
71
72
73
74
 */

#include <linux/module.h>
#include <linux/types.h>
75
#include <linux/sched/signal.h>
Linus Torvalds's avatar
Linus Torvalds committed
76
77
78
79
80
81
82
83
84
85
86
#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/slab.h>
#include <linux/major.h>
#include <linux/mm.h>
#include <linux/console.h>
#include <linux/init.h>
87
#include <linux/mutex.h>
Linus Torvalds's avatar
Linus Torvalds committed
88
89
90
91
92
93
94
95
96
97
98
#include <linux/vt_kern.h>
#include <linux/selection.h>
#include <linux/tiocl.h>
#include <linux/kbd_kern.h>
#include <linux/consolemap.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/pm.h>
#include <linux/font.h>
#include <linux/bitops.h>
99
#include <linux/notifier.h>
100
101
102
#include <linux/device.h>
#include <linux/io.h>
#include <linux/uaccess.h>
103
#include <linux/kdb.h>
104
#include <linux/ctype.h>
Linus Torvalds's avatar
Linus Torvalds committed
105

106
#define MAX_NR_CON_DRIVER 16
Linus Torvalds's avatar
Linus Torvalds committed
107

108
#define CON_DRIVER_FLAG_MODULE 1
109
110
#define CON_DRIVER_FLAG_INIT   2
#define CON_DRIVER_FLAG_ATTR   4
111
#define CON_DRIVER_FLAG_ZOMBIE 8
112
113
114
115

struct con_driver {
	const struct consw *con;
	const char *desc;
116
	struct device *dev;
117
	int node;
118
119
120
121
122
123
	int first;
	int last;
	int flag;
};

static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];
Linus Torvalds's avatar
Linus Torvalds committed
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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)
139
#define DEFAULT_CURSOR_BLINK_MS	200
Linus Torvalds's avatar
Linus Torvalds committed
140
141
142
143
144
145
146
147
148
149
150
151
152
153

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(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);
154
static int set_vesa_blanking(char __user *p);
Linus Torvalds's avatar
Linus Torvalds committed
155
156
static void set_cursor(struct vc_data *vc);
static void hide_cursor(struct vc_data *vc);
157
static void console_callback(struct work_struct *ignored);
158
static void con_driver_unregister_callback(struct work_struct *ignored);
Linus Torvalds's avatar
Linus Torvalds committed
159
160
161
static void blank_screen_t(unsigned long dummy);
static void set_palette(struct vc_data *vc);

162
163
#define vt_get_kmsg_redirect() vt_kmsg_redirect(-1)

Linus Torvalds's avatar
Linus Torvalds committed
164
static int printable;		/* Is console ready for printing? */
Jan Engelhardt's avatar
Jan Engelhardt committed
165
int default_utf8 = true;
166
module_param(default_utf8, int, S_IRUGO | S_IWUSR);
167
168
int global_cursor_default = -1;
module_param(global_cursor_default, int, S_IRUGO | S_IWUSR);
Linus Torvalds's avatar
Linus Torvalds committed
169

170
171
172
static int cur_default = CUR_DEFAULT;
module_param(cur_default, int, S_IRUGO | S_IWUSR);

Linus Torvalds's avatar
Linus Torvalds committed
173
174
175
176
177
178
179
180
181
182
183
/*
 * ignore_poke: don't unblank the screen when things are typed.  This is
 * mainly for the privacy of braille terminal users.
 */
static int ignore_poke;

int do_poke_blanked_console;
int console_blanked;

static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */
static int vesa_off_interval;
184
static int blankinterval;
185
core_param(consoleblank, blankinterval, int, 0444);
Linus Torvalds's avatar
Linus Torvalds committed
186

187
static DECLARE_WORK(console_work, console_callback);
188
static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);
Linus Torvalds's avatar
Linus Torvalds committed
189
190
191
192
193

/*
 * fg_console is the current virtual console,
 * last_console is the last used one,
 * want_console is the console we want to switch to,
194
 * saved_* variants are for save/restore around kernel debugger enter/leave
Linus Torvalds's avatar
Linus Torvalds committed
195
196
197
198
 */
int fg_console;
int last_console;
int want_console = -1;
199
200
201
202
static int saved_fg_console;
static int saved_last_console;
static int saved_want_console;
static int saved_vc_mode;
203
static int saved_console_blanked;
Linus Torvalds's avatar
Linus Torvalds committed
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230

/*
 * 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 schedule
 * all flip requests to process context with schedule-task() and run it from
 * console_callback().
 */

/*
 * For the same reason, we defer scrollback to the console callback.
 */
static int scrollback_delta;

/*
 * Hook so that the power management routines can (un)blank
 * the console on our behalf.
 */
int (*console_blank_hook)(int);

Jiri Slaby's avatar
Jiri Slaby committed
231
static DEFINE_TIMER(console_timer, blank_screen_t, 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
232
233
234
235
236
237
238
239
static int blank_state;
static int blank_timer_expired;
enum {
	blank_off = 0,
	blank_normal_wait,
	blank_vesa_wait,
};

240
241
242
243
244
245
246
247
/*
 * /sys/class/tty/tty0/
 *
 * the attribute 'active' contains the name of the current vc
 * console and it supports poll() to detect vc switches
 */
static struct device *tty0dev;

248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/*
 * Notifier list for console events.
 */
static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);

int register_vt_notifier(struct notifier_block *nb)
{
	return atomic_notifier_chain_register(&vt_notifier_list, nb);
}
EXPORT_SYMBOL_GPL(register_vt_notifier);

int unregister_vt_notifier(struct notifier_block *nb)
{
	return atomic_notifier_chain_unregister(&vt_notifier_list, nb);
}
EXPORT_SYMBOL_GPL(unregister_vt_notifier);

static void notify_write(struct vc_data *vc, unsigned int unicode)
{
267
	struct vt_notifier_param param = { .vc = vc, .c = unicode };
268
269
270
271
272
273
274
275
	atomic_notifier_call_chain(&vt_notifier_list, VT_WRITE, &param);
}

static void notify_update(struct vc_data *vc)
{
	struct vt_notifier_param param = { .vc = vc };
	atomic_notifier_call_chain(&vt_notifier_list, VT_UPDATE, &param);
}
Linus Torvalds's avatar
Linus Torvalds committed
276
277
278
279
/*
 *	Low-Level Functions
 */

280
281
282
283
284
285
286
287
288
static inline bool con_is_fg(const struct vc_data *vc)
{
	return vc->vc_num == fg_console;
}

static inline bool con_should_update(const struct vc_data *vc)
{
	return con_is_visible(vc) && !console_blanked;
}
Linus Torvalds's avatar
Linus Torvalds committed
289
290
291
292
293
294
295
296
297
298
299
300
301
302

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;
}

Alan Cox's avatar
Alan Cox committed
303
/* Called  from the keyboard irq path.. */
Linus Torvalds's avatar
Linus Torvalds committed
304
305
static inline void scrolldelta(int lines)
{
Alan Cox's avatar
Alan Cox committed
306
307
308
	/* FIXME */
	/* scrolldelta needs some kind of consistency lock, but the BKL was
	   and still is not protecting versus the scheduled back end */
Linus Torvalds's avatar
Linus Torvalds committed
309
310
311
312
313
314
315
316
317
	scrollback_delta += lines;
	schedule_console_callback();
}

void schedule_console_callback(void)
{
	schedule_work(&console_work);
}

318
319
static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
		enum con_scroll dir, unsigned int nr)
Linus Torvalds's avatar
Linus Torvalds committed
320
{
321
	u16 *clear, *d, *s;
Linus Torvalds's avatar
Linus Torvalds committed
322

323
	if (t + nr >= b)
Linus Torvalds's avatar
Linus Torvalds committed
324
325
326
		nr = b - t - 1;
	if (b > vc->vc_rows || t >= b || nr < 1)
		return;
327
	if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr))
Linus Torvalds's avatar
Linus Torvalds committed
328
329
		return;

330
331
	s = clear = (u16 *)(vc->vc_origin + vc->vc_size_row * t);
	d = (u16 *)(vc->vc_origin + vc->vc_size_row * (t + nr));
Linus Torvalds's avatar
Linus Torvalds committed
332

333
334
335
336
337
338
	if (dir == SM_UP) {
		clear = s + (b - t - nr) * vc->vc_cols;
		swap(s, d);
	}
	scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row);
	scr_memsetw(clear, vc->vc_video_erase_char, vc->vc_size_row * nr);
Linus Torvalds's avatar
Linus Torvalds committed
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
}

static void do_update_region(struct vc_data *vc, unsigned long start, int count)
{
	unsigned int xx, yy, offset;
	u16 *p;

	p = (u16 *) start;
	if (!vc->vc_sw->con_getxy) {
		offset = (start - vc->vc_origin) / 2;
		xx = offset % vc->vc_cols;
		yy = offset / vc->vc_cols;
	} else {
		int nxx, nyy;
		start = vc->vc_sw->con_getxy(vc, start, &nxx, &nyy);
		xx = nxx; yy = nyy;
	}
	for(;;) {
		u16 attrib = scr_readw(p) & 0xff00;
		int startx = xx;
		u16 *q = p;
		while (xx < vc->vc_cols && count) {
			if (attrib != (scr_readw(p) & 0xff00)) {
				if (p > q)
					vc->vc_sw->con_putcs(vc, q, p-q, yy, startx);
				startx = xx;
				q = p;
				attrib = scr_readw(p) & 0xff00;
			}
			p++;
			xx++;
			count--;
		}
		if (p > q)
			vc->vc_sw->con_putcs(vc, q, p-q, yy, startx);
		if (!count)
			break;
		xx = 0;
		yy++;
		if (vc->vc_sw->con_getxy) {
			p = (u16 *)start;
			start = vc->vc_sw->con_getxy(vc, start, NULL, NULL);
		}
	}
}

void update_region(struct vc_data *vc, unsigned long start, int count)
{
	WARN_CONSOLE_UNLOCKED();

389
	if (con_should_update(vc)) {
Linus Torvalds's avatar
Linus Torvalds committed
390
391
392
393
394
395
396
397
		hide_cursor(vc);
		do_update_region(vc, start, count);
		set_cursor(vc);
	}
}

/* Structure of attributes is hardware-dependent */

398
399
static u8 build_attr(struct vc_data *vc, u8 _color, u8 _intensity, u8 _blink,
    u8 _underline, u8 _reverse, u8 _italic)
Linus Torvalds's avatar
Linus Torvalds committed
400
401
{
	if (vc->vc_sw->con_build_attr)
402
403
		return vc->vc_sw->con_build_attr(vc, _color, _intensity,
		       _blink, _underline, _reverse, _italic);
Linus Torvalds's avatar
Linus Torvalds committed
404
405
406
407
408
409
410
411
412
413
414
415

/*
 * ++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
 */
	{
416
	u8 a = _color;
Linus Torvalds's avatar
Linus Torvalds committed
417
418
	if (!vc->vc_can_do_color)
		return _intensity |
419
		       (_italic ? 2 : 0) |
Linus Torvalds's avatar
Linus Torvalds committed
420
421
422
		       (_underline ? 4 : 0) |
		       (_reverse ? 8 : 0) |
		       (_blink ? 0x80 : 0);
423
424
425
	if (_italic)
		a = (a & 0xF0) | vc->vc_itcolor;
	else if (_underline)
Linus Torvalds's avatar
Linus Torvalds committed
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
		a = (a & 0xf0) | vc->vc_ulcolor;
	else if (_intensity == 0)
		a = (a & 0xf0) | vc->vc_ulcolor;
	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 update_attr(struct vc_data *vc)
{
443
444
445
446
	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) | ' ';
Linus Torvalds's avatar
Linus Torvalds committed
447
448
449
450
451
452
453
454
455
456
457
}

/* Note: inverting the screen twice should revert to the original state */
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);
Jiri Slaby's avatar
Jiri Slaby committed
458
	if (vc->vc_sw->con_invert_region) {
Linus Torvalds's avatar
Linus Torvalds committed
459
		vc->vc_sw->con_invert_region(vc, p, count);
Jiri Slaby's avatar
Jiri Slaby committed
460
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
		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++;
			}
		}
	}
Jiri Slaby's avatar
Jiri Slaby committed
488

489
	if (con_should_update(vc))
Linus Torvalds's avatar
Linus Torvalds committed
490
		do_update_region(vc, (unsigned long) p, count);
491
	notify_update(vc);
Linus Torvalds's avatar
Linus Torvalds committed
492
493
494
495
496
}

/* used by selection: complement pointer position */
void complement_pos(struct vc_data *vc, int offset)
{
497
	static int old_offset = -1;
Linus Torvalds's avatar
Linus Torvalds committed
498
499
500
501
502
	static unsigned short old;
	static unsigned short oldx, oldy;

	WARN_CONSOLE_UNLOCKED();

503
504
505
	if (old_offset != -1 && old_offset >= 0 &&
	    old_offset < vc->vc_screenbuf_size) {
		scr_writew(old, screenpos(vc, old_offset, 1));
506
		if (con_should_update(vc))
Linus Torvalds's avatar
Linus Torvalds committed
507
			vc->vc_sw->con_putc(vc, old, oldy, oldx);
508
		notify_update(vc);
Linus Torvalds's avatar
Linus Torvalds committed
509
	}
510
511
512
513
514

	old_offset = offset;

	if (offset != -1 && offset >= 0 &&
	    offset < vc->vc_screenbuf_size) {
Linus Torvalds's avatar
Linus Torvalds committed
515
		unsigned short new;
516
		unsigned short *p;
Linus Torvalds's avatar
Linus Torvalds committed
517
518
519
520
		p = screenpos(vc, offset, 1);
		old = scr_readw(p);
		new = old ^ vc->vc_complement_mask;
		scr_writew(new, p);
521
		if (con_should_update(vc)) {
Linus Torvalds's avatar
Linus Torvalds committed
522
523
524
525
			oldx = (offset >> 1) % vc->vc_cols;
			oldy = (offset >> 1) / vc->vc_cols;
			vc->vc_sw->con_putc(vc, new, oldy, oldx);
		}
526
		notify_update(vc);
Linus Torvalds's avatar
Linus Torvalds committed
527
528
529
530
531
	}
}

static void insert_char(struct vc_data *vc, unsigned int nr)
{
532
	unsigned short *p = (unsigned short *) vc->vc_pos;
Linus Torvalds's avatar
Linus Torvalds committed
533

534
	scr_memmovew(p + nr, p, (vc->vc_cols - vc->vc_x - nr) * 2);
535
	scr_memsetw(p, vc->vc_video_erase_char, nr * 2);
Linus Torvalds's avatar
Linus Torvalds committed
536
	vc->vc_need_wrap = 0;
537
	if (con_should_update(vc))
538
		do_update_region(vc, (unsigned long) p,
539
			vc->vc_cols - vc->vc_x);
Linus Torvalds's avatar
Linus Torvalds committed
540
541
542
543
}

static void delete_char(struct vc_data *vc, unsigned int nr)
{
544
	unsigned short *p = (unsigned short *) vc->vc_pos;
Linus Torvalds's avatar
Linus Torvalds committed
545

546
	scr_memcpyw(p, p + nr, (vc->vc_cols - vc->vc_x - nr) * 2);
547
548
	scr_memsetw(p + vc->vc_cols - vc->vc_x - nr, vc->vc_video_erase_char,
			nr * 2);
Linus Torvalds's avatar
Linus Torvalds committed
549
	vc->vc_need_wrap = 0;
550
	if (con_should_update(vc))
551
		do_update_region(vc, (unsigned long) p,
552
			vc->vc_cols - vc->vc_x);
Linus Torvalds's avatar
Linus Torvalds committed
553
554
}

555
static int softcursor_original = -1;
Linus Torvalds's avatar
Linus Torvalds committed
556
557
558
559
560
561
562
563
564
565
566
567
568
569

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);
570
	if (con_should_update(vc))
Linus Torvalds's avatar
Linus Torvalds committed
571
572
573
574
575
576
577
		vc->vc_sw->con_putc(vc, i, vc->vc_y, vc->vc_x);
}

static void hide_softcursor(struct vc_data *vc)
{
	if (softcursor_original != -1) {
		scr_writew(softcursor_original, (u16 *)vc->vc_pos);
578
		if (con_should_update(vc))
Linus Torvalds's avatar
Linus Torvalds committed
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
			vc->vc_sw->con_putc(vc, softcursor_original,
					vc->vc_y, vc->vc_x);
		softcursor_original = -1;
	}
}

static void hide_cursor(struct vc_data *vc)
{
	if (vc == sel_cons)
		clear_selection();
	vc->vc_sw->con_cursor(vc, CM_ERASE);
	hide_softcursor(vc);
}

static void set_cursor(struct vc_data *vc)
{
595
	if (!con_is_fg(vc) || console_blanked || vc->vc_mode == KD_GRAPHICS)
Linus Torvalds's avatar
Linus Torvalds committed
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
		return;
	if (vc->vc_deccm) {
		if (vc == sel_cons)
			clear_selection();
		add_softcursor(vc);
		if ((vc->vc_cursor_type & 0x0f) != 1)
			vc->vc_sw->con_cursor(vc, CM_DRAW);
	} else
		hide_cursor(vc);
}

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

611
	if (!con_is_visible(vc) ||
Linus Torvalds's avatar
Linus Torvalds committed
612
613
614
615
616
617
618
619
	    !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;
}

620
static void save_screen(struct vc_data *vc)
Linus Torvalds's avatar
Linus Torvalds committed
621
622
623
624
625
626
627
{
	WARN_CONSOLE_UNLOCKED();

	if (vc->vc_sw->con_save_screen)
		vc->vc_sw->con_save_screen(vc);
}

628
629
630
631
632
633
634
635
static void flush_scrollback(struct vc_data *vc)
{
	WARN_CONSOLE_UNLOCKED();

	if (vc->vc_sw->con_flush_scrollback)
		vc->vc_sw->con_flush_scrollback(vc);
}

Linus Torvalds's avatar
Linus Torvalds committed
636
637
638
639
/*
 *	Redrawing of screen
 */

640
void clear_buffer_attributes(struct vc_data *vc)
Linus Torvalds's avatar
Linus Torvalds committed
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
{
	unsigned short *p = (unsigned short *)vc->vc_origin;
	int count = vc->vc_screenbuf_size / 2;
	int mask = vc->vc_hi_font_mask | 0xff;

	for (; count > 0; count--, p++) {
		scr_writew((scr_readw(p)&mask) | (vc->vc_video_erase_char & ~mask), p);
	}
}

void redraw_screen(struct vc_data *vc, int is_switch)
{
	int redraw = 0;

	WARN_CONSOLE_UNLOCKED();

	if (!vc) {
		/* strange ... */
		/* printk("redraw_screen: tty %d not allocated ??\n", new_console+1); */
		return;
	}

	if (is_switch) {
		struct vc_data *old_vc = vc_cons[fg_console].d;
		if (old_vc == vc)
			return;
667
		if (!con_is_visible(vc))
Linus Torvalds's avatar
Linus Torvalds committed
668
669
670
671
			redraw = 1;
		*vc->vc_display_fg = vc;
		fg_console = vc->vc_num;
		hide_cursor(old_vc);
672
		if (!con_is_visible(old_vc)) {
Linus Torvalds's avatar
Linus Torvalds committed
673
674
675
			save_screen(old_vc);
			set_origin(old_vc);
		}
676
677
		if (tty0dev)
			sysfs_notify(&tty0dev->kobj, NULL, "active");
Linus Torvalds's avatar
Linus Torvalds committed
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
	} else {
		hide_cursor(vc);
		redraw = 1;
	}

	if (redraw) {
		int update;
		int old_was_color = vc->vc_can_do_color;

		set_origin(vc);
		update = vc->vc_sw->con_switch(vc);
		set_palette(vc);
		/*
		 * If console changed from mono<->color, the best we can do
		 * is to clear the buffer attributes. As it currently stands,
		 * rebuilding new attributes from the old buffer is not doable
		 * without overly complex code.
		 */
		if (old_was_color != vc->vc_can_do_color) {
			update_attr(vc);
			clear_buffer_attributes(vc);
		}
700
701
702
703

		/* Forcibly update if we're panicing */
		if ((update && vc->vc_mode != KD_GRAPHICS) ||
		    vt_force_oops_output(vc))
Linus Torvalds's avatar
Linus Torvalds committed
704
705
706
707
708
709
			do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
	}
	set_cursor(vc);
	if (is_switch) {
		set_leds();
		compute_shiftstate();
710
		notify_update(vc);
Linus Torvalds's avatar
Linus Torvalds committed
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
	}
}

/*
 *	Allocation, freeing and resizing of VTs.
 */

int vc_cons_allocated(unsigned int i)
{
	return (i < MAX_NR_CONSOLES && vc_cons[i].d);
}

static void visual_init(struct vc_data *vc, int num, int init)
{
	/* ++Geert: vc->vc_sw->con_init determines console size */
	if (vc->vc_sw)
		module_put(vc->vc_sw->owner);
	vc->vc_sw = conswitchp;
#ifndef VT_SINGLE_DRIVER
	if (con_driver_map[num])
		vc->vc_sw = con_driver_map[num];
#endif
	__module_get(vc->vc_sw->owner);
	vc->vc_num = num;
	vc->vc_display_fg = &master_display_fg;
736
737
	if (vc->vc_uni_pagedir_loc)
		con_free_unimap(vc);
Linus Torvalds's avatar
Linus Torvalds committed
738
	vc->vc_uni_pagedir_loc = &vc->vc_uni_pagedir;
739
	vc->vc_uni_pagedir = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
740
741
742
	vc->vc_hi_font_mask = 0;
	vc->vc_complement_mask = 0;
	vc->vc_can_do_color = 0;
743
	vc->vc_panic_force_write = false;
744
	vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;
Linus Torvalds's avatar
Linus Torvalds committed
745
746
747
748
749
750
751
752
753
754
	vc->vc_sw->con_init(vc, init);
	if (!vc->vc_complement_mask)
		vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
	vc->vc_s_complement_mask = vc->vc_complement_mask;
	vc->vc_size_row = vc->vc_cols << 1;
	vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row;
}

int vc_allocate(unsigned int currcons)	/* return 0 on success */
{
755
756
757
	struct vt_notifier_param param;
	struct vc_data *vc;

Linus Torvalds's avatar
Linus Torvalds committed
758
759
760
761
	WARN_CONSOLE_UNLOCKED();

	if (currcons >= MAX_NR_CONSOLES)
		return -ENXIO;
762
763
764
765
766
767
768
769
770
771
772
773

	if (vc_cons[currcons].d)
		return 0;

	/* 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 */
	param.vc = vc = kzalloc(sizeof(struct vc_data), GFP_KERNEL);
	if (!vc)
Linus Torvalds's avatar
Linus Torvalds committed
774
		return -ENOMEM;
775
776
777
778
779
780
781
782

	vc_cons[currcons].d = vc;
	tty_port_init(&vc->port);
	INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);

	visual_init(vc, currcons, 1);

	if (!*vc->vc_uni_pagedir_loc)
Linus Torvalds's avatar
Linus Torvalds committed
783
		con_set_default_unimap(vc);
784

785
786
787
788
789
790
791
792
793
794
795
796
	vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL);
	if (!vc->vc_screenbuf)
		goto err_free;

	/* If no drivers have overridden us and the user didn't pass a
	   boot option, default to displaying the cursor */
	if (global_cursor_default == -1)
		global_cursor_default = 1;

	vc_init(vc, vc->vc_rows, vc->vc_cols, 1);
	vcs_make_sysfs(currcons);
	atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, &param);
797

Linus Torvalds's avatar
Linus Torvalds committed
798
	return 0;
799
800
801
802
err_free:
	kfree(vc);
	vc_cons[currcons].d = NULL;
	return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
803
804
}

805
806
static inline int resize_screen(struct vc_data *vc, int width, int height,
				int user)
Linus Torvalds's avatar
Linus Torvalds committed
807
808
809
810
811
{
	/* Resizes the resolution of the display adapater */
	int err = 0;

	if (vc->vc_mode != KD_GRAPHICS && vc->vc_sw->con_resize)
812
813
		err = vc->vc_sw->con_resize(vc, width, height, user);

Linus Torvalds's avatar
Linus Torvalds committed
814
815
816
817
818
819
820
821
822
823
	return err;
}

/*
 * 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]
 */
#define VC_RESIZE_MAXCOL (32767)
#define VC_RESIZE_MAXROW (32767)
824
825
826
827
828
829
830
831
832
833
834

/**
 *	vc_do_resize	-	resizing method for the tty
 *	@tty: tty being resized
 *	@real_tty: real tty (different to tty if a pty/tty pair)
 *	@vc: virtual console private data
 *	@cols: columns
 *	@lines: lines
 *
 *	Resize a virtual console, clipping according to the actual constraints.
 *	If the caller passes a tty structure then update the termios winsize
835
 *	information and perform any necessary signal handling.
836
 *
837
 *	Caller must hold the console semaphore. Takes the termios rwsem and
838
839
840
 *	ctrl_lock of the tty IFF a tty is passed.
 */

Alan Cox's avatar
Alan Cox committed
841
842
static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
				unsigned int cols, unsigned int lines)
Linus Torvalds's avatar
Linus Torvalds committed
843
844
{
	unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0;
845
	unsigned long end;
Jiri Slaby's avatar
Jiri Slaby committed
846
	unsigned int old_rows, old_row_size;
Linus Torvalds's avatar
Linus Torvalds committed
847
	unsigned int new_cols, new_rows, new_row_size, new_screen_size;
848
	unsigned int user;
Linus Torvalds's avatar
Linus Torvalds committed
849
850
851
852
853
854
855
	unsigned short *newscreen;

	WARN_CONSOLE_UNLOCKED();

	if (!vc)
		return -ENXIO;

856
857
858
	user = vc->vc_resize_user;
	vc->vc_resize_user = 0;

Linus Torvalds's avatar
Linus Torvalds committed
859
860
861
862
863
864
865
866
867
868
869
	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;

870
871
	if (new_screen_size > (4 << 20))
		return -EINVAL;
872
	newscreen = kmalloc(new_screen_size, GFP_USER);
Linus Torvalds's avatar
Linus Torvalds committed
873
874
875
	if (!newscreen)
		return -ENOMEM;

876
877
878
	if (vc == sel_cons)
		clear_selection();

Linus Torvalds's avatar
Linus Torvalds committed
879
880
881
	old_rows = vc->vc_rows;
	old_row_size = vc->vc_size_row;

882
	err = resize_screen(vc, new_cols, new_rows, user);
Linus Torvalds's avatar
Linus Torvalds committed
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
	if (err) {
		kfree(newscreen);
		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;
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912

	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
			 */
			old_origin += (old_rows - new_rows) * old_row_size;
		} else {
			/*
			 * Cursor is in no man's land, copy 1/2 screenful
			 * from the top and bottom of cursor position
			 */
			old_origin += (vc->vc_y - new_rows/2) * old_row_size;
		}
913
914
915
	}

	end = old_origin + old_row_size * min(old_rows, new_rows);
Linus Torvalds's avatar
Linus Torvalds committed
916
917
918

	update_attr(vc);

919
920
921
	while (old_origin < end) {
		scr_memcpyw((unsigned short *) new_origin,
			    (unsigned short *) old_origin, rlth);
Linus Torvalds's avatar
Linus Torvalds committed
922
		if (rrem)
923
924
			scr_memsetw((void *)(new_origin + rlth),
				    vc->vc_video_erase_char, rrem);
Linus Torvalds's avatar
Linus Torvalds committed
925
926
927
928
		old_origin += old_row_size;
		new_origin += new_row_size;
	}
	if (new_scr_end > new_origin)
929
930
		scr_memsetw((void *)new_origin, vc->vc_video_erase_char,
			    new_scr_end - new_origin);
931
	kfree(vc->vc_screenbuf);
Linus Torvalds's avatar
Linus Torvalds committed
932
933
934
935
936
937
938
939
940
941
	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);

942
943
944
945
	if (tty) {
		/* Rewrite the requested winsize data with the actual
		   resulting sizes */
		struct winsize ws;
Linus Torvalds's avatar
Linus Torvalds committed
946
947
948
949
		memset(&ws, 0, sizeof(ws));
		ws.ws_row = vc->vc_rows;
		ws.ws_col = vc->vc_cols;
		ws.ws_ypixel = vc->vc_scan_lines;
Alan Cox's avatar
Alan Cox committed
950
		tty_do_resize(tty, &ws);
Linus Torvalds's avatar
Linus Torvalds committed
951
952
	}

953
	if (con_is_visible(vc))
Linus Torvalds's avatar
Linus Torvalds committed
954
		update_screen(vc);
Alan Cox's avatar
Alan Cox committed
955
	vt_event_post(VT_EVENT_RESIZE, vc->vc_num, vc->vc_num);
Linus Torvalds's avatar
Linus Torvalds committed
956
957
958
	return err;
}

959
960
961
962
963
964
965
966
967
/**
 *	vc_resize		-	resize a VT
 *	@vc: virtual console
 *	@cols: columns
 *	@rows: rows
 *
 *	Resize a virtual console as seen from the console end of things. We
 *	use the common vc_do_resize methods to update the structures. The
 *	caller must hold the console sem to protect console internals and
968
 *	vc->port.tty
969
970
971
972
 */

int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows)
{
973
	return vc_do_resize(vc->port.tty, vc, cols, rows);
974
975
976
977
978
979
980
981
982
983
984
985
}

/**
 *	vt_resize		-	resize a VT
 *	@tty: tty to resize
 *	@ws: winsize attributes
 *
 *	Resize a virtual terminal. This is called by the tty layer as we
 *	register our own handler for resizing. The mutual helper does all
 *	the actual work.
 *
 *	Takes the console sem and the called methods then take the tty
986
 *	termios_rwsem and the tty ctrl_lock in that order.
987
 */
Roel Kluin's avatar
Roel Kluin committed
988
static int vt_resize(struct tty_struct *tty, struct winsize *ws)
Alan Cox's avatar
Alan Cox committed
989
{
990
991
	struct vc_data *vc = tty->driver_data;
	int ret;
Alan Cox's avatar
Alan Cox committed
992

993
	console_lock();
Alan Cox's avatar
Alan Cox committed
994
	ret = vc_do_resize(tty, vc, ws->ws_col, ws->ws_row);
995
	console_unlock();
996
	return ret;
Alan Cox's avatar
Alan Cox committed
997
}
Linus Torvalds's avatar
Linus Torvalds committed
998

999
struct vc_data *vc_deallocate(unsigned int currcons)
Linus Torvalds's avatar
Linus Torvalds committed
1000
{
1001
1002
	struct vc_data *vc = NULL;

Linus Torvalds's avatar
Linus Torvalds committed
1003
1004
1005
	WARN_CONSOLE_UNLOCKED();

	if (vc_cons_allocated(currcons)) {
1006
		struct vt_notifier_param param;
1007

1008
		param.vc = vc = vc_cons[currcons].d;
1009
		atomic_notifier_call_chain(&vt_notifier_list, VT_DEALLOCATE, &param);
1010
		vcs_remove_sysfs(currcons);
Linus Torvalds's avatar
Linus Torvalds committed
1011
		vc->vc_sw->con_deinit(vc);
1012
		put_pid(vc->vt_pid);
1013
		module_put(vc->vc_sw->owner);
1014
		kfree(vc->vc_screenbuf);
Linus Torvalds's avatar
Linus Torvalds committed
1015
1016
		vc_cons[currcons].d = NULL;
	}
1017
	return vc;
Linus Torvalds's avatar
Linus Torvalds committed
1018
1019
1020
1021
1022
1023
}

/*
 *	VT102 emulator
 */

Alan Cox's avatar
Alan Cox committed
1024
1025
1026
#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))
Linus Torvalds's avatar
Linus Torvalds committed
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038

#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"

Jiri Slaby's avatar
Jiri Slaby committed
1039
const unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
Linus Torvalds's avatar
Linus Torvalds committed
1040
1041
1042
				       8,12,10,14, 9,13,11,15 };

/* the default colour table, for VGA+ colour systems */
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
unsigned char default_red[] = {
	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
	0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff
};
module_param_array(default_red, byte, NULL, S_IRUGO | S_IWUSR);

unsigned char default_grn[] = {
	0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa,
	0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff
};
module_param_array(default_grn, byte, NULL, S_IRUGO | S_IWUSR);

unsigned char default_blu[] = {
	0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa,
	0x55, 0x55, 0x55, 0x55, 0xff, 0xff, 0xff, 0xff
};
module_param_array(default_blu, byte, NULL, S_IRUGO | S_IWUSR);
1060

Linus Torvalds's avatar
Linus Torvalds committed
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
/*
 * 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.
 */
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;
}

/* for absolute user moves, when decom is set */
static void gotoxay(struct vc_data *vc, int new_x, int new_y)
{
	gotoxy(vc, new_x, vc->vc_decom ? (vc->vc_top + new_y) : new_y);
}

1102
void scrollback(struct vc_data *vc)
Linus Torvalds's avatar
Linus Torvalds committed
1103
{
1104
	scrolldelta(-(vc->vc_rows / 2));
Linus Torvalds's avatar
Linus Torvalds committed
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
}

void scrollfront(struct vc_data *vc, int lines)
{
	if (!lines)
		lines = vc->vc_rows / 2;
	scrolldelta(lines);
}

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)
1120
		con_scroll(vc, vc->vc_top, vc->vc_bottom, SM_UP, 1);
Linus Torvalds's avatar
Linus Torvalds committed
1121
1122
1123
1124
1125
	else if (vc->vc_y < vc->vc_rows - 1) {
	    	vc->vc_y++;
		vc->vc_pos += vc->vc_size_row;
	}
	vc->vc_need_wrap = 0;
1126
	notify_write(vc, '\n');
Linus Torvalds's avatar
Linus Torvalds committed
1127
1128
1129
1130
1131
1132
1133
1134
}

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)
1135
		con_scroll(vc, vc->vc_top, vc->vc_bottom, SM_DOWN, 1);
Linus Torvalds's avatar
Linus Torvalds committed
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
	else if (vc->vc_y > 0) {
		vc->vc_y--;
		vc->vc_pos -= vc->vc_size_row;
	}
	vc->vc_need_wrap = 0;
}

static inline void cr(struct vc_data *vc)
{
	vc->vc_pos -= vc->vc_x << 1;
	vc->vc_need_wrap = vc->vc_x = 0;
1147
	notify_write(vc, '\r');
Linus Torvalds's avatar
Linus Torvalds committed
1148
1149
1150
1151
1152
1153
1154
1155
}

static inline void bs(struct vc_data *vc)
{
	if (vc->vc_x) {
		vc->vc_pos -= 2;
		vc->vc_x--;
		vc->vc_need_wrap = 0;
1156
		notify_write(vc, '\b');
Linus Torvalds's avatar
Linus Torvalds committed
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
	}
}

static inline void del(struct vc_data *vc)
{
	/* ignored */
}

static void csi_J(struct vc_data *vc, int vpar)
{
	unsigned int count;
	unsigned short * start;

	switch (vpar) {
		case 0:	/* erase from cursor to end of display */
			count = (vc->vc_scr_end - vc->vc_pos) >> 1;
			start = (unsigned short *)vc->vc_pos;
			break;
		case 1:	/* erase from start to cursor */
			count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1;
			start = (unsigned short *)vc->vc_origin;
			break;
Petr Písař's avatar
Petr Písař committed
1179
1180
		case 3: /* erase scroll-back buffer (and whole display) */
			scr_memsetw(vc->vc_screenbuf, vc->vc_video_erase_char,
1181
				    vc->vc_screenbuf_size);
1182
			flush_scrollback(vc);
Petr Písař's avatar
Petr Písař committed
1183
			set_origin(vc);
1184
			if (con_is_visible(vc))
Petr Písař's avatar
Petr Písař committed
1185
				update_screen(vc);
Petr Písař's avatar
Petr Písař committed
1186
			/* fall through */
Linus Torvalds's avatar
Linus Torvalds committed
1187
1188
1189
1190
1191
1192
1193
1194
		case 2: /* erase whole display */
			count = vc->vc_cols * vc->vc_rows;
			start = (unsigned short *)vc->vc_origin;
			break;
		default:
			return;
	}
	scr_memsetw(start, vc->vc_video_erase_char, 2 * count);
1195
	if (con_should_update(vc))
1196
		do_update_region(vc, (unsigned long) start, count);
Linus Torvalds's avatar
Linus Torvalds committed
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
	vc->vc_need_wrap = 0;
}

static void csi_K(struct vc_data *vc, int vpar)
{
	unsigned int count;
	unsigned short * start;

	switch (vpar) {
		case 0:	/* erase from cursor to end of line */
			count = vc->vc_cols - vc->vc_x;
			start = (unsigned short *)vc->vc_pos;
			break;
		case 1:	/* erase from start of line to cursor */
			start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1));
			count = vc->vc_x + 1;
			break;
		case 2: /* erase whole line */
			start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1));
			count = vc->vc_cols;
			break;
		default:
			return;
	}
	scr_memsetw(start, vc->vc_video_erase_char, 2 * count);
	vc->vc_need_wrap = 0;
1223
	if (con_should_update(vc))
1224
		do_update_region(vc, (unsigned long) start, count);
Linus Torvalds's avatar
Linus Torvalds committed
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
}

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;

	scr_memsetw((unsigned short *)vc->vc_pos, vc->vc_video_erase_char, 2 * count);
1236
	if (con_should_update(vc))
Linus Torvalds's avatar
Linus Torvalds committed
1237
1238
1239
1240
1241
1242
1243
		vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1, count);
	vc->vc_need_wrap = 0;
}

static void default_attr(struct vc_data *vc)
{
	vc->vc_intensity = 1;
1244
	vc->vc_italic = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1245
1246
1247
1248
1249
1250
	vc->vc_underline = 0;
	vc->vc_reverse = 0;
	vc->vc_blink = 0;
	vc->vc_color = vc->vc_def_color;
}

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

1253
static void rgb_from_256(int i, struct rgb *c)
1254
1255
{
	if (i < 8) {            /* Standard colours. */
1256
1257
1258
		c->r = i&1 ? 0xaa : 0x00;
		c->g = i&2 ? 0xaa : 0x00;
		c->b = i&4 ? 0xaa : 0x00;
1259
	} else if (i < 16) {
1260
1261
1262
		c->r = i&1 ? 0xff : 0x55;
		c->g = i&2 ? 0xff : 0x55;
		c->b = i&4 ? 0xff : 0x55;
1263
	} else if (i < 232) {   /* 6x6x6 colour cube. */
1264
1265
1266
		c->r = (i - 16) / 36 * 85 / 2;
		c->g = (i - 16) / 6 % 6 * 85 / 2;
		c->b = (i - 16) % 6 * 85 / 2;
1267
	} else                  /* Grayscale ramp. */
1268
		c->r = c->g = c->b = i * 10 - 2312;
1269
1270
}

1271
static void rgb_foreground(struct vc_data *vc, const struct rgb *c)
1272
{
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
	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;
1287
	else
1288
1289
		vc->vc_intensity = 1;

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

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

1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
/*
 * ITU T.416 Higher colour modes. They break the usual properties of SGR codes
 * and thus need to be detected and ignored by hand. Strictly speaking, that
 * standard also wants : rather than ; as separators, contrary to ECMA-48, but
 * no one produces such codes and almost no one accepts them.
 *
 * Subcommands 3 (CMY) and 4 (CMYK) are so insane there's no point in
 * supporting them.
 */
static int vc_t416_color(struct vc_data *vc, int i,
1310
		void(*set_color)(struct vc_data *vc, const struct rgb *c))
1311
{
1312
1313
	struct rgb c;

1314
1315
1316
1317
	i++;
	if (i > vc->vc_npar)
		return i;

1318
	if (vc->vc_par[i] == 5 && i + 1 <= vc->vc_npar) {
1319
		/* 256 colours */
1320
		i++;
1321
		rgb_from_256(vc->vc_par[i], &c);
1322
	} else if (vc->vc_par[i] == 2 && i + 3 <= vc->vc_npar) {
1323
		/* 24 bit */
1324
1325
1326
		c.r = vc->vc_par[i + 1];
		c.g = vc->vc_par[i + 2];
		c.b = vc->vc_par[i + 3];
1327
		i += 3;
1328
1329
1330
1331
	} else
		return i;

	set_color(vc, &c);
1332
1333
1334
1335

	return i;
}

1336
/* console_lock is held */
Linus Torvalds's avatar
Linus Torvalds committed
1337
1338
1339
1340
1341
1342
static void csi_m(struct vc_data *vc)
{
	int i;

	for (i = 0; i <= vc->vc_npar; i++)
		switch (vc->vc_par[i]) {
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
		case 0:	/* all attributes off */
			default_attr(vc);
			break;
		case 1:
			vc->vc_intensity = 2;
			break;
		case 2:
			vc->vc_intensity = 0;
			break;
		case 3:
			vc->vc_italic = 1;
			break;
		case 4:
			vc->vc_underline = 1;
			break;
		case 5:
			vc->vc_blink = 1;
			break;
		case 7:
			vc->vc_reverse = 1;
			break;
		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.
			  */
			vc->vc_translate = set_translate(vc->vc_charset == 0
					? vc->vc_G0_charset
					: vc->vc_G1_charset, vc);
			vc->vc_disp_ctrl = 0;
			vc->vc_toggle_meta = 0;
			break;
		case 11: /* ANSI X3.64-1979 (SCO-ish?)
			  * Select first alternate font, lets chars < 32 be
			  * displayed as ROM chars.
			  */
			vc->vc_translate = set_translate(IBMPC_MAP, vc);
			vc->vc_disp_ctrl = 1;
			vc->vc_toggle_meta = 0;
			break;
		case 12: /* ANSI X3.64-1979 (SCO-ish?)
			  * Select second alternate font, toggle high bit
			  * before displaying as ROM char.
			  */
			vc->vc_translate = set_translate(IBMPC_MAP, vc);
			vc->vc_disp_ctrl = 1;
			vc->vc_toggle_meta = 1;
			break;
		case 21:
		case 22:
			vc->vc_intensity = 1;
			break;
		case 23:
			vc->vc_italic = 0;
			break;
		case 24:
			vc->vc_underline = 0;
			break;
		case 25:
			vc->vc_blink = 0;
			break;
		case 27:
			vc->vc_reverse = 0;
			break;
		case 38:
			i = vc_t416_color(vc, i, rgb_foreground);
			break;
		case 48:
			i = vc_t416_color(vc, i, rgb_background);
			break;
		case 39:
			vc->vc_color = (vc->vc_def_color & 0x0f) |
				(vc->vc_color & 0xf0);
			break;
		case 49:
			vc->vc_color = (vc->vc_def_color & 0xf0) |
				(vc->vc_color & 0x0f);
			break;
		default:
1421
1422
1423
			if (vc->vc_par[i] >= 90 && vc->vc_par[i] <= 107) {
				if (vc->vc_par[i] < 100)
					vc->vc_intensity = 2;
1424
1425
				vc->vc_par[i] -= 60;
			}
1426
1427
1428
1429
1430
1431
1432
			if (vc->vc_par[i] >= 30 && vc->vc_par[i] <= 37)
				vc->vc_color = color_table[vc->vc_par[i] - 30]
					| (vc->vc_color & 0xf0);
			else if (vc->vc_par[i] >= 40 && vc->vc_par[i] <= 47)
				vc->vc_color = (color_table[vc->vc_par[i] - 40] << 4)
					| (vc->vc_color & 0x0f);
			break;
Linus Torvalds's avatar
Linus Torvalds committed
1433
1434
1435
1436
		}
	update_attr(vc);
}

Jiri Slaby's avatar
Jiri Slaby committed
1437
static void respond_string(const char *p, struct tty_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
1438
1439
{
	while (*p) {
Jiri Slaby's avatar
Jiri Slaby committed
1440
		tty_insert_flip_char(port, *p, 0);
Linus Torvalds's avatar
Linus Torvalds committed
1441
1442
		p++;
	}
Jiri Slaby's avatar
Jiri Slaby committed
1443
	tty_schedule_flip(port);
Linus Torvalds's avatar
Linus Torvalds committed
1444
1445
1446
1447
1448
1449
1450
}

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);
Jiri Slaby's avatar
Jiri Slaby committed
1451
	respond_string(buf, tty->port);
Linus Torvalds's avatar
Linus Torvalds committed
1452
1453
1454
1455
}

static inline void status_report(struct tty_struct *tty)
{
Jiri Slaby's avatar
Jiri Slaby committed
1456
	respond_string("\033[0n", tty->port);	/* Terminal ok */
Linus Torvalds's avatar
Linus Torvalds committed
1457
1458
}

Jiri Slaby's avatar
Jiri Slaby committed
1459
static inline void respond_ID(struct tty_struct *tty)
Linus Torvalds's avatar
Linus Torvalds committed
1460
{
Jiri Slaby's avatar
Jiri Slaby committed
1461
	respond_string(VT102ID, tty->port);
Linus Torvalds's avatar
Linus Torvalds committed
1462
1463
1464
1465
1466
1467
1468
1469
}

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));
Jiri Slaby's avatar
Jiri Slaby committed
1470
	respond_string