The vt and other related code is moved into the drivers/tty/vt directory.
Acked-by: Arnd Bergmann <arnd@arndb.de>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
# Makefile for the kernel character device drivers.
#
-#
-# This file contains the font map for the default (hardware) font
-#
-FONTMAPFILE = cp437.uni
-
obj-y += mem.o random.o
obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o
obj-y += misc.o
-obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o
obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o
-obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o
-obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o
obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o
obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o
obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o
obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o
-
-# Files generated that shall be removed upon make clean
-clean-files := consolemap_deftbl.c defkeymap.c
-
-quiet_cmd_conmk = CONMK $@
- cmd_conmk = scripts/conmakehash $< > $@
-
-$(obj)/consolemap_deftbl.c: $(src)/$(FONTMAPFILE)
- $(call cmd,conmk)
-
-$(obj)/defkeymap.o: $(obj)/defkeymap.c
-
-# Uncomment if you're changing the keymap and have an appropriate
-# loadkeys version for the map. By default, we'll use the shipped
-# versions.
-# GENERATE_KEYMAP := 1
-
-ifdef GENERATE_KEYMAP
-
-$(obj)/defkeymap.c: $(obj)/%.c: $(src)/%.map
- loadkeys --mktable $< > $@.tmp
- sed -e 's/^static *//' $@.tmp > $@
- rm $@.tmp
-
-endif
+++ /dev/null
-/*
- * consolemap.c
- *
- * Mapping from internal code (such as Latin-1 or Unicode or IBM PC code)
- * to font positions.
- *
- * aeb, 950210
- *
- * Support for multiple unimaps by Jakub Jelinek <jj@ultra.linux.cz>, July 1998
- *
- * Fix bug in inverse translation. Stanislav Voronyi <stas@cnti.uanet.kharkov.ua>, Dec 1998
- */
-
-#include <linux/module.h>
-#include <linux/kd.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/tty.h>
-#include <asm/uaccess.h>
-#include <linux/consolemap.h>
-#include <linux/vt_kern.h>
-
-static unsigned short translations[][256] = {
- /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */
- {
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
- 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
- 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
- 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
- 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
- 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
- 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
- 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
- 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
- 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
- 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
- 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
- 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
- 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
- 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
- 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
- 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
- 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
- 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
- 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
- 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
- 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
- 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
- },
- /* VT100 graphics mapped to Unicode */
- {
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
- 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
- 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
- 0x0028, 0x0029, 0x002a, 0x2192, 0x2190, 0x2191, 0x2193, 0x002f,
- 0x2588, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
- 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
- 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
- 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0,
- 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1,
- 0x2591, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba,
- 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c,
- 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
- 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
- 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
- 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
- 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
- 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
- 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
- 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
- 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
- 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
- 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
- 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
- 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
- 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
- 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
- },
- /* IBM Codepage 437 mapped to Unicode */
- {
- 0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
- 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,
- 0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,
- 0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
- 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
- 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
- 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
- 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
- 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
- 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
- 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
- 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
- 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
- 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
- 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
- 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
- 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
- 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
- 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
- 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
- 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
- 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
- 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
- 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
- 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
- },
- /* User mapping -- default to codes for direct font mapping */
- {
- 0xf000, 0xf001, 0xf002, 0xf003, 0xf004, 0xf005, 0xf006, 0xf007,
- 0xf008, 0xf009, 0xf00a, 0xf00b, 0xf00c, 0xf00d, 0xf00e, 0xf00f,
- 0xf010, 0xf011, 0xf012, 0xf013, 0xf014, 0xf015, 0xf016, 0xf017,
- 0xf018, 0xf019, 0xf01a, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f,
- 0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027,
- 0xf028, 0xf029, 0xf02a, 0xf02b, 0xf02c, 0xf02d, 0xf02e, 0xf02f,
- 0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
- 0xf038, 0xf039, 0xf03a, 0xf03b, 0xf03c, 0xf03d, 0xf03e, 0xf03f,
- 0xf040, 0xf041, 0xf042, 0xf043, 0xf044, 0xf045, 0xf046, 0xf047,
- 0xf048, 0xf049, 0xf04a, 0xf04b, 0xf04c, 0xf04d, 0xf04e, 0xf04f,
- 0xf050, 0xf051, 0xf052, 0xf053, 0xf054, 0xf055, 0xf056, 0xf057,
- 0xf058, 0xf059, 0xf05a, 0xf05b, 0xf05c, 0xf05d, 0xf05e, 0xf05f,
- 0xf060, 0xf061, 0xf062, 0xf063, 0xf064, 0xf065, 0xf066, 0xf067,
- 0xf068, 0xf069, 0xf06a, 0xf06b, 0xf06c, 0xf06d, 0xf06e, 0xf06f,
- 0xf070, 0xf071, 0xf072, 0xf073, 0xf074, 0xf075, 0xf076, 0xf077,
- 0xf078, 0xf079, 0xf07a, 0xf07b, 0xf07c, 0xf07d, 0xf07e, 0xf07f,
- 0xf080, 0xf081, 0xf082, 0xf083, 0xf084, 0xf085, 0xf086, 0xf087,
- 0xf088, 0xf089, 0xf08a, 0xf08b, 0xf08c, 0xf08d, 0xf08e, 0xf08f,
- 0xf090, 0xf091, 0xf092, 0xf093, 0xf094, 0xf095, 0xf096, 0xf097,
- 0xf098, 0xf099, 0xf09a, 0xf09b, 0xf09c, 0xf09d, 0xf09e, 0xf09f,
- 0xf0a0, 0xf0a1, 0xf0a2, 0xf0a3, 0xf0a4, 0xf0a5, 0xf0a6, 0xf0a7,
- 0xf0a8, 0xf0a9, 0xf0aa, 0xf0ab, 0xf0ac, 0xf0ad, 0xf0ae, 0xf0af,
- 0xf0b0, 0xf0b1, 0xf0b2, 0xf0b3, 0xf0b4, 0xf0b5, 0xf0b6, 0xf0b7,
- 0xf0b8, 0xf0b9, 0xf0ba, 0xf0bb, 0xf0bc, 0xf0bd, 0xf0be, 0xf0bf,
- 0xf0c0, 0xf0c1, 0xf0c2, 0xf0c3, 0xf0c4, 0xf0c5, 0xf0c6, 0xf0c7,
- 0xf0c8, 0xf0c9, 0xf0ca, 0xf0cb, 0xf0cc, 0xf0cd, 0xf0ce, 0xf0cf,
- 0xf0d0, 0xf0d1, 0xf0d2, 0xf0d3, 0xf0d4, 0xf0d5, 0xf0d6, 0xf0d7,
- 0xf0d8, 0xf0d9, 0xf0da, 0xf0db, 0xf0dc, 0xf0dd, 0xf0de, 0xf0df,
- 0xf0e0, 0xf0e1, 0xf0e2, 0xf0e3, 0xf0e4, 0xf0e5, 0xf0e6, 0xf0e7,
- 0xf0e8, 0xf0e9, 0xf0ea, 0xf0eb, 0xf0ec, 0xf0ed, 0xf0ee, 0xf0ef,
- 0xf0f0, 0xf0f1, 0xf0f2, 0xf0f3, 0xf0f4, 0xf0f5, 0xf0f6, 0xf0f7,
- 0xf0f8, 0xf0f9, 0xf0fa, 0xf0fb, 0xf0fc, 0xf0fd, 0xf0fe, 0xf0ff
- }
-};
-
-/* The standard kernel character-to-font mappings are not invertible
- -- this is just a best effort. */
-
-#define MAX_GLYPH 512 /* Max possible glyph value */
-
-static int inv_translate[MAX_NR_CONSOLES];
-
-struct uni_pagedir {
- u16 **uni_pgdir[32];
- unsigned long refcount;
- unsigned long sum;
- unsigned char *inverse_translations[4];
- u16 *inverse_trans_unicode;
- int readonly;
-};
-
-static struct uni_pagedir *dflt;
-
-static void set_inverse_transl(struct vc_data *conp, struct uni_pagedir *p, int i)
-{
- int j, glyph;
- unsigned short *t = translations[i];
- unsigned char *q;
-
- if (!p) return;
- q = p->inverse_translations[i];
-
- if (!q) {
- q = p->inverse_translations[i] = (unsigned char *)
- kmalloc(MAX_GLYPH, GFP_KERNEL);
- if (!q) return;
- }
- memset(q, 0, MAX_GLYPH);
-
- for (j = 0; j < E_TABSZ; j++) {
- glyph = conv_uni_to_pc(conp, t[j]);
- if (glyph >= 0 && glyph < MAX_GLYPH && q[glyph] < 32) {
- /* prefer '-' above SHY etc. */
- q[glyph] = j;
- }
- }
-}
-
-static void set_inverse_trans_unicode(struct vc_data *conp,
- struct uni_pagedir *p)
-{
- int i, j, k, glyph;
- u16 **p1, *p2;
- u16 *q;
-
- if (!p) return;
- q = p->inverse_trans_unicode;
- if (!q) {
- q = p->inverse_trans_unicode =
- kmalloc(MAX_GLYPH * sizeof(u16), GFP_KERNEL);
- if (!q)
- return;
- }
- memset(q, 0, MAX_GLYPH * sizeof(u16));
-
- for (i = 0; i < 32; i++) {
- p1 = p->uni_pgdir[i];
- if (!p1)
- continue;
- for (j = 0; j < 32; j++) {
- p2 = p1[j];
- if (!p2)
- continue;
- for (k = 0; k < 64; k++) {
- glyph = p2[k];
- if (glyph >= 0 && glyph < MAX_GLYPH
- && q[glyph] < 32)
- q[glyph] = (i << 11) + (j << 6) + k;
- }
- }
- }
-}
-
-unsigned short *set_translate(int m, struct vc_data *vc)
-{
- inv_translate[vc->vc_num] = m;
- return translations[m];
-}
-
-/*
- * Inverse translation is impossible for several reasons:
- * 1. The font<->character maps are not 1-1.
- * 2. The text may have been written while a different translation map
- * was active.
- * Still, it is now possible to a certain extent to cut and paste non-ASCII.
- */
-u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode)
-{
- struct uni_pagedir *p;
- int m;
- if (glyph < 0 || glyph >= MAX_GLYPH)
- return 0;
- else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc))
- return glyph;
- else if (use_unicode) {
- if (!p->inverse_trans_unicode)
- return glyph;
- else
- return p->inverse_trans_unicode[glyph];
- } else {
- m = inv_translate[conp->vc_num];
- if (!p->inverse_translations[m])
- return glyph;
- else
- return p->inverse_translations[m][glyph];
- }
-}
-EXPORT_SYMBOL_GPL(inverse_translate);
-
-static void update_user_maps(void)
-{
- int i;
- struct uni_pagedir *p, *q = NULL;
-
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
- if (!vc_cons_allocated(i))
- continue;
- p = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc;
- if (p && p != q) {
- set_inverse_transl(vc_cons[i].d, p, USER_MAP);
- set_inverse_trans_unicode(vc_cons[i].d, p);
- q = p;
- }
- }
-}
-
-/*
- * Load customizable translation table
- * arg points to a 256 byte translation table.
- *
- * The "old" variants are for translation directly to font (using the
- * 0xf000-0xf0ff "transparent" Unicodes) whereas the "new" variants set
- * Unicodes explicitly.
- */
-int con_set_trans_old(unsigned char __user * arg)
-{
- int i;
- unsigned short *p = translations[USER_MAP];
-
- if (!access_ok(VERIFY_READ, arg, E_TABSZ))
- return -EFAULT;
-
- for (i=0; i<E_TABSZ ; i++) {
- unsigned char uc;
- __get_user(uc, arg+i);
- p[i] = UNI_DIRECT_BASE | uc;
- }
-
- update_user_maps();
- return 0;
-}
-
-int con_get_trans_old(unsigned char __user * arg)
-{
- int i, ch;
- unsigned short *p = translations[USER_MAP];
-
- if (!access_ok(VERIFY_WRITE, arg, E_TABSZ))
- return -EFAULT;
-
- for (i=0; i<E_TABSZ ; i++)
- {
- ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]);
- __put_user((ch & ~0xff) ? 0 : ch, arg+i);
- }
- return 0;
-}
-
-int con_set_trans_new(ushort __user * arg)
-{
- int i;
- unsigned short *p = translations[USER_MAP];
-
- if (!access_ok(VERIFY_READ, arg, E_TABSZ*sizeof(unsigned short)))
- return -EFAULT;
-
- for (i=0; i<E_TABSZ ; i++) {
- unsigned short us;
- __get_user(us, arg+i);
- p[i] = us;
- }
-
- update_user_maps();
- return 0;
-}
-
-int con_get_trans_new(ushort __user * arg)
-{
- int i;
- unsigned short *p = translations[USER_MAP];
-
- if (!access_ok(VERIFY_WRITE, arg, E_TABSZ*sizeof(unsigned short)))
- return -EFAULT;
-
- for (i=0; i<E_TABSZ ; i++)
- __put_user(p[i], arg+i);
-
- return 0;
-}
-
-/*
- * Unicode -> current font conversion
- *
- * A font has at most 512 chars, usually 256.
- * But one font position may represent several Unicode chars.
- * A hashtable is somewhat of a pain to deal with, so use a
- * "paged table" instead. Simulation has shown the memory cost of
- * this 3-level paged table scheme to be comparable to a hash table.
- */
-
-extern u8 dfont_unicount[]; /* Defined in console_defmap.c */
-extern u16 dfont_unitable[];
-
-static void con_release_unimap(struct uni_pagedir *p)
-{
- u16 **p1;
- int i, j;
-
- if (p == dflt) dflt = NULL;
- for (i = 0; i < 32; i++) {
- if ((p1 = p->uni_pgdir[i]) != NULL) {
- for (j = 0; j < 32; j++)
- kfree(p1[j]);
- kfree(p1);
- }
- p->uni_pgdir[i] = NULL;
- }
- for (i = 0; i < 4; i++) {
- kfree(p->inverse_translations[i]);
- p->inverse_translations[i] = NULL;
- }
- if (p->inverse_trans_unicode) {
- kfree(p->inverse_trans_unicode);
- p->inverse_trans_unicode = NULL;
- }
-}
-
-void con_free_unimap(struct vc_data *vc)
-{
- struct uni_pagedir *p;
-
- p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
- if (!p)
- return;
- *vc->vc_uni_pagedir_loc = 0;
- if (--p->refcount)
- return;
- con_release_unimap(p);
- kfree(p);
-}
-
-static int con_unify_unimap(struct vc_data *conp, struct uni_pagedir *p)
-{
- int i, j, k;
- struct uni_pagedir *q;
-
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
- if (!vc_cons_allocated(i))
- continue;
- q = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc;
- if (!q || q == p || q->sum != p->sum)
- continue;
- for (j = 0; j < 32; j++) {
- u16 **p1, **q1;
- p1 = p->uni_pgdir[j]; q1 = q->uni_pgdir[j];
- if (!p1 && !q1)
- continue;
- if (!p1 || !q1)
- break;
- for (k = 0; k < 32; k++) {
- if (!p1[k] && !q1[k])
- continue;
- if (!p1[k] || !q1[k])
- break;
- if (memcmp(p1[k], q1[k], 64*sizeof(u16)))
- break;
- }
- if (k < 32)
- break;
- }
- if (j == 32) {
- q->refcount++;
- *conp->vc_uni_pagedir_loc = (unsigned long)q;
- con_release_unimap(p);
- kfree(p);
- return 1;
- }
- }
- return 0;
-}
-
-static int
-con_insert_unipair(struct uni_pagedir *p, u_short unicode, u_short fontpos)
-{
- int i, n;
- u16 **p1, *p2;
-
- if (!(p1 = p->uni_pgdir[n = unicode >> 11])) {
- p1 = p->uni_pgdir[n] = kmalloc(32*sizeof(u16 *), GFP_KERNEL);
- if (!p1) return -ENOMEM;
- for (i = 0; i < 32; i++)
- p1[i] = NULL;
- }
-
- if (!(p2 = p1[n = (unicode >> 6) & 0x1f])) {
- p2 = p1[n] = kmalloc(64*sizeof(u16), GFP_KERNEL);
- if (!p2) return -ENOMEM;
- memset(p2, 0xff, 64*sizeof(u16)); /* No glyphs for the characters (yet) */
- }
-
- p2[unicode & 0x3f] = fontpos;
-
- p->sum += (fontpos << 20) + unicode;
-
- return 0;
-}
-
-/* ui is a leftover from using a hashtable, but might be used again */
-int con_clear_unimap(struct vc_data *vc, struct unimapinit *ui)
-{
- struct uni_pagedir *p, *q;
-
- p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
- if (p && p->readonly) return -EIO;
- if (!p || --p->refcount) {
- q = kzalloc(sizeof(*p), GFP_KERNEL);
- if (!q) {
- if (p) p->refcount++;
- return -ENOMEM;
- }
- q->refcount=1;
- *vc->vc_uni_pagedir_loc = (unsigned long)q;
- } else {
- if (p == dflt) dflt = NULL;
- p->refcount++;
- p->sum = 0;
- con_release_unimap(p);
- }
- return 0;
-}
-
-int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
-{
- int err = 0, err1, i;
- struct uni_pagedir *p, *q;
-
- p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
- if (p->readonly) return -EIO;
-
- if (!ct) return 0;
-
- if (p->refcount > 1) {
- int j, k;
- u16 **p1, *p2, l;
-
- err1 = con_clear_unimap(vc, NULL);
- if (err1) return err1;
-
- q = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
- for (i = 0, l = 0; i < 32; i++)
- if ((p1 = p->uni_pgdir[i]))
- for (j = 0; j < 32; j++)
- if ((p2 = p1[j]))
- for (k = 0; k < 64; k++, l++)
- if (p2[k] != 0xffff) {
- err1 = con_insert_unipair(q, l, p2[k]);
- if (err1) {
- p->refcount++;
- *vc->vc_uni_pagedir_loc = (unsigned long)p;
- con_release_unimap(q);
- kfree(q);
- return err1;
- }
- }
- p = q;
- } else if (p == dflt)
- dflt = NULL;
-
- while (ct--) {
- unsigned short unicode, fontpos;
- __get_user(unicode, &list->unicode);
- __get_user(fontpos, &list->fontpos);
- if ((err1 = con_insert_unipair(p, unicode,fontpos)) != 0)
- err = err1;
- list++;
- }
-
- if (con_unify_unimap(vc, p))
- return err;
-
- for (i = 0; i <= 3; i++)
- set_inverse_transl(vc, p, i); /* Update all inverse translations */
- set_inverse_trans_unicode(vc, p);
-
- return err;
-}
-
-/* Loads the unimap for the hardware font, as defined in uni_hash.tbl.
- The representation used was the most compact I could come up
- with. This routine is executed at sys_setup time, and when the
- PIO_FONTRESET ioctl is called. */
-
-int con_set_default_unimap(struct vc_data *vc)
-{
- int i, j, err = 0, err1;
- u16 *q;
- struct uni_pagedir *p;
-
- if (dflt) {
- p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
- if (p == dflt)
- return 0;
- dflt->refcount++;
- *vc->vc_uni_pagedir_loc = (unsigned long)dflt;
- if (p && --p->refcount) {
- con_release_unimap(p);
- kfree(p);
- }
- return 0;
- }
-
- /* The default font is always 256 characters */
-
- err = con_clear_unimap(vc, NULL);
- if (err) return err;
-
- p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
- q = dfont_unitable;
-
- for (i = 0; i < 256; i++)
- for (j = dfont_unicount[i]; j; j--) {
- err1 = con_insert_unipair(p, *(q++), i);
- if (err1)
- err = err1;
- }
-
- if (con_unify_unimap(vc, p)) {
- dflt = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
- return err;
- }
-
- for (i = 0; i <= 3; i++)
- set_inverse_transl(vc, p, i); /* Update all inverse translations */
- set_inverse_trans_unicode(vc, p);
- dflt = p;
- return err;
-}
-EXPORT_SYMBOL(con_set_default_unimap);
-
-int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc)
-{
- struct uni_pagedir *q;
-
- if (!*src_vc->vc_uni_pagedir_loc)
- return -EINVAL;
- if (*dst_vc->vc_uni_pagedir_loc == *src_vc->vc_uni_pagedir_loc)
- return 0;
- con_free_unimap(dst_vc);
- q = (struct uni_pagedir *)*src_vc->vc_uni_pagedir_loc;
- q->refcount++;
- *dst_vc->vc_uni_pagedir_loc = (long)q;
- return 0;
-}
-
-int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list)
-{
- int i, j, k, ect;
- u16 **p1, *p2;
- struct uni_pagedir *p;
-
- ect = 0;
- if (*vc->vc_uni_pagedir_loc) {
- p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
- for (i = 0; i < 32; i++)
- if ((p1 = p->uni_pgdir[i]))
- for (j = 0; j < 32; j++)
- if ((p2 = *(p1++)))
- for (k = 0; k < 64; k++) {
- if (*p2 < MAX_GLYPH && ect++ < ct) {
- __put_user((u_short)((i<<11)+(j<<6)+k),
- &list->unicode);
- __put_user((u_short) *p2,
- &list->fontpos);
- list++;
- }
- p2++;
- }
- }
- __put_user(ect, uct);
- return ((ect <= ct) ? 0 : -ENOMEM);
-}
-
-void con_protect_unimap(struct vc_data *vc, int rdonly)
-{
- struct uni_pagedir *p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
-
- if (p)
- p->readonly = rdonly;
-}
-
-/*
- * Always use USER_MAP. These functions are used by the keyboard,
- * which shouldn't be affected by G0/G1 switching, etc.
- * If the user map still contains default values, i.e. the
- * direct-to-font mapping, then assume user is using Latin1.
- */
-/* may be called during an interrupt */
-u32 conv_8bit_to_uni(unsigned char c)
-{
- unsigned short uni = translations[USER_MAP][c];
- return uni == (0xf000 | c) ? c : uni;
-}
-
-int conv_uni_to_8bit(u32 uni)
-{
- int c;
- for (c = 0; c < 0x100; c++)
- if (translations[USER_MAP][c] == uni ||
- (translations[USER_MAP][c] == (c | 0xf000) && uni == c))
- return c;
- return -1;
-}
-
-int
-conv_uni_to_pc(struct vc_data *conp, long ucs)
-{
- int h;
- u16 **p1, *p2;
- struct uni_pagedir *p;
-
- /* Only 16-bit codes supported at this time */
- if (ucs > 0xffff)
- return -4; /* Not found */
- else if (ucs < 0x20)
- return -1; /* Not a printable character */
- else if (ucs == 0xfeff || (ucs >= 0x200b && ucs <= 0x200f))
- return -2; /* Zero-width space */
- /*
- * UNI_DIRECT_BASE indicates the start of the region in the User Zone
- * which always has a 1:1 mapping to the currently loaded font. The
- * UNI_DIRECT_MASK indicates the bit span of the region.
- */
- else if ((ucs & ~UNI_DIRECT_MASK) == UNI_DIRECT_BASE)
- return ucs & UNI_DIRECT_MASK;
-
- if (!*conp->vc_uni_pagedir_loc)
- return -3;
-
- p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc;
- if ((p1 = p->uni_pgdir[ucs >> 11]) &&
- (p2 = p1[(ucs >> 6) & 0x1f]) &&
- (h = p2[ucs & 0x3f]) < MAX_GLYPH)
- return h;
-
- return -4; /* not found */
-}
-
-/*
- * This is called at sys_setup time, after memory and the console are
- * initialized. It must be possible to call kmalloc(..., GFP_KERNEL)
- * from this function, hence the call from sys_setup.
- */
-void __init
-console_map_init(void)
-{
- int i;
-
- for (i = 0; i < MAX_NR_CONSOLES; i++)
- if (vc_cons_allocated(i) && !*vc_cons[i].d->vc_uni_pagedir_loc)
- con_set_default_unimap(vc_cons[i].d);
-}
-
-EXPORT_SYMBOL(con_copy_unimap);
+++ /dev/null
-#
-# Unicode table for IBM Codepage 437. Note that there are many more
-# substitutions that could be conceived (for example, thick-line
-# graphs probably should be replaced with double-line ones, accented
-# Latin characters should replaced with their nonaccented versions,
-# and some upper case Greek characters could be replaced by Latin), however,
-# I have limited myself to the Unicodes used by the kernel ISO 8859-1,
-# DEC VT, and IBM CP 437 tables.
-#
-# --------------------------------
-#
-# Basic IBM dingbats, some of which will never have a purpose clear
-# to mankind
-#
-0x00 U+0000
-0x01 U+263a
-0x02 U+263b
-0x03 U+2665
-0x04 U+2666 U+25c6
-0x05 U+2663
-0x06 U+2660
-0x07 U+2022
-0x08 U+25d8
-0x09 U+25cb
-0x0a U+25d9
-0x0b U+2642
-0x0c U+2640
-0x0d U+266a
-0x0e U+266b
-0x0f U+263c U+00a4
-0x10 U+25b6 U+25ba
-0x11 U+25c0 U+25c4
-0x12 U+2195
-0x13 U+203c
-0x14 U+00b6
-0x15 U+00a7
-0x16 U+25ac
-0x17 U+21a8
-0x18 U+2191
-0x19 U+2193
-0x1a U+2192
-0x1b U+2190
-0x1c U+221f
-0x1d U+2194
-0x1e U+25b2
-0x1f U+25bc
-#
-# The ASCII range is identity-mapped, but some of the characters also
-# have to act as substitutes, especially the upper-case characters.
-#
-0x20 U+0020
-0x21 U+0021
-0x22 U+0022 U+00a8
-0x23 U+0023
-0x24 U+0024
-0x25 U+0025
-0x26 U+0026
-0x27 U+0027 U+00b4
-0x28 U+0028
-0x29 U+0029
-0x2a U+002a
-0x2b U+002b
-0x2c U+002c U+00b8
-0x2d U+002d U+00ad
-0x2e U+002e
-0x2f U+002f
-0x30 U+0030
-0x31 U+0031
-0x32 U+0032
-0x33 U+0033
-0x34 U+0034
-0x35 U+0035
-0x36 U+0036
-0x37 U+0037
-0x38 U+0038
-0x39 U+0039
-0x3a U+003a
-0x3b U+003b
-0x3c U+003c
-0x3d U+003d
-0x3e U+003e
-0x3f U+003f
-0x40 U+0040
-0x41 U+0041 U+00c0 U+00c1 U+00c2 U+00c3
-0x42 U+0042
-0x43 U+0043 U+00a9
-0x44 U+0044 U+00d0
-0x45 U+0045 U+00c8 U+00ca U+00cb
-0x46 U+0046
-0x47 U+0047
-0x48 U+0048
-0x49 U+0049 U+00cc U+00cd U+00ce U+00cf
-0x4a U+004a
-0x4b U+004b U+212a
-0x4c U+004c
-0x4d U+004d
-0x4e U+004e
-0x4f U+004f U+00d2 U+00d3 U+00d4 U+00d5
-0x50 U+0050
-0x51 U+0051
-0x52 U+0052 U+00ae
-0x53 U+0053
-0x54 U+0054
-0x55 U+0055 U+00d9 U+00da U+00db
-0x56 U+0056
-0x57 U+0057
-0x58 U+0058
-0x59 U+0059 U+00dd
-0x5a U+005a
-0x5b U+005b
-0x5c U+005c
-0x5d U+005d
-0x5e U+005e
-0x5f U+005f U+23bd U+f804
-0x60 U+0060
-0x61 U+0061 U+00e3
-0x62 U+0062
-0x63 U+0063
-0x64 U+0064
-0x65 U+0065
-0x66 U+0066
-0x67 U+0067
-0x68 U+0068
-0x69 U+0069
-0x6a U+006a
-0x6b U+006b
-0x6c U+006c
-0x6d U+006d
-0x6e U+006e
-0x6f U+006f U+00f5
-0x70 U+0070
-0x71 U+0071
-0x72 U+0072
-0x73 U+0073
-0x74 U+0074
-0x75 U+0075
-0x76 U+0076
-0x77 U+0077
-0x78 U+0078 U+00d7
-0x79 U+0079 U+00fd
-0x7a U+007a
-0x7b U+007b
-0x7c U+007c U+00a6
-0x7d U+007d
-0x7e U+007e
-#
-# Okay, what on Earth is this one supposed to be used for?
-#
-0x7f U+2302
-#
-# Non-English characters, mostly lower case letters...
-#
-0x80 U+00c7
-0x81 U+00fc
-0x82 U+00e9
-0x83 U+00e2
-0x84 U+00e4
-0x85 U+00e0
-0x86 U+00e5
-0x87 U+00e7
-0x88 U+00ea
-0x89 U+00eb
-0x8a U+00e8
-0x8b U+00ef
-0x8c U+00ee
-0x8d U+00ec
-0x8e U+00c4
-0x8f U+00c5 U+212b
-0x90 U+00c9
-0x91 U+00e6
-0x92 U+00c6
-0x93 U+00f4
-0x94 U+00f6
-0x95 U+00f2
-0x96 U+00fb
-0x97 U+00f9
-0x98 U+00ff
-0x99 U+00d6
-0x9a U+00dc
-0x9b U+00a2
-0x9c U+00a3
-0x9d U+00a5
-0x9e U+20a7
-0x9f U+0192
-0xa0 U+00e1
-0xa1 U+00ed
-0xa2 U+00f3
-0xa3 U+00fa
-0xa4 U+00f1
-0xa5 U+00d1
-0xa6 U+00aa
-0xa7 U+00ba
-0xa8 U+00bf
-0xa9 U+2310
-0xaa U+00ac
-0xab U+00bd
-0xac U+00bc
-0xad U+00a1
-0xae U+00ab
-0xaf U+00bb
-#
-# Block graphics
-#
-0xb0 U+2591
-0xb1 U+2592
-0xb2 U+2593
-0xb3 U+2502
-0xb4 U+2524
-0xb5 U+2561
-0xb6 U+2562
-0xb7 U+2556
-0xb8 U+2555
-0xb9 U+2563
-0xba U+2551
-0xbb U+2557
-0xbc U+255d
-0xbd U+255c
-0xbe U+255b
-0xbf U+2510
-0xc0 U+2514
-0xc1 U+2534
-0xc2 U+252c
-0xc3 U+251c
-0xc4 U+2500
-0xc5 U+253c
-0xc6 U+255e
-0xc7 U+255f
-0xc8 U+255a
-0xc9 U+2554
-0xca U+2569
-0xcb U+2566
-0xcc U+2560
-0xcd U+2550
-0xce U+256c
-0xcf U+2567
-0xd0 U+2568
-0xd1 U+2564
-0xd2 U+2565
-0xd3 U+2559
-0xd4 U+2558
-0xd5 U+2552
-0xd6 U+2553
-0xd7 U+256b
-0xd8 U+256a
-0xd9 U+2518
-0xda U+250c
-0xdb U+2588
-0xdc U+2584
-0xdd U+258c
-0xde U+2590
-0xdf U+2580
-#
-# Greek letters and mathematical symbols
-#
-0xe0 U+03b1
-0xe1 U+03b2 U+00df
-0xe2 U+0393
-0xe3 U+03c0
-0xe4 U+03a3
-0xe5 U+03c3
-0xe6 U+00b5 U+03bc
-0xe7 U+03c4
-0xe8 U+03a6 U+00d8
-0xe9 U+0398
-0xea U+03a9 U+2126
-0xeb U+03b4 U+00f0
-0xec U+221e
-0xed U+03c6 U+00f8
-0xee U+03b5 U+2208
-0xef U+2229
-0xf0 U+2261
-0xf1 U+00b1
-0xf2 U+2265
-0xf3 U+2264
-0xf4 U+2320
-0xf5 U+2321
-0xf6 U+00f7
-0xf7 U+2248
-0xf8 U+00b0
-0xf9 U+2219
-0xfa U+00b7
-0xfb U+221a
-0xfc U+207f
-0xfd U+00b2
-#
-# Square bullet, non-spacing blank
-# Mapping U+fffd to the square bullet means it is the substitution
-# character
-#
-0xfe U+25a0 U+fffd
-0xff U+00a0
+++ /dev/null
-/* Do not edit this file! It was automatically generated by */
-/* loadkeys --mktable defkeymap.map > defkeymap.c */
-
-#include <linux/types.h>
-#include <linux/keyboard.h>
-#include <linux/kd.h>
-
-u_short plain_map[NR_KEYS] = {
- 0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036,
- 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009,
- 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69,
- 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73,
- 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b,
- 0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76,
- 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c,
- 0xf703, 0xf020, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104,
- 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307,
- 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
- 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a,
- 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
- 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
- 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short shift_map[NR_KEYS] = {
- 0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e,
- 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009,
- 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49,
- 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53,
- 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a,
- 0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56,
- 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c,
- 0xf703, 0xf020, 0xf207, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e,
- 0xf10f, 0xf110, 0xf111, 0xf112, 0xf113, 0xf213, 0xf203, 0xf307,
- 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
- 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf10a,
- 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
- 0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116,
- 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short altgr_map[NR_KEYS] = {
- 0xf200, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200,
- 0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200,
- 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69,
- 0xfb6f, 0xfb70, 0xf200, 0xf07e, 0xf201, 0xf702, 0xf914, 0xfb73,
- 0xf917, 0xf919, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf200,
- 0xf200, 0xf200, 0xf700, 0xf200, 0xfb7a, 0xfb78, 0xf916, 0xfb76,
- 0xf915, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c,
- 0xf703, 0xf200, 0xf207, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510,
- 0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf202, 0xf911,
- 0xf912, 0xf913, 0xf30b, 0xf90e, 0xf90f, 0xf910, 0xf30a, 0xf90b,
- 0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516,
- 0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
- 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
- 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short ctrl_map[NR_KEYS] = {
- 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e,
- 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200,
- 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009,
- 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf201, 0xf702, 0xf001, 0xf013,
- 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200,
- 0xf007, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016,
- 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c,
- 0xf703, 0xf000, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104,
- 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf204, 0xf307,
- 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
- 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf10a,
- 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
- 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
- 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short shift_ctrl_map[NR_KEYS] = {
- 0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200,
- 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009,
- 0xf00f, 0xf010, 0xf200, 0xf200, 0xf201, 0xf702, 0xf001, 0xf013,
- 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200,
- 0xf200, 0xf200, 0xf700, 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016,
- 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c,
- 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf208, 0xf200, 0xf307,
- 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
- 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf200,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
- 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
- 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short alt_map[NR_KEYS] = {
- 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836,
- 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809,
- 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869,
- 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873,
- 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b,
- 0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876,
- 0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c,
- 0xf703, 0xf820, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504,
- 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf907,
- 0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901,
- 0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a,
- 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
- 0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
- 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short ctrl_alt_map[NR_KEYS] = {
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809,
- 0xf80f, 0xf810, 0xf200, 0xf200, 0xf201, 0xf702, 0xf801, 0xf813,
- 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200,
- 0xf200, 0xf200, 0xf700, 0xf200, 0xf81a, 0xf818, 0xf803, 0xf816,
- 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c,
- 0xf703, 0xf200, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504,
- 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf200, 0xf307,
- 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
- 0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf200, 0xf50a,
- 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
- 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c,
- 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-ushort *key_maps[MAX_NR_KEYMAPS] = {
- plain_map, shift_map, altgr_map, NULL,
- ctrl_map, shift_ctrl_map, NULL, NULL,
- alt_map, NULL, NULL, NULL,
- ctrl_alt_map, NULL
-};
-
-unsigned int keymap_count = 7;
-
-/*
- * Philosophy: most people do not define more strings, but they who do
- * often want quite a lot of string space. So, we statically allocate
- * the default and allocate dynamically in chunks of 512 bytes.
- */
-
-char func_buf[] = {
- '\033', '[', '[', 'A', 0,
- '\033', '[', '[', 'B', 0,
- '\033', '[', '[', 'C', 0,
- '\033', '[', '[', 'D', 0,
- '\033', '[', '[', 'E', 0,
- '\033', '[', '1', '7', '~', 0,
- '\033', '[', '1', '8', '~', 0,
- '\033', '[', '1', '9', '~', 0,
- '\033', '[', '2', '0', '~', 0,
- '\033', '[', '2', '1', '~', 0,
- '\033', '[', '2', '3', '~', 0,
- '\033', '[', '2', '4', '~', 0,
- '\033', '[', '2', '5', '~', 0,
- '\033', '[', '2', '6', '~', 0,
- '\033', '[', '2', '8', '~', 0,
- '\033', '[', '2', '9', '~', 0,
- '\033', '[', '3', '1', '~', 0,
- '\033', '[', '3', '2', '~', 0,
- '\033', '[', '3', '3', '~', 0,
- '\033', '[', '3', '4', '~', 0,
- '\033', '[', '1', '~', 0,
- '\033', '[', '2', '~', 0,
- '\033', '[', '3', '~', 0,
- '\033', '[', '4', '~', 0,
- '\033', '[', '5', '~', 0,
- '\033', '[', '6', '~', 0,
- '\033', '[', 'M', 0,
- '\033', '[', 'P', 0,
-};
-
-char *funcbufptr = func_buf;
-int funcbufsize = sizeof(func_buf);
-int funcbufleft = 0; /* space left */
-
-char *func_table[MAX_NR_FUNC] = {
- func_buf + 0,
- func_buf + 5,
- func_buf + 10,
- func_buf + 15,
- func_buf + 20,
- func_buf + 25,
- func_buf + 31,
- func_buf + 37,
- func_buf + 43,
- func_buf + 49,
- func_buf + 55,
- func_buf + 61,
- func_buf + 67,
- func_buf + 73,
- func_buf + 79,
- func_buf + 85,
- func_buf + 91,
- func_buf + 97,
- func_buf + 103,
- func_buf + 109,
- func_buf + 115,
- func_buf + 120,
- func_buf + 125,
- func_buf + 130,
- func_buf + 135,
- func_buf + 140,
- func_buf + 145,
- NULL,
- NULL,
- func_buf + 149,
- NULL,
-};
-
-struct kbdiacruc accent_table[MAX_DIACR] = {
- {'`', 'A', 0300}, {'`', 'a', 0340},
- {'\'', 'A', 0301}, {'\'', 'a', 0341},
- {'^', 'A', 0302}, {'^', 'a', 0342},
- {'~', 'A', 0303}, {'~', 'a', 0343},
- {'"', 'A', 0304}, {'"', 'a', 0344},
- {'O', 'A', 0305}, {'o', 'a', 0345},
- {'0', 'A', 0305}, {'0', 'a', 0345},
- {'A', 'A', 0305}, {'a', 'a', 0345},
- {'A', 'E', 0306}, {'a', 'e', 0346},
- {',', 'C', 0307}, {',', 'c', 0347},
- {'`', 'E', 0310}, {'`', 'e', 0350},
- {'\'', 'E', 0311}, {'\'', 'e', 0351},
- {'^', 'E', 0312}, {'^', 'e', 0352},
- {'"', 'E', 0313}, {'"', 'e', 0353},
- {'`', 'I', 0314}, {'`', 'i', 0354},
- {'\'', 'I', 0315}, {'\'', 'i', 0355},
- {'^', 'I', 0316}, {'^', 'i', 0356},
- {'"', 'I', 0317}, {'"', 'i', 0357},
- {'-', 'D', 0320}, {'-', 'd', 0360},
- {'~', 'N', 0321}, {'~', 'n', 0361},
- {'`', 'O', 0322}, {'`', 'o', 0362},
- {'\'', 'O', 0323}, {'\'', 'o', 0363},
- {'^', 'O', 0324}, {'^', 'o', 0364},
- {'~', 'O', 0325}, {'~', 'o', 0365},
- {'"', 'O', 0326}, {'"', 'o', 0366},
- {'/', 'O', 0330}, {'/', 'o', 0370},
- {'`', 'U', 0331}, {'`', 'u', 0371},
- {'\'', 'U', 0332}, {'\'', 'u', 0372},
- {'^', 'U', 0333}, {'^', 'u', 0373},
- {'"', 'U', 0334}, {'"', 'u', 0374},
- {'\'', 'Y', 0335}, {'\'', 'y', 0375},
- {'T', 'H', 0336}, {'t', 'h', 0376},
- {'s', 's', 0337}, {'"', 'y', 0377},
- {'s', 'z', 0337}, {'i', 'j', 0377},
-};
-
-unsigned int accent_table_size = 68;
+++ /dev/null
-# Default kernel keymap. This uses 7 modifier combinations.
-keymaps 0-2,4-5,8,12
-# Change the above line into
-# keymaps 0-2,4-6,8,12
-# in case you want the entries
-# altgr control keycode 83 = Boot
-# altgr control keycode 111 = Boot
-# below.
-#
-# In fact AltGr is used very little, and one more keymap can
-# be saved by mapping AltGr to Alt (and adapting a few entries):
-# keycode 100 = Alt
-#
-keycode 1 = Escape Escape
- alt keycode 1 = Meta_Escape
-keycode 2 = one exclam
- alt keycode 2 = Meta_one
-keycode 3 = two at at
- control keycode 3 = nul
- shift control keycode 3 = nul
- alt keycode 3 = Meta_two
-keycode 4 = three numbersign
- control keycode 4 = Escape
- alt keycode 4 = Meta_three
-keycode 5 = four dollar dollar
- control keycode 5 = Control_backslash
- alt keycode 5 = Meta_four
-keycode 6 = five percent
- control keycode 6 = Control_bracketright
- alt keycode 6 = Meta_five
-keycode 7 = six asciicircum
- control keycode 7 = Control_asciicircum
- alt keycode 7 = Meta_six
-keycode 8 = seven ampersand braceleft
- control keycode 8 = Control_underscore
- alt keycode 8 = Meta_seven
-keycode 9 = eight asterisk bracketleft
- control keycode 9 = Delete
- alt keycode 9 = Meta_eight
-keycode 10 = nine parenleft bracketright
- alt keycode 10 = Meta_nine
-keycode 11 = zero parenright braceright
- alt keycode 11 = Meta_zero
-keycode 12 = minus underscore backslash
- control keycode 12 = Control_underscore
- shift control keycode 12 = Control_underscore
- alt keycode 12 = Meta_minus
-keycode 13 = equal plus
- alt keycode 13 = Meta_equal
-keycode 14 = Delete Delete
- control keycode 14 = BackSpace
- alt keycode 14 = Meta_Delete
-keycode 15 = Tab Tab
- alt keycode 15 = Meta_Tab
-keycode 16 = q
-keycode 17 = w
-keycode 18 = e
- altgr keycode 18 = Hex_E
-keycode 19 = r
-keycode 20 = t
-keycode 21 = y
-keycode 22 = u
-keycode 23 = i
-keycode 24 = o
-keycode 25 = p
-keycode 26 = bracketleft braceleft
- control keycode 26 = Escape
- alt keycode 26 = Meta_bracketleft
-keycode 27 = bracketright braceright asciitilde
- control keycode 27 = Control_bracketright
- alt keycode 27 = Meta_bracketright
-keycode 28 = Return
- alt keycode 28 = Meta_Control_m
-keycode 29 = Control
-keycode 30 = a
- altgr keycode 30 = Hex_A
-keycode 31 = s
-keycode 32 = d
- altgr keycode 32 = Hex_D
-keycode 33 = f
- altgr keycode 33 = Hex_F
-keycode 34 = g
-keycode 35 = h
-keycode 36 = j
-keycode 37 = k
-keycode 38 = l
-keycode 39 = semicolon colon
- alt keycode 39 = Meta_semicolon
-keycode 40 = apostrophe quotedbl
- control keycode 40 = Control_g
- alt keycode 40 = Meta_apostrophe
-keycode 41 = grave asciitilde
- control keycode 41 = nul
- alt keycode 41 = Meta_grave
-keycode 42 = Shift
-keycode 43 = backslash bar
- control keycode 43 = Control_backslash
- alt keycode 43 = Meta_backslash
-keycode 44 = z
-keycode 45 = x
-keycode 46 = c
- altgr keycode 46 = Hex_C
-keycode 47 = v
-keycode 48 = b
- altgr keycode 48 = Hex_B
-keycode 49 = n
-keycode 50 = m
-keycode 51 = comma less
- alt keycode 51 = Meta_comma
-keycode 52 = period greater
- control keycode 52 = Compose
- alt keycode 52 = Meta_period
-keycode 53 = slash question
- control keycode 53 = Delete
- alt keycode 53 = Meta_slash
-keycode 54 = Shift
-keycode 55 = KP_Multiply
-keycode 56 = Alt
-keycode 57 = space space
- control keycode 57 = nul
- alt keycode 57 = Meta_space
-keycode 58 = Caps_Lock
-keycode 59 = F1 F11 Console_13
- control keycode 59 = F1
- alt keycode 59 = Console_1
- control alt keycode 59 = Console_1
-keycode 60 = F2 F12 Console_14
- control keycode 60 = F2
- alt keycode 60 = Console_2
- control alt keycode 60 = Console_2
-keycode 61 = F3 F13 Console_15
- control keycode 61 = F3
- alt keycode 61 = Console_3
- control alt keycode 61 = Console_3
-keycode 62 = F4 F14 Console_16
- control keycode 62 = F4
- alt keycode 62 = Console_4
- control alt keycode 62 = Console_4
-keycode 63 = F5 F15 Console_17
- control keycode 63 = F5
- alt keycode 63 = Console_5
- control alt keycode 63 = Console_5
-keycode 64 = F6 F16 Console_18
- control keycode 64 = F6
- alt keycode 64 = Console_6
- control alt keycode 64 = Console_6
-keycode 65 = F7 F17 Console_19
- control keycode 65 = F7
- alt keycode 65 = Console_7
- control alt keycode 65 = Console_7
-keycode 66 = F8 F18 Console_20
- control keycode 66 = F8
- alt keycode 66 = Console_8
- control alt keycode 66 = Console_8
-keycode 67 = F9 F19 Console_21
- control keycode 67 = F9
- alt keycode 67 = Console_9
- control alt keycode 67 = Console_9
-keycode 68 = F10 F20 Console_22
- control keycode 68 = F10
- alt keycode 68 = Console_10
- control alt keycode 68 = Console_10
-keycode 69 = Num_Lock
- shift keycode 69 = Bare_Num_Lock
-keycode 70 = Scroll_Lock Show_Memory Show_Registers
- control keycode 70 = Show_State
- alt keycode 70 = Scroll_Lock
-keycode 71 = KP_7
- alt keycode 71 = Ascii_7
- altgr keycode 71 = Hex_7
-keycode 72 = KP_8
- alt keycode 72 = Ascii_8
- altgr keycode 72 = Hex_8
-keycode 73 = KP_9
- alt keycode 73 = Ascii_9
- altgr keycode 73 = Hex_9
-keycode 74 = KP_Subtract
-keycode 75 = KP_4
- alt keycode 75 = Ascii_4
- altgr keycode 75 = Hex_4
-keycode 76 = KP_5
- alt keycode 76 = Ascii_5
- altgr keycode 76 = Hex_5
-keycode 77 = KP_6
- alt keycode 77 = Ascii_6
- altgr keycode 77 = Hex_6
-keycode 78 = KP_Add
-keycode 79 = KP_1
- alt keycode 79 = Ascii_1
- altgr keycode 79 = Hex_1
-keycode 80 = KP_2
- alt keycode 80 = Ascii_2
- altgr keycode 80 = Hex_2
-keycode 81 = KP_3
- alt keycode 81 = Ascii_3
- altgr keycode 81 = Hex_3
-keycode 82 = KP_0
- alt keycode 82 = Ascii_0
- altgr keycode 82 = Hex_0
-keycode 83 = KP_Period
-# altgr control keycode 83 = Boot
- control alt keycode 83 = Boot
-keycode 84 = Last_Console
-keycode 85 =
-keycode 86 = less greater bar
- alt keycode 86 = Meta_less
-keycode 87 = F11 F11 Console_23
- control keycode 87 = F11
- alt keycode 87 = Console_11
- control alt keycode 87 = Console_11
-keycode 88 = F12 F12 Console_24
- control keycode 88 = F12
- alt keycode 88 = Console_12
- control alt keycode 88 = Console_12
-keycode 89 =
-keycode 90 =
-keycode 91 =
-keycode 92 =
-keycode 93 =
-keycode 94 =
-keycode 95 =
-keycode 96 = KP_Enter
-keycode 97 = Control
-keycode 98 = KP_Divide
-keycode 99 = Control_backslash
- control keycode 99 = Control_backslash
- alt keycode 99 = Control_backslash
-keycode 100 = AltGr
-keycode 101 = Break
-keycode 102 = Find
-keycode 103 = Up
-keycode 104 = Prior
- shift keycode 104 = Scroll_Backward
-keycode 105 = Left
- alt keycode 105 = Decr_Console
-keycode 106 = Right
- alt keycode 106 = Incr_Console
-keycode 107 = Select
-keycode 108 = Down
-keycode 109 = Next
- shift keycode 109 = Scroll_Forward
-keycode 110 = Insert
-keycode 111 = Remove
-# altgr control keycode 111 = Boot
- control alt keycode 111 = Boot
-keycode 112 = Macro
-keycode 113 = F13
-keycode 114 = F14
-keycode 115 = Help
-keycode 116 = Do
-keycode 117 = F17
-keycode 118 = KP_MinPlus
-keycode 119 = Pause
-keycode 120 =
-keycode 121 =
-keycode 122 =
-keycode 123 =
-keycode 124 =
-keycode 125 =
-keycode 126 =
-keycode 127 =
-string F1 = "\033[[A"
-string F2 = "\033[[B"
-string F3 = "\033[[C"
-string F4 = "\033[[D"
-string F5 = "\033[[E"
-string F6 = "\033[17~"
-string F7 = "\033[18~"
-string F8 = "\033[19~"
-string F9 = "\033[20~"
-string F10 = "\033[21~"
-string F11 = "\033[23~"
-string F12 = "\033[24~"
-string F13 = "\033[25~"
-string F14 = "\033[26~"
-string F15 = "\033[28~"
-string F16 = "\033[29~"
-string F17 = "\033[31~"
-string F18 = "\033[32~"
-string F19 = "\033[33~"
-string F20 = "\033[34~"
-string Find = "\033[1~"
-string Insert = "\033[2~"
-string Remove = "\033[3~"
-string Select = "\033[4~"
-string Prior = "\033[5~"
-string Next = "\033[6~"
-string Macro = "\033[M"
-string Pause = "\033[P"
-compose '`' 'A' to 'À'
-compose '`' 'a' to 'à'
-compose '\'' 'A' to 'Á'
-compose '\'' 'a' to 'á'
-compose '^' 'A' to 'Â'
-compose '^' 'a' to 'â'
-compose '~' 'A' to 'Ã'
-compose '~' 'a' to 'ã'
-compose '"' 'A' to 'Ä'
-compose '"' 'a' to 'ä'
-compose 'O' 'A' to 'Å'
-compose 'o' 'a' to 'å'
-compose '0' 'A' to 'Å'
-compose '0' 'a' to 'å'
-compose 'A' 'A' to 'Å'
-compose 'a' 'a' to 'å'
-compose 'A' 'E' to 'Æ'
-compose 'a' 'e' to 'æ'
-compose ',' 'C' to 'Ç'
-compose ',' 'c' to 'ç'
-compose '`' 'E' to 'È'
-compose '`' 'e' to 'è'
-compose '\'' 'E' to 'É'
-compose '\'' 'e' to 'é'
-compose '^' 'E' to 'Ê'
-compose '^' 'e' to 'ê'
-compose '"' 'E' to 'Ë'
-compose '"' 'e' to 'ë'
-compose '`' 'I' to 'Ì'
-compose '`' 'i' to 'ì'
-compose '\'' 'I' to 'Í'
-compose '\'' 'i' to 'í'
-compose '^' 'I' to 'Î'
-compose '^' 'i' to 'î'
-compose '"' 'I' to 'Ï'
-compose '"' 'i' to 'ï'
-compose '-' 'D' to 'Ð'
-compose '-' 'd' to 'ð'
-compose '~' 'N' to 'Ñ'
-compose '~' 'n' to 'ñ'
-compose '`' 'O' to 'Ò'
-compose '`' 'o' to 'ò'
-compose '\'' 'O' to 'Ó'
-compose '\'' 'o' to 'ó'
-compose '^' 'O' to 'Ô'
-compose '^' 'o' to 'ô'
-compose '~' 'O' to 'Õ'
-compose '~' 'o' to 'õ'
-compose '"' 'O' to 'Ö'
-compose '"' 'o' to 'ö'
-compose '/' 'O' to 'Ø'
-compose '/' 'o' to 'ø'
-compose '`' 'U' to 'Ù'
-compose '`' 'u' to 'ù'
-compose '\'' 'U' to 'Ú'
-compose '\'' 'u' to 'ú'
-compose '^' 'U' to 'Û'
-compose '^' 'u' to 'û'
-compose '"' 'U' to 'Ü'
-compose '"' 'u' to 'ü'
-compose '\'' 'Y' to 'Ý'
-compose '\'' 'y' to 'ý'
-compose 'T' 'H' to 'Þ'
-compose 't' 'h' to 'þ'
-compose 's' 's' to 'ß'
-compose '"' 'y' to 'ÿ'
-compose 's' 'z' to 'ß'
-compose 'i' 'j' to 'ÿ'
+++ /dev/null
-/*
- * linux/drivers/char/keyboard.c
- *
- * Written for linux by Johan Myreen as a translation from
- * the assembly version by Linus (with diacriticals added)
- *
- * Some additional features added by Christoph Niemann (ChN), March 1993
- *
- * Loadable keymaps by Risto Kankkunen, May 1993
- *
- * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993
- * Added decr/incr_console, dynamic keymaps, Unicode support,
- * dynamic function/string keys, led setting, Sept 1994
- * `Sticky' modifier keys, 951006.
- *
- * 11-11-96: SAK should now work in the raw mode (Martin Mares)
- *
- * Modified to provide 'generic' keyboard support by Hamish Macdonald
- * Merge with the m68k keyboard driver and split-off of the PC low-level
- * parts by Geert Uytterhoeven, May 1997
- *
- * 27-05-97: Added support for the Magic SysRq Key (Martin Mares)
- * 30-07-98: Dead keys redone, aeb@cwi.nl.
- * 21-08-02: Converted to input API, major cleanup. (Vojtech Pavlik)
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/consolemap.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/irq.h>
-
-#include <linux/kbd_kern.h>
-#include <linux/kbd_diacr.h>
-#include <linux/vt_kern.h>
-#include <linux/input.h>
-#include <linux/reboot.h>
-#include <linux/notifier.h>
-#include <linux/jiffies.h>
-
-extern void ctrl_alt_del(void);
-
-/*
- * Exported functions/variables
- */
-
-#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
-
-/*
- * Some laptops take the 789uiojklm,. keys as number pad when NumLock is on.
- * This seems a good reason to start with NumLock off. On HIL keyboards
- * of PARISC machines however there is no NumLock key and everyone expects the keypad
- * to be used for numbers.
- */
-
-#if defined(CONFIG_PARISC) && (defined(CONFIG_KEYBOARD_HIL) || defined(CONFIG_KEYBOARD_HIL_OLD))
-#define KBD_DEFLEDS (1 << VC_NUMLOCK)
-#else
-#define KBD_DEFLEDS 0
-#endif
-
-#define KBD_DEFLOCK 0
-
-void compute_shiftstate(void);
-
-/*
- * Handler Tables.
- */
-
-#define K_HANDLERS\
- k_self, k_fn, k_spec, k_pad,\
- k_dead, k_cons, k_cur, k_shift,\
- k_meta, k_ascii, k_lock, k_lowercase,\
- k_slock, k_dead2, k_brl, k_ignore
-
-typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value,
- char up_flag);
-static k_handler_fn K_HANDLERS;
-static k_handler_fn *k_handler[16] = { K_HANDLERS };
-
-#define FN_HANDLERS\
- fn_null, fn_enter, fn_show_ptregs, fn_show_mem,\
- fn_show_state, fn_send_intr, fn_lastcons, fn_caps_toggle,\
- fn_num, fn_hold, fn_scroll_forw, fn_scroll_back,\
- fn_boot_it, fn_caps_on, fn_compose, fn_SAK,\
- fn_dec_console, fn_inc_console, fn_spawn_con, fn_bare_num
-
-typedef void (fn_handler_fn)(struct vc_data *vc);
-static fn_handler_fn FN_HANDLERS;
-static fn_handler_fn *fn_handler[] = { FN_HANDLERS };
-
-/*
- * Variables exported for vt_ioctl.c
- */
-
-/* maximum values each key_handler can handle */
-const int max_vals[] = {
- 255, ARRAY_SIZE(func_table) - 1, ARRAY_SIZE(fn_handler) - 1, NR_PAD - 1,
- NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1,
- 255, NR_LOCK - 1, 255, NR_BRL - 1
-};
-
-const int NR_TYPES = ARRAY_SIZE(max_vals);
-
-struct kbd_struct kbd_table[MAX_NR_CONSOLES];
-EXPORT_SYMBOL_GPL(kbd_table);
-static struct kbd_struct *kbd = kbd_table;
-
-struct vt_spawn_console vt_spawn_con = {
- .lock = __SPIN_LOCK_UNLOCKED(vt_spawn_con.lock),
- .pid = NULL,
- .sig = 0,
-};
-
-/*
- * Variables exported for vt.c
- */
-
-int shift_state = 0;
-
-/*
- * Internal Data.
- */
-
-static struct input_handler kbd_handler;
-static DEFINE_SPINLOCK(kbd_event_lock);
-static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
-static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
-static bool dead_key_next;
-static int npadch = -1; /* -1 or number assembled on pad */
-static unsigned int diacr;
-static char rep; /* flag telling character repeat */
-
-static unsigned char ledstate = 0xff; /* undefined */
-static unsigned char ledioctl;
-
-static struct ledptr {
- unsigned int *addr;
- unsigned int mask;
- unsigned char valid:1;
-} ledptrs[3];
-
-/*
- * Notifier list for console keyboard events
- */
-static ATOMIC_NOTIFIER_HEAD(keyboard_notifier_list);
-
-int register_keyboard_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_register(&keyboard_notifier_list, nb);
-}
-EXPORT_SYMBOL_GPL(register_keyboard_notifier);
-
-int unregister_keyboard_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_unregister(&keyboard_notifier_list, nb);
-}
-EXPORT_SYMBOL_GPL(unregister_keyboard_notifier);
-
-/*
- * Translation of scancodes to keycodes. We set them on only the first
- * keyboard in the list that accepts the scancode and keycode.
- * Explanation for not choosing the first attached keyboard anymore:
- * USB keyboards for example have two event devices: one for all "normal"
- * keys and one for extra function keys (like "volume up", "make coffee",
- * etc.). So this means that scancodes for the extra function keys won't
- * be valid for the first event device, but will be for the second.
- */
-
-struct getset_keycode_data {
- struct input_keymap_entry ke;
- int error;
-};
-
-static int getkeycode_helper(struct input_handle *handle, void *data)
-{
- struct getset_keycode_data *d = data;
-
- d->error = input_get_keycode(handle->dev, &d->ke);
-
- return d->error == 0; /* stop as soon as we successfully get one */
-}
-
-int getkeycode(unsigned int scancode)
-{
- struct getset_keycode_data d = {
- .ke = {
- .flags = 0,
- .len = sizeof(scancode),
- .keycode = 0,
- },
- .error = -ENODEV,
- };
-
- memcpy(d.ke.scancode, &scancode, sizeof(scancode));
-
- input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper);
-
- return d.error ?: d.ke.keycode;
-}
-
-static int setkeycode_helper(struct input_handle *handle, void *data)
-{
- struct getset_keycode_data *d = data;
-
- d->error = input_set_keycode(handle->dev, &d->ke);
-
- return d->error == 0; /* stop as soon as we successfully set one */
-}
-
-int setkeycode(unsigned int scancode, unsigned int keycode)
-{
- struct getset_keycode_data d = {
- .ke = {
- .flags = 0,
- .len = sizeof(scancode),
- .keycode = keycode,
- },
- .error = -ENODEV,
- };
-
- memcpy(d.ke.scancode, &scancode, sizeof(scancode));
-
- input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper);
-
- return d.error;
-}
-
-/*
- * Making beeps and bells. Note that we prefer beeps to bells, but when
- * shutting the sound off we do both.
- */
-
-static int kd_sound_helper(struct input_handle *handle, void *data)
-{
- unsigned int *hz = data;
- struct input_dev *dev = handle->dev;
-
- if (test_bit(EV_SND, dev->evbit)) {
- if (test_bit(SND_TONE, dev->sndbit)) {
- input_inject_event(handle, EV_SND, SND_TONE, *hz);
- if (*hz)
- return 0;
- }
- if (test_bit(SND_BELL, dev->sndbit))
- input_inject_event(handle, EV_SND, SND_BELL, *hz ? 1 : 0);
- }
-
- return 0;
-}
-
-static void kd_nosound(unsigned long ignored)
-{
- static unsigned int zero;
-
- input_handler_for_each_handle(&kbd_handler, &zero, kd_sound_helper);
-}
-
-static DEFINE_TIMER(kd_mksound_timer, kd_nosound, 0, 0);
-
-void kd_mksound(unsigned int hz, unsigned int ticks)
-{
- del_timer_sync(&kd_mksound_timer);
-
- input_handler_for_each_handle(&kbd_handler, &hz, kd_sound_helper);
-
- if (hz && ticks)
- mod_timer(&kd_mksound_timer, jiffies + ticks);
-}
-EXPORT_SYMBOL(kd_mksound);
-
-/*
- * Setting the keyboard rate.
- */
-
-static int kbd_rate_helper(struct input_handle *handle, void *data)
-{
- struct input_dev *dev = handle->dev;
- struct kbd_repeat *rep = data;
-
- if (test_bit(EV_REP, dev->evbit)) {
-
- if (rep[0].delay > 0)
- input_inject_event(handle,
- EV_REP, REP_DELAY, rep[0].delay);
- if (rep[0].period > 0)
- input_inject_event(handle,
- EV_REP, REP_PERIOD, rep[0].period);
-
- rep[1].delay = dev->rep[REP_DELAY];
- rep[1].period = dev->rep[REP_PERIOD];
- }
-
- return 0;
-}
-
-int kbd_rate(struct kbd_repeat *rep)
-{
- struct kbd_repeat data[2] = { *rep };
-
- input_handler_for_each_handle(&kbd_handler, data, kbd_rate_helper);
- *rep = data[1]; /* Copy currently used settings */
-
- return 0;
-}
-
-/*
- * Helper Functions.
- */
-static void put_queue(struct vc_data *vc, int ch)
-{
- struct tty_struct *tty = vc->port.tty;
-
- if (tty) {
- tty_insert_flip_char(tty, ch, 0);
- con_schedule_flip(tty);
- }
-}
-
-static void puts_queue(struct vc_data *vc, char *cp)
-{
- struct tty_struct *tty = vc->port.tty;
-
- if (!tty)
- return;
-
- while (*cp) {
- tty_insert_flip_char(tty, *cp, 0);
- cp++;
- }
- con_schedule_flip(tty);
-}
-
-static void applkey(struct vc_data *vc, int key, char mode)
-{
- static char buf[] = { 0x1b, 'O', 0x00, 0x00 };
-
- buf[1] = (mode ? 'O' : '[');
- buf[2] = key;
- puts_queue(vc, buf);
-}
-
-/*
- * Many other routines do put_queue, but I think either
- * they produce ASCII, or they produce some user-assigned
- * string, and in both cases we might assume that it is
- * in utf-8 already.
- */
-static void to_utf8(struct vc_data *vc, uint c)
-{
- if (c < 0x80)
- /* 0******* */
- put_queue(vc, c);
- else if (c < 0x800) {
- /* 110***** 10****** */
- put_queue(vc, 0xc0 | (c >> 6));
- put_queue(vc, 0x80 | (c & 0x3f));
- } else if (c < 0x10000) {
- if (c >= 0xD800 && c < 0xE000)
- return;
- if (c == 0xFFFF)
- return;
- /* 1110**** 10****** 10****** */
- put_queue(vc, 0xe0 | (c >> 12));
- put_queue(vc, 0x80 | ((c >> 6) & 0x3f));
- put_queue(vc, 0x80 | (c & 0x3f));
- } else if (c < 0x110000) {
- /* 11110*** 10****** 10****** 10****** */
- put_queue(vc, 0xf0 | (c >> 18));
- put_queue(vc, 0x80 | ((c >> 12) & 0x3f));
- put_queue(vc, 0x80 | ((c >> 6) & 0x3f));
- put_queue(vc, 0x80 | (c & 0x3f));
- }
-}
-
-/*
- * Called after returning from RAW mode or when changing consoles - recompute
- * shift_down[] and shift_state from key_down[] maybe called when keymap is
- * undefined, so that shiftkey release is seen
- */
-void compute_shiftstate(void)
-{
- unsigned int i, j, k, sym, val;
-
- shift_state = 0;
- memset(shift_down, 0, sizeof(shift_down));
-
- for (i = 0; i < ARRAY_SIZE(key_down); i++) {
-
- if (!key_down[i])
- continue;
-
- k = i * BITS_PER_LONG;
-
- for (j = 0; j < BITS_PER_LONG; j++, k++) {
-
- if (!test_bit(k, key_down))
- continue;
-
- sym = U(key_maps[0][k]);
- if (KTYP(sym) != KT_SHIFT && KTYP(sym) != KT_SLOCK)
- continue;
-
- val = KVAL(sym);
- if (val == KVAL(K_CAPSSHIFT))
- val = KVAL(K_SHIFT);
-
- shift_down[val]++;
- shift_state |= (1 << val);
- }
- }
-}
-
-/*
- * We have a combining character DIACR here, followed by the character CH.
- * If the combination occurs in the table, return the corresponding value.
- * Otherwise, if CH is a space or equals DIACR, return DIACR.
- * Otherwise, conclude that DIACR was not combining after all,
- * queue it and return CH.
- */
-static unsigned int handle_diacr(struct vc_data *vc, unsigned int ch)
-{
- unsigned int d = diacr;
- unsigned int i;
-
- diacr = 0;
-
- if ((d & ~0xff) == BRL_UC_ROW) {
- if ((ch & ~0xff) == BRL_UC_ROW)
- return d | ch;
- } else {
- for (i = 0; i < accent_table_size; i++)
- if (accent_table[i].diacr == d && accent_table[i].base == ch)
- return accent_table[i].result;
- }
-
- if (ch == ' ' || ch == (BRL_UC_ROW|0) || ch == d)
- return d;
-
- if (kbd->kbdmode == VC_UNICODE)
- to_utf8(vc, d);
- else {
- int c = conv_uni_to_8bit(d);
- if (c != -1)
- put_queue(vc, c);
- }
-
- return ch;
-}
-
-/*
- * Special function handlers
- */
-static void fn_enter(struct vc_data *vc)
-{
- if (diacr) {
- if (kbd->kbdmode == VC_UNICODE)
- to_utf8(vc, diacr);
- else {
- int c = conv_uni_to_8bit(diacr);
- if (c != -1)
- put_queue(vc, c);
- }
- diacr = 0;
- }
-
- put_queue(vc, 13);
- if (vc_kbd_mode(kbd, VC_CRLF))
- put_queue(vc, 10);
-}
-
-static void fn_caps_toggle(struct vc_data *vc)
-{
- if (rep)
- return;
-
- chg_vc_kbd_led(kbd, VC_CAPSLOCK);
-}
-
-static void fn_caps_on(struct vc_data *vc)
-{
- if (rep)
- return;
-
- set_vc_kbd_led(kbd, VC_CAPSLOCK);
-}
-
-static void fn_show_ptregs(struct vc_data *vc)
-{
- struct pt_regs *regs = get_irq_regs();
-
- if (regs)
- show_regs(regs);
-}
-
-static void fn_hold(struct vc_data *vc)
-{
- struct tty_struct *tty = vc->port.tty;
-
- if (rep || !tty)
- return;
-
- /*
- * Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty);
- * these routines are also activated by ^S/^Q.
- * (And SCROLLOCK can also be set by the ioctl KDSKBLED.)
- */
- if (tty->stopped)
- start_tty(tty);
- else
- stop_tty(tty);
-}
-
-static void fn_num(struct vc_data *vc)
-{
- if (vc_kbd_mode(kbd, VC_APPLIC))
- applkey(vc, 'P', 1);
- else
- fn_bare_num(vc);
-}
-
-/*
- * Bind this to Shift-NumLock if you work in application keypad mode
- * but want to be able to change the NumLock flag.
- * Bind this to NumLock if you prefer that the NumLock key always
- * changes the NumLock flag.
- */
-static void fn_bare_num(struct vc_data *vc)
-{
- if (!rep)
- chg_vc_kbd_led(kbd, VC_NUMLOCK);
-}
-
-static void fn_lastcons(struct vc_data *vc)
-{
- /* switch to the last used console, ChN */
- set_console(last_console);
-}
-
-static void fn_dec_console(struct vc_data *vc)
-{
- int i, cur = fg_console;
-
- /* Currently switching? Queue this next switch relative to that. */
- if (want_console != -1)
- cur = want_console;
-
- for (i = cur - 1; i != cur; i--) {
- if (i == -1)
- i = MAX_NR_CONSOLES - 1;
- if (vc_cons_allocated(i))
- break;
- }
- set_console(i);
-}
-
-static void fn_inc_console(struct vc_data *vc)
-{
- int i, cur = fg_console;
-
- /* Currently switching? Queue this next switch relative to that. */
- if (want_console != -1)
- cur = want_console;
-
- for (i = cur+1; i != cur; i++) {
- if (i == MAX_NR_CONSOLES)
- i = 0;
- if (vc_cons_allocated(i))
- break;
- }
- set_console(i);
-}
-
-static void fn_send_intr(struct vc_data *vc)
-{
- struct tty_struct *tty = vc->port.tty;
-
- if (!tty)
- return;
- tty_insert_flip_char(tty, 0, TTY_BREAK);
- con_schedule_flip(tty);
-}
-
-static void fn_scroll_forw(struct vc_data *vc)
-{
- scrollfront(vc, 0);
-}
-
-static void fn_scroll_back(struct vc_data *vc)
-{
- scrollback(vc, 0);
-}
-
-static void fn_show_mem(struct vc_data *vc)
-{
- show_mem();
-}
-
-static void fn_show_state(struct vc_data *vc)
-{
- show_state();
-}
-
-static void fn_boot_it(struct vc_data *vc)
-{
- ctrl_alt_del();
-}
-
-static void fn_compose(struct vc_data *vc)
-{
- dead_key_next = true;
-}
-
-static void fn_spawn_con(struct vc_data *vc)
-{
- spin_lock(&vt_spawn_con.lock);
- if (vt_spawn_con.pid)
- if (kill_pid(vt_spawn_con.pid, vt_spawn_con.sig, 1)) {
- put_pid(vt_spawn_con.pid);
- vt_spawn_con.pid = NULL;
- }
- spin_unlock(&vt_spawn_con.lock);
-}
-
-static void fn_SAK(struct vc_data *vc)
-{
- struct work_struct *SAK_work = &vc_cons[fg_console].SAK_work;
- schedule_work(SAK_work);
-}
-
-static void fn_null(struct vc_data *vc)
-{
- compute_shiftstate();
-}
-
-/*
- * Special key handlers
- */
-static void k_ignore(struct vc_data *vc, unsigned char value, char up_flag)
-{
-}
-
-static void k_spec(struct vc_data *vc, unsigned char value, char up_flag)
-{
- if (up_flag)
- return;
- if (value >= ARRAY_SIZE(fn_handler))
- return;
- if ((kbd->kbdmode == VC_RAW ||
- kbd->kbdmode == VC_MEDIUMRAW) &&
- value != KVAL(K_SAK))
- return; /* SAK is allowed even in raw mode */
- fn_handler[value](vc);
-}
-
-static void k_lowercase(struct vc_data *vc, unsigned char value, char up_flag)
-{
- pr_err("k_lowercase was called - impossible\n");
-}
-
-static void k_unicode(struct vc_data *vc, unsigned int value, char up_flag)
-{
- if (up_flag)
- return; /* no action, if this is a key release */
-
- if (diacr)
- value = handle_diacr(vc, value);
-
- if (dead_key_next) {
- dead_key_next = false;
- diacr = value;
- return;
- }
- if (kbd->kbdmode == VC_UNICODE)
- to_utf8(vc, value);
- else {
- int c = conv_uni_to_8bit(value);
- if (c != -1)
- put_queue(vc, c);
- }
-}
-
-/*
- * Handle dead key. Note that we now may have several
- * dead keys modifying the same character. Very useful
- * for Vietnamese.
- */
-static void k_deadunicode(struct vc_data *vc, unsigned int value, char up_flag)
-{
- if (up_flag)
- return;
-
- diacr = (diacr ? handle_diacr(vc, value) : value);
-}
-
-static void k_self(struct vc_data *vc, unsigned char value, char up_flag)
-{
- k_unicode(vc, conv_8bit_to_uni(value), up_flag);
-}
-
-static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag)
-{
- k_deadunicode(vc, value, up_flag);
-}
-
-/*
- * Obsolete - for backwards compatibility only
- */
-static void k_dead(struct vc_data *vc, unsigned char value, char up_flag)
-{
- static const unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' };
-
- k_deadunicode(vc, ret_diacr[value], up_flag);
-}
-
-static void k_cons(struct vc_data *vc, unsigned char value, char up_flag)
-{
- if (up_flag)
- return;
-
- set_console(value);
-}
-
-static void k_fn(struct vc_data *vc, unsigned char value, char up_flag)
-{
- if (up_flag)
- return;
-
- if ((unsigned)value < ARRAY_SIZE(func_table)) {
- if (func_table[value])
- puts_queue(vc, func_table[value]);
- } else
- pr_err("k_fn called with value=%d\n", value);
-}
-
-static void k_cur(struct vc_data *vc, unsigned char value, char up_flag)
-{
- static const char cur_chars[] = "BDCA";
-
- if (up_flag)
- return;
-
- applkey(vc, cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE));
-}
-
-static void k_pad(struct vc_data *vc, unsigned char value, char up_flag)
-{
- static const char pad_chars[] = "0123456789+-*/\015,.?()#";
- static const char app_map[] = "pqrstuvwxylSRQMnnmPQS";
-
- if (up_flag)
- return; /* no action, if this is a key release */
-
- /* kludge... shift forces cursor/number keys */
- if (vc_kbd_mode(kbd, VC_APPLIC) && !shift_down[KG_SHIFT]) {
- applkey(vc, app_map[value], 1);
- return;
- }
-
- if (!vc_kbd_led(kbd, VC_NUMLOCK)) {
-
- switch (value) {
- case KVAL(K_PCOMMA):
- case KVAL(K_PDOT):
- k_fn(vc, KVAL(K_REMOVE), 0);
- return;
- case KVAL(K_P0):
- k_fn(vc, KVAL(K_INSERT), 0);
- return;
- case KVAL(K_P1):
- k_fn(vc, KVAL(K_SELECT), 0);
- return;
- case KVAL(K_P2):
- k_cur(vc, KVAL(K_DOWN), 0);
- return;
- case KVAL(K_P3):
- k_fn(vc, KVAL(K_PGDN), 0);
- return;
- case KVAL(K_P4):
- k_cur(vc, KVAL(K_LEFT), 0);
- return;
- case KVAL(K_P6):
- k_cur(vc, KVAL(K_RIGHT), 0);
- return;
- case KVAL(K_P7):
- k_fn(vc, KVAL(K_FIND), 0);
- return;
- case KVAL(K_P8):
- k_cur(vc, KVAL(K_UP), 0);
- return;
- case KVAL(K_P9):
- k_fn(vc, KVAL(K_PGUP), 0);
- return;
- case KVAL(K_P5):
- applkey(vc, 'G', vc_kbd_mode(kbd, VC_APPLIC));
- return;
- }
- }
-
- put_queue(vc, pad_chars[value]);
- if (value == KVAL(K_PENTER) && vc_kbd_mode(kbd, VC_CRLF))
- put_queue(vc, 10);
-}
-
-static void k_shift(struct vc_data *vc, unsigned char value, char up_flag)
-{
- int old_state = shift_state;
-
- if (rep)
- return;
- /*
- * Mimic typewriter:
- * a CapsShift key acts like Shift but undoes CapsLock
- */
- if (value == KVAL(K_CAPSSHIFT)) {
- value = KVAL(K_SHIFT);
- if (!up_flag)
- clr_vc_kbd_led(kbd, VC_CAPSLOCK);
- }
-
- if (up_flag) {
- /*
- * handle the case that two shift or control
- * keys are depressed simultaneously
- */
- if (shift_down[value])
- shift_down[value]--;
- } else
- shift_down[value]++;
-
- if (shift_down[value])
- shift_state |= (1 << value);
- else
- shift_state &= ~(1 << value);
-
- /* kludge */
- if (up_flag && shift_state != old_state && npadch != -1) {
- if (kbd->kbdmode == VC_UNICODE)
- to_utf8(vc, npadch);
- else
- put_queue(vc, npadch & 0xff);
- npadch = -1;
- }
-}
-
-static void k_meta(struct vc_data *vc, unsigned char value, char up_flag)
-{
- if (up_flag)
- return;
-
- if (vc_kbd_mode(kbd, VC_META)) {
- put_queue(vc, '\033');
- put_queue(vc, value);
- } else
- put_queue(vc, value | 0x80);
-}
-
-static void k_ascii(struct vc_data *vc, unsigned char value, char up_flag)
-{
- int base;
-
- if (up_flag)
- return;
-
- if (value < 10) {
- /* decimal input of code, while Alt depressed */
- base = 10;
- } else {
- /* hexadecimal input of code, while AltGr depressed */
- value -= 10;
- base = 16;
- }
-
- if (npadch == -1)
- npadch = value;
- else
- npadch = npadch * base + value;
-}
-
-static void k_lock(struct vc_data *vc, unsigned char value, char up_flag)
-{
- if (up_flag || rep)
- return;
-
- chg_vc_kbd_lock(kbd, value);
-}
-
-static void k_slock(struct vc_data *vc, unsigned char value, char up_flag)
-{
- k_shift(vc, value, up_flag);
- if (up_flag || rep)
- return;
-
- chg_vc_kbd_slock(kbd, value);
- /* try to make Alt, oops, AltGr and such work */
- if (!key_maps[kbd->lockstate ^ kbd->slockstate]) {
- kbd->slockstate = 0;
- chg_vc_kbd_slock(kbd, value);
- }
-}
-
-/* by default, 300ms interval for combination release */
-static unsigned brl_timeout = 300;
-MODULE_PARM_DESC(brl_timeout, "Braille keys release delay in ms (0 for commit on first key release)");
-module_param(brl_timeout, uint, 0644);
-
-static unsigned brl_nbchords = 1;
-MODULE_PARM_DESC(brl_nbchords, "Number of chords that produce a braille pattern (0 for dead chords)");
-module_param(brl_nbchords, uint, 0644);
-
-static void k_brlcommit(struct vc_data *vc, unsigned int pattern, char up_flag)
-{
- static unsigned long chords;
- static unsigned committed;
-
- if (!brl_nbchords)
- k_deadunicode(vc, BRL_UC_ROW | pattern, up_flag);
- else {
- committed |= pattern;
- chords++;
- if (chords == brl_nbchords) {
- k_unicode(vc, BRL_UC_ROW | committed, up_flag);
- chords = 0;
- committed = 0;
- }
- }
-}
-
-static void k_brl(struct vc_data *vc, unsigned char value, char up_flag)
-{
- static unsigned pressed, committing;
- static unsigned long releasestart;
-
- if (kbd->kbdmode != VC_UNICODE) {
- if (!up_flag)
- pr_warning("keyboard mode must be unicode for braille patterns\n");
- return;
- }
-
- if (!value) {
- k_unicode(vc, BRL_UC_ROW, up_flag);
- return;
- }
-
- if (value > 8)
- return;
-
- if (!up_flag) {
- pressed |= 1 << (value - 1);
- if (!brl_timeout)
- committing = pressed;
- } else if (brl_timeout) {
- if (!committing ||
- time_after(jiffies,
- releasestart + msecs_to_jiffies(brl_timeout))) {
- committing = pressed;
- releasestart = jiffies;
- }
- pressed &= ~(1 << (value - 1));
- if (!pressed && committing) {
- k_brlcommit(vc, committing, 0);
- committing = 0;
- }
- } else {
- if (committing) {
- k_brlcommit(vc, committing, 0);
- committing = 0;
- }
- pressed &= ~(1 << (value - 1));
- }
-}
-
-/*
- * The leds display either (i) the status of NumLock, CapsLock, ScrollLock,
- * or (ii) whatever pattern of lights people want to show using KDSETLED,
- * or (iii) specified bits of specified words in kernel memory.
- */
-unsigned char getledstate(void)
-{
- return ledstate;
-}
-
-void setledstate(struct kbd_struct *kbd, unsigned int led)
-{
- if (!(led & ~7)) {
- ledioctl = led;
- kbd->ledmode = LED_SHOW_IOCTL;
- } else
- kbd->ledmode = LED_SHOW_FLAGS;
-
- set_leds();
-}
-
-static inline unsigned char getleds(void)
-{
- struct kbd_struct *kbd = kbd_table + fg_console;
- unsigned char leds;
- int i;
-
- if (kbd->ledmode == LED_SHOW_IOCTL)
- return ledioctl;
-
- leds = kbd->ledflagstate;
-
- if (kbd->ledmode == LED_SHOW_MEM) {
- for (i = 0; i < 3; i++)
- if (ledptrs[i].valid) {
- if (*ledptrs[i].addr & ledptrs[i].mask)
- leds |= (1 << i);
- else
- leds &= ~(1 << i);
- }
- }
- return leds;
-}
-
-static int kbd_update_leds_helper(struct input_handle *handle, void *data)
-{
- unsigned char leds = *(unsigned char *)data;
-
- if (test_bit(EV_LED, handle->dev->evbit)) {
- input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));
- input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02));
- input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04));
- input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
- }
-
- return 0;
-}
-
-/*
- * This is the tasklet that updates LED state on all keyboards
- * attached to the box. The reason we use tasklet is that we
- * need to handle the scenario when keyboard handler is not
- * registered yet but we already getting updates form VT to
- * update led state.
- */
-static void kbd_bh(unsigned long dummy)
-{
- unsigned char leds = getleds();
-
- if (leds != ledstate) {
- input_handler_for_each_handle(&kbd_handler, &leds,
- kbd_update_leds_helper);
- ledstate = leds;
- }
-}
-
-DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);
-
-#if defined(CONFIG_X86) || defined(CONFIG_IA64) || defined(CONFIG_ALPHA) ||\
- defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) ||\
- defined(CONFIG_PARISC) || defined(CONFIG_SUPERH) ||\
- (defined(CONFIG_ARM) && defined(CONFIG_KEYBOARD_ATKBD) && !defined(CONFIG_ARCH_RPC)) ||\
- defined(CONFIG_AVR32)
-
-#define HW_RAW(dev) (test_bit(EV_MSC, dev->evbit) && test_bit(MSC_RAW, dev->mscbit) &&\
- ((dev)->id.bustype == BUS_I8042) && ((dev)->id.vendor == 0x0001) && ((dev)->id.product == 0x0001))
-
-static const unsigned short x86_keycodes[256] =
- { 0, 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, 60, 61, 62, 63,
- 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
- 80, 81, 82, 83, 84,118, 86, 87, 88,115,120,119,121,112,123, 92,
- 284,285,309, 0,312, 91,327,328,329,331,333,335,336,337,338,339,
- 367,288,302,304,350, 89,334,326,267,126,268,269,125,347,348,349,
- 360,261,262,263,268,376,100,101,321,316,373,286,289,102,351,355,
- 103,104,105,275,287,279,258,106,274,107,294,364,358,363,362,361,
- 291,108,381,281,290,272,292,305,280, 99,112,257,306,359,113,114,
- 264,117,271,374,379,265,266, 93, 94, 95, 85,259,375,260, 90,116,
- 377,109,111,277,278,282,283,295,296,297,299,300,301,293,303,307,
- 308,310,313,314,315,317,318,319,320,357,322,323,324,325,276,330,
- 332,340,365,342,343,344,345,346,356,270,341,368,369,370,371,372 };
-
-#ifdef CONFIG_SPARC
-static int sparc_l1_a_state;
-extern void sun_do_break(void);
-#endif
-
-static int emulate_raw(struct vc_data *vc, unsigned int keycode,
- unsigned char up_flag)
-{
- int code;
-
- switch (keycode) {
-
- case KEY_PAUSE:
- put_queue(vc, 0xe1);
- put_queue(vc, 0x1d | up_flag);
- put_queue(vc, 0x45 | up_flag);
- break;
-
- case KEY_HANGEUL:
- if (!up_flag)
- put_queue(vc, 0xf2);
- break;
-
- case KEY_HANJA:
- if (!up_flag)
- put_queue(vc, 0xf1);
- break;
-
- case KEY_SYSRQ:
- /*
- * Real AT keyboards (that's what we're trying
- * to emulate here emit 0xe0 0x2a 0xe0 0x37 when
- * pressing PrtSc/SysRq alone, but simply 0x54
- * when pressing Alt+PrtSc/SysRq.
- */
- if (test_bit(KEY_LEFTALT, key_down) ||
- test_bit(KEY_RIGHTALT, key_down)) {
- put_queue(vc, 0x54 | up_flag);
- } else {
- put_queue(vc, 0xe0);
- put_queue(vc, 0x2a | up_flag);
- put_queue(vc, 0xe0);
- put_queue(vc, 0x37 | up_flag);
- }
- break;
-
- default:
- if (keycode > 255)
- return -1;
-
- code = x86_keycodes[keycode];
- if (!code)
- return -1;
-
- if (code & 0x100)
- put_queue(vc, 0xe0);
- put_queue(vc, (code & 0x7f) | up_flag);
-
- break;
- }
-
- return 0;
-}
-
-#else
-
-#define HW_RAW(dev) 0
-
-static int emulate_raw(struct vc_data *vc, unsigned int keycode, unsigned char up_flag)
-{
- if (keycode > 127)
- return -1;
-
- put_queue(vc, keycode | up_flag);
- return 0;
-}
-#endif
-
-static void kbd_rawcode(unsigned char data)
-{
- struct vc_data *vc = vc_cons[fg_console].d;
-
- kbd = kbd_table + vc->vc_num;
- if (kbd->kbdmode == VC_RAW)
- put_queue(vc, data);
-}
-
-static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
-{
- struct vc_data *vc = vc_cons[fg_console].d;
- unsigned short keysym, *key_map;
- unsigned char type;
- bool raw_mode;
- struct tty_struct *tty;
- int shift_final;
- struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down };
- int rc;
-
- tty = vc->port.tty;
-
- if (tty && (!tty->driver_data)) {
- /* No driver data? Strange. Okay we fix it then. */
- tty->driver_data = vc;
- }
-
- kbd = kbd_table + vc->vc_num;
-
-#ifdef CONFIG_SPARC
- if (keycode == KEY_STOP)
- sparc_l1_a_state = down;
-#endif
-
- rep = (down == 2);
-
- raw_mode = (kbd->kbdmode == VC_RAW);
- if (raw_mode && !hw_raw)
- if (emulate_raw(vc, keycode, !down << 7))
- if (keycode < BTN_MISC && printk_ratelimit())
- pr_warning("can't emulate rawmode for keycode %d\n",
- keycode);
-
-#ifdef CONFIG_SPARC
- if (keycode == KEY_A && sparc_l1_a_state) {
- sparc_l1_a_state = false;
- sun_do_break();
- }
-#endif
-
- if (kbd->kbdmode == VC_MEDIUMRAW) {
- /*
- * This is extended medium raw mode, with keys above 127
- * encoded as 0, high 7 bits, low 7 bits, with the 0 bearing
- * the 'up' flag if needed. 0 is reserved, so this shouldn't
- * interfere with anything else. The two bytes after 0 will
- * always have the up flag set not to interfere with older
- * applications. This allows for 16384 different keycodes,
- * which should be enough.
- */
- if (keycode < 128) {
- put_queue(vc, keycode | (!down << 7));
- } else {
- put_queue(vc, !down << 7);
- put_queue(vc, (keycode >> 7) | 0x80);
- put_queue(vc, keycode | 0x80);
- }
- raw_mode = true;
- }
-
- if (down)
- set_bit(keycode, key_down);
- else
- clear_bit(keycode, key_down);
-
- if (rep &&
- (!vc_kbd_mode(kbd, VC_REPEAT) ||
- (tty && !L_ECHO(tty) && tty_chars_in_buffer(tty)))) {
- /*
- * Don't repeat a key if the input buffers are not empty and the
- * characters get aren't echoed locally. This makes key repeat
- * usable with slow applications and under heavy loads.
- */
- return;
- }
-
- param.shift = shift_final = (shift_state | kbd->slockstate) ^ kbd->lockstate;
- param.ledstate = kbd->ledflagstate;
- key_map = key_maps[shift_final];
-
- rc = atomic_notifier_call_chain(&keyboard_notifier_list,
- KBD_KEYCODE, ¶m);
- if (rc == NOTIFY_STOP || !key_map) {
- atomic_notifier_call_chain(&keyboard_notifier_list,
- KBD_UNBOUND_KEYCODE, ¶m);
- compute_shiftstate();
- kbd->slockstate = 0;
- return;
- }
-
- if (keycode < NR_KEYS)
- keysym = key_map[keycode];
- else if (keycode >= KEY_BRL_DOT1 && keycode <= KEY_BRL_DOT8)
- keysym = U(K(KT_BRL, keycode - KEY_BRL_DOT1 + 1));
- else
- return;
-
- type = KTYP(keysym);
-
- if (type < 0xf0) {
- param.value = keysym;
- rc = atomic_notifier_call_chain(&keyboard_notifier_list,
- KBD_UNICODE, ¶m);
- if (rc != NOTIFY_STOP)
- if (down && !raw_mode)
- to_utf8(vc, keysym);
- return;
- }
-
- type -= 0xf0;
-
- if (type == KT_LETTER) {
- type = KT_LATIN;
- if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
- key_map = key_maps[shift_final ^ (1 << KG_SHIFT)];
- if (key_map)
- keysym = key_map[keycode];
- }
- }
-
- param.value = keysym;
- rc = atomic_notifier_call_chain(&keyboard_notifier_list,
- KBD_KEYSYM, ¶m);
- if (rc == NOTIFY_STOP)
- return;
-
- if (raw_mode && type != KT_SPEC && type != KT_SHIFT)
- return;
-
- (*k_handler[type])(vc, keysym & 0xff, !down);
-
- param.ledstate = kbd->ledflagstate;
- atomic_notifier_call_chain(&keyboard_notifier_list, KBD_POST_KEYSYM, ¶m);
-
- if (type != KT_SLOCK)
- kbd->slockstate = 0;
-}
-
-static void kbd_event(struct input_handle *handle, unsigned int event_type,
- unsigned int event_code, int value)
-{
- /* We are called with interrupts disabled, just take the lock */
- spin_lock(&kbd_event_lock);
-
- if (event_type == EV_MSC && event_code == MSC_RAW && HW_RAW(handle->dev))
- kbd_rawcode(value);
- if (event_type == EV_KEY)
- kbd_keycode(event_code, value, HW_RAW(handle->dev));
-
- spin_unlock(&kbd_event_lock);
-
- tasklet_schedule(&keyboard_tasklet);
- do_poke_blanked_console = 1;
- schedule_console_callback();
-}
-
-static bool kbd_match(struct input_handler *handler, struct input_dev *dev)
-{
- int i;
-
- if (test_bit(EV_SND, dev->evbit))
- return true;
-
- if (test_bit(EV_KEY, dev->evbit)) {
- for (i = KEY_RESERVED; i < BTN_MISC; i++)
- if (test_bit(i, dev->keybit))
- return true;
- for (i = KEY_BRL_DOT1; i <= KEY_BRL_DOT10; i++)
- if (test_bit(i, dev->keybit))
- return true;
- }
-
- return false;
-}
-
-/*
- * When a keyboard (or other input device) is found, the kbd_connect
- * function is called. The function then looks at the device, and if it
- * likes it, it can open it and get events from it. In this (kbd_connect)
- * function, we should decide which VT to bind that keyboard to initially.
- */
-static int kbd_connect(struct input_handler *handler, struct input_dev *dev,
- const struct input_device_id *id)
-{
- struct input_handle *handle;
- int error;
-
- handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
- if (!handle)
- return -ENOMEM;
-
- handle->dev = dev;
- handle->handler = handler;
- handle->name = "kbd";
-
- error = input_register_handle(handle);
- if (error)
- goto err_free_handle;
-
- error = input_open_device(handle);
- if (error)
- goto err_unregister_handle;
-
- return 0;
-
- err_unregister_handle:
- input_unregister_handle(handle);
- err_free_handle:
- kfree(handle);
- return error;
-}
-
-static void kbd_disconnect(struct input_handle *handle)
-{
- input_close_device(handle);
- input_unregister_handle(handle);
- kfree(handle);
-}
-
-/*
- * Start keyboard handler on the new keyboard by refreshing LED state to
- * match the rest of the system.
- */
-static void kbd_start(struct input_handle *handle)
-{
- tasklet_disable(&keyboard_tasklet);
-
- if (ledstate != 0xff)
- kbd_update_leds_helper(handle, &ledstate);
-
- tasklet_enable(&keyboard_tasklet);
-}
-
-static const struct input_device_id kbd_ids[] = {
- {
- .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
- .evbit = { BIT_MASK(EV_KEY) },
- },
-
- {
- .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
- .evbit = { BIT_MASK(EV_SND) },
- },
-
- { }, /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(input, kbd_ids);
-
-static struct input_handler kbd_handler = {
- .event = kbd_event,
- .match = kbd_match,
- .connect = kbd_connect,
- .disconnect = kbd_disconnect,
- .start = kbd_start,
- .name = "kbd",
- .id_table = kbd_ids,
-};
-
-int __init kbd_init(void)
-{
- int i;
- int error;
-
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
- kbd_table[i].ledflagstate = KBD_DEFLEDS;
- kbd_table[i].default_ledflagstate = KBD_DEFLEDS;
- kbd_table[i].ledmode = LED_SHOW_FLAGS;
- kbd_table[i].lockstate = KBD_DEFLOCK;
- kbd_table[i].slockstate = 0;
- kbd_table[i].modeflags = KBD_DEFMODE;
- kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
- }
-
- error = input_register_handler(&kbd_handler);
- if (error)
- return error;
-
- tasklet_enable(&keyboard_tasklet);
- tasklet_schedule(&keyboard_tasklet);
-
- return 0;
-}
+++ /dev/null
-/*
- * linux/drivers/char/selection.c
- *
- * This module exports the functions:
- *
- * 'int set_selection(struct tiocl_selection __user *, struct tty_struct *)'
- * 'void clear_selection(void)'
- * 'int paste_selection(struct tty_struct *)'
- * 'int sel_loadlut(char __user *)'
- *
- * Now that /dev/vcs exists, most of this can disappear again.
- */
-
-#include <linux/module.h>
-#include <linux/tty.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-
-#include <asm/uaccess.h>
-
-#include <linux/kbd_kern.h>
-#include <linux/vt_kern.h>
-#include <linux/consolemap.h>
-#include <linux/selection.h>
-#include <linux/tiocl.h>
-#include <linux/console.h>
-#include <linux/smp_lock.h>
-
-/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
-#define isspace(c) ((c) == ' ')
-
-extern void poke_blanked_console(void);
-
-/* Variables for selection control. */
-/* Use a dynamic buffer, instead of static (Dec 1994) */
-struct vc_data *sel_cons; /* must not be deallocated */
-static int use_unicode;
-static volatile int sel_start = -1; /* cleared by clear_selection */
-static int sel_end;
-static int sel_buffer_lth;
-static char *sel_buffer;
-
-/* clear_selection, highlight and highlight_pointer can be called
- from interrupt (via scrollback/front) */
-
-/* set reverse video on characters s-e of console with selection. */
-static inline void highlight(const int s, const int e)
-{
- invert_screen(sel_cons, s, e-s+2, 1);
-}
-
-/* use complementary color to show the pointer */
-static inline void highlight_pointer(const int where)
-{
- complement_pos(sel_cons, where);
-}
-
-static u16
-sel_pos(int n)
-{
- return inverse_translate(sel_cons, screen_glyph(sel_cons, n),
- use_unicode);
-}
-
-/* remove the current selection highlight, if any,
- from the console holding the selection. */
-void
-clear_selection(void) {
- highlight_pointer(-1); /* hide the pointer */
- if (sel_start != -1) {
- highlight(sel_start, sel_end);
- sel_start = -1;
- }
-}
-
-/*
- * User settable table: what characters are to be considered alphabetic?
- * 256 bits
- */
-static u32 inwordLut[8]={
- 0x00000000, /* control chars */
- 0x03FF0000, /* digits */
- 0x87FFFFFE, /* uppercase and '_' */
- 0x07FFFFFE, /* lowercase */
- 0x00000000,
- 0x00000000,
- 0xFF7FFFFF, /* latin-1 accented letters, not multiplication sign */
- 0xFF7FFFFF /* latin-1 accented letters, not division sign */
-};
-
-static inline int inword(const u16 c) {
- return c > 0xff || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1);
-}
-
-/* set inwordLut contents. Invoked by ioctl(). */
-int sel_loadlut(char __user *p)
-{
- return copy_from_user(inwordLut, (u32 __user *)(p+4), 32) ? -EFAULT : 0;
-}
-
-/* does screen address p correspond to character at LH/RH edge of screen? */
-static inline int atedge(const int p, int size_row)
-{
- return (!(p % size_row) || !((p + 2) % size_row));
-}
-
-/* constrain v such that v <= u */
-static inline unsigned short limit(const unsigned short v, const unsigned short u)
-{
- return (v > u) ? u : v;
-}
-
-/* stores the char in UTF8 and returns the number of bytes used (1-3) */
-static int store_utf8(u16 c, char *p)
-{
- if (c < 0x80) {
- /* 0******* */
- p[0] = c;
- return 1;
- } else if (c < 0x800) {
- /* 110***** 10****** */
- p[0] = 0xc0 | (c >> 6);
- p[1] = 0x80 | (c & 0x3f);
- return 2;
- } else {
- /* 1110**** 10****** 10****** */
- p[0] = 0xe0 | (c >> 12);
- p[1] = 0x80 | ((c >> 6) & 0x3f);
- p[2] = 0x80 | (c & 0x3f);
- return 3;
- }
-}
-
-/* set the current selection. Invoked by ioctl() or by kernel code. */
-int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty)
-{
- struct vc_data *vc = vc_cons[fg_console].d;
- int sel_mode, new_sel_start, new_sel_end, spc;
- char *bp, *obp;
- int i, ps, pe, multiplier;
- u16 c;
- struct kbd_struct *kbd = kbd_table + fg_console;
-
- poke_blanked_console();
-
- { unsigned short xs, ys, xe, ye;
-
- if (!access_ok(VERIFY_READ, sel, sizeof(*sel)))
- return -EFAULT;
- __get_user(xs, &sel->xs);
- __get_user(ys, &sel->ys);
- __get_user(xe, &sel->xe);
- __get_user(ye, &sel->ye);
- __get_user(sel_mode, &sel->sel_mode);
- xs--; ys--; xe--; ye--;
- xs = limit(xs, vc->vc_cols - 1);
- ys = limit(ys, vc->vc_rows - 1);
- xe = limit(xe, vc->vc_cols - 1);
- ye = limit(ye, vc->vc_rows - 1);
- ps = ys * vc->vc_size_row + (xs << 1);
- pe = ye * vc->vc_size_row + (xe << 1);
-
- if (sel_mode == TIOCL_SELCLEAR) {
- /* useful for screendump without selection highlights */
- clear_selection();
- return 0;
- }
-
- if (mouse_reporting() && (sel_mode & TIOCL_SELMOUSEREPORT)) {
- mouse_report(tty, sel_mode & TIOCL_SELBUTTONMASK, xs, ys);
- return 0;
- }
- }
-
- if (ps > pe) /* make sel_start <= sel_end */
- {
- int tmp = ps;
- ps = pe;
- pe = tmp;
- }
-
- if (sel_cons != vc_cons[fg_console].d) {
- clear_selection();
- sel_cons = vc_cons[fg_console].d;
- }
- use_unicode = kbd && kbd->kbdmode == VC_UNICODE;
-
- switch (sel_mode)
- {
- case TIOCL_SELCHAR: /* character-by-character selection */
- new_sel_start = ps;
- new_sel_end = pe;
- break;
- case TIOCL_SELWORD: /* word-by-word selection */
- spc = isspace(sel_pos(ps));
- for (new_sel_start = ps; ; ps -= 2)
- {
- if ((spc && !isspace(sel_pos(ps))) ||
- (!spc && !inword(sel_pos(ps))))
- break;
- new_sel_start = ps;
- if (!(ps % vc->vc_size_row))
- break;
- }
- spc = isspace(sel_pos(pe));
- for (new_sel_end = pe; ; pe += 2)
- {
- if ((spc && !isspace(sel_pos(pe))) ||
- (!spc && !inword(sel_pos(pe))))
- break;
- new_sel_end = pe;
- if (!((pe + 2) % vc->vc_size_row))
- break;
- }
- break;
- case TIOCL_SELLINE: /* line-by-line selection */
- new_sel_start = ps - ps % vc->vc_size_row;
- new_sel_end = pe + vc->vc_size_row
- - pe % vc->vc_size_row - 2;
- break;
- case TIOCL_SELPOINTER:
- highlight_pointer(pe);
- return 0;
- default:
- return -EINVAL;
- }
-
- /* remove the pointer */
- highlight_pointer(-1);
-
- /* select to end of line if on trailing space */
- if (new_sel_end > new_sel_start &&
- !atedge(new_sel_end, vc->vc_size_row) &&
- isspace(sel_pos(new_sel_end))) {
- for (pe = new_sel_end + 2; ; pe += 2)
- if (!isspace(sel_pos(pe)) ||
- atedge(pe, vc->vc_size_row))
- break;
- if (isspace(sel_pos(pe)))
- new_sel_end = pe;
- }
- if (sel_start == -1) /* no current selection */
- highlight(new_sel_start, new_sel_end);
- else if (new_sel_start == sel_start)
- {
- if (new_sel_end == sel_end) /* no action required */
- return 0;
- else if (new_sel_end > sel_end) /* extend to right */
- highlight(sel_end + 2, new_sel_end);
- else /* contract from right */
- highlight(new_sel_end + 2, sel_end);
- }
- else if (new_sel_end == sel_end)
- {
- if (new_sel_start < sel_start) /* extend to left */
- highlight(new_sel_start, sel_start - 2);
- else /* contract from left */
- highlight(sel_start, new_sel_start - 2);
- }
- else /* some other case; start selection from scratch */
- {
- clear_selection();
- highlight(new_sel_start, new_sel_end);
- }
- sel_start = new_sel_start;
- sel_end = new_sel_end;
-
- /* Allocate a new buffer before freeing the old one ... */
- multiplier = use_unicode ? 3 : 1; /* chars can take up to 3 bytes */
- bp = kmalloc(((sel_end-sel_start)/2+1)*multiplier, GFP_KERNEL);
- if (!bp) {
- printk(KERN_WARNING "selection: kmalloc() failed\n");
- clear_selection();
- return -ENOMEM;
- }
- kfree(sel_buffer);
- sel_buffer = bp;
-
- obp = bp;
- for (i = sel_start; i <= sel_end; i += 2) {
- c = sel_pos(i);
- if (use_unicode)
- bp += store_utf8(c, bp);
- else
- *bp++ = c;
- if (!isspace(c))
- obp = bp;
- if (! ((i + 2) % vc->vc_size_row)) {
- /* strip trailing blanks from line and add newline,
- unless non-space at end of line. */
- if (obp != bp) {
- bp = obp;
- *bp++ = '\r';
- }
- obp = bp;
- }
- }
- sel_buffer_lth = bp - sel_buffer;
- return 0;
-}
-
-/* Insert the contents of the selection buffer into the
- * queue of the tty associated with the current console.
- * Invoked by ioctl().
- */
-int paste_selection(struct tty_struct *tty)
-{
- struct vc_data *vc = tty->driver_data;
- int pasted = 0;
- unsigned int count;
- struct tty_ldisc *ld;
- DECLARE_WAITQUEUE(wait, current);
-
- /* always called with BTM from vt_ioctl */
- WARN_ON(!tty_locked());
-
- acquire_console_sem();
- poke_blanked_console();
- release_console_sem();
-
- ld = tty_ldisc_ref(tty);
- if (!ld) {
- tty_unlock();
- ld = tty_ldisc_ref_wait(tty);
- tty_lock();
- }
-
- add_wait_queue(&vc->paste_wait, &wait);
- while (sel_buffer && sel_buffer_lth > pasted) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (test_bit(TTY_THROTTLED, &tty->flags)) {
- schedule();
- continue;
- }
- count = sel_buffer_lth - pasted;
- count = min(count, tty->receive_room);
- tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted,
- NULL, count);
- pasted += count;
- }
- remove_wait_queue(&vc->paste_wait, &wait);
- __set_current_state(TASK_RUNNING);
-
- tty_ldisc_deref(ld);
- return 0;
-}
+++ /dev/null
-/*
- * linux/drivers/char/vc_screen.c
- *
- * Provide access to virtual console memory.
- * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
- * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
- * [minor: N]
- *
- * /dev/vcsaN: idem, but including attributes, and prefixed with
- * the 4 bytes lines,columns,x,y (as screendump used to give).
- * Attribute/character pair is in native endianity.
- * [minor: N+128]
- *
- * This replaces screendump and part of selection, so that the system
- * administrator can control access using file system permissions.
- *
- * aeb@cwi.nl - efter Friedas begravelse - 950211
- *
- * machek@k332.feld.cvut.cz - modified not to send characters to wrong console
- * - fixed some fatal off-by-one bugs (0-- no longer == -1 -> looping and looping and looping...)
- * - making it shorter - scr_readw are macros which expand in PRETTY long code
- */
-
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/errno.h>
-#include <linux/tty.h>
-#include <linux/interrupt.h>
-#include <linux/mm.h>
-#include <linux/init.h>
-#include <linux/mutex.h>
-#include <linux/vt_kern.h>
-#include <linux/selection.h>
-#include <linux/kbd_kern.h>
-#include <linux/console.h>
-#include <linux/device.h>
-#include <linux/smp_lock.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
-#include <linux/signal.h>
-#include <linux/slab.h>
-#include <linux/notifier.h>
-
-#include <asm/uaccess.h>
-#include <asm/byteorder.h>
-#include <asm/unaligned.h>
-
-#undef attr
-#undef org
-#undef addr
-#define HEADER_SIZE 4
-
-struct vcs_poll_data {
- struct notifier_block notifier;
- unsigned int cons_num;
- bool seen_last_update;
- wait_queue_head_t waitq;
- struct fasync_struct *fasync;
-};
-
-static int
-vcs_notifier(struct notifier_block *nb, unsigned long code, void *_param)
-{
- struct vt_notifier_param *param = _param;
- struct vc_data *vc = param->vc;
- struct vcs_poll_data *poll =
- container_of(nb, struct vcs_poll_data, notifier);
- int currcons = poll->cons_num;
-
- if (code != VT_UPDATE)
- return NOTIFY_DONE;
-
- if (currcons == 0)
- currcons = fg_console;
- else
- currcons--;
- if (currcons != vc->vc_num)
- return NOTIFY_DONE;
-
- poll->seen_last_update = false;
- wake_up_interruptible(&poll->waitq);
- kill_fasync(&poll->fasync, SIGIO, POLL_IN);
- return NOTIFY_OK;
-}
-
-static void
-vcs_poll_data_free(struct vcs_poll_data *poll)
-{
- unregister_vt_notifier(&poll->notifier);
- kfree(poll);
-}
-
-static struct vcs_poll_data *
-vcs_poll_data_get(struct file *file)
-{
- struct vcs_poll_data *poll = file->private_data;
-
- if (poll)
- return poll;
-
- poll = kzalloc(sizeof(*poll), GFP_KERNEL);
- if (!poll)
- return NULL;
- poll->cons_num = iminor(file->f_path.dentry->d_inode) & 127;
- init_waitqueue_head(&poll->waitq);
- poll->notifier.notifier_call = vcs_notifier;
- if (register_vt_notifier(&poll->notifier) != 0) {
- kfree(poll);
- return NULL;
- }
-
- /*
- * This code may be called either through ->poll() or ->fasync().
- * If we have two threads using the same file descriptor, they could
- * both enter this function, both notice that the structure hasn't
- * been allocated yet and go ahead allocating it in parallel, but
- * only one of them must survive and be shared otherwise we'd leak
- * memory with a dangling notifier callback.
- */
- spin_lock(&file->f_lock);
- if (!file->private_data) {
- file->private_data = poll;
- } else {
- /* someone else raced ahead of us */
- vcs_poll_data_free(poll);
- poll = file->private_data;
- }
- spin_unlock(&file->f_lock);
-
- return poll;
-}
-
-static int
-vcs_size(struct inode *inode)
-{
- int size;
- int minor = iminor(inode);
- int currcons = minor & 127;
- struct vc_data *vc;
-
- if (currcons == 0)
- currcons = fg_console;
- else
- currcons--;
- if (!vc_cons_allocated(currcons))
- return -ENXIO;
- vc = vc_cons[currcons].d;
-
- size = vc->vc_rows * vc->vc_cols;
-
- if (minor & 128)
- size = 2*size + HEADER_SIZE;
- return size;
-}
-
-static loff_t vcs_lseek(struct file *file, loff_t offset, int orig)
-{
- int size;
-
- mutex_lock(&con_buf_mtx);
- size = vcs_size(file->f_path.dentry->d_inode);
- switch (orig) {
- default:
- mutex_unlock(&con_buf_mtx);
- return -EINVAL;
- case 2:
- offset += size;
- break;
- case 1:
- offset += file->f_pos;
- case 0:
- break;
- }
- if (offset < 0 || offset > size) {
- mutex_unlock(&con_buf_mtx);
- return -EINVAL;
- }
- file->f_pos = offset;
- mutex_unlock(&con_buf_mtx);
- return file->f_pos;
-}
-
-
-static ssize_t
-vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
-{
- struct inode *inode = file->f_path.dentry->d_inode;
- unsigned int currcons = iminor(inode);
- struct vc_data *vc;
- struct vcs_poll_data *poll;
- long pos;
- long viewed, attr, read;
- int col, maxcol;
- unsigned short *org = NULL;
- ssize_t ret;
-
- mutex_lock(&con_buf_mtx);
-
- pos = *ppos;
-
- /* Select the proper current console and verify
- * sanity of the situation under the console lock.
- */
- acquire_console_sem();
-
- attr = (currcons & 128);
- currcons = (currcons & 127);
- if (currcons == 0) {
- currcons = fg_console;
- viewed = 1;
- } else {
- currcons--;
- viewed = 0;
- }
- ret = -ENXIO;
- if (!vc_cons_allocated(currcons))
- goto unlock_out;
- vc = vc_cons[currcons].d;
-
- ret = -EINVAL;
- if (pos < 0)
- goto unlock_out;
- poll = file->private_data;
- if (count && poll)
- poll->seen_last_update = true;
- read = 0;
- ret = 0;
- while (count) {
- char *con_buf0, *con_buf_start;
- long this_round, size;
- ssize_t orig_count;
- long p = pos;
-
- /* Check whether we are above size each round,
- * as copy_to_user at the end of this loop
- * could sleep.
- */
- size = vcs_size(inode);
- if (pos >= size)
- break;
- if (count > size - pos)
- count = size - pos;
-
- this_round = count;
- if (this_round > CON_BUF_SIZE)
- this_round = CON_BUF_SIZE;
-
- /* Perform the whole read into the local con_buf.
- * Then we can drop the console spinlock and safely
- * attempt to move it to userspace.
- */
-
- con_buf_start = con_buf0 = con_buf;
- orig_count = this_round;
- maxcol = vc->vc_cols;
- if (!attr) {
- org = screen_pos(vc, p, viewed);
- col = p % maxcol;
- p += maxcol - col;
- while (this_round-- > 0) {
- *con_buf0++ = (vcs_scr_readw(vc, org++) & 0xff);
- if (++col == maxcol) {
- org = screen_pos(vc, p, viewed);
- col = 0;
- p += maxcol;
- }
- }
- } else {
- if (p < HEADER_SIZE) {
- size_t tmp_count;
-
- con_buf0[0] = (char)vc->vc_rows;
- con_buf0[1] = (char)vc->vc_cols;
- getconsxy(vc, con_buf0 + 2);
-
- con_buf_start += p;
- this_round += p;
- if (this_round > CON_BUF_SIZE) {
- this_round = CON_BUF_SIZE;
- orig_count = this_round - p;
- }
-
- tmp_count = HEADER_SIZE;
- if (tmp_count > this_round)
- tmp_count = this_round;
-
- /* Advance state pointers and move on. */
- this_round -= tmp_count;
- p = HEADER_SIZE;
- con_buf0 = con_buf + HEADER_SIZE;
- /* If this_round >= 0, then p is even... */
- } else if (p & 1) {
- /* Skip first byte for output if start address is odd
- * Update region sizes up/down depending on free
- * space in buffer.
- */
- con_buf_start++;
- if (this_round < CON_BUF_SIZE)
- this_round++;
- else
- orig_count--;
- }
- if (this_round > 0) {
- unsigned short *tmp_buf = (unsigned short *)con_buf0;
-
- p -= HEADER_SIZE;
- p /= 2;
- col = p % maxcol;
-
- org = screen_pos(vc, p, viewed);
- p += maxcol - col;
-
- /* Buffer has even length, so we can always copy
- * character + attribute. We do not copy last byte
- * to userspace if this_round is odd.
- */
- this_round = (this_round + 1) >> 1;
-
- while (this_round) {
- *tmp_buf++ = vcs_scr_readw(vc, org++);
- this_round --;
- if (++col == maxcol) {
- org = screen_pos(vc, p, viewed);
- col = 0;
- p += maxcol;
- }
- }
- }
- }
-
- /* Finally, release the console semaphore while we push
- * all the data to userspace from our temporary buffer.
- *
- * AKPM: Even though it's a semaphore, we should drop it because
- * the pagefault handling code may want to call printk().
- */
-
- release_console_sem();
- ret = copy_to_user(buf, con_buf_start, orig_count);
- acquire_console_sem();
-
- if (ret) {
- read += (orig_count - ret);
- ret = -EFAULT;
- break;
- }
- buf += orig_count;
- pos += orig_count;
- read += orig_count;
- count -= orig_count;
- }
- *ppos += read;
- if (read)
- ret = read;
-unlock_out:
- release_console_sem();
- mutex_unlock(&con_buf_mtx);
- return ret;
-}
-
-static ssize_t
-vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
-{
- struct inode *inode = file->f_path.dentry->d_inode;
- unsigned int currcons = iminor(inode);
- struct vc_data *vc;
- long pos;
- long viewed, attr, size, written;
- char *con_buf0;
- int col, maxcol;
- u16 *org0 = NULL, *org = NULL;
- size_t ret;
-
- mutex_lock(&con_buf_mtx);
-
- pos = *ppos;
-
- /* Select the proper current console and verify
- * sanity of the situation under the console lock.
- */
- acquire_console_sem();
-
- attr = (currcons & 128);
- currcons = (currcons & 127);
-
- if (currcons == 0) {
- currcons = fg_console;
- viewed = 1;
- } else {
- currcons--;
- viewed = 0;
- }
- ret = -ENXIO;
- if (!vc_cons_allocated(currcons))
- goto unlock_out;
- vc = vc_cons[currcons].d;
-
- size = vcs_size(inode);
- ret = -EINVAL;
- if (pos < 0 || pos > size)
- goto unlock_out;
- if (count > size - pos)
- count = size - pos;
- written = 0;
- while (count) {
- long this_round = count;
- size_t orig_count;
- long p;
-
- if (this_round > CON_BUF_SIZE)
- this_round = CON_BUF_SIZE;
-
- /* Temporarily drop the console lock so that we can read
- * in the write data from userspace safely.
- */
- release_console_sem();
- ret = copy_from_user(con_buf, buf, this_round);
- acquire_console_sem();
-
- if (ret) {
- this_round -= ret;
- if (!this_round) {
- /* Abort loop if no data were copied. Otherwise
- * fail with -EFAULT.
- */
- if (written)
- break;
- ret = -EFAULT;
- goto unlock_out;
- }
- }
-
- /* The vcs_size might have changed while we slept to grab
- * the user buffer, so recheck.
- * Return data written up to now on failure.
- */
- size = vcs_size(inode);
- if (pos >= size)
- break;
- if (this_round > size - pos)
- this_round = size - pos;
-
- /* OK, now actually push the write to the console
- * under the lock using the local kernel buffer.
- */
-
- con_buf0 = con_buf;
- orig_count = this_round;
- maxcol = vc->vc_cols;
- p = pos;
- if (!attr) {
- org0 = org = screen_pos(vc, p, viewed);
- col = p % maxcol;
- p += maxcol - col;
-
- while (this_round > 0) {
- unsigned char c = *con_buf0++;
-
- this_round--;
- vcs_scr_writew(vc,
- (vcs_scr_readw(vc, org) & 0xff00) | c, org);
- org++;
- if (++col == maxcol) {
- org = screen_pos(vc, p, viewed);
- col = 0;
- p += maxcol;
- }
- }
- } else {
- if (p < HEADER_SIZE) {
- char header[HEADER_SIZE];
-
- getconsxy(vc, header + 2);
- while (p < HEADER_SIZE && this_round > 0) {
- this_round--;
- header[p++] = *con_buf0++;
- }
- if (!viewed)
- putconsxy(vc, header + 2);
- }
- p -= HEADER_SIZE;
- col = (p/2) % maxcol;
- if (this_round > 0) {
- org0 = org = screen_pos(vc, p/2, viewed);
- if ((p & 1) && this_round > 0) {
- char c;
-
- this_round--;
- c = *con_buf0++;
-#ifdef __BIG_ENDIAN
- vcs_scr_writew(vc, c |
- (vcs_scr_readw(vc, org) & 0xff00), org);
-#else
- vcs_scr_writew(vc, (c << 8) |
- (vcs_scr_readw(vc, org) & 0xff), org);
-#endif
- org++;
- p++;
- if (++col == maxcol) {
- org = screen_pos(vc, p/2, viewed);
- col = 0;
- }
- }
- p /= 2;
- p += maxcol - col;
- }
- while (this_round > 1) {
- unsigned short w;
-
- w = get_unaligned(((unsigned short *)con_buf0));
- vcs_scr_writew(vc, w, org++);
- con_buf0 += 2;
- this_round -= 2;
- if (++col == maxcol) {
- org = screen_pos(vc, p, viewed);
- col = 0;
- p += maxcol;
- }
- }
- if (this_round > 0) {
- unsigned char c;
-
- c = *con_buf0++;
-#ifdef __BIG_ENDIAN
- vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff) | (c << 8), org);
-#else
- vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff00) | c, org);
-#endif
- }
- }
- count -= orig_count;
- written += orig_count;
- buf += orig_count;
- pos += orig_count;
- if (org0)
- update_region(vc, (unsigned long)(org0), org - org0);
- }
- *ppos += written;
- ret = written;
- if (written)
- vcs_scr_updated(vc);
-
-unlock_out:
- release_console_sem();
-
- mutex_unlock(&con_buf_mtx);
-
- return ret;
-}
-
-static unsigned int
-vcs_poll(struct file *file, poll_table *wait)
-{
- struct vcs_poll_data *poll = vcs_poll_data_get(file);
- int ret = 0;
-
- if (poll) {
- poll_wait(file, &poll->waitq, wait);
- if (!poll->seen_last_update)
- ret = POLLIN | POLLRDNORM;
- }
- return ret;
-}
-
-static int
-vcs_fasync(int fd, struct file *file, int on)
-{
- struct vcs_poll_data *poll = file->private_data;
-
- if (!poll) {
- /* don't allocate anything if all we want is disable fasync */
- if (!on)
- return 0;
- poll = vcs_poll_data_get(file);
- if (!poll)
- return -ENOMEM;
- }
-
- return fasync_helper(fd, file, on, &poll->fasync);
-}
-
-static int
-vcs_open(struct inode *inode, struct file *filp)
-{
- unsigned int currcons = iminor(inode) & 127;
- int ret = 0;
-
- tty_lock();
- if(currcons && !vc_cons_allocated(currcons-1))
- ret = -ENXIO;
- tty_unlock();
- return ret;
-}
-
-static int vcs_release(struct inode *inode, struct file *file)
-{
- struct vcs_poll_data *poll = file->private_data;
-
- if (poll)
- vcs_poll_data_free(poll);
- return 0;
-}
-
-static const struct file_operations vcs_fops = {
- .llseek = vcs_lseek,
- .read = vcs_read,
- .write = vcs_write,
- .poll = vcs_poll,
- .fasync = vcs_fasync,
- .open = vcs_open,
- .release = vcs_release,
-};
-
-static struct class *vc_class;
-
-void vcs_make_sysfs(int index)
-{
- device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL,
- "vcs%u", index + 1);
- device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL,
- "vcsa%u", index + 1);
-}
-
-void vcs_remove_sysfs(int index)
-{
- device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1));
- device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129));
-}
-
-int __init vcs_init(void)
-{
- unsigned int i;
-
- if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))
- panic("unable to get major %d for vcs device", VCS_MAJOR);
- vc_class = class_create(THIS_MODULE, "vc");
-
- device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs");
- device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa");
- for (i = 0; i < MIN_NR_CONSOLES; i++)
- vcs_make_sysfs(i);
- return 0;
-}
+++ /dev/null
-/*
- * linux/drivers/char/vt.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>
- *
- * 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
- *
- * Removed console_lock, enabled interrupts across all console operations
- * 13 March 2001, Andrew Morton
- *
- * 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
- */
-
-#include <linux/module.h>
-#include <linux/types.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/slab.h>
-#include <linux/major.h>
-#include <linux/mm.h>
-#include <linux/console.h>
-#include <linux/init.h>
-#include <linux/mutex.h>
-#include <linux/vt_kern.h>
-#include <linux/selection.h>
-#include <linux/smp_lock.h>
-#include <linux/tiocl.h>
-#include <linux/kbd_kern.h>
-#include <linux/consolemap.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/pm.h>
-#include <linux/font.h>
-#include <linux/bitops.h>
-#include <linux/notifier.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <asm/system.h>
-#include <linux/uaccess.h>
-#include <linux/kdb.h>
-#include <linux/ctype.h>
-
-#define MAX_NR_CON_DRIVER 16
-
-#define CON_DRIVER_FLAG_MODULE 1
-#define CON_DRIVER_FLAG_INIT 2
-#define CON_DRIVER_FLAG_ATTR 4
-
-struct con_driver {
- const struct consw *con;
- const char *desc;
- struct device *dev;
- int node;
- int first;
- int last;
- int flag;
-};
-
-static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];
-const struct consw *conswitchp;
-
-/* 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)
-
-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);
-static int set_vesa_blanking(char __user *p);
-static void set_cursor(struct vc_data *vc);
-static void hide_cursor(struct vc_data *vc);
-static void console_callback(struct work_struct *ignored);
-static void blank_screen_t(unsigned long dummy);
-static void set_palette(struct vc_data *vc);
-
-static int printable; /* Is console ready for printing? */
-int default_utf8 = true;
-module_param(default_utf8, int, S_IRUGO | S_IWUSR);
-int global_cursor_default = -1;
-module_param(global_cursor_default, int, S_IRUGO | S_IWUSR);
-
-static int cur_default = CUR_DEFAULT;
-module_param(cur_default, int, S_IRUGO | S_IWUSR);
-
-/*
- * 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;
-static int blankinterval = 10*60;
-core_param(consoleblank, blankinterval, int, 0444);
-
-static DECLARE_WORK(console_work, console_callback);
-
-/*
- * fg_console is the current virtual console,
- * last_console is the last used one,
- * want_console is the console we want to switch to,
- * saved_* variants are for save/restore around kernel debugger enter/leave
- */
-int fg_console;
-int last_console;
-int want_console = -1;
-static int saved_fg_console;
-static int saved_last_console;
-static int saved_want_console;
-static int saved_vc_mode;
-static int saved_console_blanked;
-
-/*
- * 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);
-
-static DEFINE_TIMER(console_timer, blank_screen_t, 0, 0);
-static int blank_state;
-static int blank_timer_expired;
-enum {
- blank_off = 0,
- blank_normal_wait,
- blank_vesa_wait,
-};
-
-/*
- * Notifier list for console events.
- */
-static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);
-
-int register_vt_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_register(&vt_notifier_list, nb);
-}
-EXPORT_SYMBOL_GPL(register_vt_notifier);
-
-int unregister_vt_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_unregister(&vt_notifier_list, nb);
-}
-EXPORT_SYMBOL_GPL(unregister_vt_notifier);
-
-static void notify_write(struct vc_data *vc, unsigned int unicode)
-{
- struct vt_notifier_param param = { .vc = vc, unicode = unicode };
- atomic_notifier_call_chain(&vt_notifier_list, VT_WRITE, ¶m);
-}
-
-static void notify_update(struct vc_data *vc)
-{
- struct vt_notifier_param param = { .vc = vc };
- atomic_notifier_call_chain(&vt_notifier_list, VT_UPDATE, ¶m);
-}
-/*
- * Low-Level Functions
- */
-
-#define IS_FG(vc) ((vc)->vc_num == fg_console)
-
-#ifdef VT_BUF_VRAM_ONLY
-#define DO_UPDATE(vc) 0
-#else
-#define DO_UPDATE(vc) (CON_IS_VISIBLE(vc) && !console_blanked)
-#endif
-
-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;
-}
-
-/* Called from the keyboard irq path.. */
-static inline void scrolldelta(int lines)
-{
- /* FIXME */
- /* scrolldelta needs some kind of consistency lock, but the BKL was
- and still is not protecting versus the scheduled back end */
- scrollback_delta += lines;
- schedule_console_callback();
-}
-
-void schedule_console_callback(void)
-{
- schedule_work(&console_work);
-}
-
-static void scrup(struct vc_data *vc, unsigned int t, unsigned int b, int nr)
-{
- unsigned short *d, *s;
-
- if (t+nr >= b)
- nr = b - t - 1;
- if (b > vc->vc_rows || t >= b || nr < 1)
- return;
- if (CON_IS_VISIBLE(vc) && vc->vc_sw->con_scroll(vc, t, b, SM_UP, nr))
- return;
- d = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t);
- s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * (t + nr));
- scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row);
- scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_video_erase_char,
- vc->vc_size_row * nr);
-}
-
-static void scrdown(struct vc_data *vc, unsigned int t, unsigned int b, int nr)
-{
- unsigned short *s;
- unsigned int step;
-
- if (t+nr >= b)
- nr = b - t - 1;
- if (b > vc->vc_rows || t >= b || nr < 1)
- return;
- if (CON_IS_VISIBLE(vc) && vc->vc_sw->con_scroll(vc, t, b, SM_DOWN, nr))
- return;
- s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t);
- step = vc->vc_cols * nr;
- scr_memmovew(s + step, s, (b - t - nr) * vc->vc_size_row);
- scr_memsetw(s, vc->vc_video_erase_char, 2 * step);
-}
-
-static void do_update_region(struct vc_data *vc, unsigned long start, int count)
-{
-#ifndef VT_BUF_VRAM_ONLY
- 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);
- }
- }
-#endif
-}
-
-void update_region(struct vc_data *vc, unsigned long start, int count)
-{
- WARN_CONSOLE_UNLOCKED();
-
- if (DO_UPDATE(vc)) {
- hide_cursor(vc);
- do_update_region(vc, start, count);
- set_cursor(vc);
- }
-}
-
-/* Structure of attributes is hardware-dependent */
-
-static u8 build_attr(struct vc_data *vc, u8 _color, u8 _intensity, u8 _blink,
- u8 _underline, u8 _reverse, u8 _italic)
-{
- if (vc->vc_sw->con_build_attr)
- return vc->vc_sw->con_build_attr(vc, _color, _intensity,
- _blink, _underline, _reverse, _italic);
-
-#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;
- if (!vc->vc_can_do_color)
- return _intensity |
- (_italic ? 2 : 0) |
- (_underline ? 4 : 0) |
- (_reverse ? 8 : 0) |
- (_blink ? 0x80 : 0);
- if (_italic)
- a = (a & 0xF0) | vc->vc_itcolor;
- else if (_underline)
- a = (a & 0xf0) | vc->vc_ulcolor;
- else if (_intensity == 0)
- a = (a & 0xf0) | vc->vc_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;
- }
-#else
- return 0;
-#endif
-}
-
-static void update_attr(struct vc_data *vc)
-{
- vc->vc_attr = build_attr(vc, vc->vc_color, vc->vc_intensity,
- vc->vc_blink, vc->vc_underline,
- vc->vc_reverse ^ vc->vc_decscnm, vc->vc_italic);
- vc->vc_video_erase_char = (build_attr(vc, vc->vc_color, 1, vc->vc_blink, 0, vc->vc_decscnm, 0) << 8) | ' ';
-}
-
-/* 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);
- if (vc->vc_sw->con_invert_region)
- vc->vc_sw->con_invert_region(vc, p, count);
-#ifndef VT_BUF_VRAM_ONLY
- else {
- u16 *q = p;
- int cnt = count;
- u16 a;
-
- if (!vc->vc_can_do_color) {
- while (cnt--) {
- a = scr_readw(q);
- a ^= 0x0800;
- scr_writew(a, q);
- q++;
- }
- } else if (vc->vc_hi_font_mask == 0x100) {
- while (cnt--) {
- a = scr_readw(q);
- a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) | (((a) & 0x0e00) << 4);
- scr_writew(a, q);
- q++;
- }
- } else {
- while (cnt--) {
- a = scr_readw(q);
- a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4);
- scr_writew(a, q);
- q++;
- }
- }
- }
-#endif
- if (DO_UPDATE(vc))
- do_update_region(vc, (unsigned long) p, count);
-}
-
-/* used by selection: complement pointer position */
-void complement_pos(struct vc_data *vc, int offset)
-{
- static int old_offset = -1;
- static unsigned short old;
- static unsigned short oldx, oldy;
-
- WARN_CONSOLE_UNLOCKED();
-
- if (old_offset != -1 && old_offset >= 0 &&
- old_offset < vc->vc_screenbuf_size) {
- scr_writew(old, screenpos(vc, old_offset, 1));
- if (DO_UPDATE(vc))
- vc->vc_sw->con_putc(vc, old, oldy, oldx);
- }
-
- old_offset = offset;
-
- if (offset != -1 && offset >= 0 &&
- offset < vc->vc_screenbuf_size) {
- unsigned short new;
- unsigned short *p;
- p = screenpos(vc, offset, 1);
- old = scr_readw(p);
- new = old ^ vc->vc_complement_mask;
- scr_writew(new, p);
- if (DO_UPDATE(vc)) {
- oldx = (offset >> 1) % vc->vc_cols;
- oldy = (offset >> 1) / vc->vc_cols;
- vc->vc_sw->con_putc(vc, new, oldy, oldx);
- }
- }
-
-}
-
-static void insert_char(struct vc_data *vc, unsigned int nr)
-{
- unsigned short *p, *q = (unsigned short *)vc->vc_pos;
-
- p = q + vc->vc_cols - nr - vc->vc_x;
- while (--p >= q)
- scr_writew(scr_readw(p), p + nr);
- scr_memsetw(q, vc->vc_video_erase_char, nr * 2);
- vc->vc_need_wrap = 0;
- if (DO_UPDATE(vc)) {
- unsigned short oldattr = vc->vc_attr;
- vc->vc_sw->con_bmove(vc, vc->vc_y, vc->vc_x, vc->vc_y, vc->vc_x + nr, 1,
- vc->vc_cols - vc->vc_x - nr);
- vc->vc_attr = vc->vc_video_erase_char >> 8;
- while (nr--)
- vc->vc_sw->con_putc(vc, vc->vc_video_erase_char, vc->vc_y, vc->vc_x + nr);
- vc->vc_attr = oldattr;
- }
-}
-
-static void delete_char(struct vc_data *vc, unsigned int nr)
-{
- unsigned int i = vc->vc_x;
- unsigned short *p = (unsigned short *)vc->vc_pos;
-
- while (++i <= vc->vc_cols - nr) {
- scr_writew(scr_readw(p+nr), p);
- p++;
- }
- scr_memsetw(p, vc->vc_video_erase_char, nr * 2);
- vc->vc_need_wrap = 0;
- if (DO_UPDATE(vc)) {
- unsigned short oldattr = vc->vc_attr;
- vc->vc_sw->con_bmove(vc, vc->vc_y, vc->vc_x + nr, vc->vc_y, vc->vc_x, 1,
- vc->vc_cols - vc->vc_x - nr);
- vc->vc_attr = vc->vc_video_erase_char >> 8;
- while (nr--)
- vc->vc_sw->con_putc(vc, vc->vc_video_erase_char, vc->vc_y,
- vc->vc_cols - 1 - nr);
- vc->vc_attr = oldattr;
- }
-}
-
-static int softcursor_original;
-
-static void add_softcursor(struct vc_data *vc)
-{
- int i = scr_readw((u16 *) vc->vc_pos);
- u32 type = vc->vc_cursor_type;
-
- if (! (type & 0x10)) return;
- if (softcursor_original != -1) return;
- softcursor_original = i;
- i |= ((type >> 8) & 0xff00 );
- i ^= ((type) & 0xff00 );
- if ((type & 0x20) && ((softcursor_original & 0x7000) == (i & 0x7000))) i ^= 0x7000;
- if ((type & 0x40) && ((i & 0x700) == ((i & 0x7000) >> 4))) i ^= 0x0700;
- scr_writew(i, (u16 *) vc->vc_pos);
- if (DO_UPDATE(vc))
- 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);
- if (DO_UPDATE(vc))
- 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)
-{
- if (!IS_FG(vc) || console_blanked ||
- vc->vc_mode == KD_GRAPHICS)
- 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();
-
- if (!CON_IS_VISIBLE(vc) ||
- !vc->vc_sw->con_set_origin ||
- !vc->vc_sw->con_set_origin(vc))
- vc->vc_origin = (unsigned long)vc->vc_screenbuf;
- vc->vc_visible_origin = vc->vc_origin;
- vc->vc_scr_end = vc->vc_origin + vc->vc_screenbuf_size;
- vc->vc_pos = vc->vc_origin + vc->vc_size_row * vc->vc_y + 2 * vc->vc_x;
-}
-
-static inline void save_screen(struct vc_data *vc)
-{
- WARN_CONSOLE_UNLOCKED();
-
- if (vc->vc_sw->con_save_screen)
- vc->vc_sw->con_save_screen(vc);
-}
-
-/*
- * Redrawing of screen
- */
-
-static void clear_buffer_attributes(struct vc_data *vc)
-{
- 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;
- if (!CON_IS_VISIBLE(vc))
- redraw = 1;
- *vc->vc_display_fg = vc;
- fg_console = vc->vc_num;
- hide_cursor(old_vc);
- if (!CON_IS_VISIBLE(old_vc)) {
- save_screen(old_vc);
- set_origin(old_vc);
- }
- } 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);
- }
-
- /* Forcibly update if we're panicing */
- if ((update && vc->vc_mode != KD_GRAPHICS) ||
- vt_force_oops_output(vc))
- do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
- }
- set_cursor(vc);
- if (is_switch) {
- set_leds();
- compute_shiftstate();
- notify_update(vc);
- }
-}
-
-/*
- * 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;
- vc->vc_uni_pagedir_loc = &vc->vc_uni_pagedir;
- vc->vc_uni_pagedir = 0;
- vc->vc_hi_font_mask = 0;
- vc->vc_complement_mask = 0;
- vc->vc_can_do_color = 0;
- vc->vc_panic_force_write = false;
- 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 */
-{
- WARN_CONSOLE_UNLOCKED();
-
- if (currcons >= MAX_NR_CONSOLES)
- return -ENXIO;
- if (!vc_cons[currcons].d) {
- struct vc_data *vc;
- struct vt_notifier_param param;
-
- /* prevent users from taking too much memory */
- if (currcons >= MAX_NR_USER_CONSOLES && !capable(CAP_SYS_RESOURCE))
- 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 */
- param.vc = vc = kzalloc(sizeof(struct vc_data), GFP_KERNEL);
- if (!vc)
- return -ENOMEM;
- 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)
- con_set_default_unimap(vc);
- vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL);
- if (!vc->vc_screenbuf) {
- kfree(vc);
- vc_cons[currcons].d = NULL;
- return -ENOMEM;
- }
-
- /* 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, ¶m);
- }
- return 0;
-}
-
-static inline int resize_screen(struct vc_data *vc, int width, int height,
- int user)
-{
- /* Resizes the resolution of the display adapater */
- int err = 0;
-
- if (vc->vc_mode != KD_GRAPHICS && vc->vc_sw->con_resize)
- err = vc->vc_sw->con_resize(vc, width, height, user);
-
- 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)
-
-/**
- * vc_do_resize - resizing method for the tty
- * @tty: tty being resized
- * @real_tty: real tty (different to tty if a pty/tty pair)
- * @vc: virtual console private data
- * @cols: columns
- * @lines: lines
- *
- * Resize a virtual console, clipping according to the actual constraints.
- * If the caller passes a tty structure then update the termios winsize
- * information and perform any necessary signal handling.
- *
- * Caller must hold the console semaphore. Takes the termios mutex and
- * ctrl_lock of the tty IFF a tty is passed.
- */
-
-static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
- unsigned int cols, unsigned int lines)
-{
- unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0;
- unsigned long end;
- unsigned int old_cols, old_rows, old_row_size, old_screen_size;
- unsigned int new_cols, new_rows, new_row_size, new_screen_size;
- unsigned int user;
- unsigned short *newscreen;
-
- WARN_CONSOLE_UNLOCKED();
-
- if (!vc)
- return -ENXIO;
-
- user = vc->vc_resize_user;
- vc->vc_resize_user = 0;
-
- if (cols > VC_RESIZE_MAXCOL || lines > VC_RESIZE_MAXROW)
- return -EINVAL;
-
- new_cols = (cols ? cols : vc->vc_cols);
- new_rows = (lines ? lines : vc->vc_rows);
- new_row_size = new_cols << 1;
- new_screen_size = new_row_size * new_rows;
-
- if (new_cols == vc->vc_cols && new_rows == vc->vc_rows)
- return 0;
-
- newscreen = kmalloc(new_screen_size, GFP_USER);
- if (!newscreen)
- return -ENOMEM;
-
- old_rows = vc->vc_rows;
- old_cols = vc->vc_cols;
- old_row_size = vc->vc_size_row;
- old_screen_size = vc->vc_screenbuf_size;
-
- err = resize_screen(vc, new_cols, new_rows, user);
- 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;
-
- 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;
- }
- }
-
- end = old_origin + old_row_size * min(old_rows, new_rows);
-
- update_attr(vc);
-
- while (old_origin < end) {
- scr_memcpyw((unsigned short *) new_origin,
- (unsigned short *) old_origin, rlth);
- if (rrem)
- scr_memsetw((void *)(new_origin + rlth),
- vc->vc_video_erase_char, rrem);
- old_origin += old_row_size;
- new_origin += new_row_size;
- }
- if (new_scr_end > new_origin)
- scr_memsetw((void *)new_origin, vc->vc_video_erase_char,
- new_scr_end - new_origin);
- kfree(vc->vc_screenbuf);
- vc->vc_screenbuf = newscreen;
- vc->vc_screenbuf_size = new_screen_size;
- set_origin(vc);
-
- /* do part of a reset_terminal() */
- vc->vc_top = 0;
- vc->vc_bottom = vc->vc_rows;
- gotoxy(vc, vc->vc_x, vc->vc_y);
- save_cur(vc);
-
- if (tty) {
- /* Rewrite the requested winsize data with the actual
- resulting sizes */
- struct winsize ws;
- memset(&ws, 0, sizeof(ws));
- ws.ws_row = vc->vc_rows;
- ws.ws_col = vc->vc_cols;
- ws.ws_ypixel = vc->vc_scan_lines;
- tty_do_resize(tty, &ws);
- }
-
- if (CON_IS_VISIBLE(vc))
- update_screen(vc);
- vt_event_post(VT_EVENT_RESIZE, vc->vc_num, vc->vc_num);
- return err;
-}
-
-/**
- * vc_resize - resize a VT
- * @vc: virtual console
- * @cols: columns
- * @rows: rows
- *
- * Resize a virtual console as seen from the console end of things. We
- * use the common vc_do_resize methods to update the structures. The
- * caller must hold the console sem to protect console internals and
- * vc->port.tty
- */
-
-int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows)
-{
- return vc_do_resize(vc->port.tty, vc, cols, rows);
-}
-
-/**
- * vt_resize - resize a VT
- * @tty: tty to resize
- * @ws: winsize attributes
- *
- * Resize a virtual terminal. This is called by the tty layer as we
- * register our own handler for resizing. The mutual helper does all
- * the actual work.
- *
- * Takes the console sem and the called methods then take the tty
- * termios_mutex and the tty ctrl_lock in that order.
- */
-static int vt_resize(struct tty_struct *tty, struct winsize *ws)
-{
- struct vc_data *vc = tty->driver_data;
- int ret;
-
- acquire_console_sem();
- ret = vc_do_resize(tty, vc, ws->ws_col, ws->ws_row);
- release_console_sem();
- return ret;
-}
-
-void vc_deallocate(unsigned int currcons)
-{
- WARN_CONSOLE_UNLOCKED();
-
- if (vc_cons_allocated(currcons)) {
- struct vc_data *vc = vc_cons[currcons].d;
- struct vt_notifier_param param = { .vc = vc };
-
- atomic_notifier_call_chain(&vt_notifier_list, VT_DEALLOCATE, ¶m);
- vcs_remove_sysfs(currcons);
- vc->vc_sw->con_deinit(vc);
- put_pid(vc->vt_pid);
- module_put(vc->vc_sw->owner);
- kfree(vc->vc_screenbuf);
- if (currcons >= MIN_NR_CONSOLES)
- kfree(vc);
- vc_cons[currcons].d = NULL;
- }
-}
-
-/*
- * VT102 emulator
- */
-
-#define set_kbd(vc, x) set_vc_kbd_mode(kbd_table + (vc)->vc_num, (x))
-#define clr_kbd(vc, x) clr_vc_kbd_mode(kbd_table + (vc)->vc_num, (x))
-#define is_kbd(vc, x) vc_kbd_mode(kbd_table + (vc)->vc_num, (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};
-
-module_param_array(default_red, int, NULL, S_IRUGO | S_IWUSR);
-module_param_array(default_grn, int, NULL, S_IRUGO | S_IWUSR);
-module_param_array(default_blu, int, NULL, S_IRUGO | S_IWUSR);
-
-/*
- * gotoxy() must verify all boundaries, because the arguments
- * might also be negative. If the given position is out of
- * 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);
-}
-
-void scrollback(struct vc_data *vc, int lines)
-{
- if (!lines)
- lines = vc->vc_rows / 2;
- scrolldelta(-lines);
-}
-
-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)
- scrup(vc, vc->vc_top, vc->vc_bottom, 1);
- else if (vc->vc_y < vc->vc_rows - 1) {
- vc->vc_y++;
- vc->vc_pos += vc->vc_size_row;
- }
- vc->vc_need_wrap = 0;
- notify_write(vc, '\n');
-}
-
-static void 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)
- scrdown(vc, vc->vc_top, vc->vc_bottom, 1);
- 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;
- notify_write(vc, '\r');
-}
-
-static inline void bs(struct vc_data *vc)
-{
- if (vc->vc_x) {
- vc->vc_pos -= 2;
- vc->vc_x--;
- vc->vc_need_wrap = 0;
- notify_write(vc, '\b');
- }
-}
-
-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;
- if (DO_UPDATE(vc)) {
- /* do in two stages */
- vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1,
- vc->vc_cols - vc->vc_x);
- vc->vc_sw->con_clear(vc, vc->vc_y + 1, 0,
- vc->vc_rows - vc->vc_y - 1,
- vc->vc_cols);
- }
- break;
- case 1: /* erase from start to cursor */
- count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1;
- start = (unsigned short *)vc->vc_origin;
- if (DO_UPDATE(vc)) {
- /* do in two stages */
- vc->vc_sw->con_clear(vc, 0, 0, vc->vc_y,
- vc->vc_cols);
- vc->vc_sw->con_clear(vc, vc->vc_y, 0, 1,
- vc->vc_x + 1);
- }
- break;
- case 2: /* erase whole display */
- count = vc->vc_cols * vc->vc_rows;
- start = (unsigned short *)vc->vc_origin;
- if (DO_UPDATE(vc))
- vc->vc_sw->con_clear(vc, 0, 0,
- vc->vc_rows,
- vc->vc_cols);
- break;
- default:
- return;
- }
- scr_memsetw(start, vc->vc_video_erase_char, 2 * count);
- 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;
- if (DO_UPDATE(vc))
- vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1,
- vc->vc_cols - vc->vc_x);
- 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;
- if (DO_UPDATE(vc))
- vc->vc_sw->con_clear(vc, vc->vc_y, 0, 1,
- vc->vc_x + 1);
- break;
- case 2: /* erase whole line */
- start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1));
- count = vc->vc_cols;
- if (DO_UPDATE(vc))
- vc->vc_sw->con_clear(vc, vc->vc_y, 0, 1,
- vc->vc_cols);
- break;
- default:
- return;
- }
- scr_memsetw(start, vc->vc_video_erase_char, 2 * count);
- vc->vc_need_wrap = 0;
-}
-
-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);
- if (DO_UPDATE(vc))
- 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;
- vc->vc_italic = 0;
- vc->vc_underline = 0;
- vc->vc_reverse = 0;
- vc->vc_blink = 0;
- vc->vc_color = vc->vc_def_color;
-}
-
-/* console_sem is held */
-static void csi_m(struct vc_data *vc)
-{
- int i;
-
- for (i = 0; i <= vc->vc_npar; i++)
- switch (vc->vc_par[i]) {
- 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: /* ANSI X3.64-1979 (SCO-ish?)
- * Enables underscore, white foreground
- * with white underscore (Linux - use
- * default foreground).
- */
- vc->vc_color = (vc->vc_def_color & 0x0f) | (vc->vc_color & 0xf0);
- vc->vc_underline = 1;
- break;
- case 39: /* ANSI X3.64-1979 (SCO-ish?)
- * Disable underline option.
- * Reset colour to default? It did this
- * before...
- */
- vc->vc_color = (vc->vc_def_color & 0x0f) | (vc->vc_color & 0xf0);
- vc->vc_underline = 0;
- break;
- case 49:
- vc->vc_color = (vc->vc_def_color & 0xf0) | (vc->vc_color & 0x0f);
- break;
- default:
- 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;
- }
- update_attr(vc);
-}
-
-static void respond_string(const char *p, struct tty_struct *tty)
-{
- while (*p) {
- tty_insert_flip_char(tty, *p, 0);
- p++;
- }
- con_schedule_flip(tty);
-}
-
-static void cursor_report(struct vc_data *vc, struct tty_struct *tty)
-{
- char buf[40];
-
- sprintf(buf, "\033[%d;%dR", vc->vc_y + (vc->vc_decom ? vc->vc_top + 1 : 1), vc->vc_x + 1);
- respond_string(buf, tty);
-}
-
-static inline void status_report(struct tty_struct *tty)
-{
- respond_string("\033[0n", tty); /* Terminal ok */
-}
-
-static inline void respond_ID(struct tty_struct * tty)
-{
- respond_string(VT102ID, tty);
-}
-
-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 */
-int mouse_reporting(void)
-{
- return vc_cons[fg_console].d->vc_report_mouse;
-}
-
-/* console_sem is held */
-static void set_mode(struct vc_data *vc, int on_off)
-{
- int i;
-
- for (i = 0; i <= vc->vc_npar; i++)
- if (vc->vc_ques) {
- switch(vc->vc_par[i]) { /* DEC private modes set/reset */
- case 1: /* Cursor keys send ^[Ox/^[[x */
- if (on_off)
- set_kbd(vc, decckm);
- else
- clr_kbd(vc, decckm);
- break;
- case 3: /* 80/132 mode switch unimplemented */
- vc->vc_deccolm = on_off;
-#if 0
- vc_resize(deccolm ? 132 : 80, vc->vc_rows);
- /* this alone does not suffice; some user mode
- utility has to change the hardware regs */
-#endif
- break;
- case 5: /* Inverted screen on/off */
- if (vc->vc_decscnm != on_off) {
- vc->vc_decscnm = on_off;
- invert_screen(vc, 0, vc->vc_screenbuf_size, 0);
- update_attr(vc);
- }
- break;
- case 6: /* Origin relative/absolute */
- vc->vc_decom = on_off;
- gotoxay(vc, 0, 0);
- break;
- case 7: /* Autowrap on/off */
- vc->vc_decawm = on_off;
- break;
- case 8: /* Autorepeat on/off */
- if (on_off)
- set_kbd(vc, decarm);
- else
- clr_kbd(vc, decarm);
- break;
- case 9:
- vc->vc_report_mouse = on_off ? 1 : 0;
- break;
- case 25: /* Cursor on/off */
- vc->vc_deccm = on_off;
- break;
- case 1000:
- vc->vc_report_mouse = on_off ? 2 : 0;
- break;
- }
- } else {
- switch(vc->vc_par[i]) { /* ANSI modes set/reset */
- case 3: /* Monitor (display ctrls) */
- vc->vc_disp_ctrl = on_off;
- break;
- case 4: /* Insert Mode on/off */
- vc->vc_decim = on_off;
- break;
- case 20: /* Lf, Enter == CrLf/Lf */
- if (on_off)
- set_kbd(vc, lnm);
- else
- clr_kbd(vc, lnm);
- break;
- }
- }
-}
-
-/* console_sem is held */
-static void setterm_command(struct vc_data *vc)
-{
- switch(vc->vc_par[0]) {
- case 1: /* set color for underline mode */
- if (vc->vc_can_do_color &&
- vc->vc_par[1] < 16) {
- vc->vc_ulcolor = color_table[vc->vc_par[1]];
- if (vc->vc_underline)
- update_attr(vc);
- }
- break;
- case 2: /* set color for half intensity mode */
- if (vc->vc_can_do_color &&
- vc->vc_par[1] < 16) {
- vc->vc_halfcolor = color_table[vc->vc_par[1]];
- if (vc->vc_intensity == 0)
- update_attr(vc);
- }
- break;
- case 8: /* store colors as defaults */
- vc->vc_def_color = vc->vc_attr;
- if (vc->vc_hi_font_mask == 0x100)
- vc->vc_def_color >>= 1;
- default_attr(vc);
- update_attr(vc);
- break;
- case 9: /* set blanking interval */
- blankinterval = ((vc->vc_par[1] < 60) ? vc->vc_par[1] : 60) * 60;
- poke_blanked_console();
- break;
- case 10: /* set bell frequency in Hz */
- if (vc->vc_npar >= 1)
- vc->vc_bell_pitch = vc->vc_par[1];
- else
- vc->vc_bell_pitch = DEFAULT_BELL_PITCH;
- break;
- case 11: /* set bell duration in msec */
- if (vc->vc_npar >= 1)
- vc->vc_bell_duration = (vc->vc_par[1] < 2000) ?
- vc->vc_par[1] * HZ / 1000 : 0;
- else
- vc->vc_bell_duration = DEFAULT_BELL_DURATION;
- break;
- case 12: /* bring specified console to the front */
- if (vc->vc_par[1] >= 1 && vc_cons_allocated(vc->vc_par[1] - 1))
- set_console(vc->vc_par[1] - 1);
- break;
- case 13: /* unblank the screen */
- poke_blanked_console();
- break;
- case 14: /* set vesa powerdown interval */
- vesa_off_interval = ((vc->vc_par[1] < 60) ? vc->vc_par[1] : 60) * 60 * HZ;
- break;
- case 15: /* activate the previous console */
- set_console(last_console);
- break;
- }
-}
-
-/* console_sem is held */
-static void csi_at(struct vc_data *vc, unsigned int nr)
-{
- if (nr > vc->vc_cols - vc->vc_x)
- nr = vc->vc_cols - vc->vc_x;
- else if (!nr)
- nr = 1;
- insert_char(vc, nr);
-}
-
-/* console_sem is held */
-static void csi_L(struct vc_data *vc, unsigned int nr)
-{
- if (nr > vc->vc_rows - vc->vc_y)
- nr = vc->vc_rows - vc->vc_y;
- else if (!nr)
- nr = 1;
- scrdown(vc, vc->vc_y, vc->vc_bottom, nr);
- vc->vc_need_wrap = 0;
-}
-
-/* console_sem is held */
-static void csi_P(struct vc_data *vc, unsigned int nr)
-{
- if (nr > vc->vc_cols - vc->vc_x)
- nr = vc->vc_cols - vc->vc_x;
- else if (!nr)
- nr = 1;
- delete_char(vc, nr);
-}
-
-/* console_sem is held */
-static void csi_M(struct vc_data *vc, unsigned int nr)
-{
- if (nr > vc->vc_rows - vc->vc_y)
- nr = vc->vc_rows - vc->vc_y;
- else if (!nr)
- nr=1;
- scrup(vc, vc->vc_y, vc->vc_bottom, nr);
- vc->vc_need_wrap = 0;
-}
-
-/* console_sem is held (except via vc_init->reset_terminal */
-static void save_cur(struct vc_data *vc)
-{
- vc->vc_saved_x = vc->vc_x;
- vc->vc_saved_y = vc->vc_y;
- vc->vc_s_intensity = vc->vc_intensity;
- vc->vc_s_italic = vc->vc_italic;
- vc->vc_s_underline = vc->vc_underline;
- vc->vc_s_blink = vc->vc_blink;
- vc->vc_s_reverse = vc->vc_reverse;
- vc->vc_s_charset = vc->vc_charset;
- vc->vc_s_color = vc->vc_color;
- vc->vc_saved_G0 = vc->vc_G0_charset;
- vc->vc_saved_G1 = vc->vc_G1_charset;
-}
-
-/* console_sem is held */
-static void restore_cur(struct vc_data *vc)
-{
- gotoxy(vc, vc->vc_saved_x, vc->vc_saved_y);
- vc->vc_intensity = vc->vc_s_intensity;
- vc->vc_italic = vc->vc_s_italic;
- vc->vc_underline = vc->vc_s_underline;
- vc->vc_blink = vc->vc_s_blink;
- vc->vc_reverse = vc->vc_s_reverse;
- vc->vc_charset = vc->vc_s_charset;
- vc->vc_color = vc->vc_s_color;
- vc->vc_G0_charset = vc->vc_saved_G0;
- vc->vc_G1_charset = vc->vc_saved_G1;
- vc->vc_translate = set_translate(vc->vc_charset ? vc->vc_G1_charset : vc->vc_G0_charset, vc);
- update_attr(vc);
- vc->vc_need_wrap = 0;
-}
-
-enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
- EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
- ESpalette };
-
-/* console_sem is held (except via vc_init()) */
-static void reset_terminal(struct vc_data *vc, int do_clear)
-{
- vc->vc_top = 0;
- vc->vc_bottom = vc->vc_rows;
- vc->vc_state = ESnormal;
- vc->vc_ques = 0;
- vc->vc_translate = set_translate(LAT1_MAP, vc);
- vc->vc_G0_charset = LAT1_MAP;
- vc->vc_G1_charset = GRAF_MAP;
- vc->vc_charset = 0;
- vc->vc_need_wrap = 0;
- vc->vc_report_mouse = 0;
- vc->vc_utf = default_utf8;
- vc->vc_utf_count = 0;
-
- vc->vc_disp_ctrl = 0;
- vc->vc_toggle_meta = 0;
-
- vc->vc_decscnm = 0;
- vc->vc_decom = 0;
- vc->vc_decawm = 1;
- vc->vc_deccm = global_cursor_default;
- vc->vc_decim = 0;
-
- set_kbd(vc, decarm);
- clr_kbd(vc, decckm);
- clr_kbd(vc, kbdapplic);
- clr_kbd(vc, lnm);
- kbd_table[vc->vc_num].lockstate = 0;
- kbd_table[vc->vc_num].slockstate = 0;
- kbd_table[vc->vc_num].ledmode = LED_SHOW_FLAGS;
- kbd_table[vc->vc_num].ledflagstate = kbd_table[vc->vc_num].default_ledflagstate;
- /* do not do set_leds here because this causes an endless tasklet loop
- when the keyboard hasn't been initialized yet */
-
- vc->vc_cursor_type = cur_default;
- vc->vc_complement_mask = vc->vc_s_complement_mask;
-
- default_attr(vc);
- update_attr(vc);
-
- vc->vc_tab_stop[0] = 0x01010100;
- vc->vc_tab_stop[1] =
- vc->vc_tab_stop[2] =
- vc->vc_tab_stop[3] =
- vc->vc_tab_stop[4] =
- vc->vc_tab_stop[5] =
- vc->vc_tab_stop[6] =
- vc->vc_tab_stop[7] = 0x01010101;
-
- vc->vc_bell_pitch = DEFAULT_BELL_PITCH;
- vc->vc_bell_duration = DEFAULT_BELL_DURATION;
-
- gotoxy(vc, 0, 0);
- save_cur(vc);
- if (do_clear)
- csi_J(vc, 2);
-}
-
-/* console_sem is held */
-static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
-{
- /*
- * Control characters can be used in the _middle_
- * of an escape sequence.
- */
- switch (c) {
- case 0:
- return;
- case 7:
- if (vc->vc_bell_duration)
- kd_mksound(vc->vc_bell_pitch, vc->vc_bell_duration);
- return;
- case 8:
- bs(vc);
- return;
- case 9:
- vc->vc_pos -= (vc->vc_x << 1);
- while (vc->vc_x < vc->vc_cols - 1) {
- vc->vc_x++;
- if (vc->vc_tab_stop[vc->vc_x >> 5] & (1 << (vc->vc_x & 31)))
- break;
- }
- vc->vc_pos += (vc->vc_x << 1);
- notify_write(vc, '\t');
- return;
- case 10: case 11: case 12:
- lf(vc);
- if (!is_kbd(vc, lnm))
- return;
- case 13:
- cr(vc);
- return;
- case 14:
- vc->vc_charset = 1;
- vc->vc_translate = set_translate(vc->vc_G1_charset, vc);
- vc->vc_disp_ctrl = 1;
- return;
- case 15:
- vc->vc_charset = 0;
- vc->vc_translate = set_translate(vc->vc_G0_charset, vc);
- vc->vc_disp_ctrl = 0;
- return;
- case 24: case 26:
- vc->vc_state = ESnormal;
- return;
- case 27:
- vc->vc_state = ESesc;
- return;
- case 127:
- del(vc);
- return;
- case 128+27:
- vc->vc_state = ESsquare;
- return;
- }
- switch(vc->vc_state) {
- case ESesc:
- vc->vc_state = ESnormal;
- switch (c) {
- case '[':
- vc->vc_state = ESsquare;
- return;
- case ']':
- vc->vc_state = ESnonstd;
- return;
- case '%':
- vc->vc_state = ESpercent;
- return;
- case 'E':
- cr(vc);
- lf(vc);
- return;
- case 'M':
- ri(vc);
- return;
- case 'D':
- lf(vc);
- return;
- case 'H':
- vc->vc_tab_stop[vc->vc_x >> 5] |= (1 << (vc->vc_x & 31));
- return;
- case 'Z':
- respond_ID(tty);
- return;
- case '7':
- save_cur(vc);
- return;
- case '8':
- restore_cur(vc);
- return;
- case '(':
- vc->vc_state = ESsetG0;
- return;
- case ')':
- vc->vc_state = ESsetG1;
- return;
- case '#':
- vc->vc_state = EShash;
- return;
- case 'c':
- reset_terminal(vc, 1);
- return;
- case '>': /* Numeric keypad */
- clr_kbd(vc, kbdapplic);
- return;
- case '=': /* Appl. keypad */
- set_kbd(vc, kbdapplic);
- return;
- }
- return;
- case ESnonstd:
- if (c=='P') { /* palette escape sequence */
- for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++)
- vc->vc_par[vc->vc_npar] = 0;
- vc->vc_npar = 0;
- vc->vc_state = ESpalette;
- return;
- } else if (c=='R') { /* reset palette */
- reset_palette(vc);
- vc->vc_state = ESnormal;
- } else
- vc->vc_state = ESnormal;
- return;
- case ESpalette:
- if (isxdigit(c)) {
- vc->vc_par[vc->vc_npar++] = hex_to_bin(c);
- if (vc->vc_npar == 7) {
- int i = vc->vc_par[0] * 3, j = 1;
- vc->vc_palette[i] = 16 * vc->vc_par[j++];
- vc->vc_palette[i++] += vc->vc_par[j++];
- vc->vc_palette[i] = 16 * vc->vc_par[j++];
- vc->vc_palette[i++] += vc->vc_par[j++];
- vc->vc_palette[i] = 16 * vc->vc_par[j++];
- vc->vc_palette[i] += vc->vc_par[j];
- set_palette(vc);
- vc->vc_state = ESnormal;
- }
- } else
- vc->vc_state = ESnormal;
- return;
- case ESsquare:
- for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++)
- vc->vc_par[vc->vc_npar] = 0;
- vc->vc_npar = 0;
- vc->vc_state = ESgetpars;
- if (c == '[') { /* Function key */
- vc->vc_state=ESfunckey;
- return;
- }
- vc->vc_ques = (c == '?');
- if (vc->vc_ques)
- return;
- case ESgetpars:
- if (c == ';' && vc->vc_npar < NPAR - 1) {
- vc->vc_npar++;
- return;
- } else if (c>='0' && c<='9') {
- vc->vc_par[vc->vc_npar] *= 10;
- vc->vc_par[vc->vc_npar] += c - '0';
- return;
- } else
- vc->vc_state = ESgotpars;
- case ESgotpars:
- vc->vc_state = ESnormal;
- switch(c) {
- case 'h':
- set_mode(vc, 1);
- return;
- case 'l':
- set_mode(vc, 0);
- return;
- case 'c':
- if (vc->vc_ques) {
- if (vc->vc_par[0])
- vc->vc_cursor_type = vc->vc_par[0] | (vc->vc_par[1] << 8) | (vc->vc_par[2] << 16);
- else
- vc->vc_cursor_type = cur_default;
- return;
- }
- break;
- case 'm':
- if (vc->vc_ques) {
- clear_selection();
- if (vc->vc_par[0])
- vc->vc_complement_mask = vc->vc_par[0] << 8 | vc->vc_par[1];
- else
- vc->vc_complement_mask = vc->vc_s_complement_mask;
- return;
- }
- break;
- case 'n':
- if (!vc->vc_ques) {
- if (vc->vc_par[0] == 5)
- status_report(tty);
- else if (vc->vc_par[0] == 6)
- cursor_report(vc, tty);
- }
- return;
- }
- if (vc->vc_ques) {
- vc->vc_ques = 0;
- return;
- }
- switch(c) {
- case 'G': case '`':
- if (vc->vc_par[0])
- vc->vc_par[0]--;
- gotoxy(vc, vc->vc_par[0], vc->vc_y);
- return;
- case 'A':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- gotoxy(vc, vc->vc_x, vc->vc_y - vc->vc_par[0]);
- return;
- case 'B': case 'e':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- gotoxy(vc, vc->vc_x, vc->vc_y + vc->vc_par[0]);
- return;
- case 'C': case 'a':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- gotoxy(vc, vc->vc_x + vc->vc_par[0], vc->vc_y);
- return;
- case 'D':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- gotoxy(vc, vc->vc_x - vc->vc_par[0], vc->vc_y);
- return;
- case 'E':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- gotoxy(vc, 0, vc->vc_y + vc->vc_par[0]);
- return;
- case 'F':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- gotoxy(vc, 0, vc->vc_y - vc->vc_par[0]);
- return;
- case 'd':
- if (vc->vc_par[0])
- vc->vc_par[0]--;
- gotoxay(vc, vc->vc_x ,vc->vc_par[0]);
- return;
- case 'H': case 'f':
- if (vc->vc_par[0])
- vc->vc_par[0]--;
- if (vc->vc_par[1])
- vc->vc_par[1]--;
- gotoxay(vc, vc->vc_par[1], vc->vc_par[0]);
- return;
- case 'J':
- csi_J(vc, vc->vc_par[0]);
- return;
- case 'K':
- csi_K(vc, vc->vc_par[0]);
- return;
- case 'L':
- csi_L(vc, vc->vc_par[0]);
- return;
- case 'M':
- csi_M(vc, vc->vc_par[0]);
- return;
- case 'P':
- csi_P(vc, vc->vc_par[0]);
- return;
- case 'c':
- if (!vc->vc_par[0])
- respond_ID(tty);
- return;
- case 'g':
- if (!vc->vc_par[0])
- vc->vc_tab_stop[vc->vc_x >> 5] &= ~(1 << (vc->vc_x & 31));
- else if (vc->vc_par[0] == 3) {
- vc->vc_tab_stop[0] =
- vc->vc_tab_stop[1] =
- vc->vc_tab_stop[2] =
- vc->vc_tab_stop[3] =
- vc->vc_tab_stop[4] =
- vc->vc_tab_stop[5] =
- vc->vc_tab_stop[6] =
- vc->vc_tab_stop[7] = 0;
- }
- return;
- case 'm':
- csi_m(vc);
- return;
- case 'q': /* DECLL - but only 3 leds */
- /* map 0,1,2,3 to 0,1,2,4 */
- if (vc->vc_par[0] < 4)
- setledstate(kbd_table + vc->vc_num,
- (vc->vc_par[0] < 3) ? vc->vc_par[0] : 4);
- return;
- case 'r':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- if (!vc->vc_par[1])
- vc->vc_par[1] = vc->vc_rows;
- /* Minimum allowed region is 2 lines */
- if (vc->vc_par[0] < vc->vc_par[1] &&
- vc->vc_par[1] <= vc->vc_rows) {
- vc->vc_top = vc->vc_par[0] - 1;
- vc->vc_bottom = vc->vc_par[1];
- gotoxay(vc, 0, 0);
- }
- return;
- case 's':
- save_cur(vc);
- return;
- case 'u':
- restore_cur(vc);
- return;
- case 'X':
- csi_X(vc, vc->vc_par[0]);
- return;
- case '@':
- csi_at(vc, vc->vc_par[0]);
- return;
- case ']': /* setterm functions */
- setterm_command(vc);
- return;
- }
- return;
- case ESpercent:
- vc->vc_state = ESnormal;
- switch (c) {
- case '@': /* defined in ISO 2022 */
- vc->vc_utf = 0;
- return;
- case 'G': /* prelim official escape code */
- case '8': /* retained for compatibility */
- vc->vc_utf = 1;
- return;
- }
- return;
- case ESfunckey:
- vc->vc_state = ESnormal;
- return;
- case EShash:
- vc->vc_state = ESnormal;
- if (c == '8') {
- /* DEC screen alignment test. kludge :-) */
- vc->vc_video_erase_char =
- (vc->vc_video_erase_char & 0xff00) | 'E';
- csi_J(vc, 2);
- vc->vc_video_erase_char =
- (vc->vc_video_erase_char & 0xff00) | ' ';
- do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
- }
- return;
- case ESsetG0:
- if (c == '0')
- vc->vc_G0_charset = GRAF_MAP;
- else if (c == 'B')
- vc->vc_G0_charset = LAT1_MAP;
- else if (c == 'U')
- vc->vc_G0_charset = IBMPC_MAP;
- else if (c == 'K')
- vc->vc_G0_charset = USER_MAP;
- if (vc->vc_charset == 0)
- vc->vc_translate = set_translate(vc->vc_G0_charset, vc);
- vc->vc_state = ESnormal;
- return;
- case ESsetG1:
- if (c == '0')
- vc->vc_G1_charset = GRAF_MAP;
- else if (c == 'B')
- vc->vc_G1_charset = LAT1_MAP;
- else if (c == 'U')
- vc->vc_G1_charset = IBMPC_MAP;
- else if (c == 'K')
- vc->vc_G1_charset = USER_MAP;
- if (vc->vc_charset == 1)
- vc->vc_translate = set_translate(vc->vc_G1_charset, vc);
- vc->vc_state = ESnormal;
- return;
- default:
- vc->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[CON_BUF_SIZE];
-DEFINE_MUTEX(con_buf_mtx);
-
-/* is_double_width() is based on the wcwidth() implementation by
- * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
- * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
- */
-struct interval {
- uint32_t first;
- uint32_t last;
-};
-
-static int bisearch(uint32_t ucs, const struct interval *table, int max)
-{
- int min = 0;
- int mid;
-
- if (ucs < table[0].first || ucs > table[max].last)
- return 0;
- while (max >= min) {
- mid = (min + max) / 2;
- if (ucs > table[mid].last)
- min = mid + 1;
- else if (ucs < table[mid].first)
- max = mid - 1;
- else
- return 1;
- }
- return 0;
-}
-
-static int is_double_width(uint32_t ucs)
-{
- static const struct interval double_width[] = {
- { 0x1100, 0x115F }, { 0x2329, 0x232A }, { 0x2E80, 0x303E },
- { 0x3040, 0xA4CF }, { 0xAC00, 0xD7A3 }, { 0xF900, 0xFAFF },
- { 0xFE10, 0xFE19 }, { 0xFE30, 0xFE6F }, { 0xFF00, 0xFF60 },
- { 0xFFE0, 0xFFE6 }, { 0x20000, 0x2FFFD }, { 0x30000, 0x3FFFD }
- };
- return bisearch(ucs, double_width, ARRAY_SIZE(double_width) - 1);
-}
-
-/* acquires console_sem */
-static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int count)
-{
-#ifdef VT_BUF_VRAM_ONLY
-#define FLUSH do { } while(0);
-#else
-#define FLUSH if (draw_x >= 0) { \
- vc->vc_sw->con_putcs(vc, (u16 *)draw_from, (u16 *)draw_to - (u16 *)draw_from, vc->vc_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 vc_data *vc;
- unsigned char vc_attr;
- struct vt_notifier_param param;
- uint8_t rescan;
- uint8_t inverse;
- uint8_t width;
- u16 himask, charmask;
-
- if (in_interrupt())
- return count;
-
- might_sleep();
-
- acquire_console_sem();
- vc = tty->driver_data;
- if (vc == NULL) {
- printk(KERN_ERR "vt: argh, driver_data is NULL !\n");
- release_console_sem();
- return 0;
- }
-
- currcons = vc->vc_num;
- if (!vc_cons_allocated(currcons)) {
- /* could this happen? */
- printk_once("con_write: tty %d not allocated\n", currcons+1);
- release_console_sem();
- return 0;
- }
-
- himask = vc->vc_hi_font_mask;
- charmask = himask ? 0x1ff : 0xff;
-
- /* undraw cursor first */
- if (IS_FG(vc))
- hide_cursor(vc);
-
- param.vc = vc;
-
- while (!tty->stopped && count) {
- int orig = *buf;
- c = orig;
- buf++;
- n++;
- count--;
- rescan = 0;
- inverse = 0;
- width = 1;
-
- /* Do no translation at all in control states */
- if (vc->vc_state != ESnormal) {
- tc = c;
- } else if (vc->vc_utf && !vc->vc_disp_ctrl) {
- /* Combine UTF-8 into Unicode in vc_utf_char.
- * vc_utf_count is the number of continuation bytes still
- * expected to arrive.
- * vc_npar is the number of continuation bytes arrived so
- * far
- */
-rescan_last_byte:
- if ((c & 0xc0) == 0x80) {
- /* Continuation byte received */
- static const uint32_t utf8_length_changes[] = { 0x0000007f, 0x000007ff, 0x0000ffff, 0x001fffff, 0x03ffffff, 0x7fffffff };
- if (vc->vc_utf_count) {
- vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f);
- vc->vc_npar++;
- if (--vc->vc_utf_count) {
- /* Still need some bytes */
- continue;
- }
- /* Got a whole character */
- c = vc->vc_utf_char;
- /* Reject overlong sequences */
- if (c <= utf8_length_changes[vc->vc_npar - 1] ||
- c > utf8_length_changes[vc->vc_npar])
- c = 0xfffd;
- } else {
- /* Unexpected continuation byte */
- vc->vc_utf_count = 0;
- c = 0xfffd;
- }
- } else {
- /* Single ASCII byte or first byte of a sequence received */
- if (vc->vc_utf_count) {
- /* Continuation byte expected */
- rescan = 1;
- vc->vc_utf_count = 0;
- c = 0xfffd;
- } else if (c > 0x7f) {
- /* First byte of a multibyte sequence received */
- vc->vc_npar = 0;
- if ((c & 0xe0) == 0xc0) {
- vc->vc_utf_count = 1;
- vc->vc_utf_char = (c & 0x1f);
- } else if ((c & 0xf0) == 0xe0) {
- vc->vc_utf_count = 2;
- vc->vc_utf_char = (c & 0x0f);
- } else if ((c & 0xf8) == 0xf0) {
- vc->vc_utf_count = 3;
- vc->vc_utf_char = (c & 0x07);
- } else if ((c & 0xfc) == 0xf8) {
- vc->vc_utf_count = 4;
- vc->vc_utf_char = (c & 0x03);
- } else if ((c & 0xfe) == 0xfc) {
- vc->vc_utf_count = 5;
- vc->vc_utf_char = (c & 0x01);
- } else {
- /* 254 and 255 are invalid */
- c = 0xfffd;
- }
- if (vc->vc_utf_count) {
- /* Still need some bytes */
- continue;
- }
- }
- /* Nothing to do if an ASCII byte was received */
- }
- /* End of UTF-8 decoding. */
- /* c is the received character, or U+FFFD for invalid sequences. */
- /* Replace invalid Unicode code points with U+FFFD too */
- if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff)
- c = 0xfffd;
- tc = c;
- } else { /* no utf or alternate charset mode */
- tc = vc_translate(vc, c);
- }
-
- param.c = tc;
- if (atomic_notifier_call_chain(&vt_notifier_list, VT_PREWRITE,
- ¶m) == NOTIFY_STOP)
- continue;
-
- /* If the original code was a control character we
- * only allow a glyph to be displayed if the code is
- * not normally used (such as for cursor movement) or
- * 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 ||
- !(vc->vc_disp_ctrl ? (CTRL_ALWAYS >> c) & 1 :
- vc->vc_utf || ((CTRL_ACTION >> c) & 1)))
- && (c != 127 || vc->vc_disp_ctrl)
- && (c != 128+27);
-
- if (vc->vc_state == ESnormal && ok) {
- if (vc->vc_utf && !vc->vc_disp_ctrl) {
- if (is_double_width(c))
- width = 2;
- }
- /* Now try to find out how to display it */
- tc = conv_uni_to_pc(vc, tc);
- if (tc & ~charmask) {
- if (tc == -1 || tc == -2) {
- continue; /* nothing to display */
- }
- /* Glyph not found */
- if ((!(vc->vc_utf && !vc->vc_disp_ctrl) || c < 128) && !(c & ~charmask)) {
- /* In legacy mode use the glyph we get by a 1:1 mapping.
- This would make absolutely no sense with Unicode in mind,
- but do this for ASCII characters since a font may lack
- Unicode mapping info and we don't want to end up with
- having question marks only. */
- tc = c;
- } else {
- /* Display U+FFFD. If it's not found, display an inverse question mark. */
- tc = conv_uni_to_pc(vc, 0xfffd);
- if (tc < 0) {
- inverse = 1;
- tc = conv_uni_to_pc(vc, '?');
- if (tc < 0) tc = '?';
- }
- }
- }
-
- if (!inverse) {
- vc_attr = vc->vc_attr;
- } else {
- /* invert vc_attr */
- if (!vc->vc_can_do_color) {
- vc_attr = (vc->vc_attr) ^ 0x08;
- } else if (vc->vc_hi_font_mask == 0x100) {
- vc_attr = ((vc->vc_attr) & 0x11) | (((vc->vc_attr) & 0xe0) >> 4) | (((vc->vc_attr) & 0x0e) << 4);
- } else {
- vc_attr = ((vc->vc_attr) & 0x88) | (((vc->vc_attr) & 0x70) >> 4) | (((vc->vc_attr) & 0x07) << 4);
- }
- FLUSH
- }
-
- while (1) {
- if (vc->vc_need_wrap || vc->vc_decim)
- FLUSH
- if (vc->vc_need_wrap) {
- cr(vc);
- lf(vc);
- }
- if (vc->vc_decim)
- insert_char(vc, 1);
- scr_writew(himask ?
- ((vc_attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) :
- (vc_attr << 8) + tc,
- (u16 *) vc->vc_pos);
- if (DO_UPDATE(vc) && draw_x < 0) {
- draw_x = vc->vc_x;
- draw_from = vc->vc_pos;
- }
- if (vc->vc_x == vc->vc_cols - 1) {
- vc->vc_need_wrap = vc->vc_decawm;
- draw_to = vc->vc_pos + 2;
- } else {
- vc->vc_x++;
- draw_to = (vc->vc_pos += 2);
- }
-
- if (!--width) break;
-
- tc = conv_uni_to_pc(vc, ' '); /* A space is printed in the second column */
- if (tc < 0) tc = ' ';
- }
- notify_write(vc, c);
-
- if (inverse) {
- FLUSH
- }
-
- if (rescan) {
- rescan = 0;
- inverse = 0;
- width = 1;
- c = orig;
- goto rescan_last_byte;
- }
- continue;
- }
- FLUSH
- do_con_trol(tty, vc, orig);
- }
- FLUSH
- console_conditional_schedule();
- release_console_sem();
- notify_update(vc);
- return n;
-#undef FLUSH
-}
-
-/*
- * This is the console switching callback.
- *
- * Doing console switching in a process context 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_sem.
- */
-static void console_callback(struct work_struct *ignored)
-{
- acquire_console_sem();
-
- if (want_console >= 0) {
- if (want_console != fg_console &&
- vc_cons_allocated(want_console)) {
- hide_cursor(vc_cons[fg_console].d);
- change_console(vc_cons[want_console].d);
- /* we only changed when the console had already
- been allocated - a new console is not created
- in an interrupt routine */
- }
- want_console = -1;
- }
- if (do_poke_blanked_console) { /* do not unblank for a LED change */
- do_poke_blanked_console = 0;
- poke_blanked_console();
- }
- if (scrollback_delta) {
- struct vc_data *vc = vc_cons[fg_console].d;
- clear_selection();
- if (vc->vc_mode == KD_TEXT)
- vc->vc_sw->con_scrolldelta(vc, scrollback_delta);
- scrollback_delta = 0;
- }
- if (blank_timer_expired) {
- do_blank_screen(0);
- blank_timer_expired = 0;
- }
- notify_update(vc_cons[fg_console].d);
-
- release_console_sem();
-}
-
-int set_console(int nr)
-{
- struct vc_data *vc = vc_cons[fg_console].d;
-
- if (!vc_cons_allocated(nr) || vt_dont_switch ||
- (vc->vt_mode.mode == VT_AUTO && vc->vc_mode == KD_GRAPHICS)) {
-
- /*
- * Console switch will fail in console_callback() or
- * change_console() so there is no point scheduling
- * the callback
- *
- * Existing set_console() users don't check the return
- * value so this shouldn't break anything
- */
- return -EINVAL;
- }
-
- want_console = nr;
- schedule_console_callback();
-
- return 0;
-}
-
-struct tty_driver *console_driver;
-
-#ifdef CONFIG_VT_CONSOLE
-
-/**
- * vt_kmsg_redirect() - Sets/gets the kernel message console
- * @new: The new virtual terminal number or -1 if the console should stay
- * unchanged
- *
- * By default, the kernel messages are always printed on the current virtual
- * console. However, the user may modify that default with the
- * TIOCL_SETKMSGREDIRECT ioctl call.
- *
- * This function sets the kernel message console to be @new. It returns the old
- * virtual console number. The virtual terminal number 0 (both as parameter and
- * return value) means no redirection (i.e. always printed on the currently
- * active console).
- *
- * The parameter -1 means that only the current console is returned, but the
- * value is not modified. You may use the macro vt_get_kmsg_redirect() in that
- * case to make the code more understandable.
- *
- * When the kernel is compiled without CONFIG_VT_CONSOLE, this function ignores
- * the parameter and always returns 0.
- */
-int vt_kmsg_redirect(int new)
-{
- static int kmsg_con;
-
- if (new != -1)
- return xchg(&kmsg_con, new);
- else
- return kmsg_con;
-}
-
-/*
- * Console on virtual terminal
- *
- * The console must be locked when we get here.
- */
-
-static void vt_console_print(struct console *co, const char *b, unsigned count)
-{
- struct vc_data *vc = vc_cons[fg_console].d;
- unsigned char c;
- static DEFINE_SPINLOCK(printing_lock);
- const ushort *start;
- ushort cnt = 0;
- ushort myx;
- int kmsg_console;
-
- /* console busy or not yet initialized */
- if (!printable)
- return;
- if (!spin_trylock(&printing_lock))
- return;
-
- kmsg_console = vt_get_kmsg_redirect();
- if (kmsg_console && vc_cons_allocated(kmsg_console - 1))
- vc = vc_cons[kmsg_console - 1].d;
-
- /* read `x' only after setting currcons properly (otherwise
- the `x' macro will read the x of the foreground console). */
- myx = vc->vc_x;
-
- if (!vc_cons_allocated(fg_console)) {
- /* impossible */
- /* printk("vt_console_print: tty %d not allocated ??\n", currcons+1); */
- goto quit;
- }
-
- if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc))
- goto quit;
-
- /* undraw cursor first */
- if (IS_FG(vc))
- hide_cursor(vc);
-
- start = (ushort *)vc->vc_pos;
-
- /* Contrived structure to try to emulate original need_wrap behaviour
- * Problems caused when we have need_wrap set on '\n' character */
- while (count--) {
- c = *b++;
- if (c == 10 || c == 13 || c == 8 || vc->vc_need_wrap) {
- if (cnt > 0) {
- if (CON_IS_VISIBLE(vc))
- vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, vc->vc_x);
- vc->vc_x += cnt;
- if (vc->vc_need_wrap)
- vc->vc_x--;
- cnt = 0;
- }
- if (c == 8) { /* backspace */
- bs(vc);
- start = (ushort *)vc->vc_pos;
- myx = vc->vc_x;
- continue;
- }
- if (c != 13)
- lf(vc);
- cr(vc);
- start = (ushort *)vc->vc_pos;
- myx = vc->vc_x;
- if (c == 10 || c == 13)
- continue;
- }
- scr_writew((vc->vc_attr << 8) + c, (unsigned short *)vc->vc_pos);
- notify_write(vc, c);
- cnt++;
- if (myx == vc->vc_cols - 1) {
- vc->vc_need_wrap = 1;
- continue;
- }
- vc->vc_pos += 2;
- myx++;
- }
- if (cnt > 0) {
- if (CON_IS_VISIBLE(vc))
- vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, vc->vc_x);
- vc->vc_x += cnt;
- if (vc->vc_x == vc->vc_cols) {
- vc->vc_x--;
- vc->vc_need_wrap = 1;
- }
- }
- set_cursor(vc);
- notify_update(vc);
-
-quit:
- spin_unlock(&printing_lock);
-}
-
-static struct tty_driver *vt_console_device(struct console *c, int *index)
-{
- *index = c->index ? c->index-1 : fg_console;
- return console_driver;
-}
-
-static struct console vt_console_driver = {
- .name = "tty",
- .write = vt_console_print,
- .device = vt_console_device,
- .unblank = unblank_screen,
- .flags = CON_PRINTBUFFER,
- .index = -1,
-};
-#endif
-
-/*
- * Handling of Linux-specific VC ioctls
- */
-
-/*
- * Generally a bit racy with respect to console_sem().
- *
- * There are some functions which don't need it.
- *
- * There are some functions which can sleep for arbitrary periods
- * (paste_selection) but we don't need the lock there anyway.
- *
- * set_selection has locking, and definitely needs it
- */
-
-int tioclinux(struct tty_struct *tty, unsigned long arg)
-{
- char type, data;
- char __user *p = (char __user *)arg;
- int lines;
- int ret;
-
- if (current->signal->tty != tty && !capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (get_user(type, p))
- return -EFAULT;
- ret = 0;
-
- switch (type)
- {
- case TIOCL_SETSEL:
- acquire_console_sem();
- ret = set_selection((struct tiocl_selection __user *)(p+1), tty);
- release_console_sem();
- break;
- case TIOCL_PASTESEL:
- ret = paste_selection(tty);
- break;
- case TIOCL_UNBLANKSCREEN:
- acquire_console_sem();
- unblank_screen();
- release_console_sem();
- break;
- case TIOCL_SELLOADLUT:
- ret = sel_loadlut(p);
- break;
- case TIOCL_GETSHIFTSTATE:
-
- /*
- * 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;
- ret = __put_user(data, p);
- break;
- case TIOCL_GETMOUSEREPORTING:
- data = mouse_reporting();
- ret = __put_user(data, p);
- break;
- case TIOCL_SETVESABLANK:
- ret = set_vesa_blanking(p);
- break;
- case TIOCL_GETKMSGREDIRECT:
- data = vt_get_kmsg_redirect();
- ret = __put_user(data, p);
- break;
- case TIOCL_SETKMSGREDIRECT:
- if (!capable(CAP_SYS_ADMIN)) {
- ret = -EPERM;
- } else {
- if (get_user(data, p+1))
- ret = -EFAULT;
- else
- vt_kmsg_redirect(data);
- }
- break;
- case TIOCL_GETFGCONSOLE:
- ret = fg_console;
- break;
- case TIOCL_SCROLLCONSOLE:
- if (get_user(lines, (s32 __user *)(p+4))) {
- ret = -EFAULT;
- } else {
- scrollfront(vc_cons[fg_console].d, lines);
- ret = 0;
- }
- break;
- case TIOCL_BLANKSCREEN: /* until explicitly unblanked, not only poked */
- acquire_console_sem();
- ignore_poke = 1;
- do_blank_screen(0);
- release_console_sem();
- break;
- case TIOCL_BLANKEDSCREEN:
- ret = console_blanked;
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-/*
- * /dev/ttyN handling
- */
-
-static int con_write(struct tty_struct *tty, const unsigned char *buf, int count)
-{
- int retval;
-
- retval = do_con_write(tty, buf, count);
- con_flush_chars(tty);
-
- return retval;
-}
-
-static int con_put_char(struct tty_struct *tty, unsigned char ch)
-{
- if (in_interrupt())
- return 0; /* n_r3964 calls put_char() from interrupt context */
- return do_con_write(tty, &ch, 1);
-}
-
-static int con_write_room(struct tty_struct *tty)
-{
- if (tty->stopped)
- return 0;
- return 32768; /* No limit, really; we're not buffering */
-}
-
-static int con_chars_in_buffer(struct tty_struct *tty)
-{
- 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...
- */
-static void con_throttle(struct tty_struct *tty)
-{
-}
-
-static void con_unthrottle(struct tty_struct *tty)
-{
- struct vc_data *vc = tty->driver_data;
-
- wake_up_interruptible(&vc->paste_wait);
-}
-
-/*
- * Turn the Scroll-Lock LED on when the tty is stopped
- */
-static void con_stop(struct tty_struct *tty)
-{
- int console_num;
- if (!tty)
- return;
- console_num = tty->index;
- if (!vc_cons_allocated(console_num))
- return;
- set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
- set_leds();
-}
-
-/*
- * Turn the Scroll-Lock LED off when the console is started
- */
-static void con_start(struct tty_struct *tty)
-{
- int console_num;
- if (!tty)
- return;
- console_num = tty->index;
- if (!vc_cons_allocated(console_num))
- return;
- clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
- set_leds();
-}
-
-static void con_flush_chars(struct tty_struct *tty)
-{
- struct vc_data *vc;
-
- if (in_interrupt()) /* from flush_to_ldisc */
- return;
-
- /* if we race with con_close(), vt may be null */
- acquire_console_sem();
- vc = tty->driver_data;
- if (vc)
- set_cursor(vc);
- release_console_sem();
-}
-
-/*
- * Allocate the console screen memory.
- */
-static int con_open(struct tty_struct *tty, struct file *filp)
-{
- unsigned int currcons = tty->index;
- int ret = 0;
-
- acquire_console_sem();
- if (tty->driver_data == NULL) {
- ret = vc_allocate(currcons);
- if (ret == 0) {
- struct vc_data *vc = vc_cons[currcons].d;
-
- /* Still being freed */
- if (vc->port.tty) {
- release_console_sem();
- return -ERESTARTSYS;
- }
- tty->driver_data = vc;
- vc->port.tty = tty;
-
- if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
- tty->winsize.ws_row = vc_cons[currcons].d->vc_rows;
- tty->winsize.ws_col = vc_cons[currcons].d->vc_cols;
- }
- if (vc->vc_utf)
- tty->termios->c_iflag |= IUTF8;
- else
- tty->termios->c_iflag &= ~IUTF8;
- release_console_sem();
- return ret;
- }
- }
- release_console_sem();
- return ret;
-}
-
-static void con_close(struct tty_struct *tty, struct file *filp)
-{
- /* Nothing to do - we defer to shutdown */
-}
-
-static void con_shutdown(struct tty_struct *tty)
-{
- struct vc_data *vc = tty->driver_data;
- BUG_ON(vc == NULL);
- acquire_console_sem();
- vc->port.tty = NULL;
- release_console_sem();
- tty_shutdown(tty);
-}
-
-static int default_italic_color = 2; // green (ASCII)
-static int default_underline_color = 3; // cyan (ASCII)
-module_param_named(italic, default_italic_color, int, S_IRUGO | S_IWUSR);
-module_param_named(underline, default_underline_color, int, S_IRUGO | S_IWUSR);
-
-static void vc_init(struct vc_data *vc, unsigned int rows,
- unsigned int cols, int do_clear)
-{
- int j, k ;
-
- vc->vc_cols = cols;
- vc->vc_rows = rows;
- vc->vc_size_row = cols << 1;
- vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row;
-
- set_origin(vc);
- vc->vc_pos = vc->vc_origin;
- reset_vc(vc);
- for (j=k=0; j<16; j++) {
- vc->vc_palette[k++] = default_red[j] ;
- vc->vc_palette[k++] = default_grn[j] ;
- vc->vc_palette[k++] = default_blu[j] ;
- }
- vc->vc_def_color = 0x07; /* white */
- vc->vc_ulcolor = default_underline_color;
- vc->vc_itcolor = default_italic_color;
- vc->vc_halfcolor = 0x08; /* grey */
- init_waitqueue_head(&vc->paste_wait);
- reset_terminal(vc, do_clear);
-}
-
-/*
- * This routine initializes console interrupts, and does nothing
- * else. If you want the screen to clear, call tty_write with
- * the appropriate escape-sequence.
- */
-
-static int __init con_init(void)
-{
- const char *display_desc = NULL;
- struct vc_data *vc;
- unsigned int currcons = 0, i;
-
- acquire_console_sem();
-
- if (conswitchp)
- display_desc = conswitchp->con_startup();
- if (!display_desc) {
- fg_console = 0;
- release_console_sem();
- return 0;
- }
-
- for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
- struct con_driver *con_driver = ®istered_con_driver[i];
-
- if (con_driver->con == NULL) {
- con_driver->con = conswitchp;
- con_driver->desc = display_desc;
- con_driver->flag = CON_DRIVER_FLAG_INIT;
- con_driver->first = 0;
- con_driver->last = MAX_NR_CONSOLES - 1;
- break;
- }
- }
-
- for (i = 0; i < MAX_NR_CONSOLES; i++)
- con_driver_map[i] = conswitchp;
-
- if (blankinterval) {
- blank_state = blank_normal_wait;
- mod_timer(&console_timer, jiffies + (blankinterval * HZ));
- }
-
- for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
- vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT);
- INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
- tty_port_init(&vc->port);
- visual_init(vc, currcons, 1);
- vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
- vc_init(vc, vc->vc_rows, vc->vc_cols,
- currcons || !vc->vc_sw->con_save_screen);
- }
- currcons = fg_console = 0;
- master_display_fg = vc = vc_cons[currcons].d;
- set_origin(vc);
- save_screen(vc);
- gotoxy(vc, vc->vc_x, vc->vc_y);
- csi_J(vc, 0);
- update_screen(vc);
- printk("Console: %s %s %dx%d",
- vc->vc_can_do_color ? "colour" : "mono",
- display_desc, vc->vc_cols, vc->vc_rows);
- printable = 1;
- printk("\n");
-
- release_console_sem();
-
-#ifdef CONFIG_VT_CONSOLE
- register_console(&vt_console_driver);
-#endif
- return 0;
-}
-console_initcall(con_init);
-
-static const struct tty_operations con_ops = {
- .open = con_open,
- .close = con_close,
- .write = con_write,
- .write_room = con_write_room,
- .put_char = con_put_char,
- .flush_chars = con_flush_chars,
- .chars_in_buffer = con_chars_in_buffer,
- .ioctl = vt_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = vt_compat_ioctl,
-#endif
- .stop = con_stop,
- .start = con_start,
- .throttle = con_throttle,
- .unthrottle = con_unthrottle,
- .resize = vt_resize,
- .shutdown = con_shutdown
-};
-
-static struct cdev vc0_cdev;
-
-int __init vty_init(const struct file_operations *console_fops)
-{
- cdev_init(&vc0_cdev, console_fops);
- if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
- register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
- panic("Couldn't register /dev/tty0 driver\n");
- device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
-
- vcs_init();
-
- console_driver = alloc_tty_driver(MAX_NR_CONSOLES);
- if (!console_driver)
- panic("Couldn't allocate console driver\n");
- console_driver->owner = THIS_MODULE;
- console_driver->name = "tty";
- console_driver->name_base = 1;
- console_driver->major = TTY_MAJOR;
- console_driver->minor_start = 1;
- console_driver->type = TTY_DRIVER_TYPE_CONSOLE;
- console_driver->init_termios = tty_std_termios;
- if (default_utf8)
- console_driver->init_termios.c_iflag |= IUTF8;
- console_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
- tty_set_operations(console_driver, &con_ops);
- if (tty_register_driver(console_driver))
- panic("Couldn't register console driver\n");
- kbd_init();
- console_map_init();
-#ifdef CONFIG_MDA_CONSOLE
- mda_console_init();
-#endif
- return 0;
-}
-
-#ifndef VT_SINGLE_DRIVER
-
-static struct class *vtconsole_class;
-
-static int bind_con_driver(const struct consw *csw, int first, int last,
- int deflt)
-{
- struct module *owner = csw->owner;
- const char *desc = NULL;
- struct con_driver *con_driver;
- int i, j = -1, k = -1, retval = -ENODEV;
-
- if (!try_module_get(owner))
- return -ENODEV;
-
- acquire_console_sem();
-
- /* check if driver is registered */
- for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
- con_driver = ®istered_con_driver[i];
-
- if (con_driver->con == csw) {
- desc = con_driver->desc;
- retval = 0;
- break;
- }
- }
-
- if (retval)
- goto err;
-
- if (!(con_driver->flag & CON_DRIVER_FLAG_INIT)) {
- csw->con_startup();
- con_driver->flag |= CON_DRIVER_FLAG_INIT;
- }
-
- if (deflt) {
- if (conswitchp)
- module_put(conswitchp->owner);
-
- __module_get(owner);
- conswitchp = csw;
- }
-
- first = max(first, con_driver->first);
- last = min(last, con_driver->last);
-
- for (i = first; i <= last; i++) {
- int old_was_color;
- struct vc_data *vc = vc_cons[i].d;
-
- if (con_driver_map[i])
- module_put(con_driver_map[i]->owner);
- __module_get(owner);
- con_driver_map[i] = csw;
-
- if (!vc || !vc->vc_sw)
- continue;
-
- j = i;
-
- if (CON_IS_VISIBLE(vc)) {
- k = i;
- save_screen(vc);
- }
-
- old_was_color = vc->vc_can_do_color;
- vc->vc_sw->con_deinit(vc);
- vc->vc_origin = (unsigned long)vc->vc_screenbuf;
- visual_init(vc, i, 0);
- set_origin(vc);
- update_attr(vc);
-
- /* If the console changed between mono <-> color, then
- * the attributes in the screenbuf will be wrong. The
- * following resets all attributes to something sane.
- */
- if (old_was_color != vc->vc_can_do_color)
- clear_buffer_attributes(vc);
- }
-
- printk("Console: switching ");
- if (!deflt)
- printk("consoles %d-%d ", first+1, last+1);
- if (j >= 0) {
- struct vc_data *vc = vc_cons[j].d;
-
- printk("to %s %s %dx%d\n",
- vc->vc_can_do_color ? "colour" : "mono",
- desc, vc->vc_cols, vc->vc_rows);
-
- if (k >= 0) {
- vc = vc_cons[k].d;
- update_screen(vc);
- }
- } else
- printk("to %s\n", desc);
-
- retval = 0;
-err:
- release_console_sem();
- module_put(owner);
- return retval;
-};
-
-#ifdef CONFIG_VT_HW_CONSOLE_BINDING
-static int con_is_graphics(const struct consw *csw, int first, int last)
-{
- int i, retval = 0;
-
- for (i = first; i <= last; i++) {
- struct vc_data *vc = vc_cons[i].d;
-
- if (vc && vc->vc_mode == KD_GRAPHICS) {
- retval = 1;
- break;
- }
- }
-
- return retval;
-}
-
-/**
- * unbind_con_driver - unbind a console driver
- * @csw: pointer to console driver to unregister
- * @first: first in range of consoles that @csw should be unbound from
- * @last: last in range of consoles that @csw should be unbound from
- * @deflt: should next bound console driver be default after @csw is unbound?
- *
- * To unbind a driver from all possible consoles, pass 0 as @first and
- * %MAX_NR_CONSOLES as @last.
- *
- * @deflt controls whether the console that ends up replacing @csw should be
- * the default console.
- *
- * RETURNS:
- * -ENODEV if @csw isn't a registered console driver or can't be unregistered
- * or 0 on success.
- */
-int unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
-{
- struct module *owner = csw->owner;
- const struct consw *defcsw = NULL;
- struct con_driver *con_driver = NULL, *con_back = NULL;
- int i, retval = -ENODEV;
-
- if (!try_module_get(owner))
- return -ENODEV;
-
- acquire_console_sem();
-
- /* check if driver is registered and if it is unbindable */
- for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
- con_driver = ®istered_con_driver[i];
-
- if (con_driver->con == csw &&
- con_driver->flag & CON_DRIVER_FLAG_MODULE) {
- retval = 0;
- break;
- }
- }
-
- if (retval) {
- release_console_sem();
- goto err;
- }
-
- retval = -ENODEV;
-
- /* check if backup driver exists */
- for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
- con_back = ®istered_con_driver[i];
-
- if (con_back->con &&
- !(con_back->flag & CON_DRIVER_FLAG_MODULE)) {
- defcsw = con_back->con;
- retval = 0;
- break;
- }
- }
-
- if (retval) {
- release_console_sem();
- goto err;
- }
-
- if (!con_is_bound(csw)) {
- release_console_sem();
- goto err;
- }
-
- first = max(first, con_driver->first);
- last = min(last, con_driver->last);
-
- for (i = first; i <= last; i++) {
- if (con_driver_map[i] == csw) {
- module_put(csw->owner);
- con_driver_map[i] = NULL;
- }
- }
-
- if (!con_is_bound(defcsw)) {
- const struct consw *defconsw = conswitchp;
-
- defcsw->con_startup();
- con_back->flag |= CON_DRIVER_FLAG_INIT;
- /*
- * vgacon may change the default driver to point
- * to dummycon, we restore it here...
- */
- conswitchp = defconsw;
- }
-
- if (!con_is_bound(csw))
- con_driver->flag &= ~CON_DRIVER_FLAG_INIT;
-
- release_console_sem();
- /* ignore return value, binding should not fail */
- bind_con_driver(defcsw, first, last, deflt);
-err:
- module_put(owner);
- return retval;
-
-}
-EXPORT_SYMBOL(unbind_con_driver);
-
-static int vt_bind(struct con_driver *con)
-{
- const struct consw *defcsw = NULL, *csw = NULL;
- int i, more = 1, first = -1, last = -1, deflt = 0;
-
- if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) ||
- con_is_graphics(con->con, con->first, con->last))
- goto err;
-
- csw = con->con;
-
- for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
- struct con_driver *con = ®istered_con_driver[i];
-
- if (con->con && !(con->flag & CON_DRIVER_FLAG_MODULE)) {
- defcsw = con->con;
- break;
- }
- }
-
- if (!defcsw)
- goto err;
-
- while (more) {
- more = 0;
-
- for (i = con->first; i <= con->last; i++) {
- if (con_driver_map[i] == defcsw) {
- if (first == -1)
- first = i;
- last = i;
- more = 1;
- } else if (first != -1)
- break;
- }
-
- if (first == 0 && last == MAX_NR_CONSOLES -1)
- deflt = 1;
-
- if (first != -1)
- bind_con_driver(csw, first, last, deflt);
-
- first = -1;
- last = -1;
- deflt = 0;
- }
-
-err:
- return 0;
-}
-
-static int vt_unbind(struct con_driver *con)
-{
- const struct consw *csw = NULL;
- int i, more = 1, first = -1, last = -1, deflt = 0;
-
- if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) ||
- con_is_graphics(con->con, con->first, con->last))
- goto err;
-
- csw = con->con;
-
- while (more) {
- more = 0;
-
- for (i = con->first; i <= con->last; i++) {
- if (con_driver_map[i] == csw) {
- if (first == -1)
- first = i;
- last = i;
- more = 1;
- } else if (first != -1)
- break;
- }
-
- if (first == 0 && last == MAX_NR_CONSOLES -1)
- deflt = 1;
-
- if (first != -1)
- unbind_con_driver(csw, first, last, deflt);
-
- first = -1;
- last = -1;
- deflt = 0;
- }
-
-err:
- return 0;
-}
-#else
-static inline int vt_bind(struct con_driver *con)
-{
- return 0;
-}
-static inline int vt_unbind(struct con_driver *con)
-{
- return 0;
-}
-#endif /* CONFIG_VT_HW_CONSOLE_BINDING */
-
-static ssize_t store_bind(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct con_driver *con = dev_get_drvdata(dev);
- int bind = simple_strtoul(buf, NULL, 0);
-
- if (bind)
- vt_bind(con);
- else
- vt_unbind(con);
-
- return count;
-}
-
-static ssize_t show_bind(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct con_driver *con = dev_get_drvdata(dev);
- int bind = con_is_bound(con->con);
-
- return snprintf(buf, PAGE_SIZE, "%i\n", bind);
-}
-
-static ssize_t show_name(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct con_driver *con = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE, "%s %s\n",
- (con->flag & CON_DRIVER_FLAG_MODULE) ? "(M)" : "(S)",
- con->desc);
-
-}
-
-static struct device_attribute device_attrs[] = {
- __ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind),
- __ATTR(name, S_IRUGO, show_name, NULL),
-};
-
-static int vtconsole_init_device(struct con_driver *con)
-{
- int i;
- int error = 0;
-
- con->flag |= CON_DRIVER_FLAG_ATTR;
- dev_set_drvdata(con->dev, con);
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
- error = device_create_file(con->dev, &device_attrs[i]);
- if (error)
- break;
- }
-
- if (error) {
- while (--i >= 0)
- device_remove_file(con->dev, &device_attrs[i]);
- con->flag &= ~CON_DRIVER_FLAG_ATTR;
- }
-
- return error;
-}
-
-static void vtconsole_deinit_device(struct con_driver *con)
-{
- int i;
-
- if (con->flag & CON_DRIVER_FLAG_ATTR) {
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
- device_remove_file(con->dev, &device_attrs[i]);
- con->flag &= ~CON_DRIVER_FLAG_ATTR;
- }
-}
-
-/**
- * con_is_bound - checks if driver is bound to the console
- * @csw: console driver
- *
- * RETURNS: zero if unbound, nonzero if bound
- *
- * Drivers can call this and if zero, they should release
- * all resources allocated on con_startup()
- */
-int con_is_bound(const struct consw *csw)
-{
- int i, bound = 0;
-
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
- if (con_driver_map[i] == csw) {
- bound = 1;
- break;
- }
- }
-
- return bound;
-}
-EXPORT_SYMBOL(con_is_bound);
-
-/**
- * con_debug_enter - prepare the console for the kernel debugger
- * @sw: console driver
- *
- * Called when the console is taken over by the kernel debugger, this
- * function needs to save the current console state, then put the console
- * into a state suitable for the kernel debugger.
- *
- * RETURNS:
- * Zero on success, nonzero if a failure occurred when trying to prepare
- * the console for the debugger.
- */
-int con_debug_enter(struct vc_data *vc)
-{
- int ret = 0;
-
- saved_fg_console = fg_console;
- saved_last_console = last_console;
- saved_want_console = want_console;
- saved_vc_mode = vc->vc_mode;
- saved_console_blanked = console_blanked;
- vc->vc_mode = KD_TEXT;
- console_blanked = 0;
- if (vc->vc_sw->con_debug_enter)
- ret = vc->vc_sw->con_debug_enter(vc);
-#ifdef CONFIG_KGDB_KDB
- /* Set the initial LINES variable if it is not already set */
- if (vc->vc_rows < 999) {
- int linecount;
- char lns[4];
- const char *setargs[3] = {
- "set",
- "LINES",
- lns,
- };
- if (kdbgetintenv(setargs[0], &linecount)) {
- snprintf(lns, 4, "%i", vc->vc_rows);
- kdb_set(2, setargs);
- }
- }
-#endif /* CONFIG_KGDB_KDB */
- return ret;
-}
-EXPORT_SYMBOL_GPL(con_debug_enter);
-
-/**
- * con_debug_leave - restore console state
- * @sw: console driver
- *
- * Restore the console state to what it was before the kernel debugger
- * was invoked.
- *
- * RETURNS:
- * Zero on success, nonzero if a failure occurred when trying to restore
- * the console.
- */
-int con_debug_leave(void)
-{
- struct vc_data *vc;
- int ret = 0;
-
- fg_console = saved_fg_console;
- last_console = saved_last_console;
- want_console = saved_want_console;
- console_blanked = saved_console_blanked;
- vc_cons[fg_console].d->vc_mode = saved_vc_mode;
-
- vc = vc_cons[fg_console].d;
- if (vc->vc_sw->con_debug_leave)
- ret = vc->vc_sw->con_debug_leave(vc);
- return ret;
-}
-EXPORT_SYMBOL_GPL(con_debug_leave);
-
-/**
- * register_con_driver - register console driver to console layer
- * @csw: console driver
- * @first: the first console to take over, minimum value is 0
- * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1
- *
- * DESCRIPTION: This function registers a console driver which can later
- * bind to a range of consoles specified by @first and @last. It will
- * also initialize the console driver by calling con_startup().
- */
-int register_con_driver(const struct consw *csw, int first, int last)
-{
- struct module *owner = csw->owner;
- struct con_driver *con_driver;
- const char *desc;
- int i, retval = 0;
-
- if (!try_module_get(owner))
- return -ENODEV;
-
- acquire_console_sem();
-
- for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
- con_driver = ®istered_con_driver[i];
-
- /* already registered */
- if (con_driver->con == csw)
- retval = -EINVAL;
- }
-
- if (retval)
- goto err;
-
- desc = csw->con_startup();
-
- if (!desc)
- goto err;
-
- retval = -EINVAL;
-
- for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
- con_driver = ®istered_con_driver[i];
-
- if (con_driver->con == NULL) {
- con_driver->con = csw;
- con_driver->desc = desc;
- con_driver->node = i;
- con_driver->flag = CON_DRIVER_FLAG_MODULE |
- CON_DRIVER_FLAG_INIT;
- con_driver->first = first;
- con_driver->last = last;
- retval = 0;
- break;
- }
- }
-
- if (retval)
- goto err;
-
- con_driver->dev = device_create(vtconsole_class, NULL,
- MKDEV(0, con_driver->node),
- NULL, "vtcon%i",
- con_driver->node);
-
- if (IS_ERR(con_driver->dev)) {
- printk(KERN_WARNING "Unable to create device for %s; "
- "errno = %ld\n", con_driver->desc,
- PTR_ERR(con_driver->dev));
- con_driver->dev = NULL;
- } else {
- vtconsole_init_device(con_driver);
- }
-
-err:
- release_console_sem();
- module_put(owner);
- return retval;
-}
-EXPORT_SYMBOL(register_con_driver);
-
-/**
- * unregister_con_driver - unregister console driver from console layer
- * @csw: console driver
- *
- * DESCRIPTION: All drivers that registers to the console layer must
- * call this function upon exit, or if the console driver is in a state
- * where it won't be able to handle console services, such as the
- * framebuffer console without loaded framebuffer drivers.
- *
- * The driver must unbind first prior to unregistration.
- */
-int unregister_con_driver(const struct consw *csw)
-{
- int i, retval = -ENODEV;
-
- acquire_console_sem();
-
- /* cannot unregister a bound driver */
- if (con_is_bound(csw))
- goto err;
-
- for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
- struct con_driver *con_driver = ®istered_con_driver[i];
-
- if (con_driver->con == csw &&
- con_driver->flag & CON_DRIVER_FLAG_MODULE) {
- vtconsole_deinit_device(con_driver);
- device_destroy(vtconsole_class,
- MKDEV(0, con_driver->node));
- con_driver->con = NULL;
- con_driver->desc = NULL;
- con_driver->dev = NULL;
- con_driver->node = 0;
- con_driver->flag = 0;
- con_driver->first = 0;
- con_driver->last = 0;
- retval = 0;
- break;
- }
- }
-err:
- release_console_sem();
- return retval;
-}
-EXPORT_SYMBOL(unregister_con_driver);
-
-/*
- * 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.
- *
- * take_over_console is basically a register followed by unbind
- */
-int take_over_console(const struct consw *csw, int first, int last, int deflt)
-{
- int err;
-
- err = register_con_driver(csw, first, last);
-
- if (!err)
- bind_con_driver(csw, first, last, deflt);
-
- return err;
-}
-
-/*
- * give_up_console is a wrapper to unregister_con_driver. It will only
- * work if driver is fully unbound.
- */
-void give_up_console(const struct consw *csw)
-{
- unregister_con_driver(csw);
-}
-
-static int __init vtconsole_class_init(void)
-{
- int i;
-
- vtconsole_class = class_create(THIS_MODULE, "vtconsole");
- if (IS_ERR(vtconsole_class)) {
- printk(KERN_WARNING "Unable to create vt console class; "
- "errno = %ld\n", PTR_ERR(vtconsole_class));
- vtconsole_class = NULL;
- }
-
- /* Add system drivers to sysfs */
- for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
- struct con_driver *con = ®istered_con_driver[i];
-
- if (con->con && !con->dev) {
- con->dev = device_create(vtconsole_class, NULL,
- MKDEV(0, con->node),
- NULL, "vtcon%i",
- con->node);
-
- if (IS_ERR(con->dev)) {
- printk(KERN_WARNING "Unable to create "
- "device for %s; errno = %ld\n",
- con->desc, PTR_ERR(con->dev));
- con->dev = NULL;
- } else {
- vtconsole_init_device(con);
- }
- }
- }
-
- return 0;
-}
-postcore_initcall(vtconsole_class_init);
-
-#endif
-
-/*
- * Screen blanking
- */
-
-static int set_vesa_blanking(char __user *p)
-{
- unsigned int mode;
-
- if (get_user(mode, p + 1))
- return -EFAULT;
-
- vesa_blank_mode = (mode < 4) ? mode : 0;
- return 0;
-}
-
-void do_blank_screen(int entering_gfx)
-{
- struct vc_data *vc = vc_cons[fg_console].d;
- int i;
-
- WARN_CONSOLE_UNLOCKED();
-
- if (console_blanked) {
- if (blank_state == blank_vesa_wait) {
- blank_state = blank_off;
- vc->vc_sw->con_blank(vc, vesa_blank_mode + 1, 0);
- }
- return;
- }
-
- /* entering graphics mode? */
- if (entering_gfx) {
- hide_cursor(vc);
- save_screen(vc);
- vc->vc_sw->con_blank(vc, -1, 1);
- console_blanked = fg_console + 1;
- blank_state = blank_off;
- set_origin(vc);
- return;
- }
-
- if (blank_state != blank_normal_wait)
- return;
- blank_state = blank_off;
-
- /* don't blank graphics */
- if (vc->vc_mode != KD_TEXT) {
- console_blanked = fg_console + 1;
- return;
- }
-
- hide_cursor(vc);
- del_timer_sync(&console_timer);
- blank_timer_expired = 0;
-
- save_screen(vc);
- /* In case we need to reset origin, blanking hook returns 1 */
- i = vc->vc_sw->con_blank(vc, vesa_off_interval ? 1 : (vesa_blank_mode + 1), 0);
- console_blanked = fg_console + 1;
- if (i)
- set_origin(vc);
-
- if (console_blank_hook && console_blank_hook(1))
- return;
-
- if (vesa_off_interval && vesa_blank_mode) {
- blank_state = blank_vesa_wait;
- mod_timer(&console_timer, jiffies + vesa_off_interval);
- }
- vt_event_post(VT_EVENT_BLANK, vc->vc_num, vc->vc_num);
-}
-EXPORT_SYMBOL(do_blank_screen);
-
-/*
- * Called by timer as well as from vt_console_driver
- */
-void do_unblank_screen(int leaving_gfx)
-{
- struct vc_data *vc;
-
- /* This should now always be called from a "sane" (read: can schedule)
- * context for the sake of the low level drivers, except in the special
- * case of oops_in_progress
- */
- if (!oops_in_progress)
- might_sleep();
-
- WARN_CONSOLE_UNLOCKED();
-
- ignore_poke = 0;
- if (!console_blanked)
- return;
- if (!vc_cons_allocated(fg_console)) {
- /* impossible */
- printk("unblank_screen: tty %d not allocated ??\n", fg_console+1);
- return;
- }
- vc = vc_cons[fg_console].d;
- /* Try to unblank in oops case too */
- if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc))
- return; /* but leave console_blanked != 0 */
-
- if (blankinterval) {
- mod_timer(&console_timer, jiffies + (blankinterval * HZ));
- blank_state = blank_normal_wait;
- }
-
- console_blanked = 0;
- if (vc->vc_sw->con_blank(vc, 0, leaving_gfx) || vt_force_oops_output(vc))
- /* Low-level driver cannot restore -> do it ourselves */
- update_screen(vc);
- if (console_blank_hook)
- console_blank_hook(0);
- set_palette(vc);
- set_cursor(vc);
- vt_event_post(VT_EVENT_UNBLANK, vc->vc_num, vc->vc_num);
-}
-EXPORT_SYMBOL(do_unblank_screen);
-
-/*
- * This is called by the outside world to cause a forced unblank, mostly for
- * oopses. Currently, I just call do_unblank_screen(0), but we could eventually
- * call it with 1 as an argument and so force a mode restore... that may kill
- * X or at least garbage the screen but would also make the Oops visible...
- */
-void unblank_screen(void)
-{
- do_unblank_screen(0);
-}
-
-/*
- * We defer the timer blanking to work queue so it can take the console mutex
- * (console operations can still happen at irq time, but only from printk which
- * has the console mutex. Not perfect yet, but better than no locking
- */
-static void blank_screen_t(unsigned long dummy)
-{
- if (unlikely(!keventd_up())) {
- mod_timer(&console_timer, jiffies + (blankinterval * HZ));
- return;
- }
- blank_timer_expired = 1;
- schedule_work(&console_work);
-}
-
-void poke_blanked_console(void)
-{
- WARN_CONSOLE_UNLOCKED();
-
- /* Add this so we quickly catch whoever might call us in a non
- * safe context. Nowadays, unblank_screen() isn't to be called in
- * atomic contexts and is allowed to schedule (with the special case
- * of oops_in_progress, but that isn't of any concern for this
- * function. --BenH.
- */
- might_sleep();
-
- /* This isn't perfectly race free, but a race here would be mostly harmless,
- * at worse, we'll do a spurrious blank and it's unlikely
- */
- del_timer(&console_timer);
- blank_timer_expired = 0;
-
- if (ignore_poke || !vc_cons[fg_console].d || vc_cons[fg_console].d->vc_mode == KD_GRAPHICS)
- return;
- if (console_blanked)
- unblank_screen();
- else if (blankinterval) {
- mod_timer(&console_timer, jiffies + (blankinterval * HZ));
- blank_state = blank_normal_wait;
- }
-}
-
-/*
- * Palettes
- */
-
-static void set_palette(struct vc_data *vc)
-{
- WARN_CONSOLE_UNLOCKED();
-
- if (vc->vc_mode != KD_GRAPHICS)
- vc->vc_sw->con_set_palette(vc, color_table);
-}
-
-static int set_get_cmap(unsigned char __user *arg, int set)
-{
- int i, j, k;
-
- WARN_CONSOLE_UNLOCKED();
-
- for (i = 0; i < 16; i++)
- if (set) {
- get_user(default_red[i], arg++);
- get_user(default_grn[i], arg++);
- get_user(default_blu[i], arg++);
- } else {
- put_user(default_red[i], arg++);
- put_user(default_grn[i], arg++);
- put_user(default_blu[i], arg++);
- }
- if (set) {
- for (i = 0; i < MAX_NR_CONSOLES; i++)
- if (vc_cons_allocated(i)) {
- 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(vc_cons[i].d);
- }
- }
- 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.
- */
-
-int con_set_cmap(unsigned char __user *arg)
-{
- int rc;
-
- acquire_console_sem();
- rc = set_get_cmap (arg,1);
- release_console_sem();
-
- return rc;
-}
-
-int con_get_cmap(unsigned char __user *arg)
-{
- int rc;
-
- acquire_console_sem();
- rc = set_get_cmap (arg,0);
- release_console_sem();
-
- return rc;
-}
-
-void reset_palette(struct vc_data *vc)
-{
- int j, k;
- for (j=k=0; j<16; j++) {
- vc->vc_palette[k++] = default_red[j];
- vc->vc_palette[k++] = default_grn[j];
- vc->vc_palette[k++] = default_blu[j];
- }
- set_palette(vc);
-}
-
-/*
- * 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 won't have to convert the fontdata all the time.
- * /Jes
- */
-
-#define max_font_size 65536
-
-static int con_font_get(struct vc_data *vc, struct console_font_op *op)
-{
- struct console_font font;
- int rc = -EINVAL;
- int c;
-
- if (vc->vc_mode != KD_TEXT)
- return -EINVAL;
-
- if (op->data) {
- font.data = kmalloc(max_font_size, GFP_KERNEL);
- if (!font.data)
- return -ENOMEM;
- } else
- font.data = NULL;
-
- acquire_console_sem();
- if (vc->vc_sw->con_font_get)
- rc = vc->vc_sw->con_font_get(vc, &font);
- else
- rc = -ENOSYS;
- release_console_sem();
-
- if (rc)
- goto out;
-
- c = (font.width+7)/8 * 32 * font.charcount;
-
- if (op->data && font.charcount > op->charcount)
- rc = -ENOSPC;
- if (!(op->flags & KD_FONT_FLAG_OLD)) {
- if (font.width > op->width || font.height > op->height)
- rc = -ENOSPC;
- } else {
- if (font.width != 8)
- rc = -EIO;
- else if ((op->height && font.height > op->height) ||
- font.height > 32)
- rc = -ENOSPC;
- }
- if (rc)
- goto out;
-
- op->height = font.height;
- op->width = font.width;
- op->charcount = font.charcount;
-
- if (op->data && copy_to_user(op->data, font.data, c))
- rc = -EFAULT;
-
-out:
- kfree(font.data);
- return rc;
-}
-
-static int con_font_set(struct vc_data *vc, struct console_font_op *op)
-{
- struct console_font font;
- int rc = -EINVAL;
- int size;
-
- if (vc->vc_mode != KD_TEXT)
- return -EINVAL;
- if (!op->data)
- return -EINVAL;
- if (op->charcount > 512)
- return -EINVAL;
- if (!op->height) { /* Need to guess font height [compat] */
- int h, i;
- u8 __user *charmap = op->data;
- u8 tmp;
-
- /* If from KDFONTOP ioctl, don't allow things which can be done in userland,
- so that we can get rid of this soon */
- if (!(op->flags & KD_FONT_FLAG_OLD))
- return -EINVAL;
- for (h = 32; h > 0; h--)
- for (i = 0; i < op->charcount; i++) {
- if (get_user(tmp, &charmap[32*i+h-1]))
- return -EFAULT;
- if (tmp)
- goto nonzero;
- }
- return -EINVAL;
- nonzero:
- op->height = h;
- }
- if (op->width <= 0 || op->width > 32 || op->height > 32)
- return -EINVAL;
- size = (op->width+7)/8 * 32 * op->charcount;
- if (size > max_font_size)
- return -ENOSPC;
- font.charcount = op->charcount;
- font.height = op->height;
- font.width = op->width;
- font.data = memdup_user(op->data, size);
- if (IS_ERR(font.data))
- return PTR_ERR(font.data);
- acquire_console_sem();
- if (vc->vc_sw->con_font_set)
- rc = vc->vc_sw->con_font_set(vc, &font, op->flags);
- else
- rc = -ENOSYS;
- release_console_sem();
- kfree(font.data);
- return rc;
-}
-
-static int con_font_default(struct vc_data *vc, struct console_font_op *op)
-{
- struct console_font font = {.width = op->width, .height = op->height};
- char name[MAX_FONT_NAME];
- char *s = name;
- int rc;
-
- if (vc->vc_mode != KD_TEXT)
- return -EINVAL;
-
- if (!op->data)
- s = NULL;
- else if (strncpy_from_user(name, op->data, MAX_FONT_NAME - 1) < 0)
- return -EFAULT;
- else
- name[MAX_FONT_NAME - 1] = 0;
-
- acquire_console_sem();
- if (vc->vc_sw->con_font_default)
- rc = vc->vc_sw->con_font_default(vc, &font, s);
- else
- rc = -ENOSYS;
- release_console_sem();
- if (!rc) {
- op->width = font.width;
- op->height = font.height;
- }
- return rc;
-}
-
-static int con_font_copy(struct vc_data *vc, struct console_font_op *op)
-{
- int con = op->height;
- int rc;
-
- if (vc->vc_mode != KD_TEXT)
- return -EINVAL;
-
- acquire_console_sem();
- if (!vc->vc_sw->con_font_copy)
- rc = -ENOSYS;
- else if (con < 0 || !vc_cons_allocated(con))
- rc = -ENOTTY;
- else if (con == vc->vc_num) /* nothing to do */
- rc = 0;
- else
- rc = vc->vc_sw->con_font_copy(vc, con);
- release_console_sem();
- return rc;
-}
-
-int con_font_op(struct vc_data *vc, struct console_font_op *op)
-{
- switch (op->op) {
- case KD_FONT_OP_SET:
- return con_font_set(vc, op);
- case KD_FONT_OP_GET:
- return con_font_get(vc, op);
- case KD_FONT_OP_SET_DEFAULT:
- return con_font_default(vc, op);
- case KD_FONT_OP_COPY:
- return con_font_copy(vc, op);
- }
- return -ENOSYS;
-}
-
-/*
- * Interface exported to selection and vcs.
- */
-
-/* used by selection */
-u16 screen_glyph(struct vc_data *vc, int offset)
-{
- u16 w = scr_readw(screenpos(vc, offset, 1));
- u16 c = w & 0xff;
-
- if (w & vc->vc_hi_font_mask)
- c |= 0x100;
- return c;
-}
-EXPORT_SYMBOL_GPL(screen_glyph);
-
-/* used by vcs - note the word offset */
-unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed)
-{
- return screenpos(vc, 2 * w_offset, viewed);
-}
-
-void getconsxy(struct vc_data *vc, unsigned char *p)
-{
- p[0] = vc->vc_x;
- p[1] = vc->vc_y;
-}
-
-void putconsxy(struct vc_data *vc, unsigned char *p)
-{
- hide_cursor(vc);
- gotoxy(vc, p[0], p[1]);
- set_cursor(vc);
-}
-
-u16 vcs_scr_readw(struct vc_data *vc, const u16 *org)
-{
- if ((unsigned long)org == vc->vc_pos && softcursor_original != -1)
- return softcursor_original;
- return scr_readw(org);
-}
-
-void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org)
-{
- scr_writew(val, org);
- if ((unsigned long)org == vc->vc_pos) {
- softcursor_original = -1;
- add_softcursor(vc);
- }
-}
-
-void vcs_scr_updated(struct vc_data *vc)
-{
- notify_update(vc);
-}
-
-/*
- * Visible symbols for modules
- */
-
-EXPORT_SYMBOL(color_table);
-EXPORT_SYMBOL(default_red);
-EXPORT_SYMBOL(default_grn);
-EXPORT_SYMBOL(default_blu);
-EXPORT_SYMBOL(update_region);
-EXPORT_SYMBOL(redraw_screen);
-EXPORT_SYMBOL(vc_resize);
-EXPORT_SYMBOL(fg_console);
-EXPORT_SYMBOL(console_blank_hook);
-EXPORT_SYMBOL(console_blanked);
-EXPORT_SYMBOL(vc_cons);
-EXPORT_SYMBOL(global_cursor_default);
-#ifndef VT_SINGLE_DRIVER
-EXPORT_SYMBOL(take_over_console);
-EXPORT_SYMBOL(give_up_console);
-#endif
+++ /dev/null
-/*
- * linux/drivers/char/vt_ioctl.c
- *
- * Copyright (C) 1992 obz under the linux copyright
- *
- * Dynamic diacritical handling - aeb@cwi.nl - Dec 1993
- * Dynamic keymap and string allocation - aeb@cwi.nl - May 1994
- * Restrict VT switching via ioctl() - grif@cs.ucr.edu - Dec 1995
- * Some code moved for less code duplication - Andi Kleen - Mar 1997
- * Check put/get_user, cleanups - acme@conectiva.com.br - Jun 2001
- */
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/tty.h>
-#include <linux/timer.h>
-#include <linux/kernel.h>
-#include <linux/compat.h>
-#include <linux/module.h>
-#include <linux/kd.h>
-#include <linux/vt.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/major.h>
-#include <linux/fs.h>
-#include <linux/console.h>
-#include <linux/consolemap.h>
-#include <linux/signal.h>
-#include <linux/smp_lock.h>
-#include <linux/timex.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-
-#include <linux/kbd_kern.h>
-#include <linux/vt_kern.h>
-#include <linux/kbd_diacr.h>
-#include <linux/selection.h>
-
-char vt_dont_switch;
-extern struct tty_driver *console_driver;
-
-#define VT_IS_IN_USE(i) (console_driver->ttys[i] && console_driver->ttys[i]->count)
-#define VT_BUSY(i) (VT_IS_IN_USE(i) || i == fg_console || vc_cons[i].d == sel_cons)
-
-/*
- * Console (vt and kd) routines, as defined by USL SVR4 manual, and by
- * experimentation and study of X386 SYSV handling.
- *
- * One point of difference: SYSV vt's are /dev/vtX, which X >= 0, and
- * /dev/console is a separate ttyp. Under Linux, /dev/tty0 is /dev/console,
- * and the vc start at /dev/ttyX, X >= 1. We maintain that here, so we will
- * always treat our set of vt as numbered 1..MAX_NR_CONSOLES (corresponding to
- * ttys 0..MAX_NR_CONSOLES-1). Explicitly naming VT 0 is illegal, but using
- * /dev/tty0 (fg_console) as a target is legal, since an implicit aliasing
- * to the current console is done by the main ioctl code.
- */
-
-#ifdef CONFIG_X86
-#include <linux/syscalls.h>
-#endif
-
-static void complete_change_console(struct vc_data *vc);
-
-/*
- * User space VT_EVENT handlers
- */
-
-struct vt_event_wait {
- struct list_head list;
- struct vt_event event;
- int done;
-};
-
-static LIST_HEAD(vt_events);
-static DEFINE_SPINLOCK(vt_event_lock);
-static DECLARE_WAIT_QUEUE_HEAD(vt_event_waitqueue);
-
-/**
- * vt_event_post
- * @event: the event that occurred
- * @old: old console
- * @new: new console
- *
- * Post an VT event to interested VT handlers
- */
-
-void vt_event_post(unsigned int event, unsigned int old, unsigned int new)
-{
- struct list_head *pos, *head;
- unsigned long flags;
- int wake = 0;
-
- spin_lock_irqsave(&vt_event_lock, flags);
- head = &vt_events;
-
- list_for_each(pos, head) {
- struct vt_event_wait *ve = list_entry(pos,
- struct vt_event_wait, list);
- if (!(ve->event.event & event))
- continue;
- ve->event.event = event;
- /* kernel view is consoles 0..n-1, user space view is
- console 1..n with 0 meaning current, so we must bias */
- ve->event.oldev = old + 1;
- ve->event.newev = new + 1;
- wake = 1;
- ve->done = 1;
- }
- spin_unlock_irqrestore(&vt_event_lock, flags);
- if (wake)
- wake_up_interruptible(&vt_event_waitqueue);
-}
-
-/**
- * vt_event_wait - wait for an event
- * @vw: our event
- *
- * Waits for an event to occur which completes our vt_event_wait
- * structure. On return the structure has wv->done set to 1 for success
- * or 0 if some event such as a signal ended the wait.
- */
-
-static void vt_event_wait(struct vt_event_wait *vw)
-{
- unsigned long flags;
- /* Prepare the event */
- INIT_LIST_HEAD(&vw->list);
- vw->done = 0;
- /* Queue our event */
- spin_lock_irqsave(&vt_event_lock, flags);
- list_add(&vw->list, &vt_events);
- spin_unlock_irqrestore(&vt_event_lock, flags);
- /* Wait for it to pass */
- wait_event_interruptible_tty(vt_event_waitqueue, vw->done);
- /* Dequeue it */
- spin_lock_irqsave(&vt_event_lock, flags);
- list_del(&vw->list);
- spin_unlock_irqrestore(&vt_event_lock, flags);
-}
-
-/**
- * vt_event_wait_ioctl - event ioctl handler
- * @arg: argument to ioctl
- *
- * Implement the VT_WAITEVENT ioctl using the VT event interface
- */
-
-static int vt_event_wait_ioctl(struct vt_event __user *event)
-{
- struct vt_event_wait vw;
-
- if (copy_from_user(&vw.event, event, sizeof(struct vt_event)))
- return -EFAULT;
- /* Highest supported event for now */
- if (vw.event.event & ~VT_MAX_EVENT)
- return -EINVAL;
-
- vt_event_wait(&vw);
- /* If it occurred report it */
- if (vw.done) {
- if (copy_to_user(event, &vw.event, sizeof(struct vt_event)))
- return -EFAULT;
- return 0;
- }
- return -EINTR;
-}
-
-/**
- * vt_waitactive - active console wait
- * @event: event code
- * @n: new console
- *
- * Helper for event waits. Used to implement the legacy
- * event waiting ioctls in terms of events
- */
-
-int vt_waitactive(int n)
-{
- struct vt_event_wait vw;
- do {
- if (n == fg_console + 1)
- break;
- vw.event.event = VT_EVENT_SWITCH;
- vt_event_wait(&vw);
- if (vw.done == 0)
- return -EINTR;
- } while (vw.event.newev != n);
- return 0;
-}
-
-/*
- * these are the valid i/o ports we're allowed to change. they map all the
- * video ports
- */
-#define GPFIRST 0x3b4
-#define GPLAST 0x3df
-#define GPNUM (GPLAST - GPFIRST + 1)
-
-#define i (tmp.kb_index)
-#define s (tmp.kb_table)
-#define v (tmp.kb_value)
-static inline int
-do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, struct kbd_struct *kbd)
-{
- struct kbentry tmp;
- ushort *key_map, val, ov;
-
- if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
- return -EFAULT;
-
- if (!capable(CAP_SYS_TTY_CONFIG))
- perm = 0;
-
- switch (cmd) {
- case KDGKBENT:
- key_map = key_maps[s];
- if (key_map) {
- val = U(key_map[i]);
- if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
- val = K_HOLE;
- } else
- val = (i ? K_HOLE : K_NOSUCHMAP);
- return put_user(val, &user_kbe->kb_value);
- case KDSKBENT:
- if (!perm)
- return -EPERM;
- if (!i && v == K_NOSUCHMAP) {
- /* deallocate map */
- key_map = key_maps[s];
- if (s && key_map) {
- key_maps[s] = NULL;
- if (key_map[0] == U(K_ALLOCATED)) {
- kfree(key_map);
- keymap_count--;
- }
- }
- break;
- }
-
- if (KTYP(v) < NR_TYPES) {
- if (KVAL(v) > max_vals[KTYP(v)])
- return -EINVAL;
- } else
- if (kbd->kbdmode != VC_UNICODE)
- return -EINVAL;
-
- /* ++Geert: non-PC keyboards may generate keycode zero */
-#if !defined(__mc68000__) && !defined(__powerpc__)
- /* assignment to entry 0 only tests validity of args */
- if (!i)
- break;
-#endif
-
- if (!(key_map = key_maps[s])) {
- int j;
-
- if (keymap_count >= MAX_NR_OF_USER_KEYMAPS &&
- !capable(CAP_SYS_RESOURCE))
- return -EPERM;
-
- key_map = kmalloc(sizeof(plain_map),
- GFP_KERNEL);
- if (!key_map)
- return -ENOMEM;
- key_maps[s] = key_map;
- key_map[0] = U(K_ALLOCATED);
- for (j = 1; j < NR_KEYS; j++)
- key_map[j] = U(K_HOLE);
- keymap_count++;
- }
- ov = U(key_map[i]);
- if (v == ov)
- break; /* nothing to do */
- /*
- * Attention Key.
- */
- if (((ov == K_SAK) || (v == K_SAK)) && !capable(CAP_SYS_ADMIN))
- return -EPERM;
- key_map[i] = U(v);
- if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
- compute_shiftstate();
- break;
- }
- return 0;
-}
-#undef i
-#undef s
-#undef v
-
-static inline int
-do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, int perm)
-{
- struct kbkeycode tmp;
- int kc = 0;
-
- if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode)))
- return -EFAULT;
- switch (cmd) {
- case KDGETKEYCODE:
- kc = getkeycode(tmp.scancode);
- if (kc >= 0)
- kc = put_user(kc, &user_kbkc->keycode);
- break;
- case KDSETKEYCODE:
- if (!perm)
- return -EPERM;
- kc = setkeycode(tmp.scancode, tmp.keycode);
- break;
- }
- return kc;
-}
-
-static inline int
-do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
-{
- struct kbsentry *kbs;
- char *p;
- u_char *q;
- u_char __user *up;
- int sz;
- int delta;
- char *first_free, *fj, *fnw;
- int i, j, k;
- int ret;
-
- if (!capable(CAP_SYS_TTY_CONFIG))
- perm = 0;
-
- kbs = kmalloc(sizeof(*kbs), GFP_KERNEL);
- if (!kbs) {
- ret = -ENOMEM;
- goto reterr;
- }
-
- /* we mostly copy too much here (512bytes), but who cares ;) */
- if (copy_from_user(kbs, user_kdgkb, sizeof(struct kbsentry))) {
- ret = -EFAULT;
- goto reterr;
- }
- kbs->kb_string[sizeof(kbs->kb_string)-1] = '\0';
- i = kbs->kb_func;
-
- switch (cmd) {
- case KDGKBSENT:
- sz = sizeof(kbs->kb_string) - 1; /* sz should have been
- a struct member */
- up = user_kdgkb->kb_string;
- p = func_table[i];
- if(p)
- for ( ; *p && sz; p++, sz--)
- if (put_user(*p, up++)) {
- ret = -EFAULT;
- goto reterr;
- }
- if (put_user('\0', up)) {
- ret = -EFAULT;
- goto reterr;
- }
- kfree(kbs);
- return ((p && *p) ? -EOVERFLOW : 0);
- case KDSKBSENT:
- if (!perm) {
- ret = -EPERM;
- goto reterr;
- }
-
- q = func_table[i];
- first_free = funcbufptr + (funcbufsize - funcbufleft);
- for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++)
- ;
- if (j < MAX_NR_FUNC)
- fj = func_table[j];
- else
- fj = first_free;
-
- delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string);
- if (delta <= funcbufleft) { /* it fits in current buf */
- if (j < MAX_NR_FUNC) {
- memmove(fj + delta, fj, first_free - fj);
- for (k = j; k < MAX_NR_FUNC; k++)
- if (func_table[k])
- func_table[k] += delta;
- }
- if (!q)
- func_table[i] = fj;
- funcbufleft -= delta;
- } else { /* allocate a larger buffer */
- sz = 256;
- while (sz < funcbufsize - funcbufleft + delta)
- sz <<= 1;
- fnw = kmalloc(sz, GFP_KERNEL);
- if(!fnw) {
- ret = -ENOMEM;
- goto reterr;
- }
-
- if (!q)
- func_table[i] = fj;
- if (fj > funcbufptr)
- memmove(fnw, funcbufptr, fj - funcbufptr);
- for (k = 0; k < j; k++)
- if (func_table[k])
- func_table[k] = fnw + (func_table[k] - funcbufptr);
-
- if (first_free > fj) {
- memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
- for (k = j; k < MAX_NR_FUNC; k++)
- if (func_table[k])
- func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
- }
- if (funcbufptr != func_buf)
- kfree(funcbufptr);
- funcbufptr = fnw;
- funcbufleft = funcbufleft - delta + sz - funcbufsize;
- funcbufsize = sz;
- }
- strcpy(func_table[i], kbs->kb_string);
- break;
- }
- ret = 0;
-reterr:
- kfree(kbs);
- return ret;
-}
-
-static inline int
-do_fontx_ioctl(int cmd, struct consolefontdesc __user *user_cfd, int perm, struct console_font_op *op)
-{
- struct consolefontdesc cfdarg;
- int i;
-
- if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc)))
- return -EFAULT;
-
- switch (cmd) {
- case PIO_FONTX:
- if (!perm)
- return -EPERM;
- op->op = KD_FONT_OP_SET;
- op->flags = KD_FONT_FLAG_OLD;
- op->width = 8;
- op->height = cfdarg.charheight;
- op->charcount = cfdarg.charcount;
- op->data = cfdarg.chardata;
- return con_font_op(vc_cons[fg_console].d, op);
- case GIO_FONTX: {
- op->op = KD_FONT_OP_GET;
- op->flags = KD_FONT_FLAG_OLD;
- op->width = 8;
- op->height = cfdarg.charheight;
- op->charcount = cfdarg.charcount;
- op->data = cfdarg.chardata;
- i = con_font_op(vc_cons[fg_console].d, op);
- if (i)
- return i;
- cfdarg.charheight = op->height;
- cfdarg.charcount = op->charcount;
- if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc)))
- return -EFAULT;
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static inline int
-do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_data *vc)
-{
- struct unimapdesc tmp;
-
- if (copy_from_user(&tmp, user_ud, sizeof tmp))
- return -EFAULT;
- if (tmp.entries)
- if (!access_ok(VERIFY_WRITE, tmp.entries,
- tmp.entry_ct*sizeof(struct unipair)))
- return -EFAULT;
- switch (cmd) {
- case PIO_UNIMAP:
- if (!perm)
- return -EPERM;
- return con_set_unimap(vc, tmp.entry_ct, tmp.entries);
- case GIO_UNIMAP:
- if (!perm && fg_console != vc->vc_num)
- return -EPERM;
- return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp.entries);
- }
- return 0;
-}
-
-
-
-/*
- * We handle the console-specific ioctl's here. We allow the
- * capability to modify any console, not just the fg_console.
- */
-int vt_ioctl(struct tty_struct *tty, struct file * file,
- unsigned int cmd, unsigned long arg)
-{
- struct vc_data *vc = tty->driver_data;
- struct console_font_op op; /* used in multiple places here */
- struct kbd_struct * kbd;
- unsigned int console;
- unsigned char ucval;
- unsigned int uival;
- void __user *up = (void __user *)arg;
- int i, perm;
- int ret = 0;
-
- console = vc->vc_num;
-
- tty_lock();
-
- if (!vc_cons_allocated(console)) { /* impossible? */
- ret = -ENOIOCTLCMD;
- goto out;
- }
-
-
- /*
- * To have permissions to do most of the vt ioctls, we either have
- * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.
- */
- perm = 0;
- if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG))
- perm = 1;
-
- kbd = kbd_table + console;
- switch (cmd) {
- case TIOCLINUX:
- ret = tioclinux(tty, arg);
- break;
- case KIOCSOUND:
- if (!perm)
- goto eperm;
- /*
- * The use of PIT_TICK_RATE is historic, it used to be
- * the platform-dependent CLOCK_TICK_RATE between 2.6.12
- * and 2.6.36, which was a minor but unfortunate ABI
- * change.
- */
- if (arg)
- arg = PIT_TICK_RATE / arg;
- kd_mksound(arg, 0);
- break;
-
- case KDMKTONE:
- if (!perm)
- goto eperm;
- {
- unsigned int ticks, count;
-
- /*
- * Generate the tone for the appropriate number of ticks.
- * If the time is zero, turn off sound ourselves.
- */
- ticks = HZ * ((arg >> 16) & 0xffff) / 1000;
- count = ticks ? (arg & 0xffff) : 0;
- if (count)
- count = PIT_TICK_RATE / count;
- kd_mksound(count, ticks);
- break;
- }
-
- case KDGKBTYPE:
- /*
- * this is naive.
- */
- ucval = KB_101;
- goto setchar;
-
- /*
- * These cannot be implemented on any machine that implements
- * ioperm() in user level (such as Alpha PCs) or not at all.
- *
- * XXX: you should never use these, just call ioperm directly..
- */
-#ifdef CONFIG_X86
- case KDADDIO:
- case KDDELIO:
- /*
- * KDADDIO and KDDELIO may be able to add ports beyond what
- * we reject here, but to be safe...
- */
- if (arg < GPFIRST || arg > GPLAST) {
- ret = -EINVAL;
- break;
- }
- ret = sys_ioperm(arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0;
- break;
-
- case KDENABIO:
- case KDDISABIO:
- ret = sys_ioperm(GPFIRST, GPNUM,
- (cmd == KDENABIO)) ? -ENXIO : 0;
- break;
-#endif
-
- /* Linux m68k/i386 interface for setting the keyboard delay/repeat rate */
-
- case KDKBDREP:
- {
- struct kbd_repeat kbrep;
-
- if (!capable(CAP_SYS_TTY_CONFIG))
- goto eperm;
-
- if (copy_from_user(&kbrep, up, sizeof(struct kbd_repeat))) {
- ret = -EFAULT;
- break;
- }
- ret = kbd_rate(&kbrep);
- if (ret)
- break;
- if (copy_to_user(up, &kbrep, sizeof(struct kbd_repeat)))
- ret = -EFAULT;
- break;
- }
-
- case KDSETMODE:
- /*
- * currently, setting the mode from KD_TEXT to KD_GRAPHICS
- * doesn't do a whole lot. i'm not sure if it should do any
- * restoration of modes or what...
- *
- * XXX It should at least call into the driver, fbdev's definitely
- * need to restore their engine state. --BenH
- */
- if (!perm)
- goto eperm;
- switch (arg) {
- case KD_GRAPHICS:
- break;
- case KD_TEXT0:
- case KD_TEXT1:
- arg = KD_TEXT;
- case KD_TEXT:
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
- if (vc->vc_mode == (unsigned char) arg)
- break;
- vc->vc_mode = (unsigned char) arg;
- if (console != fg_console)
- break;
- /*
- * explicitly blank/unblank the screen if switching modes
- */
- acquire_console_sem();
- if (arg == KD_TEXT)
- do_unblank_screen(1);
- else
- do_blank_screen(1);
- release_console_sem();
- break;
-
- case KDGETMODE:
- uival = vc->vc_mode;
- goto setint;
-
- case KDMAPDISP:
- case KDUNMAPDISP:
- /*
- * these work like a combination of mmap and KDENABIO.
- * this could be easily finished.
- */
- ret = -EINVAL;
- break;
-
- case KDSKBMODE:
- if (!perm)
- goto eperm;
- switch(arg) {
- case K_RAW:
- kbd->kbdmode = VC_RAW;
- break;
- case K_MEDIUMRAW:
- kbd->kbdmode = VC_MEDIUMRAW;
- break;
- case K_XLATE:
- kbd->kbdmode = VC_XLATE;
- compute_shiftstate();
- break;
- case K_UNICODE:
- kbd->kbdmode = VC_UNICODE;
- compute_shiftstate();
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
- tty_ldisc_flush(tty);
- break;
-
- case KDGKBMODE:
- uival = ((kbd->kbdmode == VC_RAW) ? K_RAW :
- (kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW :
- (kbd->kbdmode == VC_UNICODE) ? K_UNICODE :
- K_XLATE);
- goto setint;
-
- /* this could be folded into KDSKBMODE, but for compatibility
- reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */
- case KDSKBMETA:
- switch(arg) {
- case K_METABIT:
- clr_vc_kbd_mode(kbd, VC_META);
- break;
- case K_ESCPREFIX:
- set_vc_kbd_mode(kbd, VC_META);
- break;
- default:
- ret = -EINVAL;
- }
- break;
-
- case KDGKBMETA:
- uival = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT);
- setint:
- ret = put_user(uival, (int __user *)arg);
- break;
-
- case KDGETKEYCODE:
- case KDSETKEYCODE:
- if(!capable(CAP_SYS_TTY_CONFIG))
- perm = 0;
- ret = do_kbkeycode_ioctl(cmd, up, perm);
- break;
-
- case KDGKBENT:
- case KDSKBENT:
- ret = do_kdsk_ioctl(cmd, up, perm, kbd);
- break;
-
- case KDGKBSENT:
- case KDSKBSENT:
- ret = do_kdgkb_ioctl(cmd, up, perm);
- break;
-
- case KDGKBDIACR:
- {
- struct kbdiacrs __user *a = up;
- struct kbdiacr diacr;
- int i;
-
- if (put_user(accent_table_size, &a->kb_cnt)) {
- ret = -EFAULT;
- break;
- }
- for (i = 0; i < accent_table_size; i++) {
- diacr.diacr = conv_uni_to_8bit(accent_table[i].diacr);
- diacr.base = conv_uni_to_8bit(accent_table[i].base);
- diacr.result = conv_uni_to_8bit(accent_table[i].result);
- if (copy_to_user(a->kbdiacr + i, &diacr, sizeof(struct kbdiacr))) {
- ret = -EFAULT;
- break;
- }
- }
- break;
- }
- case KDGKBDIACRUC:
- {
- struct kbdiacrsuc __user *a = up;
-
- if (put_user(accent_table_size, &a->kb_cnt))
- ret = -EFAULT;
- else if (copy_to_user(a->kbdiacruc, accent_table,
- accent_table_size*sizeof(struct kbdiacruc)))
- ret = -EFAULT;
- break;
- }
-
- case KDSKBDIACR:
- {
- struct kbdiacrs __user *a = up;
- struct kbdiacr diacr;
- unsigned int ct;
- int i;
-
- if (!perm)
- goto eperm;
- if (get_user(ct,&a->kb_cnt)) {
- ret = -EFAULT;
- break;
- }
- if (ct >= MAX_DIACR) {
- ret = -EINVAL;
- break;
- }
- accent_table_size = ct;
- for (i = 0; i < ct; i++) {
- if (copy_from_user(&diacr, a->kbdiacr + i, sizeof(struct kbdiacr))) {
- ret = -EFAULT;
- break;
- }
- accent_table[i].diacr = conv_8bit_to_uni(diacr.diacr);
- accent_table[i].base = conv_8bit_to_uni(diacr.base);
- accent_table[i].result = conv_8bit_to_uni(diacr.result);
- }
- break;
- }
-
- case KDSKBDIACRUC:
- {
- struct kbdiacrsuc __user *a = up;
- unsigned int ct;
-
- if (!perm)
- goto eperm;
- if (get_user(ct,&a->kb_cnt)) {
- ret = -EFAULT;
- break;
- }
- if (ct >= MAX_DIACR) {
- ret = -EINVAL;
- break;
- }
- accent_table_size = ct;
- if (copy_from_user(accent_table, a->kbdiacruc, ct*sizeof(struct kbdiacruc)))
- ret = -EFAULT;
- break;
- }
-
- /* the ioctls below read/set the flags usually shown in the leds */
- /* don't use them - they will go away without warning */
- case KDGKBLED:
- ucval = kbd->ledflagstate | (kbd->default_ledflagstate << 4);
- goto setchar;
-
- case KDSKBLED:
- if (!perm)
- goto eperm;
- if (arg & ~0x77) {
- ret = -EINVAL;
- break;
- }
- kbd->ledflagstate = (arg & 7);
- kbd->default_ledflagstate = ((arg >> 4) & 7);
- set_leds();
- break;
-
- /* the ioctls below only set the lights, not the functions */
- /* for those, see KDGKBLED and KDSKBLED above */
- case KDGETLED:
- ucval = getledstate();
- setchar:
- ret = put_user(ucval, (char __user *)arg);
- break;
-
- case KDSETLED:
- if (!perm)
- goto eperm;
- setledstate(kbd, arg);
- break;
-
- /*
- * A process can indicate its willingness to accept signals
- * generated by pressing an appropriate key combination.
- * Thus, one can have a daemon that e.g. spawns a new console
- * upon a keypress and then changes to it.
- * See also the kbrequest field of inittab(5).
- */
- case KDSIGACCEPT:
- {
- if (!perm || !capable(CAP_KILL))
- goto eperm;
- if (!valid_signal(arg) || arg < 1 || arg == SIGKILL)
- ret = -EINVAL;
- else {
- spin_lock_irq(&vt_spawn_con.lock);
- put_pid(vt_spawn_con.pid);
- vt_spawn_con.pid = get_pid(task_pid(current));
- vt_spawn_con.sig = arg;
- spin_unlock_irq(&vt_spawn_con.lock);
- }
- break;
- }
-
- case VT_SETMODE:
- {
- struct vt_mode tmp;
-
- if (!perm)
- goto eperm;
- if (copy_from_user(&tmp, up, sizeof(struct vt_mode))) {
- ret = -EFAULT;
- goto out;
- }
- if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS) {
- ret = -EINVAL;
- goto out;
- }
- acquire_console_sem();
- vc->vt_mode = tmp;
- /* the frsig is ignored, so we set it to 0 */
- vc->vt_mode.frsig = 0;
- put_pid(vc->vt_pid);
- vc->vt_pid = get_pid(task_pid(current));
- /* no switch is required -- saw@shade.msu.ru */
- vc->vt_newvt = -1;
- release_console_sem();
- break;
- }
-
- case VT_GETMODE:
- {
- struct vt_mode tmp;
- int rc;
-
- acquire_console_sem();
- memcpy(&tmp, &vc->vt_mode, sizeof(struct vt_mode));
- release_console_sem();
-
- rc = copy_to_user(up, &tmp, sizeof(struct vt_mode));
- if (rc)
- ret = -EFAULT;
- break;
- }
-
- /*
- * Returns global vt state. Note that VT 0 is always open, since
- * it's an alias for the current VT, and people can't use it here.
- * We cannot return state for more than 16 VTs, since v_state is short.
- */
- case VT_GETSTATE:
- {
- struct vt_stat __user *vtstat = up;
- unsigned short state, mask;
-
- if (put_user(fg_console + 1, &vtstat->v_active))
- ret = -EFAULT;
- else {
- state = 1; /* /dev/tty0 is always open */
- for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask;
- ++i, mask <<= 1)
- if (VT_IS_IN_USE(i))
- state |= mask;
- ret = put_user(state, &vtstat->v_state);
- }
- break;
- }
-
- /*
- * Returns the first available (non-opened) console.
- */
- case VT_OPENQRY:
- for (i = 0; i < MAX_NR_CONSOLES; ++i)
- if (! VT_IS_IN_USE(i))
- break;
- uival = i < MAX_NR_CONSOLES ? (i+1) : -1;
- goto setint;
-
- /*
- * ioctl(fd, VT_ACTIVATE, num) will cause us to switch to vt # num,
- * with num >= 1 (switches to vt 0, our console, are not allowed, just
- * to preserve sanity).
- */
- case VT_ACTIVATE:
- if (!perm)
- goto eperm;
- if (arg == 0 || arg > MAX_NR_CONSOLES)
- ret = -ENXIO;
- else {
- arg--;
- acquire_console_sem();
- ret = vc_allocate(arg);
- release_console_sem();
- if (ret)
- break;
- set_console(arg);
- }
- break;
-
- case VT_SETACTIVATE:
- {
- struct vt_setactivate vsa;
-
- if (!perm)
- goto eperm;
-
- if (copy_from_user(&vsa, (struct vt_setactivate __user *)arg,
- sizeof(struct vt_setactivate))) {
- ret = -EFAULT;
- goto out;
- }
- if (vsa.console == 0 || vsa.console > MAX_NR_CONSOLES)
- ret = -ENXIO;
- else {
- vsa.console--;
- acquire_console_sem();
- ret = vc_allocate(vsa.console);
- if (ret == 0) {
- struct vc_data *nvc;
- /* This is safe providing we don't drop the
- console sem between vc_allocate and
- finishing referencing nvc */
- nvc = vc_cons[vsa.console].d;
- nvc->vt_mode = vsa.mode;
- nvc->vt_mode.frsig = 0;
- put_pid(nvc->vt_pid);
- nvc->vt_pid = get_pid(task_pid(current));
- }
- release_console_sem();
- if (ret)
- break;
- /* Commence switch and lock */
- set_console(arg);
- }
- }
-
- /*
- * wait until the specified VT has been activated
- */
- case VT_WAITACTIVE:
- if (!perm)
- goto eperm;
- if (arg == 0 || arg > MAX_NR_CONSOLES)
- ret = -ENXIO;
- else
- ret = vt_waitactive(arg);
- break;
-
- /*
- * If a vt is under process control, the kernel will not switch to it
- * immediately, but postpone the operation until the process calls this
- * ioctl, allowing the switch to complete.
- *
- * According to the X sources this is the behavior:
- * 0: pending switch-from not OK
- * 1: pending switch-from OK
- * 2: completed switch-to OK
- */
- case VT_RELDISP:
- if (!perm)
- goto eperm;
-
- if (vc->vt_mode.mode != VT_PROCESS) {
- ret = -EINVAL;
- break;
- }
- /*
- * Switching-from response
- */
- acquire_console_sem();
- if (vc->vt_newvt >= 0) {
- if (arg == 0)
- /*
- * Switch disallowed, so forget we were trying
- * to do it.
- */
- vc->vt_newvt = -1;
-
- else {
- /*
- * The current vt has been released, so
- * complete the switch.
- */
- int newvt;
- newvt = vc->vt_newvt;
- vc->vt_newvt = -1;
- ret = vc_allocate(newvt);
- if (ret) {
- release_console_sem();
- break;
- }
- /*
- * When we actually do the console switch,
- * make sure we are atomic with respect to
- * other console switches..
- */
- complete_change_console(vc_cons[newvt].d);
- }
- } else {
- /*
- * Switched-to response
- */
- /*
- * If it's just an ACK, ignore it
- */
- if (arg != VT_ACKACQ)
- ret = -EINVAL;
- }
- release_console_sem();
- break;
-
- /*
- * Disallocate memory associated to VT (but leave VT1)
- */
- case VT_DISALLOCATE:
- if (arg > MAX_NR_CONSOLES) {
- ret = -ENXIO;
- break;
- }
- if (arg == 0) {
- /* deallocate all unused consoles, but leave 0 */
- acquire_console_sem();
- for (i=1; i<MAX_NR_CONSOLES; i++)
- if (! VT_BUSY(i))
- vc_deallocate(i);
- release_console_sem();
- } else {
- /* deallocate a single console, if possible */
- arg--;
- if (VT_BUSY(arg))
- ret = -EBUSY;
- else if (arg) { /* leave 0 */
- acquire_console_sem();
- vc_deallocate(arg);
- release_console_sem();
- }
- }
- break;
-
- case VT_RESIZE:
- {
- struct vt_sizes __user *vtsizes = up;
- struct vc_data *vc;
-
- ushort ll,cc;
- if (!perm)
- goto eperm;
- if (get_user(ll, &vtsizes->v_rows) ||
- get_user(cc, &vtsizes->v_cols))
- ret = -EFAULT;
- else {
- acquire_console_sem();
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
- vc = vc_cons[i].d;
-
- if (vc) {
- vc->vc_resize_user = 1;
- vc_resize(vc_cons[i].d, cc, ll);
- }
- }
- release_console_sem();
- }
- break;
- }
-
- case VT_RESIZEX:
- {
- struct vt_consize __user *vtconsize = up;
- ushort ll,cc,vlin,clin,vcol,ccol;
- if (!perm)
- goto eperm;
- if (!access_ok(VERIFY_READ, vtconsize,
- sizeof(struct vt_consize))) {
- ret = -EFAULT;
- break;
- }
- /* FIXME: Should check the copies properly */
- __get_user(ll, &vtconsize->v_rows);
- __get_user(cc, &vtconsize->v_cols);
- __get_user(vlin, &vtconsize->v_vlin);
- __get_user(clin, &vtconsize->v_clin);
- __get_user(vcol, &vtconsize->v_vcol);
- __get_user(ccol, &vtconsize->v_ccol);
- vlin = vlin ? vlin : vc->vc_scan_lines;
- if (clin) {
- if (ll) {
- if (ll != vlin/clin) {
- /* Parameters don't add up */
- ret = -EINVAL;
- break;
- }
- } else
- ll = vlin/clin;
- }
- if (vcol && ccol) {
- if (cc) {
- if (cc != vcol/ccol) {
- ret = -EINVAL;
- break;
- }
- } else
- cc = vcol/ccol;
- }
-
- if (clin > 32) {
- ret = -EINVAL;
- break;
- }
-
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
- if (!vc_cons[i].d)
- continue;
- acquire_console_sem();
- if (vlin)
- vc_cons[i].d->vc_scan_lines = vlin;
- if (clin)
- vc_cons[i].d->vc_font.height = clin;
- vc_cons[i].d->vc_resize_user = 1;
- vc_resize(vc_cons[i].d, cc, ll);
- release_console_sem();
- }
- break;
- }
-
- case PIO_FONT: {
- if (!perm)
- goto eperm;
- op.op = KD_FONT_OP_SET;
- op.flags = KD_FONT_FLAG_OLD | KD_FONT_FLAG_DONT_RECALC; /* Compatibility */
- op.width = 8;
- op.height = 0;
- op.charcount = 256;
- op.data = up;
- ret = con_font_op(vc_cons[fg_console].d, &op);
- break;
- }
-
- case GIO_FONT: {
- op.op = KD_FONT_OP_GET;
- op.flags = KD_FONT_FLAG_OLD;
- op.width = 8;
- op.height = 32;
- op.charcount = 256;
- op.data = up;
- ret = con_font_op(vc_cons[fg_console].d, &op);
- break;
- }
-
- case PIO_CMAP:
- if (!perm)
- ret = -EPERM;
- else
- ret = con_set_cmap(up);
- break;
-
- case GIO_CMAP:
- ret = con_get_cmap(up);
- break;
-
- case PIO_FONTX:
- case GIO_FONTX:
- ret = do_fontx_ioctl(cmd, up, perm, &op);
- break;
-
- case PIO_FONTRESET:
- {
- if (!perm)
- goto eperm;
-
-#ifdef BROKEN_GRAPHICS_PROGRAMS
- /* With BROKEN_GRAPHICS_PROGRAMS defined, the default
- font is not saved. */
- ret = -ENOSYS;
- break;
-#else
- {
- op.op = KD_FONT_OP_SET_DEFAULT;
- op.data = NULL;
- ret = con_font_op(vc_cons[fg_console].d, &op);
- if (ret)
- break;
- con_set_default_unimap(vc_cons[fg_console].d);
- break;
- }
-#endif
- }
-
- case KDFONTOP: {
- if (copy_from_user(&op, up, sizeof(op))) {
- ret = -EFAULT;
- break;
- }
- if (!perm && op.op != KD_FONT_OP_GET)
- goto eperm;
- ret = con_font_op(vc, &op);
- if (ret)
- break;
- if (copy_to_user(up, &op, sizeof(op)))
- ret = -EFAULT;
- break;
- }
-
- case PIO_SCRNMAP:
- if (!perm)
- ret = -EPERM;
- else
- ret = con_set_trans_old(up);
- break;
-
- case GIO_SCRNMAP:
- ret = con_get_trans_old(up);
- break;
-
- case PIO_UNISCRNMAP:
- if (!perm)
- ret = -EPERM;
- else
- ret = con_set_trans_new(up);
- break;
-
- case GIO_UNISCRNMAP:
- ret = con_get_trans_new(up);
- break;
-
- case PIO_UNIMAPCLR:
- { struct unimapinit ui;
- if (!perm)
- goto eperm;
- ret = copy_from_user(&ui, up, sizeof(struct unimapinit));
- if (ret)
- ret = -EFAULT;
- else
- con_clear_unimap(vc, &ui);
- break;
- }
-
- case PIO_UNIMAP:
- case GIO_UNIMAP:
- ret = do_unimap_ioctl(cmd, up, perm, vc);
- break;
-
- case VT_LOCKSWITCH:
- if (!capable(CAP_SYS_TTY_CONFIG))
- goto eperm;
- vt_dont_switch = 1;
- break;
- case VT_UNLOCKSWITCH:
- if (!capable(CAP_SYS_TTY_CONFIG))
- goto eperm;
- vt_dont_switch = 0;
- break;
- case VT_GETHIFONTMASK:
- ret = put_user(vc->vc_hi_font_mask,
- (unsigned short __user *)arg);
- break;
- case VT_WAITEVENT:
- ret = vt_event_wait_ioctl((struct vt_event __user *)arg);
- break;
- default:
- ret = -ENOIOCTLCMD;
- }
-out:
- tty_unlock();
- return ret;
-eperm:
- ret = -EPERM;
- goto out;
-}
-
-void reset_vc(struct vc_data *vc)
-{
- vc->vc_mode = KD_TEXT;
- kbd_table[vc->vc_num].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
- vc->vt_mode.mode = VT_AUTO;
- vc->vt_mode.waitv = 0;
- vc->vt_mode.relsig = 0;
- vc->vt_mode.acqsig = 0;
- vc->vt_mode.frsig = 0;
- put_pid(vc->vt_pid);
- vc->vt_pid = NULL;
- vc->vt_newvt = -1;
- if (!in_interrupt()) /* Via keyboard.c:SAK() - akpm */
- reset_palette(vc);
-}
-
-void vc_SAK(struct work_struct *work)
-{
- struct vc *vc_con =
- container_of(work, struct vc, SAK_work);
- struct vc_data *vc;
- struct tty_struct *tty;
-
- acquire_console_sem();
- vc = vc_con->d;
- if (vc) {
- tty = vc->port.tty;
- /*
- * SAK should also work in all raw modes and reset
- * them properly.
- */
- if (tty)
- __do_SAK(tty);
- reset_vc(vc);
- }
- release_console_sem();
-}
-
-#ifdef CONFIG_COMPAT
-
-struct compat_consolefontdesc {
- unsigned short charcount; /* characters in font (256 or 512) */
- unsigned short charheight; /* scan lines per character (1-32) */
- compat_caddr_t chardata; /* font data in expanded form */
-};
-
-static inline int
-compat_fontx_ioctl(int cmd, struct compat_consolefontdesc __user *user_cfd,
- int perm, struct console_font_op *op)
-{
- struct compat_consolefontdesc cfdarg;
- int i;
-
- if (copy_from_user(&cfdarg, user_cfd, sizeof(struct compat_consolefontdesc)))
- return -EFAULT;
-
- switch (cmd) {
- case PIO_FONTX:
- if (!perm)
- return -EPERM;
- op->op = KD_FONT_OP_SET;
- op->flags = KD_FONT_FLAG_OLD;
- op->width = 8;
- op->height = cfdarg.charheight;
- op->charcount = cfdarg.charcount;
- op->data = compat_ptr(cfdarg.chardata);
- return con_font_op(vc_cons[fg_console].d, op);
- case GIO_FONTX:
- op->op = KD_FONT_OP_GET;
- op->flags = KD_FONT_FLAG_OLD;
- op->width = 8;
- op->height = cfdarg.charheight;
- op->charcount = cfdarg.charcount;
- op->data = compat_ptr(cfdarg.chardata);
- i = con_font_op(vc_cons[fg_console].d, op);
- if (i)
- return i;
- cfdarg.charheight = op->height;
- cfdarg.charcount = op->charcount;
- if (copy_to_user(user_cfd, &cfdarg, sizeof(struct compat_consolefontdesc)))
- return -EFAULT;
- return 0;
- }
- return -EINVAL;
-}
-
-struct compat_console_font_op {
- compat_uint_t op; /* operation code KD_FONT_OP_* */
- compat_uint_t flags; /* KD_FONT_FLAG_* */
- compat_uint_t width, height; /* font size */
- compat_uint_t charcount;
- compat_caddr_t data; /* font data with height fixed to 32 */
-};
-
-static inline int
-compat_kdfontop_ioctl(struct compat_console_font_op __user *fontop,
- int perm, struct console_font_op *op, struct vc_data *vc)
-{
- int i;
-
- if (copy_from_user(op, fontop, sizeof(struct compat_console_font_op)))
- return -EFAULT;
- if (!perm && op->op != KD_FONT_OP_GET)
- return -EPERM;
- op->data = compat_ptr(((struct compat_console_font_op *)op)->data);
- op->flags |= KD_FONT_FLAG_OLD;
- i = con_font_op(vc, op);
- if (i)
- return i;
- ((struct compat_console_font_op *)op)->data = (unsigned long)op->data;
- if (copy_to_user(fontop, op, sizeof(struct compat_console_font_op)))
- return -EFAULT;
- return 0;
-}
-
-struct compat_unimapdesc {
- unsigned short entry_ct;
- compat_caddr_t entries;
-};
-
-static inline int
-compat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud,
- int perm, struct vc_data *vc)
-{
- struct compat_unimapdesc tmp;
- struct unipair __user *tmp_entries;
-
- if (copy_from_user(&tmp, user_ud, sizeof tmp))
- return -EFAULT;
- tmp_entries = compat_ptr(tmp.entries);
- if (tmp_entries)
- if (!access_ok(VERIFY_WRITE, tmp_entries,
- tmp.entry_ct*sizeof(struct unipair)))
- return -EFAULT;
- switch (cmd) {
- case PIO_UNIMAP:
- if (!perm)
- return -EPERM;
- return con_set_unimap(vc, tmp.entry_ct, tmp_entries);
- case GIO_UNIMAP:
- if (!perm && fg_console != vc->vc_num)
- return -EPERM;
- return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp_entries);
- }
- return 0;
-}
-
-long vt_compat_ioctl(struct tty_struct *tty, struct file * file,
- unsigned int cmd, unsigned long arg)
-{
- struct vc_data *vc = tty->driver_data;
- struct console_font_op op; /* used in multiple places here */
- struct kbd_struct *kbd;
- unsigned int console;
- void __user *up = (void __user *)arg;
- int perm;
- int ret = 0;
-
- console = vc->vc_num;
-
- tty_lock();
-
- if (!vc_cons_allocated(console)) { /* impossible? */
- ret = -ENOIOCTLCMD;
- goto out;
- }
-
- /*
- * To have permissions to do most of the vt ioctls, we either have
- * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.
- */
- perm = 0;
- if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG))
- perm = 1;
-
- kbd = kbd_table + console;
- switch (cmd) {
- /*
- * these need special handlers for incompatible data structures
- */
- case PIO_FONTX:
- case GIO_FONTX:
- ret = compat_fontx_ioctl(cmd, up, perm, &op);
- break;
-
- case KDFONTOP:
- ret = compat_kdfontop_ioctl(up, perm, &op, vc);
- break;
-
- case PIO_UNIMAP:
- case GIO_UNIMAP:
- ret = compat_unimap_ioctl(cmd, up, perm, vc);
- break;
-
- /*
- * all these treat 'arg' as an integer
- */
- case KIOCSOUND:
- case KDMKTONE:
-#ifdef CONFIG_X86
- case KDADDIO:
- case KDDELIO:
-#endif
- case KDSETMODE:
- case KDMAPDISP:
- case KDUNMAPDISP:
- case KDSKBMODE:
- case KDSKBMETA:
- case KDSKBLED:
- case KDSETLED:
- case KDSIGACCEPT:
- case VT_ACTIVATE:
- case VT_WAITACTIVE:
- case VT_RELDISP:
- case VT_DISALLOCATE:
- case VT_RESIZE:
- case VT_RESIZEX:
- goto fallback;
-
- /*
- * the rest has a compatible data structure behind arg,
- * but we have to convert it to a proper 64 bit pointer.
- */
- default:
- arg = (unsigned long)compat_ptr(arg);
- goto fallback;
- }
-out:
- tty_unlock();
- return ret;
-
-fallback:
- tty_unlock();
- return vt_ioctl(tty, file, cmd, arg);
-}
-
-
-#endif /* CONFIG_COMPAT */
-
-
-/*
- * Performs the back end of a vt switch. Called under the console
- * semaphore.
- */
-static void complete_change_console(struct vc_data *vc)
-{
- unsigned char old_vc_mode;
- int old = fg_console;
-
- last_console = fg_console;
-
- /*
- * If we're switching, we could be going from KD_GRAPHICS to
- * KD_TEXT mode or vice versa, which means we need to blank or
- * unblank the screen later.
- */
- old_vc_mode = vc_cons[fg_console].d->vc_mode;
- switch_screen(vc);
-
- /*
- * This can't appear below a successful kill_pid(). If it did,
- * then the *blank_screen operation could occur while X, having
- * received acqsig, is waking up on another processor. This
- * condition can lead to overlapping accesses to the VGA range
- * and the framebuffer (causing system lockups).
- *
- * To account for this we duplicate this code below only if the
- * controlling process is gone and we've called reset_vc.
- */
- if (old_vc_mode != vc->vc_mode) {
- if (vc->vc_mode == KD_TEXT)
- do_unblank_screen(1);
- else
- do_blank_screen(1);
- }
-
- /*
- * If this new console is under process control, send it a signal
- * telling it that it has acquired. Also check if it has died and
- * clean up (similar to logic employed in change_console())
- */
- if (vc->vt_mode.mode == VT_PROCESS) {
- /*
- * Send the signal as privileged - kill_pid() will
- * tell us if the process has gone or something else
- * is awry
- */
- if (kill_pid(vc->vt_pid, vc->vt_mode.acqsig, 1) != 0) {
- /*
- * The controlling process has died, so we revert back to
- * normal operation. In this case, we'll also change back
- * to KD_TEXT mode. I'm not sure if this is strictly correct
- * but it saves the agony when the X server dies and the screen
- * remains blanked due to KD_GRAPHICS! It would be nice to do
- * this outside of VT_PROCESS but there is no single process
- * to account for and tracking tty count may be undesirable.
- */
- reset_vc(vc);
-
- if (old_vc_mode != vc->vc_mode) {
- if (vc->vc_mode == KD_TEXT)
- do_unblank_screen(1);
- else
- do_blank_screen(1);
- }
- }
- }
-
- /*
- * Wake anyone waiting for their VT to activate
- */
- vt_event_post(VT_EVENT_SWITCH, old, vc->vc_num);
- return;
-}
-
-/*
- * Performs the front-end of a vt switch
- */
-void change_console(struct vc_data *new_vc)
-{
- struct vc_data *vc;
-
- if (!new_vc || new_vc->vc_num == fg_console || vt_dont_switch)
- return;
-
- /*
- * If this vt is in process mode, then we need to handshake with
- * that process before switching. Essentially, we store where that
- * vt wants to switch to and wait for it to tell us when it's done
- * (via VT_RELDISP ioctl).
- *
- * We also check to see if the controlling process still exists.
- * If it doesn't, we reset this vt to auto mode and continue.
- * This is a cheap way to track process control. The worst thing
- * that can happen is: we send a signal to a process, it dies, and
- * the switch gets "lost" waiting for a response; hopefully, the
- * user will try again, we'll detect the process is gone (unless
- * the user waits just the right amount of time :-) and revert the
- * vt to auto control.
- */
- vc = vc_cons[fg_console].d;
- if (vc->vt_mode.mode == VT_PROCESS) {
- /*
- * Send the signal as privileged - kill_pid() will
- * tell us if the process has gone or something else
- * is awry.
- *
- * We need to set vt_newvt *before* sending the signal or we
- * have a race.
- */
- vc->vt_newvt = new_vc->vc_num;
- if (kill_pid(vc->vt_pid, vc->vt_mode.relsig, 1) == 0) {
- /*
- * It worked. Mark the vt to switch to and
- * return. The process needs to send us a
- * VT_RELDISP ioctl to complete the switch.
- */
- return;
- }
-
- /*
- * The controlling process has died, so we revert back to
- * normal operation. In this case, we'll also change back
- * to KD_TEXT mode. I'm not sure if this is strictly correct
- * but it saves the agony when the X server dies and the screen
- * remains blanked due to KD_GRAPHICS! It would be nice to do
- * this outside of VT_PROCESS but there is no single process
- * to account for and tracking tty count may be undesirable.
- */
- reset_vc(vc);
-
- /*
- * Fall through to normal (VT_AUTO) handling of the switch...
- */
- }
-
- /*
- * Ignore all switches in KD_GRAPHICS+VT_AUTO mode
- */
- if (vc->vc_mode == KD_GRAPHICS)
- return;
-
- complete_change_console(new_vc);
-}
-
-/* Perform a kernel triggered VT switch for suspend/resume */
-
-static int disable_vt_switch;
-
-int vt_move_to_console(unsigned int vt, int alloc)
-{
- int prev;
-
- acquire_console_sem();
- /* Graphics mode - up to X */
- if (disable_vt_switch) {
- release_console_sem();
- return 0;
- }
- prev = fg_console;
-
- if (alloc && vc_allocate(vt)) {
- /* we can't have a free VC for now. Too bad,
- * we don't want to mess the screen for now. */
- release_console_sem();
- return -ENOSPC;
- }
-
- if (set_console(vt)) {
- /*
- * We're unable to switch to the SUSPEND_CONSOLE.
- * Let the calling function know so it can decide
- * what to do.
- */
- release_console_sem();
- return -EIO;
- }
- release_console_sem();
- tty_lock();
- if (vt_waitactive(vt + 1)) {
- pr_debug("Suspend: Can't switch VCs.");
- tty_unlock();
- return -EINTR;
- }
- tty_unlock();
- return prev;
-}
-
-/*
- * Normally during a suspend, we allocate a new console and switch to it.
- * When we resume, we switch back to the original console. This switch
- * can be slow, so on systems where the framebuffer can handle restoration
- * of video registers anyways, there's little point in doing the console
- * switch. This function allows you to disable it by passing it '0'.
- */
-void pm_set_vt_switch(int do_switch)
-{
- acquire_console_sem();
- disable_vt_switch = !do_switch;
- release_console_sem();
-}
-EXPORT_SYMBOL(pm_set_vt_switch);
obj-$(CONFIG_N_HDLC) += n_hdlc.o
obj-$(CONFIG_N_GSM) += n_gsm.o
obj-$(CONFIG_R3964) += n_r3964.o
+
+obj-y += vt/
--- /dev/null
+#
+# This file contains the font map for the default (hardware) font
+#
+FONTMAPFILE = cp437.uni
+
+obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o \
+ selection.o keyboard.o
+obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o
+obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o
+
+# Files generated that shall be removed upon make clean
+clean-files := consolemap_deftbl.c defkeymap.c
+
+quiet_cmd_conmk = CONMK $@
+ cmd_conmk = scripts/conmakehash $< > $@
+
+$(obj)/consolemap_deftbl.c: $(src)/$(FONTMAPFILE)
+ $(call cmd,conmk)
+
+$(obj)/defkeymap.o: $(obj)/defkeymap.c
+
+# Uncomment if you're changing the keymap and have an appropriate
+# loadkeys version for the map. By default, we'll use the shipped
+# versions.
+# GENERATE_KEYMAP := 1
+
+ifdef GENERATE_KEYMAP
+
+$(obj)/defkeymap.c: $(obj)/%.c: $(src)/%.map
+ loadkeys --mktable $< > $@.tmp
+ sed -e 's/^static *//' $@.tmp > $@
+ rm $@.tmp
+
+endif
--- /dev/null
+/*
+ * consolemap.c
+ *
+ * Mapping from internal code (such as Latin-1 or Unicode or IBM PC code)
+ * to font positions.
+ *
+ * aeb, 950210
+ *
+ * Support for multiple unimaps by Jakub Jelinek <jj@ultra.linux.cz>, July 1998
+ *
+ * Fix bug in inverse translation. Stanislav Voronyi <stas@cnti.uanet.kharkov.ua>, Dec 1998
+ */
+
+#include <linux/module.h>
+#include <linux/kd.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <asm/uaccess.h>
+#include <linux/consolemap.h>
+#include <linux/vt_kern.h>
+
+static unsigned short translations[][256] = {
+ /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */
+ {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
+ },
+ /* VT100 graphics mapped to Unicode */
+ {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x2192, 0x2190, 0x2191, 0x2193, 0x002f,
+ 0x2588, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0,
+ 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1,
+ 0x2591, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba,
+ 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c,
+ 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
+ },
+ /* IBM Codepage 437 mapped to Unicode */
+ {
+ 0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
+ 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,
+ 0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,
+ 0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
+ 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
+ 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
+ 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
+ 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
+ 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
+ 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+ 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
+ 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
+ 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
+ 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
+ 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
+ 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
+ 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
+ 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
+ },
+ /* User mapping -- default to codes for direct font mapping */
+ {
+ 0xf000, 0xf001, 0xf002, 0xf003, 0xf004, 0xf005, 0xf006, 0xf007,
+ 0xf008, 0xf009, 0xf00a, 0xf00b, 0xf00c, 0xf00d, 0xf00e, 0xf00f,
+ 0xf010, 0xf011, 0xf012, 0xf013, 0xf014, 0xf015, 0xf016, 0xf017,
+ 0xf018, 0xf019, 0xf01a, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f,
+ 0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027,
+ 0xf028, 0xf029, 0xf02a, 0xf02b, 0xf02c, 0xf02d, 0xf02e, 0xf02f,
+ 0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
+ 0xf038, 0xf039, 0xf03a, 0xf03b, 0xf03c, 0xf03d, 0xf03e, 0xf03f,
+ 0xf040, 0xf041, 0xf042, 0xf043, 0xf044, 0xf045, 0xf046, 0xf047,
+ 0xf048, 0xf049, 0xf04a, 0xf04b, 0xf04c, 0xf04d, 0xf04e, 0xf04f,
+ 0xf050, 0xf051, 0xf052, 0xf053, 0xf054, 0xf055, 0xf056, 0xf057,
+ 0xf058, 0xf059, 0xf05a, 0xf05b, 0xf05c, 0xf05d, 0xf05e, 0xf05f,
+ 0xf060, 0xf061, 0xf062, 0xf063, 0xf064, 0xf065, 0xf066, 0xf067,
+ 0xf068, 0xf069, 0xf06a, 0xf06b, 0xf06c, 0xf06d, 0xf06e, 0xf06f,
+ 0xf070, 0xf071, 0xf072, 0xf073, 0xf074, 0xf075, 0xf076, 0xf077,
+ 0xf078, 0xf079, 0xf07a, 0xf07b, 0xf07c, 0xf07d, 0xf07e, 0xf07f,
+ 0xf080, 0xf081, 0xf082, 0xf083, 0xf084, 0xf085, 0xf086, 0xf087,
+ 0xf088, 0xf089, 0xf08a, 0xf08b, 0xf08c, 0xf08d, 0xf08e, 0xf08f,
+ 0xf090, 0xf091, 0xf092, 0xf093, 0xf094, 0xf095, 0xf096, 0xf097,
+ 0xf098, 0xf099, 0xf09a, 0xf09b, 0xf09c, 0xf09d, 0xf09e, 0xf09f,
+ 0xf0a0, 0xf0a1, 0xf0a2, 0xf0a3, 0xf0a4, 0xf0a5, 0xf0a6, 0xf0a7,
+ 0xf0a8, 0xf0a9, 0xf0aa, 0xf0ab, 0xf0ac, 0xf0ad, 0xf0ae, 0xf0af,
+ 0xf0b0, 0xf0b1, 0xf0b2, 0xf0b3, 0xf0b4, 0xf0b5, 0xf0b6, 0xf0b7,
+ 0xf0b8, 0xf0b9, 0xf0ba, 0xf0bb, 0xf0bc, 0xf0bd, 0xf0be, 0xf0bf,
+ 0xf0c0, 0xf0c1, 0xf0c2, 0xf0c3, 0xf0c4, 0xf0c5, 0xf0c6, 0xf0c7,
+ 0xf0c8, 0xf0c9, 0xf0ca, 0xf0cb, 0xf0cc, 0xf0cd, 0xf0ce, 0xf0cf,
+ 0xf0d0, 0xf0d1, 0xf0d2, 0xf0d3, 0xf0d4, 0xf0d5, 0xf0d6, 0xf0d7,
+ 0xf0d8, 0xf0d9, 0xf0da, 0xf0db, 0xf0dc, 0xf0dd, 0xf0de, 0xf0df,
+ 0xf0e0, 0xf0e1, 0xf0e2, 0xf0e3, 0xf0e4, 0xf0e5, 0xf0e6, 0xf0e7,
+ 0xf0e8, 0xf0e9, 0xf0ea, 0xf0eb, 0xf0ec, 0xf0ed, 0xf0ee, 0xf0ef,
+ 0xf0f0, 0xf0f1, 0xf0f2, 0xf0f3, 0xf0f4, 0xf0f5, 0xf0f6, 0xf0f7,
+ 0xf0f8, 0xf0f9, 0xf0fa, 0xf0fb, 0xf0fc, 0xf0fd, 0xf0fe, 0xf0ff
+ }
+};
+
+/* The standard kernel character-to-font mappings are not invertible
+ -- this is just a best effort. */
+
+#define MAX_GLYPH 512 /* Max possible glyph value */
+
+static int inv_translate[MAX_NR_CONSOLES];
+
+struct uni_pagedir {
+ u16 **uni_pgdir[32];
+ unsigned long refcount;
+ unsigned long sum;
+ unsigned char *inverse_translations[4];
+ u16 *inverse_trans_unicode;
+ int readonly;
+};
+
+static struct uni_pagedir *dflt;
+
+static void set_inverse_transl(struct vc_data *conp, struct uni_pagedir *p, int i)
+{
+ int j, glyph;
+ unsigned short *t = translations[i];
+ unsigned char *q;
+
+ if (!p) return;
+ q = p->inverse_translations[i];
+
+ if (!q) {
+ q = p->inverse_translations[i] = (unsigned char *)
+ kmalloc(MAX_GLYPH, GFP_KERNEL);
+ if (!q) return;
+ }
+ memset(q, 0, MAX_GLYPH);
+
+ for (j = 0; j < E_TABSZ; j++) {
+ glyph = conv_uni_to_pc(conp, t[j]);
+ if (glyph >= 0 && glyph < MAX_GLYPH && q[glyph] < 32) {
+ /* prefer '-' above SHY etc. */
+ q[glyph] = j;
+ }
+ }
+}
+
+static void set_inverse_trans_unicode(struct vc_data *conp,
+ struct uni_pagedir *p)
+{
+ int i, j, k, glyph;
+ u16 **p1, *p2;
+ u16 *q;
+
+ if (!p) return;
+ q = p->inverse_trans_unicode;
+ if (!q) {
+ q = p->inverse_trans_unicode =
+ kmalloc(MAX_GLYPH * sizeof(u16), GFP_KERNEL);
+ if (!q)
+ return;
+ }
+ memset(q, 0, MAX_GLYPH * sizeof(u16));
+
+ for (i = 0; i < 32; i++) {
+ p1 = p->uni_pgdir[i];
+ if (!p1)
+ continue;
+ for (j = 0; j < 32; j++) {
+ p2 = p1[j];
+ if (!p2)
+ continue;
+ for (k = 0; k < 64; k++) {
+ glyph = p2[k];
+ if (glyph >= 0 && glyph < MAX_GLYPH
+ && q[glyph] < 32)
+ q[glyph] = (i << 11) + (j << 6) + k;
+ }
+ }
+ }
+}
+
+unsigned short *set_translate(int m, struct vc_data *vc)
+{
+ inv_translate[vc->vc_num] = m;
+ return translations[m];
+}
+
+/*
+ * Inverse translation is impossible for several reasons:
+ * 1. The font<->character maps are not 1-1.
+ * 2. The text may have been written while a different translation map
+ * was active.
+ * Still, it is now possible to a certain extent to cut and paste non-ASCII.
+ */
+u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode)
+{
+ struct uni_pagedir *p;
+ int m;
+ if (glyph < 0 || glyph >= MAX_GLYPH)
+ return 0;
+ else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc))
+ return glyph;
+ else if (use_unicode) {
+ if (!p->inverse_trans_unicode)
+ return glyph;
+ else
+ return p->inverse_trans_unicode[glyph];
+ } else {
+ m = inv_translate[conp->vc_num];
+ if (!p->inverse_translations[m])
+ return glyph;
+ else
+ return p->inverse_translations[m][glyph];
+ }
+}
+EXPORT_SYMBOL_GPL(inverse_translate);
+
+static void update_user_maps(void)
+{
+ int i;
+ struct uni_pagedir *p, *q = NULL;
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ if (!vc_cons_allocated(i))
+ continue;
+ p = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc;
+ if (p && p != q) {
+ set_inverse_transl(vc_cons[i].d, p, USER_MAP);
+ set_inverse_trans_unicode(vc_cons[i].d, p);
+ q = p;
+ }
+ }
+}
+
+/*
+ * Load customizable translation table
+ * arg points to a 256 byte translation table.
+ *
+ * The "old" variants are for translation directly to font (using the
+ * 0xf000-0xf0ff "transparent" Unicodes) whereas the "new" variants set
+ * Unicodes explicitly.
+ */
+int con_set_trans_old(unsigned char __user * arg)
+{
+ int i;
+ unsigned short *p = translations[USER_MAP];
+
+ if (!access_ok(VERIFY_READ, arg, E_TABSZ))
+ return -EFAULT;
+
+ for (i=0; i<E_TABSZ ; i++) {
+ unsigned char uc;
+ __get_user(uc, arg+i);
+ p[i] = UNI_DIRECT_BASE | uc;
+ }
+
+ update_user_maps();
+ return 0;
+}
+
+int con_get_trans_old(unsigned char __user * arg)
+{
+ int i, ch;
+ unsigned short *p = translations[USER_MAP];
+
+ if (!access_ok(VERIFY_WRITE, arg, E_TABSZ))
+ return -EFAULT;
+
+ for (i=0; i<E_TABSZ ; i++)
+ {
+ ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]);
+ __put_user((ch & ~0xff) ? 0 : ch, arg+i);
+ }
+ return 0;
+}
+
+int con_set_trans_new(ushort __user * arg)
+{
+ int i;
+ unsigned short *p = translations[USER_MAP];
+
+ if (!access_ok(VERIFY_READ, arg, E_TABSZ*sizeof(unsigned short)))
+ return -EFAULT;
+
+ for (i=0; i<E_TABSZ ; i++) {
+ unsigned short us;
+ __get_user(us, arg+i);
+ p[i] = us;
+ }
+
+ update_user_maps();
+ return 0;
+}
+
+int con_get_trans_new(ushort __user * arg)
+{
+ int i;
+ unsigned short *p = translations[USER_MAP];
+
+ if (!access_ok(VERIFY_WRITE, arg, E_TABSZ*sizeof(unsigned short)))
+ return -EFAULT;
+
+ for (i=0; i<E_TABSZ ; i++)
+ __put_user(p[i], arg+i);
+
+ return 0;
+}
+
+/*
+ * Unicode -> current font conversion
+ *
+ * A font has at most 512 chars, usually 256.
+ * But one font position may represent several Unicode chars.
+ * A hashtable is somewhat of a pain to deal with, so use a
+ * "paged table" instead. Simulation has shown the memory cost of
+ * this 3-level paged table scheme to be comparable to a hash table.
+ */
+
+extern u8 dfont_unicount[]; /* Defined in console_defmap.c */
+extern u16 dfont_unitable[];
+
+static void con_release_unimap(struct uni_pagedir *p)
+{
+ u16 **p1;
+ int i, j;
+
+ if (p == dflt) dflt = NULL;
+ for (i = 0; i < 32; i++) {
+ if ((p1 = p->uni_pgdir[i]) != NULL) {
+ for (j = 0; j < 32; j++)
+ kfree(p1[j]);
+ kfree(p1);
+ }
+ p->uni_pgdir[i] = NULL;
+ }
+ for (i = 0; i < 4; i++) {
+ kfree(p->inverse_translations[i]);
+ p->inverse_translations[i] = NULL;
+ }
+ if (p->inverse_trans_unicode) {
+ kfree(p->inverse_trans_unicode);
+ p->inverse_trans_unicode = NULL;
+ }
+}
+
+void con_free_unimap(struct vc_data *vc)
+{
+ struct uni_pagedir *p;
+
+ p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
+ if (!p)
+ return;
+ *vc->vc_uni_pagedir_loc = 0;
+ if (--p->refcount)
+ return;
+ con_release_unimap(p);
+ kfree(p);
+}
+
+static int con_unify_unimap(struct vc_data *conp, struct uni_pagedir *p)
+{
+ int i, j, k;
+ struct uni_pagedir *q;
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ if (!vc_cons_allocated(i))
+ continue;
+ q = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc;
+ if (!q || q == p || q->sum != p->sum)
+ continue;
+ for (j = 0; j < 32; j++) {
+ u16 **p1, **q1;
+ p1 = p->uni_pgdir[j]; q1 = q->uni_pgdir[j];
+ if (!p1 && !q1)
+ continue;
+ if (!p1 || !q1)
+ break;
+ for (k = 0; k < 32; k++) {
+ if (!p1[k] && !q1[k])
+ continue;
+ if (!p1[k] || !q1[k])
+ break;
+ if (memcmp(p1[k], q1[k], 64*sizeof(u16)))
+ break;
+ }
+ if (k < 32)
+ break;
+ }
+ if (j == 32) {
+ q->refcount++;
+ *conp->vc_uni_pagedir_loc = (unsigned long)q;
+ con_release_unimap(p);
+ kfree(p);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+con_insert_unipair(struct uni_pagedir *p, u_short unicode, u_short fontpos)
+{
+ int i, n;
+ u16 **p1, *p2;
+
+ if (!(p1 = p->uni_pgdir[n = unicode >> 11])) {
+ p1 = p->uni_pgdir[n] = kmalloc(32*sizeof(u16 *), GFP_KERNEL);
+ if (!p1) return -ENOMEM;
+ for (i = 0; i < 32; i++)
+ p1[i] = NULL;
+ }
+
+ if (!(p2 = p1[n = (unicode >> 6) & 0x1f])) {
+ p2 = p1[n] = kmalloc(64*sizeof(u16), GFP_KERNEL);
+ if (!p2) return -ENOMEM;
+ memset(p2, 0xff, 64*sizeof(u16)); /* No glyphs for the characters (yet) */
+ }
+
+ p2[unicode & 0x3f] = fontpos;
+
+ p->sum += (fontpos << 20) + unicode;
+
+ return 0;
+}
+
+/* ui is a leftover from using a hashtable, but might be used again */
+int con_clear_unimap(struct vc_data *vc, struct unimapinit *ui)
+{
+ struct uni_pagedir *p, *q;
+
+ p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
+ if (p && p->readonly) return -EIO;
+ if (!p || --p->refcount) {
+ q = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!q) {
+ if (p) p->refcount++;
+ return -ENOMEM;
+ }
+ q->refcount=1;
+ *vc->vc_uni_pagedir_loc = (unsigned long)q;
+ } else {
+ if (p == dflt) dflt = NULL;
+ p->refcount++;
+ p->sum = 0;
+ con_release_unimap(p);
+ }
+ return 0;
+}
+
+int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
+{
+ int err = 0, err1, i;
+ struct uni_pagedir *p, *q;
+
+ p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
+ if (p->readonly) return -EIO;
+
+ if (!ct) return 0;
+
+ if (p->refcount > 1) {
+ int j, k;
+ u16 **p1, *p2, l;
+
+ err1 = con_clear_unimap(vc, NULL);
+ if (err1) return err1;
+
+ q = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
+ for (i = 0, l = 0; i < 32; i++)
+ if ((p1 = p->uni_pgdir[i]))
+ for (j = 0; j < 32; j++)
+ if ((p2 = p1[j]))
+ for (k = 0; k < 64; k++, l++)
+ if (p2[k] != 0xffff) {
+ err1 = con_insert_unipair(q, l, p2[k]);
+ if (err1) {
+ p->refcount++;
+ *vc->vc_uni_pagedir_loc = (unsigned long)p;
+ con_release_unimap(q);
+ kfree(q);
+ return err1;
+ }
+ }
+ p = q;
+ } else if (p == dflt)
+ dflt = NULL;
+
+ while (ct--) {
+ unsigned short unicode, fontpos;
+ __get_user(unicode, &list->unicode);
+ __get_user(fontpos, &list->fontpos);
+ if ((err1 = con_insert_unipair(p, unicode,fontpos)) != 0)
+ err = err1;
+ list++;
+ }
+
+ if (con_unify_unimap(vc, p))
+ return err;
+
+ for (i = 0; i <= 3; i++)
+ set_inverse_transl(vc, p, i); /* Update all inverse translations */
+ set_inverse_trans_unicode(vc, p);
+
+ return err;
+}
+
+/* Loads the unimap for the hardware font, as defined in uni_hash.tbl.
+ The representation used was the most compact I could come up
+ with. This routine is executed at sys_setup time, and when the
+ PIO_FONTRESET ioctl is called. */
+
+int con_set_default_unimap(struct vc_data *vc)
+{
+ int i, j, err = 0, err1;
+ u16 *q;
+ struct uni_pagedir *p;
+
+ if (dflt) {
+ p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
+ if (p == dflt)
+ return 0;
+ dflt->refcount++;
+ *vc->vc_uni_pagedir_loc = (unsigned long)dflt;
+ if (p && --p->refcount) {
+ con_release_unimap(p);
+ kfree(p);
+ }
+ return 0;
+ }
+
+ /* The default font is always 256 characters */
+
+ err = con_clear_unimap(vc, NULL);
+ if (err) return err;
+
+ p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
+ q = dfont_unitable;
+
+ for (i = 0; i < 256; i++)
+ for (j = dfont_unicount[i]; j; j--) {
+ err1 = con_insert_unipair(p, *(q++), i);
+ if (err1)
+ err = err1;
+ }
+
+ if (con_unify_unimap(vc, p)) {
+ dflt = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
+ return err;
+ }
+
+ for (i = 0; i <= 3; i++)
+ set_inverse_transl(vc, p, i); /* Update all inverse translations */
+ set_inverse_trans_unicode(vc, p);
+ dflt = p;
+ return err;
+}
+EXPORT_SYMBOL(con_set_default_unimap);
+
+int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc)
+{
+ struct uni_pagedir *q;
+
+ if (!*src_vc->vc_uni_pagedir_loc)
+ return -EINVAL;
+ if (*dst_vc->vc_uni_pagedir_loc == *src_vc->vc_uni_pagedir_loc)
+ return 0;
+ con_free_unimap(dst_vc);
+ q = (struct uni_pagedir *)*src_vc->vc_uni_pagedir_loc;
+ q->refcount++;
+ *dst_vc->vc_uni_pagedir_loc = (long)q;
+ return 0;
+}
+
+int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list)
+{
+ int i, j, k, ect;
+ u16 **p1, *p2;
+ struct uni_pagedir *p;
+
+ ect = 0;
+ if (*vc->vc_uni_pagedir_loc) {
+ p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
+ for (i = 0; i < 32; i++)
+ if ((p1 = p->uni_pgdir[i]))
+ for (j = 0; j < 32; j++)
+ if ((p2 = *(p1++)))
+ for (k = 0; k < 64; k++) {
+ if (*p2 < MAX_GLYPH && ect++ < ct) {
+ __put_user((u_short)((i<<11)+(j<<6)+k),
+ &list->unicode);
+ __put_user((u_short) *p2,
+ &list->fontpos);
+ list++;
+ }
+ p2++;
+ }
+ }
+ __put_user(ect, uct);
+ return ((ect <= ct) ? 0 : -ENOMEM);
+}
+
+void con_protect_unimap(struct vc_data *vc, int rdonly)
+{
+ struct uni_pagedir *p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
+
+ if (p)
+ p->readonly = rdonly;
+}
+
+/*
+ * Always use USER_MAP. These functions are used by the keyboard,
+ * which shouldn't be affected by G0/G1 switching, etc.
+ * If the user map still contains default values, i.e. the
+ * direct-to-font mapping, then assume user is using Latin1.
+ */
+/* may be called during an interrupt */
+u32 conv_8bit_to_uni(unsigned char c)
+{
+ unsigned short uni = translations[USER_MAP][c];
+ return uni == (0xf000 | c) ? c : uni;
+}
+
+int conv_uni_to_8bit(u32 uni)
+{
+ int c;
+ for (c = 0; c < 0x100; c++)
+ if (translations[USER_MAP][c] == uni ||
+ (translations[USER_MAP][c] == (c | 0xf000) && uni == c))
+ return c;
+ return -1;
+}
+
+int
+conv_uni_to_pc(struct vc_data *conp, long ucs)
+{
+ int h;
+ u16 **p1, *p2;
+ struct uni_pagedir *p;
+
+ /* Only 16-bit codes supported at this time */
+ if (ucs > 0xffff)
+ return -4; /* Not found */
+ else if (ucs < 0x20)
+ return -1; /* Not a printable character */
+ else if (ucs == 0xfeff || (ucs >= 0x200b && ucs <= 0x200f))
+ return -2; /* Zero-width space */
+ /*
+ * UNI_DIRECT_BASE indicates the start of the region in the User Zone
+ * which always has a 1:1 mapping to the currently loaded font. The
+ * UNI_DIRECT_MASK indicates the bit span of the region.
+ */
+ else if ((ucs & ~UNI_DIRECT_MASK) == UNI_DIRECT_BASE)
+ return ucs & UNI_DIRECT_MASK;
+
+ if (!*conp->vc_uni_pagedir_loc)
+ return -3;
+
+ p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc;
+ if ((p1 = p->uni_pgdir[ucs >> 11]) &&
+ (p2 = p1[(ucs >> 6) & 0x1f]) &&
+ (h = p2[ucs & 0x3f]) < MAX_GLYPH)
+ return h;
+
+ return -4; /* not found */
+}
+
+/*
+ * This is called at sys_setup time, after memory and the console are
+ * initialized. It must be possible to call kmalloc(..., GFP_KERNEL)
+ * from this function, hence the call from sys_setup.
+ */
+void __init
+console_map_init(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ if (vc_cons_allocated(i) && !*vc_cons[i].d->vc_uni_pagedir_loc)
+ con_set_default_unimap(vc_cons[i].d);
+}
+
+EXPORT_SYMBOL(con_copy_unimap);
--- /dev/null
+#
+# Unicode table for IBM Codepage 437. Note that there are many more
+# substitutions that could be conceived (for example, thick-line
+# graphs probably should be replaced with double-line ones, accented
+# Latin characters should replaced with their nonaccented versions,
+# and some upper case Greek characters could be replaced by Latin), however,
+# I have limited myself to the Unicodes used by the kernel ISO 8859-1,
+# DEC VT, and IBM CP 437 tables.
+#
+# --------------------------------
+#
+# Basic IBM dingbats, some of which will never have a purpose clear
+# to mankind
+#
+0x00 U+0000
+0x01 U+263a
+0x02 U+263b
+0x03 U+2665
+0x04 U+2666 U+25c6
+0x05 U+2663
+0x06 U+2660
+0x07 U+2022
+0x08 U+25d8
+0x09 U+25cb
+0x0a U+25d9
+0x0b U+2642
+0x0c U+2640
+0x0d U+266a
+0x0e U+266b
+0x0f U+263c U+00a4
+0x10 U+25b6 U+25ba
+0x11 U+25c0 U+25c4
+0x12 U+2195
+0x13 U+203c
+0x14 U+00b6
+0x15 U+00a7
+0x16 U+25ac
+0x17 U+21a8
+0x18 U+2191
+0x19 U+2193
+0x1a U+2192
+0x1b U+2190
+0x1c U+221f
+0x1d U+2194
+0x1e U+25b2
+0x1f U+25bc
+#
+# The ASCII range is identity-mapped, but some of the characters also
+# have to act as substitutes, especially the upper-case characters.
+#
+0x20 U+0020
+0x21 U+0021
+0x22 U+0022 U+00a8
+0x23 U+0023
+0x24 U+0024
+0x25 U+0025
+0x26 U+0026
+0x27 U+0027 U+00b4
+0x28 U+0028
+0x29 U+0029
+0x2a U+002a
+0x2b U+002b
+0x2c U+002c U+00b8
+0x2d U+002d U+00ad
+0x2e U+002e
+0x2f U+002f
+0x30 U+0030
+0x31 U+0031
+0x32 U+0032
+0x33 U+0033
+0x34 U+0034
+0x35 U+0035
+0x36 U+0036
+0x37 U+0037
+0x38 U+0038
+0x39 U+0039
+0x3a U+003a
+0x3b U+003b
+0x3c U+003c
+0x3d U+003d
+0x3e U+003e
+0x3f U+003f
+0x40 U+0040
+0x41 U+0041 U+00c0 U+00c1 U+00c2 U+00c3
+0x42 U+0042
+0x43 U+0043 U+00a9
+0x44 U+0044 U+00d0
+0x45 U+0045 U+00c8 U+00ca U+00cb
+0x46 U+0046
+0x47 U+0047
+0x48 U+0048
+0x49 U+0049 U+00cc U+00cd U+00ce U+00cf
+0x4a U+004a
+0x4b U+004b U+212a
+0x4c U+004c
+0x4d U+004d
+0x4e U+004e
+0x4f U+004f U+00d2 U+00d3 U+00d4 U+00d5
+0x50 U+0050
+0x51 U+0051
+0x52 U+0052 U+00ae
+0x53 U+0053
+0x54 U+0054
+0x55 U+0055 U+00d9 U+00da U+00db
+0x56 U+0056
+0x57 U+0057
+0x58 U+0058
+0x59 U+0059 U+00dd
+0x5a U+005a
+0x5b U+005b
+0x5c U+005c
+0x5d U+005d
+0x5e U+005e
+0x5f U+005f U+23bd U+f804
+0x60 U+0060
+0x61 U+0061 U+00e3
+0x62 U+0062
+0x63 U+0063
+0x64 U+0064
+0x65 U+0065
+0x66 U+0066
+0x67 U+0067
+0x68 U+0068
+0x69 U+0069
+0x6a U+006a
+0x6b U+006b
+0x6c U+006c
+0x6d U+006d
+0x6e U+006e
+0x6f U+006f U+00f5
+0x70 U+0070
+0x71 U+0071
+0x72 U+0072
+0x73 U+0073
+0x74 U+0074
+0x75 U+0075
+0x76 U+0076
+0x77 U+0077
+0x78 U+0078 U+00d7
+0x79 U+0079 U+00fd
+0x7a U+007a
+0x7b U+007b
+0x7c U+007c U+00a6
+0x7d U+007d
+0x7e U+007e
+#
+# Okay, what on Earth is this one supposed to be used for?
+#
+0x7f U+2302
+#
+# Non-English characters, mostly lower case letters...
+#
+0x80 U+00c7
+0x81 U+00fc
+0x82 U+00e9
+0x83 U+00e2
+0x84 U+00e4
+0x85 U+00e0
+0x86 U+00e5
+0x87 U+00e7
+0x88 U+00ea
+0x89 U+00eb
+0x8a U+00e8
+0x8b U+00ef
+0x8c U+00ee
+0x8d U+00ec
+0x8e U+00c4
+0x8f U+00c5 U+212b
+0x90 U+00c9
+0x91 U+00e6
+0x92 U+00c6
+0x93 U+00f4
+0x94 U+00f6
+0x95 U+00f2
+0x96 U+00fb
+0x97 U+00f9
+0x98 U+00ff
+0x99 U+00d6
+0x9a U+00dc
+0x9b U+00a2
+0x9c U+00a3
+0x9d U+00a5
+0x9e U+20a7
+0x9f U+0192
+0xa0 U+00e1
+0xa1 U+00ed
+0xa2 U+00f3
+0xa3 U+00fa
+0xa4 U+00f1
+0xa5 U+00d1
+0xa6 U+00aa
+0xa7 U+00ba
+0xa8 U+00bf
+0xa9 U+2310
+0xaa U+00ac
+0xab U+00bd
+0xac U+00bc
+0xad U+00a1
+0xae U+00ab
+0xaf U+00bb
+#
+# Block graphics
+#
+0xb0 U+2591
+0xb1 U+2592
+0xb2 U+2593
+0xb3 U+2502
+0xb4 U+2524
+0xb5 U+2561
+0xb6 U+2562
+0xb7 U+2556
+0xb8 U+2555
+0xb9 U+2563
+0xba U+2551
+0xbb U+2557
+0xbc U+255d
+0xbd U+255c
+0xbe U+255b
+0xbf U+2510
+0xc0 U+2514
+0xc1 U+2534
+0xc2 U+252c
+0xc3 U+251c
+0xc4 U+2500
+0xc5 U+253c
+0xc6 U+255e
+0xc7 U+255f
+0xc8 U+255a
+0xc9 U+2554
+0xca U+2569
+0xcb U+2566
+0xcc U+2560
+0xcd U+2550
+0xce U+256c
+0xcf U+2567
+0xd0 U+2568
+0xd1 U+2564
+0xd2 U+2565
+0xd3 U+2559
+0xd4 U+2558
+0xd5 U+2552
+0xd6 U+2553
+0xd7 U+256b
+0xd8 U+256a
+0xd9 U+2518
+0xda U+250c
+0xdb U+2588
+0xdc U+2584
+0xdd U+258c
+0xde U+2590
+0xdf U+2580
+#
+# Greek letters and mathematical symbols
+#
+0xe0 U+03b1
+0xe1 U+03b2 U+00df
+0xe2 U+0393
+0xe3 U+03c0
+0xe4 U+03a3
+0xe5 U+03c3
+0xe6 U+00b5 U+03bc
+0xe7 U+03c4
+0xe8 U+03a6 U+00d8
+0xe9 U+0398
+0xea U+03a9 U+2126
+0xeb U+03b4 U+00f0
+0xec U+221e
+0xed U+03c6 U+00f8
+0xee U+03b5 U+2208
+0xef U+2229
+0xf0 U+2261
+0xf1 U+00b1
+0xf2 U+2265
+0xf3 U+2264
+0xf4 U+2320
+0xf5 U+2321
+0xf6 U+00f7
+0xf7 U+2248
+0xf8 U+00b0
+0xf9 U+2219
+0xfa U+00b7
+0xfb U+221a
+0xfc U+207f
+0xfd U+00b2
+#
+# Square bullet, non-spacing blank
+# Mapping U+fffd to the square bullet means it is the substitution
+# character
+#
+0xfe U+25a0 U+fffd
+0xff U+00a0
--- /dev/null
+/* Do not edit this file! It was automatically generated by */
+/* loadkeys --mktable defkeymap.map > defkeymap.c */
+
+#include <linux/types.h>
+#include <linux/keyboard.h>
+#include <linux/kd.h>
+
+u_short plain_map[NR_KEYS] = {
+ 0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036,
+ 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009,
+ 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69,
+ 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73,
+ 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b,
+ 0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76,
+ 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c,
+ 0xf703, 0xf020, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104,
+ 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307,
+ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
+ 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a,
+ 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short shift_map[NR_KEYS] = {
+ 0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e,
+ 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009,
+ 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49,
+ 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53,
+ 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a,
+ 0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56,
+ 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c,
+ 0xf703, 0xf020, 0xf207, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e,
+ 0xf10f, 0xf110, 0xf111, 0xf112, 0xf113, 0xf213, 0xf203, 0xf307,
+ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
+ 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf10a,
+ 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short altgr_map[NR_KEYS] = {
+ 0xf200, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200,
+ 0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200,
+ 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69,
+ 0xfb6f, 0xfb70, 0xf200, 0xf07e, 0xf201, 0xf702, 0xf914, 0xfb73,
+ 0xf917, 0xf919, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf200,
+ 0xf200, 0xf200, 0xf700, 0xf200, 0xfb7a, 0xfb78, 0xf916, 0xfb76,
+ 0xf915, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c,
+ 0xf703, 0xf200, 0xf207, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510,
+ 0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf202, 0xf911,
+ 0xf912, 0xf913, 0xf30b, 0xf90e, 0xf90f, 0xf910, 0xf30a, 0xf90b,
+ 0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516,
+ 0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short ctrl_map[NR_KEYS] = {
+ 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e,
+ 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200,
+ 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009,
+ 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf201, 0xf702, 0xf001, 0xf013,
+ 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200,
+ 0xf007, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016,
+ 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c,
+ 0xf703, 0xf000, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104,
+ 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf204, 0xf307,
+ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
+ 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf10a,
+ 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short shift_ctrl_map[NR_KEYS] = {
+ 0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200,
+ 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009,
+ 0xf00f, 0xf010, 0xf200, 0xf200, 0xf201, 0xf702, 0xf001, 0xf013,
+ 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200,
+ 0xf200, 0xf200, 0xf700, 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016,
+ 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c,
+ 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf208, 0xf200, 0xf307,
+ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
+ 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short alt_map[NR_KEYS] = {
+ 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836,
+ 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809,
+ 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869,
+ 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873,
+ 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b,
+ 0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876,
+ 0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c,
+ 0xf703, 0xf820, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504,
+ 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf907,
+ 0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901,
+ 0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a,
+ 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short ctrl_alt_map[NR_KEYS] = {
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809,
+ 0xf80f, 0xf810, 0xf200, 0xf200, 0xf201, 0xf702, 0xf801, 0xf813,
+ 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200,
+ 0xf200, 0xf200, 0xf700, 0xf200, 0xf81a, 0xf818, 0xf803, 0xf816,
+ 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c,
+ 0xf703, 0xf200, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504,
+ 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf200, 0xf307,
+ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
+ 0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf200, 0xf50a,
+ 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+ushort *key_maps[MAX_NR_KEYMAPS] = {
+ plain_map, shift_map, altgr_map, NULL,
+ ctrl_map, shift_ctrl_map, NULL, NULL,
+ alt_map, NULL, NULL, NULL,
+ ctrl_alt_map, NULL
+};
+
+unsigned int keymap_count = 7;
+
+/*
+ * Philosophy: most people do not define more strings, but they who do
+ * often want quite a lot of string space. So, we statically allocate
+ * the default and allocate dynamically in chunks of 512 bytes.
+ */
+
+char func_buf[] = {
+ '\033', '[', '[', 'A', 0,
+ '\033', '[', '[', 'B', 0,
+ '\033', '[', '[', 'C', 0,
+ '\033', '[', '[', 'D', 0,
+ '\033', '[', '[', 'E', 0,
+ '\033', '[', '1', '7', '~', 0,
+ '\033', '[', '1', '8', '~', 0,
+ '\033', '[', '1', '9', '~', 0,
+ '\033', '[', '2', '0', '~', 0,
+ '\033', '[', '2', '1', '~', 0,
+ '\033', '[', '2', '3', '~', 0,
+ '\033', '[', '2', '4', '~', 0,
+ '\033', '[', '2', '5', '~', 0,
+ '\033', '[', '2', '6', '~', 0,
+ '\033', '[', '2', '8', '~', 0,
+ '\033', '[', '2', '9', '~', 0,
+ '\033', '[', '3', '1', '~', 0,
+ '\033', '[', '3', '2', '~', 0,
+ '\033', '[', '3', '3', '~', 0,
+ '\033', '[', '3', '4', '~', 0,
+ '\033', '[', '1', '~', 0,
+ '\033', '[', '2', '~', 0,
+ '\033', '[', '3', '~', 0,
+ '\033', '[', '4', '~', 0,
+ '\033', '[', '5', '~', 0,
+ '\033', '[', '6', '~', 0,
+ '\033', '[', 'M', 0,
+ '\033', '[', 'P', 0,
+};
+
+char *funcbufptr = func_buf;
+int funcbufsize = sizeof(func_buf);
+int funcbufleft = 0; /* space left */
+
+char *func_table[MAX_NR_FUNC] = {
+ func_buf + 0,
+ func_buf + 5,
+ func_buf + 10,
+ func_buf + 15,
+ func_buf + 20,
+ func_buf + 25,
+ func_buf + 31,
+ func_buf + 37,
+ func_buf + 43,
+ func_buf + 49,
+ func_buf + 55,
+ func_buf + 61,
+ func_buf + 67,
+ func_buf + 73,
+ func_buf + 79,
+ func_buf + 85,
+ func_buf + 91,
+ func_buf + 97,
+ func_buf + 103,
+ func_buf + 109,
+ func_buf + 115,
+ func_buf + 120,
+ func_buf + 125,
+ func_buf + 130,
+ func_buf + 135,
+ func_buf + 140,
+ func_buf + 145,
+ NULL,
+ NULL,
+ func_buf + 149,
+ NULL,
+};
+
+struct kbdiacruc accent_table[MAX_DIACR] = {
+ {'`', 'A', 0300}, {'`', 'a', 0340},
+ {'\'', 'A', 0301}, {'\'', 'a', 0341},
+ {'^', 'A', 0302}, {'^', 'a', 0342},
+ {'~', 'A', 0303}, {'~', 'a', 0343},
+ {'"', 'A', 0304}, {'"', 'a', 0344},
+ {'O', 'A', 0305}, {'o', 'a', 0345},
+ {'0', 'A', 0305}, {'0', 'a', 0345},
+ {'A', 'A', 0305}, {'a', 'a', 0345},
+ {'A', 'E', 0306}, {'a', 'e', 0346},
+ {',', 'C', 0307}, {',', 'c', 0347},
+ {'`', 'E', 0310}, {'`', 'e', 0350},
+ {'\'', 'E', 0311}, {'\'', 'e', 0351},
+ {'^', 'E', 0312}, {'^', 'e', 0352},
+ {'"', 'E', 0313}, {'"', 'e', 0353},
+ {'`', 'I', 0314}, {'`', 'i', 0354},
+ {'\'', 'I', 0315}, {'\'', 'i', 0355},
+ {'^', 'I', 0316}, {'^', 'i', 0356},
+ {'"', 'I', 0317}, {'"', 'i', 0357},
+ {'-', 'D', 0320}, {'-', 'd', 0360},
+ {'~', 'N', 0321}, {'~', 'n', 0361},
+ {'`', 'O', 0322}, {'`', 'o', 0362},
+ {'\'', 'O', 0323}, {'\'', 'o', 0363},
+ {'^', 'O', 0324}, {'^', 'o', 0364},
+ {'~', 'O', 0325}, {'~', 'o', 0365},
+ {'"', 'O', 0326}, {'"', 'o', 0366},
+ {'/', 'O', 0330}, {'/', 'o', 0370},
+ {'`', 'U', 0331}, {'`', 'u', 0371},
+ {'\'', 'U', 0332}, {'\'', 'u', 0372},
+ {'^', 'U', 0333}, {'^', 'u', 0373},
+ {'"', 'U', 0334}, {'"', 'u', 0374},
+ {'\'', 'Y', 0335}, {'\'', 'y', 0375},
+ {'T', 'H', 0336}, {'t', 'h', 0376},
+ {'s', 's', 0337}, {'"', 'y', 0377},
+ {'s', 'z', 0337}, {'i', 'j', 0377},
+};
+
+unsigned int accent_table_size = 68;
--- /dev/null
+# Default kernel keymap. This uses 7 modifier combinations.
+keymaps 0-2,4-5,8,12
+# Change the above line into
+# keymaps 0-2,4-6,8,12
+# in case you want the entries
+# altgr control keycode 83 = Boot
+# altgr control keycode 111 = Boot
+# below.
+#
+# In fact AltGr is used very little, and one more keymap can
+# be saved by mapping AltGr to Alt (and adapting a few entries):
+# keycode 100 = Alt
+#
+keycode 1 = Escape Escape
+ alt keycode 1 = Meta_Escape
+keycode 2 = one exclam
+ alt keycode 2 = Meta_one
+keycode 3 = two at at
+ control keycode 3 = nul
+ shift control keycode 3 = nul
+ alt keycode 3 = Meta_two
+keycode 4 = three numbersign
+ control keycode 4 = Escape
+ alt keycode 4 = Meta_three
+keycode 5 = four dollar dollar
+ control keycode 5 = Control_backslash
+ alt keycode 5 = Meta_four
+keycode 6 = five percent
+ control keycode 6 = Control_bracketright
+ alt keycode 6 = Meta_five
+keycode 7 = six asciicircum
+ control keycode 7 = Control_asciicircum
+ alt keycode 7 = Meta_six
+keycode 8 = seven ampersand braceleft
+ control keycode 8 = Control_underscore
+ alt keycode 8 = Meta_seven
+keycode 9 = eight asterisk bracketleft
+ control keycode 9 = Delete
+ alt keycode 9 = Meta_eight
+keycode 10 = nine parenleft bracketright
+ alt keycode 10 = Meta_nine
+keycode 11 = zero parenright braceright
+ alt keycode 11 = Meta_zero
+keycode 12 = minus underscore backslash
+ control keycode 12 = Control_underscore
+ shift control keycode 12 = Control_underscore
+ alt keycode 12 = Meta_minus
+keycode 13 = equal plus
+ alt keycode 13 = Meta_equal
+keycode 14 = Delete Delete
+ control keycode 14 = BackSpace
+ alt keycode 14 = Meta_Delete
+keycode 15 = Tab Tab
+ alt keycode 15 = Meta_Tab
+keycode 16 = q
+keycode 17 = w
+keycode 18 = e
+ altgr keycode 18 = Hex_E
+keycode 19 = r
+keycode 20 = t
+keycode 21 = y
+keycode 22 = u
+keycode 23 = i
+keycode 24 = o
+keycode 25 = p
+keycode 26 = bracketleft braceleft
+ control keycode 26 = Escape
+ alt keycode 26 = Meta_bracketleft
+keycode 27 = bracketright braceright asciitilde
+ control keycode 27 = Control_bracketright
+ alt keycode 27 = Meta_bracketright
+keycode 28 = Return
+ alt keycode 28 = Meta_Control_m
+keycode 29 = Control
+keycode 30 = a
+ altgr keycode 30 = Hex_A
+keycode 31 = s
+keycode 32 = d
+ altgr keycode 32 = Hex_D
+keycode 33 = f
+ altgr keycode 33 = Hex_F
+keycode 34 = g
+keycode 35 = h
+keycode 36 = j
+keycode 37 = k
+keycode 38 = l
+keycode 39 = semicolon colon
+ alt keycode 39 = Meta_semicolon
+keycode 40 = apostrophe quotedbl
+ control keycode 40 = Control_g
+ alt keycode 40 = Meta_apostrophe
+keycode 41 = grave asciitilde
+ control keycode 41 = nul
+ alt keycode 41 = Meta_grave
+keycode 42 = Shift
+keycode 43 = backslash bar
+ control keycode 43 = Control_backslash
+ alt keycode 43 = Meta_backslash
+keycode 44 = z
+keycode 45 = x
+keycode 46 = c
+ altgr keycode 46 = Hex_C
+keycode 47 = v
+keycode 48 = b
+ altgr keycode 48 = Hex_B
+keycode 49 = n
+keycode 50 = m
+keycode 51 = comma less
+ alt keycode 51 = Meta_comma
+keycode 52 = period greater
+ control keycode 52 = Compose
+ alt keycode 52 = Meta_period
+keycode 53 = slash question
+ control keycode 53 = Delete
+ alt keycode 53 = Meta_slash
+keycode 54 = Shift
+keycode 55 = KP_Multiply
+keycode 56 = Alt
+keycode 57 = space space
+ control keycode 57 = nul
+ alt keycode 57 = Meta_space
+keycode 58 = Caps_Lock
+keycode 59 = F1 F11 Console_13
+ control keycode 59 = F1
+ alt keycode 59 = Console_1
+ control alt keycode 59 = Console_1
+keycode 60 = F2 F12 Console_14
+ control keycode 60 = F2
+ alt keycode 60 = Console_2
+ control alt keycode 60 = Console_2
+keycode 61 = F3 F13 Console_15
+ control keycode 61 = F3
+ alt keycode 61 = Console_3
+ control alt keycode 61 = Console_3
+keycode 62 = F4 F14 Console_16
+ control keycode 62 = F4
+ alt keycode 62 = Console_4
+ control alt keycode 62 = Console_4
+keycode 63 = F5 F15 Console_17
+ control keycode 63 = F5
+ alt keycode 63 = Console_5
+ control alt keycode 63 = Console_5
+keycode 64 = F6 F16 Console_18
+ control keycode 64 = F6
+ alt keycode 64 = Console_6
+ control alt keycode 64 = Console_6
+keycode 65 = F7 F17 Console_19
+ control keycode 65 = F7
+ alt keycode 65 = Console_7
+ control alt keycode 65 = Console_7
+keycode 66 = F8 F18 Console_20
+ control keycode 66 = F8
+ alt keycode 66 = Console_8
+ control alt keycode 66 = Console_8
+keycode 67 = F9 F19 Console_21
+ control keycode 67 = F9
+ alt keycode 67 = Console_9
+ control alt keycode 67 = Console_9
+keycode 68 = F10 F20 Console_22
+ control keycode 68 = F10
+ alt keycode 68 = Console_10
+ control alt keycode 68 = Console_10
+keycode 69 = Num_Lock
+ shift keycode 69 = Bare_Num_Lock
+keycode 70 = Scroll_Lock Show_Memory Show_Registers
+ control keycode 70 = Show_State
+ alt keycode 70 = Scroll_Lock
+keycode 71 = KP_7
+ alt keycode 71 = Ascii_7
+ altgr keycode 71 = Hex_7
+keycode 72 = KP_8
+ alt keycode 72 = Ascii_8
+ altgr keycode 72 = Hex_8
+keycode 73 = KP_9
+ alt keycode 73 = Ascii_9
+ altgr keycode 73 = Hex_9
+keycode 74 = KP_Subtract
+keycode 75 = KP_4
+ alt keycode 75 = Ascii_4
+ altgr keycode 75 = Hex_4
+keycode 76 = KP_5
+ alt keycode 76 = Ascii_5
+ altgr keycode 76 = Hex_5
+keycode 77 = KP_6
+ alt keycode 77 = Ascii_6
+ altgr keycode 77 = Hex_6
+keycode 78 = KP_Add
+keycode 79 = KP_1
+ alt keycode 79 = Ascii_1
+ altgr keycode 79 = Hex_1
+keycode 80 = KP_2
+ alt keycode 80 = Ascii_2
+ altgr keycode 80 = Hex_2
+keycode 81 = KP_3
+ alt keycode 81 = Ascii_3
+ altgr keycode 81 = Hex_3
+keycode 82 = KP_0
+ alt keycode 82 = Ascii_0
+ altgr keycode 82 = Hex_0
+keycode 83 = KP_Period
+# altgr control keycode 83 = Boot
+ control alt keycode 83 = Boot
+keycode 84 = Last_Console
+keycode 85 =
+keycode 86 = less greater bar
+ alt keycode 86 = Meta_less
+keycode 87 = F11 F11 Console_23
+ control keycode 87 = F11
+ alt keycode 87 = Console_11
+ control alt keycode 87 = Console_11
+keycode 88 = F12 F12 Console_24
+ control keycode 88 = F12
+ alt keycode 88 = Console_12
+ control alt keycode 88 = Console_12
+keycode 89 =
+keycode 90 =
+keycode 91 =
+keycode 92 =
+keycode 93 =
+keycode 94 =
+keycode 95 =
+keycode 96 = KP_Enter
+keycode 97 = Control
+keycode 98 = KP_Divide
+keycode 99 = Control_backslash
+ control keycode 99 = Control_backslash
+ alt keycode 99 = Control_backslash
+keycode 100 = AltGr
+keycode 101 = Break
+keycode 102 = Find
+keycode 103 = Up
+keycode 104 = Prior
+ shift keycode 104 = Scroll_Backward
+keycode 105 = Left
+ alt keycode 105 = Decr_Console
+keycode 106 = Right
+ alt keycode 106 = Incr_Console
+keycode 107 = Select
+keycode 108 = Down
+keycode 109 = Next
+ shift keycode 109 = Scroll_Forward
+keycode 110 = Insert
+keycode 111 = Remove
+# altgr control keycode 111 = Boot
+ control alt keycode 111 = Boot
+keycode 112 = Macro
+keycode 113 = F13
+keycode 114 = F14
+keycode 115 = Help
+keycode 116 = Do
+keycode 117 = F17
+keycode 118 = KP_MinPlus
+keycode 119 = Pause
+keycode 120 =
+keycode 121 =
+keycode 122 =
+keycode 123 =
+keycode 124 =
+keycode 125 =
+keycode 126 =
+keycode 127 =
+string F1 = "\033[[A"
+string F2 = "\033[[B"
+string F3 = "\033[[C"
+string F4 = "\033[[D"
+string F5 = "\033[[E"
+string F6 = "\033[17~"
+string F7 = "\033[18~"
+string F8 = "\033[19~"
+string F9 = "\033[20~"
+string F10 = "\033[21~"
+string F11 = "\033[23~"
+string F12 = "\033[24~"
+string F13 = "\033[25~"
+string F14 = "\033[26~"
+string F15 = "\033[28~"
+string F16 = "\033[29~"
+string F17 = "\033[31~"
+string F18 = "\033[32~"
+string F19 = "\033[33~"
+string F20 = "\033[34~"
+string Find = "\033[1~"
+string Insert = "\033[2~"
+string Remove = "\033[3~"
+string Select = "\033[4~"
+string Prior = "\033[5~"
+string Next = "\033[6~"
+string Macro = "\033[M"
+string Pause = "\033[P"
+compose '`' 'A' to 'À'
+compose '`' 'a' to 'à'
+compose '\'' 'A' to 'Á'
+compose '\'' 'a' to 'á'
+compose '^' 'A' to 'Â'
+compose '^' 'a' to 'â'
+compose '~' 'A' to 'Ã'
+compose '~' 'a' to 'ã'
+compose '"' 'A' to 'Ä'
+compose '"' 'a' to 'ä'
+compose 'O' 'A' to 'Å'
+compose 'o' 'a' to 'å'
+compose '0' 'A' to 'Å'
+compose '0' 'a' to 'å'
+compose 'A' 'A' to 'Å'
+compose 'a' 'a' to 'å'
+compose 'A' 'E' to 'Æ'
+compose 'a' 'e' to 'æ'
+compose ',' 'C' to 'Ç'
+compose ',' 'c' to 'ç'
+compose '`' 'E' to 'È'
+compose '`' 'e' to 'è'
+compose '\'' 'E' to 'É'
+compose '\'' 'e' to 'é'
+compose '^' 'E' to 'Ê'
+compose '^' 'e' to 'ê'
+compose '"' 'E' to 'Ë'
+compose '"' 'e' to 'ë'
+compose '`' 'I' to 'Ì'
+compose '`' 'i' to 'ì'
+compose '\'' 'I' to 'Í'
+compose '\'' 'i' to 'í'
+compose '^' 'I' to 'Î'
+compose '^' 'i' to 'î'
+compose '"' 'I' to 'Ï'
+compose '"' 'i' to 'ï'
+compose '-' 'D' to 'Ð'
+compose '-' 'd' to 'ð'
+compose '~' 'N' to 'Ñ'
+compose '~' 'n' to 'ñ'
+compose '`' 'O' to 'Ò'
+compose '`' 'o' to 'ò'
+compose '\'' 'O' to 'Ó'
+compose '\'' 'o' to 'ó'
+compose '^' 'O' to 'Ô'
+compose '^' 'o' to 'ô'
+compose '~' 'O' to 'Õ'
+compose '~' 'o' to 'õ'
+compose '"' 'O' to 'Ö'
+compose '"' 'o' to 'ö'
+compose '/' 'O' to 'Ø'
+compose '/' 'o' to 'ø'
+compose '`' 'U' to 'Ù'
+compose '`' 'u' to 'ù'
+compose '\'' 'U' to 'Ú'
+compose '\'' 'u' to 'ú'
+compose '^' 'U' to 'Û'
+compose '^' 'u' to 'û'
+compose '"' 'U' to 'Ü'
+compose '"' 'u' to 'ü'
+compose '\'' 'Y' to 'Ý'
+compose '\'' 'y' to 'ý'
+compose 'T' 'H' to 'Þ'
+compose 't' 'h' to 'þ'
+compose 's' 's' to 'ß'
+compose '"' 'y' to 'ÿ'
+compose 's' 'z' to 'ß'
+compose 'i' 'j' to 'ÿ'
--- /dev/null
+/*
+ * linux/drivers/char/keyboard.c
+ *
+ * Written for linux by Johan Myreen as a translation from
+ * the assembly version by Linus (with diacriticals added)
+ *
+ * Some additional features added by Christoph Niemann (ChN), March 1993
+ *
+ * Loadable keymaps by Risto Kankkunen, May 1993
+ *
+ * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993
+ * Added decr/incr_console, dynamic keymaps, Unicode support,
+ * dynamic function/string keys, led setting, Sept 1994
+ * `Sticky' modifier keys, 951006.
+ *
+ * 11-11-96: SAK should now work in the raw mode (Martin Mares)
+ *
+ * Modified to provide 'generic' keyboard support by Hamish Macdonald
+ * Merge with the m68k keyboard driver and split-off of the PC low-level
+ * parts by Geert Uytterhoeven, May 1997
+ *
+ * 27-05-97: Added support for the Magic SysRq Key (Martin Mares)
+ * 30-07-98: Dead keys redone, aeb@cwi.nl.
+ * 21-08-02: Converted to input API, major cleanup. (Vojtech Pavlik)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/consolemap.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/kbd_diacr.h>
+#include <linux/vt_kern.h>
+#include <linux/input.h>
+#include <linux/reboot.h>
+#include <linux/notifier.h>
+#include <linux/jiffies.h>
+
+extern void ctrl_alt_del(void);
+
+/*
+ * Exported functions/variables
+ */
+
+#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
+
+/*
+ * Some laptops take the 789uiojklm,. keys as number pad when NumLock is on.
+ * This seems a good reason to start with NumLock off. On HIL keyboards
+ * of PARISC machines however there is no NumLock key and everyone expects the keypad
+ * to be used for numbers.
+ */
+
+#if defined(CONFIG_PARISC) && (defined(CONFIG_KEYBOARD_HIL) || defined(CONFIG_KEYBOARD_HIL_OLD))
+#define KBD_DEFLEDS (1 << VC_NUMLOCK)
+#else
+#define KBD_DEFLEDS 0
+#endif
+
+#define KBD_DEFLOCK 0
+
+void compute_shiftstate(void);
+
+/*
+ * Handler Tables.
+ */
+
+#define K_HANDLERS\
+ k_self, k_fn, k_spec, k_pad,\
+ k_dead, k_cons, k_cur, k_shift,\
+ k_meta, k_ascii, k_lock, k_lowercase,\
+ k_slock, k_dead2, k_brl, k_ignore
+
+typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value,
+ char up_flag);
+static k_handler_fn K_HANDLERS;
+static k_handler_fn *k_handler[16] = { K_HANDLERS };
+
+#define FN_HANDLERS\
+ fn_null, fn_enter, fn_show_ptregs, fn_show_mem,\
+ fn_show_state, fn_send_intr, fn_lastcons, fn_caps_toggle,\
+ fn_num, fn_hold, fn_scroll_forw, fn_scroll_back,\
+ fn_boot_it, fn_caps_on, fn_compose, fn_SAK,\
+ fn_dec_console, fn_inc_console, fn_spawn_con, fn_bare_num
+
+typedef void (fn_handler_fn)(struct vc_data *vc);
+static fn_handler_fn FN_HANDLERS;
+static fn_handler_fn *fn_handler[] = { FN_HANDLERS };
+
+/*
+ * Variables exported for vt_ioctl.c
+ */
+
+/* maximum values each key_handler can handle */
+const int max_vals[] = {
+ 255, ARRAY_SIZE(func_table) - 1, ARRAY_SIZE(fn_handler) - 1, NR_PAD - 1,
+ NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1,
+ 255, NR_LOCK - 1, 255, NR_BRL - 1
+};
+
+const int NR_TYPES = ARRAY_SIZE(max_vals);
+
+struct kbd_struct kbd_table[MAX_NR_CONSOLES];
+EXPORT_SYMBOL_GPL(kbd_table);
+static struct kbd_struct *kbd = kbd_table;
+
+struct vt_spawn_console vt_spawn_con = {
+ .lock = __SPIN_LOCK_UNLOCKED(vt_spawn_con.lock),
+ .pid = NULL,
+ .sig = 0,
+};
+
+/*
+ * Variables exported for vt.c
+ */
+
+int shift_state = 0;
+
+/*
+ * Internal Data.
+ */
+
+static struct input_handler kbd_handler;
+static DEFINE_SPINLOCK(kbd_event_lock);
+static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
+static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
+static bool dead_key_next;
+static int npadch = -1; /* -1 or number assembled on pad */
+static unsigned int diacr;
+static char rep; /* flag telling character repeat */
+
+static unsigned char ledstate = 0xff; /* undefined */
+static unsigned char ledioctl;
+
+static struct ledptr {
+ unsigned int *addr;
+ unsigned int mask;
+ unsigned char valid:1;
+} ledptrs[3];
+
+/*
+ * Notifier list for console keyboard events
+ */
+static ATOMIC_NOTIFIER_HEAD(keyboard_notifier_list);
+
+int register_keyboard_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&keyboard_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(register_keyboard_notifier);
+
+int unregister_keyboard_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&keyboard_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_keyboard_notifier);
+
+/*
+ * Translation of scancodes to keycodes. We set them on only the first
+ * keyboard in the list that accepts the scancode and keycode.
+ * Explanation for not choosing the first attached keyboard anymore:
+ * USB keyboards for example have two event devices: one for all "normal"
+ * keys and one for extra function keys (like "volume up", "make coffee",
+ * etc.). So this means that scancodes for the extra function keys won't
+ * be valid for the first event device, but will be for the second.
+ */
+
+struct getset_keycode_data {
+ struct input_keymap_entry ke;
+ int error;
+};
+
+static int getkeycode_helper(struct input_handle *handle, void *data)
+{
+ struct getset_keycode_data *d = data;
+
+ d->error = input_get_keycode(handle->dev, &d->ke);
+
+ return d->error == 0; /* stop as soon as we successfully get one */
+}
+
+int getkeycode(unsigned int scancode)
+{
+ struct getset_keycode_data d = {
+ .ke = {
+ .flags = 0,
+ .len = sizeof(scancode),
+ .keycode = 0,
+ },
+ .error = -ENODEV,
+ };
+
+ memcpy(d.ke.scancode, &scancode, sizeof(scancode));
+
+ input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper);
+
+ return d.error ?: d.ke.keycode;
+}
+
+static int setkeycode_helper(struct input_handle *handle, void *data)
+{
+ struct getset_keycode_data *d = data;
+
+ d->error = input_set_keycode(handle->dev, &d->ke);
+
+ return d->error == 0; /* stop as soon as we successfully set one */
+}
+
+int setkeycode(unsigned int scancode, unsigned int keycode)
+{
+ struct getset_keycode_data d = {
+ .ke = {
+ .flags = 0,
+ .len = sizeof(scancode),
+ .keycode = keycode,
+ },
+ .error = -ENODEV,
+ };
+
+ memcpy(d.ke.scancode, &scancode, sizeof(scancode));
+
+ input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper);
+
+ return d.error;
+}
+
+/*
+ * Making beeps and bells. Note that we prefer beeps to bells, but when
+ * shutting the sound off we do both.
+ */
+
+static int kd_sound_helper(struct input_handle *handle, void *data)
+{
+ unsigned int *hz = data;
+ struct input_dev *dev = handle->dev;
+
+ if (test_bit(EV_SND, dev->evbit)) {
+ if (test_bit(SND_TONE, dev->sndbit)) {
+ input_inject_event(handle, EV_SND, SND_TONE, *hz);
+ if (*hz)
+ return 0;
+ }
+ if (test_bit(SND_BELL, dev->sndbit))
+ input_inject_event(handle, EV_SND, SND_BELL, *hz ? 1 : 0);
+ }
+
+ return 0;
+}
+
+static void kd_nosound(unsigned long ignored)
+{
+ static unsigned int zero;
+
+ input_handler_for_each_handle(&kbd_handler, &zero, kd_sound_helper);
+}
+
+static DEFINE_TIMER(kd_mksound_timer, kd_nosound, 0, 0);
+
+void kd_mksound(unsigned int hz, unsigned int ticks)
+{
+ del_timer_sync(&kd_mksound_timer);
+
+ input_handler_for_each_handle(&kbd_handler, &hz, kd_sound_helper);
+
+ if (hz && ticks)
+ mod_timer(&kd_mksound_timer, jiffies + ticks);
+}
+EXPORT_SYMBOL(kd_mksound);
+
+/*
+ * Setting the keyboard rate.
+ */
+
+static int kbd_rate_helper(struct input_handle *handle, void *data)
+{
+ struct input_dev *dev = handle->dev;
+ struct kbd_repeat *rep = data;
+
+ if (test_bit(EV_REP, dev->evbit)) {
+
+ if (rep[0].delay > 0)
+ input_inject_event(handle,
+ EV_REP, REP_DELAY, rep[0].delay);
+ if (rep[0].period > 0)
+ input_inject_event(handle,
+ EV_REP, REP_PERIOD, rep[0].period);
+
+ rep[1].delay = dev->rep[REP_DELAY];
+ rep[1].period = dev->rep[REP_PERIOD];
+ }
+
+ return 0;
+}
+
+int kbd_rate(struct kbd_repeat *rep)
+{
+ struct kbd_repeat data[2] = { *rep };
+
+ input_handler_for_each_handle(&kbd_handler, data, kbd_rate_helper);
+ *rep = data[1]; /* Copy currently used settings */
+
+ return 0;
+}
+
+/*
+ * Helper Functions.
+ */
+static void put_queue(struct vc_data *vc, int ch)
+{
+ struct tty_struct *tty = vc->port.tty;
+
+ if (tty) {
+ tty_insert_flip_char(tty, ch, 0);
+ con_schedule_flip(tty);
+ }
+}
+
+static void puts_queue(struct vc_data *vc, char *cp)
+{
+ struct tty_struct *tty = vc->port.tty;
+
+ if (!tty)
+ return;
+
+ while (*cp) {
+ tty_insert_flip_char(tty, *cp, 0);
+ cp++;
+ }
+ con_schedule_flip(tty);
+}
+
+static void applkey(struct vc_data *vc, int key, char mode)
+{
+ static char buf[] = { 0x1b, 'O', 0x00, 0x00 };
+
+ buf[1] = (mode ? 'O' : '[');
+ buf[2] = key;
+ puts_queue(vc, buf);
+}
+
+/*
+ * Many other routines do put_queue, but I think either
+ * they produce ASCII, or they produce some user-assigned
+ * string, and in both cases we might assume that it is
+ * in utf-8 already.
+ */
+static void to_utf8(struct vc_data *vc, uint c)
+{
+ if (c < 0x80)
+ /* 0******* */
+ put_queue(vc, c);
+ else if (c < 0x800) {
+ /* 110***** 10****** */
+ put_queue(vc, 0xc0 | (c >> 6));
+ put_queue(vc, 0x80 | (c & 0x3f));
+ } else if (c < 0x10000) {
+ if (c >= 0xD800 && c < 0xE000)
+ return;
+ if (c == 0xFFFF)
+ return;
+ /* 1110**** 10****** 10****** */
+ put_queue(vc, 0xe0 | (c >> 12));
+ put_queue(vc, 0x80 | ((c >> 6) & 0x3f));
+ put_queue(vc, 0x80 | (c & 0x3f));
+ } else if (c < 0x110000) {
+ /* 11110*** 10****** 10****** 10****** */
+ put_queue(vc, 0xf0 | (c >> 18));
+ put_queue(vc, 0x80 | ((c >> 12) & 0x3f));
+ put_queue(vc, 0x80 | ((c >> 6) & 0x3f));
+ put_queue(vc, 0x80 | (c & 0x3f));
+ }
+}
+
+/*
+ * Called after returning from RAW mode or when changing consoles - recompute
+ * shift_down[] and shift_state from key_down[] maybe called when keymap is
+ * undefined, so that shiftkey release is seen
+ */
+void compute_shiftstate(void)
+{
+ unsigned int i, j, k, sym, val;
+
+ shift_state = 0;
+ memset(shift_down, 0, sizeof(shift_down));
+
+ for (i = 0; i < ARRAY_SIZE(key_down); i++) {
+
+ if (!key_down[i])
+ continue;
+
+ k = i * BITS_PER_LONG;
+
+ for (j = 0; j < BITS_PER_LONG; j++, k++) {
+
+ if (!test_bit(k, key_down))
+ continue;
+
+ sym = U(key_maps[0][k]);
+ if (KTYP(sym) != KT_SHIFT && KTYP(sym) != KT_SLOCK)
+ continue;
+
+ val = KVAL(sym);
+ if (val == KVAL(K_CAPSSHIFT))
+ val = KVAL(K_SHIFT);
+
+ shift_down[val]++;
+ shift_state |= (1 << val);
+ }
+ }
+}
+
+/*
+ * We have a combining character DIACR here, followed by the character CH.
+ * If the combination occurs in the table, return the corresponding value.
+ * Otherwise, if CH is a space or equals DIACR, return DIACR.
+ * Otherwise, conclude that DIACR was not combining after all,
+ * queue it and return CH.
+ */
+static unsigned int handle_diacr(struct vc_data *vc, unsigned int ch)
+{
+ unsigned int d = diacr;
+ unsigned int i;
+
+ diacr = 0;
+
+ if ((d & ~0xff) == BRL_UC_ROW) {
+ if ((ch & ~0xff) == BRL_UC_ROW)
+ return d | ch;
+ } else {
+ for (i = 0; i < accent_table_size; i++)
+ if (accent_table[i].diacr == d && accent_table[i].base == ch)
+ return accent_table[i].result;
+ }
+
+ if (ch == ' ' || ch == (BRL_UC_ROW|0) || ch == d)
+ return d;
+
+ if (kbd->kbdmode == VC_UNICODE)
+ to_utf8(vc, d);
+ else {
+ int c = conv_uni_to_8bit(d);
+ if (c != -1)
+ put_queue(vc, c);
+ }
+
+ return ch;
+}
+
+/*
+ * Special function handlers
+ */
+static void fn_enter(struct vc_data *vc)
+{
+ if (diacr) {
+ if (kbd->kbdmode == VC_UNICODE)
+ to_utf8(vc, diacr);
+ else {
+ int c = conv_uni_to_8bit(diacr);
+ if (c != -1)
+ put_queue(vc, c);
+ }
+ diacr = 0;
+ }
+
+ put_queue(vc, 13);
+ if (vc_kbd_mode(kbd, VC_CRLF))
+ put_queue(vc, 10);
+}
+
+static void fn_caps_toggle(struct vc_data *vc)
+{
+ if (rep)
+ return;
+
+ chg_vc_kbd_led(kbd, VC_CAPSLOCK);
+}
+
+static void fn_caps_on(struct vc_data *vc)
+{
+ if (rep)
+ return;
+
+ set_vc_kbd_led(kbd, VC_CAPSLOCK);
+}
+
+static void fn_show_ptregs(struct vc_data *vc)
+{
+ struct pt_regs *regs = get_irq_regs();
+
+ if (regs)
+ show_regs(regs);
+}
+
+static void fn_hold(struct vc_data *vc)
+{
+ struct tty_struct *tty = vc->port.tty;
+
+ if (rep || !tty)
+ return;
+
+ /*
+ * Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty);
+ * these routines are also activated by ^S/^Q.
+ * (And SCROLLOCK can also be set by the ioctl KDSKBLED.)
+ */
+ if (tty->stopped)
+ start_tty(tty);
+ else
+ stop_tty(tty);
+}
+
+static void fn_num(struct vc_data *vc)
+{
+ if (vc_kbd_mode(kbd, VC_APPLIC))
+ applkey(vc, 'P', 1);
+ else
+ fn_bare_num(vc);
+}
+
+/*
+ * Bind this to Shift-NumLock if you work in application keypad mode
+ * but want to be able to change the NumLock flag.
+ * Bind this to NumLock if you prefer that the NumLock key always
+ * changes the NumLock flag.
+ */
+static void fn_bare_num(struct vc_data *vc)
+{
+ if (!rep)
+ chg_vc_kbd_led(kbd, VC_NUMLOCK);
+}
+
+static void fn_lastcons(struct vc_data *vc)
+{
+ /* switch to the last used console, ChN */
+ set_console(last_console);
+}
+
+static void fn_dec_console(struct vc_data *vc)
+{
+ int i, cur = fg_console;
+
+ /* Currently switching? Queue this next switch relative to that. */
+ if (want_console != -1)
+ cur = want_console;
+
+ for (i = cur - 1; i != cur; i--) {
+ if (i == -1)
+ i = MAX_NR_CONSOLES - 1;
+ if (vc_cons_allocated(i))
+ break;
+ }
+ set_console(i);
+}
+
+static void fn_inc_console(struct vc_data *vc)
+{
+ int i, cur = fg_console;
+
+ /* Currently switching? Queue this next switch relative to that. */
+ if (want_console != -1)
+ cur = want_console;
+
+ for (i = cur+1; i != cur; i++) {
+ if (i == MAX_NR_CONSOLES)
+ i = 0;
+ if (vc_cons_allocated(i))
+ break;
+ }
+ set_console(i);
+}
+
+static void fn_send_intr(struct vc_data *vc)
+{
+ struct tty_struct *tty = vc->port.tty;
+
+ if (!tty)
+ return;
+ tty_insert_flip_char(tty, 0, TTY_BREAK);
+ con_schedule_flip(tty);
+}
+
+static void fn_scroll_forw(struct vc_data *vc)
+{
+ scrollfront(vc, 0);
+}
+
+static void fn_scroll_back(struct vc_data *vc)
+{
+ scrollback(vc, 0);
+}
+
+static void fn_show_mem(struct vc_data *vc)
+{
+ show_mem();
+}
+
+static void fn_show_state(struct vc_data *vc)
+{
+ show_state();
+}
+
+static void fn_boot_it(struct vc_data *vc)
+{
+ ctrl_alt_del();
+}
+
+static void fn_compose(struct vc_data *vc)
+{
+ dead_key_next = true;
+}
+
+static void fn_spawn_con(struct vc_data *vc)
+{
+ spin_lock(&vt_spawn_con.lock);
+ if (vt_spawn_con.pid)
+ if (kill_pid(vt_spawn_con.pid, vt_spawn_con.sig, 1)) {
+ put_pid(vt_spawn_con.pid);
+ vt_spawn_con.pid = NULL;
+ }
+ spin_unlock(&vt_spawn_con.lock);
+}
+
+static void fn_SAK(struct vc_data *vc)
+{
+ struct work_struct *SAK_work = &vc_cons[fg_console].SAK_work;
+ schedule_work(SAK_work);
+}
+
+static void fn_null(struct vc_data *vc)
+{
+ compute_shiftstate();
+}
+
+/*
+ * Special key handlers
+ */
+static void k_ignore(struct vc_data *vc, unsigned char value, char up_flag)
+{
+}
+
+static void k_spec(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ if (up_flag)
+ return;
+ if (value >= ARRAY_SIZE(fn_handler))
+ return;
+ if ((kbd->kbdmode == VC_RAW ||
+ kbd->kbdmode == VC_MEDIUMRAW) &&
+ value != KVAL(K_SAK))
+ return; /* SAK is allowed even in raw mode */
+ fn_handler[value](vc);
+}
+
+static void k_lowercase(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ pr_err("k_lowercase was called - impossible\n");
+}
+
+static void k_unicode(struct vc_data *vc, unsigned int value, char up_flag)
+{
+ if (up_flag)
+ return; /* no action, if this is a key release */
+
+ if (diacr)
+ value = handle_diacr(vc, value);
+
+ if (dead_key_next) {
+ dead_key_next = false;
+ diacr = value;
+ return;
+ }
+ if (kbd->kbdmode == VC_UNICODE)
+ to_utf8(vc, value);
+ else {
+ int c = conv_uni_to_8bit(value);
+ if (c != -1)
+ put_queue(vc, c);
+ }
+}
+
+/*
+ * Handle dead key. Note that we now may have several
+ * dead keys modifying the same character. Very useful
+ * for Vietnamese.
+ */
+static void k_deadunicode(struct vc_data *vc, unsigned int value, char up_flag)
+{
+ if (up_flag)
+ return;
+
+ diacr = (diacr ? handle_diacr(vc, value) : value);
+}
+
+static void k_self(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ k_unicode(vc, conv_8bit_to_uni(value), up_flag);
+}
+
+static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ k_deadunicode(vc, value, up_flag);
+}
+
+/*
+ * Obsolete - for backwards compatibility only
+ */
+static void k_dead(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ static const unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' };
+
+ k_deadunicode(vc, ret_diacr[value], up_flag);
+}
+
+static void k_cons(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ if (up_flag)
+ return;
+
+ set_console(value);
+}
+
+static void k_fn(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ if (up_flag)
+ return;
+
+ if ((unsigned)value < ARRAY_SIZE(func_table)) {
+ if (func_table[value])
+ puts_queue(vc, func_table[value]);
+ } else
+ pr_err("k_fn called with value=%d\n", value);
+}
+
+static void k_cur(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ static const char cur_chars[] = "BDCA";
+
+ if (up_flag)
+ return;
+
+ applkey(vc, cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE));
+}
+
+static void k_pad(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ static const char pad_chars[] = "0123456789+-*/\015,.?()#";
+ static const char app_map[] = "pqrstuvwxylSRQMnnmPQS";
+
+ if (up_flag)
+ return; /* no action, if this is a key release */
+
+ /* kludge... shift forces cursor/number keys */
+ if (vc_kbd_mode(kbd, VC_APPLIC) && !shift_down[KG_SHIFT]) {
+ applkey(vc, app_map[value], 1);
+ return;
+ }
+
+ if (!vc_kbd_led(kbd, VC_NUMLOCK)) {
+
+ switch (value) {
+ case KVAL(K_PCOMMA):
+ case KVAL(K_PDOT):
+ k_fn(vc, KVAL(K_REMOVE), 0);
+ return;
+ case KVAL(K_P0):
+ k_fn(vc, KVAL(K_INSERT), 0);
+ return;
+ case KVAL(K_P1):
+ k_fn(vc, KVAL(K_SELECT), 0);
+ return;
+ case KVAL(K_P2):
+ k_cur(vc, KVAL(K_DOWN), 0);
+ return;
+ case KVAL(K_P3):
+ k_fn(vc, KVAL(K_PGDN), 0);
+ return;
+ case KVAL(K_P4):
+ k_cur(vc, KVAL(K_LEFT), 0);
+ return;
+ case KVAL(K_P6):
+ k_cur(vc, KVAL(K_RIGHT), 0);
+ return;
+ case KVAL(K_P7):
+ k_fn(vc, KVAL(K_FIND), 0);
+ return;
+ case KVAL(K_P8):
+ k_cur(vc, KVAL(K_UP), 0);
+ return;
+ case KVAL(K_P9):
+ k_fn(vc, KVAL(K_PGUP), 0);
+ return;
+ case KVAL(K_P5):
+ applkey(vc, 'G', vc_kbd_mode(kbd, VC_APPLIC));
+ return;
+ }
+ }
+
+ put_queue(vc, pad_chars[value]);
+ if (value == KVAL(K_PENTER) && vc_kbd_mode(kbd, VC_CRLF))
+ put_queue(vc, 10);
+}
+
+static void k_shift(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ int old_state = shift_state;
+
+ if (rep)
+ return;
+ /*
+ * Mimic typewriter:
+ * a CapsShift key acts like Shift but undoes CapsLock
+ */
+ if (value == KVAL(K_CAPSSHIFT)) {
+ value = KVAL(K_SHIFT);
+ if (!up_flag)
+ clr_vc_kbd_led(kbd, VC_CAPSLOCK);
+ }
+
+ if (up_flag) {
+ /*
+ * handle the case that two shift or control
+ * keys are depressed simultaneously
+ */
+ if (shift_down[value])
+ shift_down[value]--;
+ } else
+ shift_down[value]++;
+
+ if (shift_down[value])
+ shift_state |= (1 << value);
+ else
+ shift_state &= ~(1 << value);
+
+ /* kludge */
+ if (up_flag && shift_state != old_state && npadch != -1) {
+ if (kbd->kbdmode == VC_UNICODE)
+ to_utf8(vc, npadch);
+ else
+ put_queue(vc, npadch & 0xff);
+ npadch = -1;
+ }
+}
+
+static void k_meta(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ if (up_flag)
+ return;
+
+ if (vc_kbd_mode(kbd, VC_META)) {
+ put_queue(vc, '\033');
+ put_queue(vc, value);
+ } else
+ put_queue(vc, value | 0x80);
+}
+
+static void k_ascii(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ int base;
+
+ if (up_flag)
+ return;
+
+ if (value < 10) {
+ /* decimal input of code, while Alt depressed */
+ base = 10;
+ } else {
+ /* hexadecimal input of code, while AltGr depressed */
+ value -= 10;
+ base = 16;
+ }
+
+ if (npadch == -1)
+ npadch = value;
+ else
+ npadch = npadch * base + value;
+}
+
+static void k_lock(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ if (up_flag || rep)
+ return;
+
+ chg_vc_kbd_lock(kbd, value);
+}
+
+static void k_slock(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ k_shift(vc, value, up_flag);
+ if (up_flag || rep)
+ return;
+
+ chg_vc_kbd_slock(kbd, value);
+ /* try to make Alt, oops, AltGr and such work */
+ if (!key_maps[kbd->lockstate ^ kbd->slockstate]) {
+ kbd->slockstate = 0;
+ chg_vc_kbd_slock(kbd, value);
+ }
+}
+
+/* by default, 300ms interval for combination release */
+static unsigned brl_timeout = 300;
+MODULE_PARM_DESC(brl_timeout, "Braille keys release delay in ms (0 for commit on first key release)");
+module_param(brl_timeout, uint, 0644);
+
+static unsigned brl_nbchords = 1;
+MODULE_PARM_DESC(brl_nbchords, "Number of chords that produce a braille pattern (0 for dead chords)");
+module_param(brl_nbchords, uint, 0644);
+
+static void k_brlcommit(struct vc_data *vc, unsigned int pattern, char up_flag)
+{
+ static unsigned long chords;
+ static unsigned committed;
+
+ if (!brl_nbchords)
+ k_deadunicode(vc, BRL_UC_ROW | pattern, up_flag);
+ else {
+ committed |= pattern;
+ chords++;
+ if (chords == brl_nbchords) {
+ k_unicode(vc, BRL_UC_ROW | committed, up_flag);
+ chords = 0;
+ committed = 0;
+ }
+ }
+}
+
+static void k_brl(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ static unsigned pressed, committing;
+ static unsigned long releasestart;
+
+ if (kbd->kbdmode != VC_UNICODE) {
+ if (!up_flag)
+ pr_warning("keyboard mode must be unicode for braille patterns\n");
+ return;
+ }
+
+ if (!value) {
+ k_unicode(vc, BRL_UC_ROW, up_flag);
+ return;
+ }
+
+ if (value > 8)
+ return;
+
+ if (!up_flag) {
+ pressed |= 1 << (value - 1);
+ if (!brl_timeout)
+ committing = pressed;
+ } else if (brl_timeout) {
+ if (!committing ||
+ time_after(jiffies,
+ releasestart + msecs_to_jiffies(brl_timeout))) {
+ committing = pressed;
+ releasestart = jiffies;
+ }
+ pressed &= ~(1 << (value - 1));
+ if (!pressed && committing) {
+ k_brlcommit(vc, committing, 0);
+ committing = 0;
+ }
+ } else {
+ if (committing) {
+ k_brlcommit(vc, committing, 0);
+ committing = 0;
+ }
+ pressed &= ~(1 << (value - 1));
+ }
+}
+
+/*
+ * The leds display either (i) the status of NumLock, CapsLock, ScrollLock,
+ * or (ii) whatever pattern of lights people want to show using KDSETLED,
+ * or (iii) specified bits of specified words in kernel memory.
+ */
+unsigned char getledstate(void)
+{
+ return ledstate;
+}
+
+void setledstate(struct kbd_struct *kbd, unsigned int led)
+{
+ if (!(led & ~7)) {
+ ledioctl = led;
+ kbd->ledmode = LED_SHOW_IOCTL;
+ } else
+ kbd->ledmode = LED_SHOW_FLAGS;
+
+ set_leds();
+}
+
+static inline unsigned char getleds(void)
+{
+ struct kbd_struct *kbd = kbd_table + fg_console;
+ unsigned char leds;
+ int i;
+
+ if (kbd->ledmode == LED_SHOW_IOCTL)
+ return ledioctl;
+
+ leds = kbd->ledflagstate;
+
+ if (kbd->ledmode == LED_SHOW_MEM) {
+ for (i = 0; i < 3; i++)
+ if (ledptrs[i].valid) {
+ if (*ledptrs[i].addr & ledptrs[i].mask)
+ leds |= (1 << i);
+ else
+ leds &= ~(1 << i);
+ }
+ }
+ return leds;
+}
+
+static int kbd_update_leds_helper(struct input_handle *handle, void *data)
+{
+ unsigned char leds = *(unsigned char *)data;
+
+ if (test_bit(EV_LED, handle->dev->evbit)) {
+ input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));
+ input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02));
+ input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04));
+ input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * This is the tasklet that updates LED state on all keyboards
+ * attached to the box. The reason we use tasklet is that we
+ * need to handle the scenario when keyboard handler is not
+ * registered yet but we already getting updates form VT to
+ * update led state.
+ */
+static void kbd_bh(unsigned long dummy)
+{
+ unsigned char leds = getleds();
+
+ if (leds != ledstate) {
+ input_handler_for_each_handle(&kbd_handler, &leds,
+ kbd_update_leds_helper);
+ ledstate = leds;
+ }
+}
+
+DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);
+
+#if defined(CONFIG_X86) || defined(CONFIG_IA64) || defined(CONFIG_ALPHA) ||\
+ defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) ||\
+ defined(CONFIG_PARISC) || defined(CONFIG_SUPERH) ||\
+ (defined(CONFIG_ARM) && defined(CONFIG_KEYBOARD_ATKBD) && !defined(CONFIG_ARCH_RPC)) ||\
+ defined(CONFIG_AVR32)
+
+#define HW_RAW(dev) (test_bit(EV_MSC, dev->evbit) && test_bit(MSC_RAW, dev->mscbit) &&\
+ ((dev)->id.bustype == BUS_I8042) && ((dev)->id.vendor == 0x0001) && ((dev)->id.product == 0x0001))
+
+static const unsigned short x86_keycodes[256] =
+ { 0, 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, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84,118, 86, 87, 88,115,120,119,121,112,123, 92,
+ 284,285,309, 0,312, 91,327,328,329,331,333,335,336,337,338,339,
+ 367,288,302,304,350, 89,334,326,267,126,268,269,125,347,348,349,
+ 360,261,262,263,268,376,100,101,321,316,373,286,289,102,351,355,
+ 103,104,105,275,287,279,258,106,274,107,294,364,358,363,362,361,
+ 291,108,381,281,290,272,292,305,280, 99,112,257,306,359,113,114,
+ 264,117,271,374,379,265,266, 93, 94, 95, 85,259,375,260, 90,116,
+ 377,109,111,277,278,282,283,295,296,297,299,300,301,293,303,307,
+ 308,310,313,314,315,317,318,319,320,357,322,323,324,325,276,330,
+ 332,340,365,342,343,344,345,346,356,270,341,368,369,370,371,372 };
+
+#ifdef CONFIG_SPARC
+static int sparc_l1_a_state;
+extern void sun_do_break(void);
+#endif
+
+static int emulate_raw(struct vc_data *vc, unsigned int keycode,
+ unsigned char up_flag)
+{
+ int code;
+
+ switch (keycode) {
+
+ case KEY_PAUSE:
+ put_queue(vc, 0xe1);
+ put_queue(vc, 0x1d | up_flag);
+ put_queue(vc, 0x45 | up_flag);
+ break;
+
+ case KEY_HANGEUL:
+ if (!up_flag)
+ put_queue(vc, 0xf2);
+ break;
+
+ case KEY_HANJA:
+ if (!up_flag)
+ put_queue(vc, 0xf1);
+ break;
+
+ case KEY_SYSRQ:
+ /*
+ * Real AT keyboards (that's what we're trying
+ * to emulate here emit 0xe0 0x2a 0xe0 0x37 when
+ * pressing PrtSc/SysRq alone, but simply 0x54
+ * when pressing Alt+PrtSc/SysRq.
+ */
+ if (test_bit(KEY_LEFTALT, key_down) ||
+ test_bit(KEY_RIGHTALT, key_down)) {
+ put_queue(vc, 0x54 | up_flag);
+ } else {
+ put_queue(vc, 0xe0);
+ put_queue(vc, 0x2a | up_flag);
+ put_queue(vc, 0xe0);
+ put_queue(vc, 0x37 | up_flag);
+ }
+ break;
+
+ default:
+ if (keycode > 255)
+ return -1;
+
+ code = x86_keycodes[keycode];
+ if (!code)
+ return -1;
+
+ if (code & 0x100)
+ put_queue(vc, 0xe0);
+ put_queue(vc, (code & 0x7f) | up_flag);
+
+ break;
+ }
+
+ return 0;
+}
+
+#else
+
+#define HW_RAW(dev) 0
+
+static int emulate_raw(struct vc_data *vc, unsigned int keycode, unsigned char up_flag)
+{
+ if (keycode > 127)
+ return -1;
+
+ put_queue(vc, keycode | up_flag);
+ return 0;
+}
+#endif
+
+static void kbd_rawcode(unsigned char data)
+{
+ struct vc_data *vc = vc_cons[fg_console].d;
+
+ kbd = kbd_table + vc->vc_num;
+ if (kbd->kbdmode == VC_RAW)
+ put_queue(vc, data);
+}
+
+static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
+{
+ struct vc_data *vc = vc_cons[fg_console].d;
+ unsigned short keysym, *key_map;
+ unsigned char type;
+ bool raw_mode;
+ struct tty_struct *tty;
+ int shift_final;
+ struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down };
+ int rc;
+
+ tty = vc->port.tty;
+
+ if (tty && (!tty->driver_data)) {
+ /* No driver data? Strange. Okay we fix it then. */
+ tty->driver_data = vc;
+ }
+
+ kbd = kbd_table + vc->vc_num;
+
+#ifdef CONFIG_SPARC
+ if (keycode == KEY_STOP)
+ sparc_l1_a_state = down;
+#endif
+
+ rep = (down == 2);
+
+ raw_mode = (kbd->kbdmode == VC_RAW);
+ if (raw_mode && !hw_raw)
+ if (emulate_raw(vc, keycode, !down << 7))
+ if (keycode < BTN_MISC && printk_ratelimit())
+ pr_warning("can't emulate rawmode for keycode %d\n",
+ keycode);
+
+#ifdef CONFIG_SPARC
+ if (keycode == KEY_A && sparc_l1_a_state) {
+ sparc_l1_a_state = false;
+ sun_do_break();
+ }
+#endif
+
+ if (kbd->kbdmode == VC_MEDIUMRAW) {
+ /*
+ * This is extended medium raw mode, with keys above 127
+ * encoded as 0, high 7 bits, low 7 bits, with the 0 bearing
+ * the 'up' flag if needed. 0 is reserved, so this shouldn't
+ * interfere with anything else. The two bytes after 0 will
+ * always have the up flag set not to interfere with older
+ * applications. This allows for 16384 different keycodes,
+ * which should be enough.
+ */
+ if (keycode < 128) {
+ put_queue(vc, keycode | (!down << 7));
+ } else {
+ put_queue(vc, !down << 7);
+ put_queue(vc, (keycode >> 7) | 0x80);
+ put_queue(vc, keycode | 0x80);
+ }
+ raw_mode = true;
+ }
+
+ if (down)
+ set_bit(keycode, key_down);
+ else
+ clear_bit(keycode, key_down);
+
+ if (rep &&
+ (!vc_kbd_mode(kbd, VC_REPEAT) ||
+ (tty && !L_ECHO(tty) && tty_chars_in_buffer(tty)))) {
+ /*
+ * Don't repeat a key if the input buffers are not empty and the
+ * characters get aren't echoed locally. This makes key repeat
+ * usable with slow applications and under heavy loads.
+ */
+ return;
+ }
+
+ param.shift = shift_final = (shift_state | kbd->slockstate) ^ kbd->lockstate;
+ param.ledstate = kbd->ledflagstate;
+ key_map = key_maps[shift_final];
+
+ rc = atomic_notifier_call_chain(&keyboard_notifier_list,
+ KBD_KEYCODE, ¶m);
+ if (rc == NOTIFY_STOP || !key_map) {
+ atomic_notifier_call_chain(&keyboard_notifier_list,
+ KBD_UNBOUND_KEYCODE, ¶m);
+ compute_shiftstate();
+ kbd->slockstate = 0;
+ return;
+ }
+
+ if (keycode < NR_KEYS)
+ keysym = key_map[keycode];
+ else if (keycode >= KEY_BRL_DOT1 && keycode <= KEY_BRL_DOT8)
+ keysym = U(K(KT_BRL, keycode - KEY_BRL_DOT1 + 1));
+ else
+ return;
+
+ type = KTYP(keysym);
+
+ if (type < 0xf0) {
+ param.value = keysym;
+ rc = atomic_notifier_call_chain(&keyboard_notifier_list,
+ KBD_UNICODE, ¶m);
+ if (rc != NOTIFY_STOP)
+ if (down && !raw_mode)
+ to_utf8(vc, keysym);
+ return;
+ }
+
+ type -= 0xf0;
+
+ if (type == KT_LETTER) {
+ type = KT_LATIN;
+ if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
+ key_map = key_maps[shift_final ^ (1 << KG_SHIFT)];
+ if (key_map)
+ keysym = key_map[keycode];
+ }
+ }
+
+ param.value = keysym;
+ rc = atomic_notifier_call_chain(&keyboard_notifier_list,
+ KBD_KEYSYM, ¶m);
+ if (rc == NOTIFY_STOP)
+ return;
+
+ if (raw_mode && type != KT_SPEC && type != KT_SHIFT)
+ return;
+
+ (*k_handler[type])(vc, keysym & 0xff, !down);
+
+ param.ledstate = kbd->ledflagstate;
+ atomic_notifier_call_chain(&keyboard_notifier_list, KBD_POST_KEYSYM, ¶m);
+
+ if (type != KT_SLOCK)
+ kbd->slockstate = 0;
+}
+
+static void kbd_event(struct input_handle *handle, unsigned int event_type,
+ unsigned int event_code, int value)
+{
+ /* We are called with interrupts disabled, just take the lock */
+ spin_lock(&kbd_event_lock);
+
+ if (event_type == EV_MSC && event_code == MSC_RAW && HW_RAW(handle->dev))
+ kbd_rawcode(value);
+ if (event_type == EV_KEY)
+ kbd_keycode(event_code, value, HW_RAW(handle->dev));
+
+ spin_unlock(&kbd_event_lock);
+
+ tasklet_schedule(&keyboard_tasklet);
+ do_poke_blanked_console = 1;
+ schedule_console_callback();
+}
+
+static bool kbd_match(struct input_handler *handler, struct input_dev *dev)
+{
+ int i;
+
+ if (test_bit(EV_SND, dev->evbit))
+ return true;
+
+ if (test_bit(EV_KEY, dev->evbit)) {
+ for (i = KEY_RESERVED; i < BTN_MISC; i++)
+ if (test_bit(i, dev->keybit))
+ return true;
+ for (i = KEY_BRL_DOT1; i <= KEY_BRL_DOT10; i++)
+ if (test_bit(i, dev->keybit))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * When a keyboard (or other input device) is found, the kbd_connect
+ * function is called. The function then looks at the device, and if it
+ * likes it, it can open it and get events from it. In this (kbd_connect)
+ * function, we should decide which VT to bind that keyboard to initially.
+ */
+static int kbd_connect(struct input_handler *handler, struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ struct input_handle *handle;
+ int error;
+
+ handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = "kbd";
+
+ error = input_register_handle(handle);
+ if (error)
+ goto err_free_handle;
+
+ error = input_open_device(handle);
+ if (error)
+ goto err_unregister_handle;
+
+ return 0;
+
+ err_unregister_handle:
+ input_unregister_handle(handle);
+ err_free_handle:
+ kfree(handle);
+ return error;
+}
+
+static void kbd_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+/*
+ * Start keyboard handler on the new keyboard by refreshing LED state to
+ * match the rest of the system.
+ */
+static void kbd_start(struct input_handle *handle)
+{
+ tasklet_disable(&keyboard_tasklet);
+
+ if (ledstate != 0xff)
+ kbd_update_leds_helper(handle, &ledstate);
+
+ tasklet_enable(&keyboard_tasklet);
+}
+
+static const struct input_device_id kbd_ids[] = {
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_KEY) },
+ },
+
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_SND) },
+ },
+
+ { }, /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, kbd_ids);
+
+static struct input_handler kbd_handler = {
+ .event = kbd_event,
+ .match = kbd_match,
+ .connect = kbd_connect,
+ .disconnect = kbd_disconnect,
+ .start = kbd_start,
+ .name = "kbd",
+ .id_table = kbd_ids,
+};
+
+int __init kbd_init(void)
+{
+ int i;
+ int error;
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ kbd_table[i].ledflagstate = KBD_DEFLEDS;
+ kbd_table[i].default_ledflagstate = KBD_DEFLEDS;
+ kbd_table[i].ledmode = LED_SHOW_FLAGS;
+ kbd_table[i].lockstate = KBD_DEFLOCK;
+ kbd_table[i].slockstate = 0;
+ kbd_table[i].modeflags = KBD_DEFMODE;
+ kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
+ }
+
+ error = input_register_handler(&kbd_handler);
+ if (error)
+ return error;
+
+ tasklet_enable(&keyboard_tasklet);
+ tasklet_schedule(&keyboard_tasklet);
+
+ return 0;
+}
--- /dev/null
+/*
+ * linux/drivers/char/selection.c
+ *
+ * This module exports the functions:
+ *
+ * 'int set_selection(struct tiocl_selection __user *, struct tty_struct *)'
+ * 'void clear_selection(void)'
+ * 'int paste_selection(struct tty_struct *)'
+ * 'int sel_loadlut(char __user *)'
+ *
+ * Now that /dev/vcs exists, most of this can disappear again.
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/consolemap.h>
+#include <linux/selection.h>
+#include <linux/tiocl.h>
+#include <linux/console.h>
+#include <linux/smp_lock.h>
+
+/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
+#define isspace(c) ((c) == ' ')
+
+extern void poke_blanked_console(void);
+
+/* Variables for selection control. */
+/* Use a dynamic buffer, instead of static (Dec 1994) */
+struct vc_data *sel_cons; /* must not be deallocated */
+static int use_unicode;
+static volatile int sel_start = -1; /* cleared by clear_selection */
+static int sel_end;
+static int sel_buffer_lth;
+static char *sel_buffer;
+
+/* clear_selection, highlight and highlight_pointer can be called
+ from interrupt (via scrollback/front) */
+
+/* set reverse video on characters s-e of console with selection. */
+static inline void highlight(const int s, const int e)
+{
+ invert_screen(sel_cons, s, e-s+2, 1);
+}
+
+/* use complementary color to show the pointer */
+static inline void highlight_pointer(const int where)
+{
+ complement_pos(sel_cons, where);
+}
+
+static u16
+sel_pos(int n)
+{
+ return inverse_translate(sel_cons, screen_glyph(sel_cons, n),
+ use_unicode);
+}
+
+/* remove the current selection highlight, if any,
+ from the console holding the selection. */
+void
+clear_selection(void) {
+ highlight_pointer(-1); /* hide the pointer */
+ if (sel_start != -1) {
+ highlight(sel_start, sel_end);
+ sel_start = -1;
+ }
+}
+
+/*
+ * User settable table: what characters are to be considered alphabetic?
+ * 256 bits
+ */
+static u32 inwordLut[8]={
+ 0x00000000, /* control chars */
+ 0x03FF0000, /* digits */
+ 0x87FFFFFE, /* uppercase and '_' */
+ 0x07FFFFFE, /* lowercase */
+ 0x00000000,
+ 0x00000000,
+ 0xFF7FFFFF, /* latin-1 accented letters, not multiplication sign */
+ 0xFF7FFFFF /* latin-1 accented letters, not division sign */
+};
+
+static inline int inword(const u16 c) {
+ return c > 0xff || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1);
+}
+
+/* set inwordLut contents. Invoked by ioctl(). */
+int sel_loadlut(char __user *p)
+{
+ return copy_from_user(inwordLut, (u32 __user *)(p+4), 32) ? -EFAULT : 0;
+}
+
+/* does screen address p correspond to character at LH/RH edge of screen? */
+static inline int atedge(const int p, int size_row)
+{
+ return (!(p % size_row) || !((p + 2) % size_row));
+}
+
+/* constrain v such that v <= u */
+static inline unsigned short limit(const unsigned short v, const unsigned short u)
+{
+ return (v > u) ? u : v;
+}
+
+/* stores the char in UTF8 and returns the number of bytes used (1-3) */
+static int store_utf8(u16 c, char *p)
+{
+ if (c < 0x80) {
+ /* 0******* */
+ p[0] = c;
+ return 1;
+ } else if (c < 0x800) {
+ /* 110***** 10****** */
+ p[0] = 0xc0 | (c >> 6);
+ p[1] = 0x80 | (c & 0x3f);
+ return 2;
+ } else {
+ /* 1110**** 10****** 10****** */
+ p[0] = 0xe0 | (c >> 12);
+ p[1] = 0x80 | ((c >> 6) & 0x3f);
+ p[2] = 0x80 | (c & 0x3f);
+ return 3;
+ }
+}
+
+/* set the current selection. Invoked by ioctl() or by kernel code. */
+int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty)
+{
+ struct vc_data *vc = vc_cons[fg_console].d;
+ int sel_mode, new_sel_start, new_sel_end, spc;
+ char *bp, *obp;
+ int i, ps, pe, multiplier;
+ u16 c;
+ struct kbd_struct *kbd = kbd_table + fg_console;
+
+ poke_blanked_console();
+
+ { unsigned short xs, ys, xe, ye;
+
+ if (!access_ok(VERIFY_READ, sel, sizeof(*sel)))
+ return -EFAULT;
+ __get_user(xs, &sel->xs);
+ __get_user(ys, &sel->ys);
+ __get_user(xe, &sel->xe);
+ __get_user(ye, &sel->ye);
+ __get_user(sel_mode, &sel->sel_mode);
+ xs--; ys--; xe--; ye--;
+ xs = limit(xs, vc->vc_cols - 1);
+ ys = limit(ys, vc->vc_rows - 1);
+ xe = limit(xe, vc->vc_cols - 1);
+ ye = limit(ye, vc->vc_rows - 1);
+ ps = ys * vc->vc_size_row + (xs << 1);
+ pe = ye * vc->vc_size_row + (xe << 1);
+
+ if (sel_mode == TIOCL_SELCLEAR) {
+ /* useful for screendump without selection highlights */
+ clear_selection();
+ return 0;
+ }
+
+ if (mouse_reporting() && (sel_mode & TIOCL_SELMOUSEREPORT)) {
+ mouse_report(tty, sel_mode & TIOCL_SELBUTTONMASK, xs, ys);
+ return 0;
+ }
+ }
+
+ if (ps > pe) /* make sel_start <= sel_end */
+ {
+ int tmp = ps;
+ ps = pe;
+ pe = tmp;
+ }
+
+ if (sel_cons != vc_cons[fg_console].d) {
+ clear_selection();
+ sel_cons = vc_cons[fg_console].d;
+ }
+ use_unicode = kbd && kbd->kbdmode == VC_UNICODE;
+
+ switch (sel_mode)
+ {
+ case TIOCL_SELCHAR: /* character-by-character selection */
+ new_sel_start = ps;
+ new_sel_end = pe;
+ break;
+ case TIOCL_SELWORD: /* word-by-word selection */
+ spc = isspace(sel_pos(ps));
+ for (new_sel_start = ps; ; ps -= 2)
+ {
+ if ((spc && !isspace(sel_pos(ps))) ||
+ (!spc && !inword(sel_pos(ps))))
+ break;
+ new_sel_start = ps;
+ if (!(ps % vc->vc_size_row))
+ break;
+ }
+ spc = isspace(sel_pos(pe));
+ for (new_sel_end = pe; ; pe += 2)
+ {
+ if ((spc && !isspace(sel_pos(pe))) ||
+ (!spc && !inword(sel_pos(pe))))
+ break;
+ new_sel_end = pe;
+ if (!((pe + 2) % vc->vc_size_row))
+ break;
+ }
+ break;
+ case TIOCL_SELLINE: /* line-by-line selection */
+ new_sel_start = ps - ps % vc->vc_size_row;
+ new_sel_end = pe + vc->vc_size_row
+ - pe % vc->vc_size_row - 2;
+ break;
+ case TIOCL_SELPOINTER:
+ highlight_pointer(pe);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ /* remove the pointer */
+ highlight_pointer(-1);
+
+ /* select to end of line if on trailing space */
+ if (new_sel_end > new_sel_start &&
+ !atedge(new_sel_end, vc->vc_size_row) &&
+ isspace(sel_pos(new_sel_end))) {
+ for (pe = new_sel_end + 2; ; pe += 2)
+ if (!isspace(sel_pos(pe)) ||
+ atedge(pe, vc->vc_size_row))
+ break;
+ if (isspace(sel_pos(pe)))
+ new_sel_end = pe;
+ }
+ if (sel_start == -1) /* no current selection */
+ highlight(new_sel_start, new_sel_end);
+ else if (new_sel_start == sel_start)
+ {
+ if (new_sel_end == sel_end) /* no action required */
+ return 0;
+ else if (new_sel_end > sel_end) /* extend to right */
+ highlight(sel_end + 2, new_sel_end);
+ else /* contract from right */
+ highlight(new_sel_end + 2, sel_end);
+ }
+ else if (new_sel_end == sel_end)
+ {
+ if (new_sel_start < sel_start) /* extend to left */
+ highlight(new_sel_start, sel_start - 2);
+ else /* contract from left */
+ highlight(sel_start, new_sel_start - 2);
+ }
+ else /* some other case; start selection from scratch */
+ {
+ clear_selection();
+ highlight(new_sel_start, new_sel_end);
+ }
+ sel_start = new_sel_start;
+ sel_end = new_sel_end;
+
+ /* Allocate a new buffer before freeing the old one ... */
+ multiplier = use_unicode ? 3 : 1; /* chars can take up to 3 bytes */
+ bp = kmalloc(((sel_end-sel_start)/2+1)*multiplier, GFP_KERNEL);
+ if (!bp) {
+ printk(KERN_WARNING "selection: kmalloc() failed\n");
+ clear_selection();
+ return -ENOMEM;
+ }
+ kfree(sel_buffer);
+ sel_buffer = bp;
+
+ obp = bp;
+ for (i = sel_start; i <= sel_end; i += 2) {
+ c = sel_pos(i);
+ if (use_unicode)
+ bp += store_utf8(c, bp);
+ else
+ *bp++ = c;
+ if (!isspace(c))
+ obp = bp;
+ if (! ((i + 2) % vc->vc_size_row)) {
+ /* strip trailing blanks from line and add newline,
+ unless non-space at end of line. */
+ if (obp != bp) {
+ bp = obp;
+ *bp++ = '\r';
+ }
+ obp = bp;
+ }
+ }
+ sel_buffer_lth = bp - sel_buffer;
+ return 0;
+}
+
+/* Insert the contents of the selection buffer into the
+ * queue of the tty associated with the current console.
+ * Invoked by ioctl().
+ */
+int paste_selection(struct tty_struct *tty)
+{
+ struct vc_data *vc = tty->driver_data;
+ int pasted = 0;
+ unsigned int count;
+ struct tty_ldisc *ld;
+ DECLARE_WAITQUEUE(wait, current);
+
+ /* always called with BTM from vt_ioctl */
+ WARN_ON(!tty_locked());
+
+ acquire_console_sem();
+ poke_blanked_console();
+ release_console_sem();
+
+ ld = tty_ldisc_ref(tty);
+ if (!ld) {
+ tty_unlock();
+ ld = tty_ldisc_ref_wait(tty);
+ tty_lock();
+ }
+
+ add_wait_queue(&vc->paste_wait, &wait);
+ while (sel_buffer && sel_buffer_lth > pasted) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (test_bit(TTY_THROTTLED, &tty->flags)) {
+ schedule();
+ continue;
+ }
+ count = sel_buffer_lth - pasted;
+ count = min(count, tty->receive_room);
+ tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted,
+ NULL, count);
+ pasted += count;
+ }
+ remove_wait_queue(&vc->paste_wait, &wait);
+ __set_current_state(TASK_RUNNING);
+
+ tty_ldisc_deref(ld);
+ return 0;
+}
--- /dev/null
+/*
+ * linux/drivers/char/vc_screen.c
+ *
+ * Provide access to virtual console memory.
+ * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
+ * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
+ * [minor: N]
+ *
+ * /dev/vcsaN: idem, but including attributes, and prefixed with
+ * the 4 bytes lines,columns,x,y (as screendump used to give).
+ * Attribute/character pair is in native endianity.
+ * [minor: N+128]
+ *
+ * This replaces screendump and part of selection, so that the system
+ * administrator can control access using file system permissions.
+ *
+ * aeb@cwi.nl - efter Friedas begravelse - 950211
+ *
+ * machek@k332.feld.cvut.cz - modified not to send characters to wrong console
+ * - fixed some fatal off-by-one bugs (0-- no longer == -1 -> looping and looping and looping...)
+ * - making it shorter - scr_readw are macros which expand in PRETTY long code
+ */
+
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+#include <linux/kbd_kern.h>
+#include <linux/console.h>
+#include <linux/device.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/notifier.h>
+
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+#undef attr
+#undef org
+#undef addr
+#define HEADER_SIZE 4
+
+struct vcs_poll_data {
+ struct notifier_block notifier;
+ unsigned int cons_num;
+ bool seen_last_update;
+ wait_queue_head_t waitq;
+ struct fasync_struct *fasync;
+};
+
+static int
+vcs_notifier(struct notifier_block *nb, unsigned long code, void *_param)
+{
+ struct vt_notifier_param *param = _param;
+ struct vc_data *vc = param->vc;
+ struct vcs_poll_data *poll =
+ container_of(nb, struct vcs_poll_data, notifier);
+ int currcons = poll->cons_num;
+
+ if (code != VT_UPDATE)
+ return NOTIFY_DONE;
+
+ if (currcons == 0)
+ currcons = fg_console;
+ else
+ currcons--;
+ if (currcons != vc->vc_num)
+ return NOTIFY_DONE;
+
+ poll->seen_last_update = false;
+ wake_up_interruptible(&poll->waitq);
+ kill_fasync(&poll->fasync, SIGIO, POLL_IN);
+ return NOTIFY_OK;
+}
+
+static void
+vcs_poll_data_free(struct vcs_poll_data *poll)
+{
+ unregister_vt_notifier(&poll->notifier);
+ kfree(poll);
+}
+
+static struct vcs_poll_data *
+vcs_poll_data_get(struct file *file)
+{
+ struct vcs_poll_data *poll = file->private_data;
+
+ if (poll)
+ return poll;
+
+ poll = kzalloc(sizeof(*poll), GFP_KERNEL);
+ if (!poll)
+ return NULL;
+ poll->cons_num = iminor(file->f_path.dentry->d_inode) & 127;
+ init_waitqueue_head(&poll->waitq);
+ poll->notifier.notifier_call = vcs_notifier;
+ if (register_vt_notifier(&poll->notifier) != 0) {
+ kfree(poll);
+ return NULL;
+ }
+
+ /*
+ * This code may be called either through ->poll() or ->fasync().
+ * If we have two threads using the same file descriptor, they could
+ * both enter this function, both notice that the structure hasn't
+ * been allocated yet and go ahead allocating it in parallel, but
+ * only one of them must survive and be shared otherwise we'd leak
+ * memory with a dangling notifier callback.
+ */
+ spin_lock(&file->f_lock);
+ if (!file->private_data) {
+ file->private_data = poll;
+ } else {
+ /* someone else raced ahead of us */
+ vcs_poll_data_free(poll);
+ poll = file->private_data;
+ }
+ spin_unlock(&file->f_lock);
+
+ return poll;
+}
+
+static int
+vcs_size(struct inode *inode)
+{
+ int size;
+ int minor = iminor(inode);
+ int currcons = minor & 127;
+ struct vc_data *vc;
+
+ if (currcons == 0)
+ currcons = fg_console;
+ else
+ currcons--;
+ if (!vc_cons_allocated(currcons))
+ return -ENXIO;
+ vc = vc_cons[currcons].d;
+
+ size = vc->vc_rows * vc->vc_cols;
+
+ if (minor & 128)
+ size = 2*size + HEADER_SIZE;
+ return size;
+}
+
+static loff_t vcs_lseek(struct file *file, loff_t offset, int orig)
+{
+ int size;
+
+ mutex_lock(&con_buf_mtx);
+ size = vcs_size(file->f_path.dentry->d_inode);
+ switch (orig) {
+ default:
+ mutex_unlock(&con_buf_mtx);
+ return -EINVAL;
+ case 2:
+ offset += size;
+ break;
+ case 1:
+ offset += file->f_pos;
+ case 0:
+ break;
+ }
+ if (offset < 0 || offset > size) {
+ mutex_unlock(&con_buf_mtx);
+ return -EINVAL;
+ }
+ file->f_pos = offset;
+ mutex_unlock(&con_buf_mtx);
+ return file->f_pos;
+}
+
+
+static ssize_t
+vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ unsigned int currcons = iminor(inode);
+ struct vc_data *vc;
+ struct vcs_poll_data *poll;
+ long pos;
+ long viewed, attr, read;
+ int col, maxcol;
+ unsigned short *org = NULL;
+ ssize_t ret;
+
+ mutex_lock(&con_buf_mtx);
+
+ pos = *ppos;
+
+ /* Select the proper current console and verify
+ * sanity of the situation under the console lock.
+ */
+ acquire_console_sem();
+
+ attr = (currcons & 128);
+ currcons = (currcons & 127);
+ if (currcons == 0) {
+ currcons = fg_console;
+ viewed = 1;
+ } else {
+ currcons--;
+ viewed = 0;
+ }
+ ret = -ENXIO;
+ if (!vc_cons_allocated(currcons))
+ goto unlock_out;
+ vc = vc_cons[currcons].d;
+
+ ret = -EINVAL;
+ if (pos < 0)
+ goto unlock_out;
+ poll = file->private_data;
+ if (count && poll)
+ poll->seen_last_update = true;
+ read = 0;
+ ret = 0;
+ while (count) {
+ char *con_buf0, *con_buf_start;
+ long this_round, size;
+ ssize_t orig_count;
+ long p = pos;
+
+ /* Check whether we are above size each round,
+ * as copy_to_user at the end of this loop
+ * could sleep.
+ */
+ size = vcs_size(inode);
+ if (pos >= size)
+ break;
+ if (count > size - pos)
+ count = size - pos;
+
+ this_round = count;
+ if (this_round > CON_BUF_SIZE)
+ this_round = CON_BUF_SIZE;
+
+ /* Perform the whole read into the local con_buf.
+ * Then we can drop the console spinlock and safely
+ * attempt to move it to userspace.
+ */
+
+ con_buf_start = con_buf0 = con_buf;
+ orig_count = this_round;
+ maxcol = vc->vc_cols;
+ if (!attr) {
+ org = screen_pos(vc, p, viewed);
+ col = p % maxcol;
+ p += maxcol - col;
+ while (this_round-- > 0) {
+ *con_buf0++ = (vcs_scr_readw(vc, org++) & 0xff);
+ if (++col == maxcol) {
+ org = screen_pos(vc, p, viewed);
+ col = 0;
+ p += maxcol;
+ }
+ }
+ } else {
+ if (p < HEADER_SIZE) {
+ size_t tmp_count;
+
+ con_buf0[0] = (char)vc->vc_rows;
+ con_buf0[1] = (char)vc->vc_cols;
+ getconsxy(vc, con_buf0 + 2);
+
+ con_buf_start += p;
+ this_round += p;
+ if (this_round > CON_BUF_SIZE) {
+ this_round = CON_BUF_SIZE;
+ orig_count = this_round - p;
+ }
+
+ tmp_count = HEADER_SIZE;
+ if (tmp_count > this_round)
+ tmp_count = this_round;
+
+ /* Advance state pointers and move on. */
+ this_round -= tmp_count;
+ p = HEADER_SIZE;
+ con_buf0 = con_buf + HEADER_SIZE;
+ /* If this_round >= 0, then p is even... */
+ } else if (p & 1) {
+ /* Skip first byte for output if start address is odd
+ * Update region sizes up/down depending on free
+ * space in buffer.
+ */
+ con_buf_start++;
+ if (this_round < CON_BUF_SIZE)
+ this_round++;
+ else
+ orig_count--;
+ }
+ if (this_round > 0) {
+ unsigned short *tmp_buf = (unsigned short *)con_buf0;
+
+ p -= HEADER_SIZE;
+ p /= 2;
+ col = p % maxcol;
+
+ org = screen_pos(vc, p, viewed);
+ p += maxcol - col;
+
+ /* Buffer has even length, so we can always copy
+ * character + attribute. We do not copy last byte
+ * to userspace if this_round is odd.
+ */
+ this_round = (this_round + 1) >> 1;
+
+ while (this_round) {
+ *tmp_buf++ = vcs_scr_readw(vc, org++);
+ this_round --;
+ if (++col == maxcol) {
+ org = screen_pos(vc, p, viewed);
+ col = 0;
+ p += maxcol;
+ }
+ }
+ }
+ }
+
+ /* Finally, release the console semaphore while we push
+ * all the data to userspace from our temporary buffer.
+ *
+ * AKPM: Even though it's a semaphore, we should drop it because
+ * the pagefault handling code may want to call printk().
+ */
+
+ release_console_sem();
+ ret = copy_to_user(buf, con_buf_start, orig_count);
+ acquire_console_sem();
+
+ if (ret) {
+ read += (orig_count - ret);
+ ret = -EFAULT;
+ break;
+ }
+ buf += orig_count;
+ pos += orig_count;
+ read += orig_count;
+ count -= orig_count;
+ }
+ *ppos += read;
+ if (read)
+ ret = read;
+unlock_out:
+ release_console_sem();
+ mutex_unlock(&con_buf_mtx);
+ return ret;
+}
+
+static ssize_t
+vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ unsigned int currcons = iminor(inode);
+ struct vc_data *vc;
+ long pos;
+ long viewed, attr, size, written;
+ char *con_buf0;
+ int col, maxcol;
+ u16 *org0 = NULL, *org = NULL;
+ size_t ret;
+
+ mutex_lock(&con_buf_mtx);
+
+ pos = *ppos;
+
+ /* Select the proper current console and verify
+ * sanity of the situation under the console lock.
+ */
+ acquire_console_sem();
+
+ attr = (currcons & 128);
+ currcons = (currcons & 127);
+
+ if (currcons == 0) {
+ currcons = fg_console;
+ viewed = 1;
+ } else {
+ currcons--;
+ viewed = 0;
+ }
+ ret = -ENXIO;
+ if (!vc_cons_allocated(currcons))
+ goto unlock_out;
+ vc = vc_cons[currcons].d;
+
+ size = vcs_size(inode);
+ ret = -EINVAL;
+ if (pos < 0 || pos > size)
+ goto unlock_out;
+ if (count > size - pos)
+ count = size - pos;
+ written = 0;
+ while (count) {
+ long this_round = count;
+ size_t orig_count;
+ long p;
+
+ if (this_round > CON_BUF_SIZE)
+ this_round = CON_BUF_SIZE;
+
+ /* Temporarily drop the console lock so that we can read
+ * in the write data from userspace safely.
+ */
+ release_console_sem();
+ ret = copy_from_user(con_buf, buf, this_round);
+ acquire_console_sem();
+
+ if (ret) {
+ this_round -= ret;
+ if (!this_round) {
+ /* Abort loop if no data were copied. Otherwise
+ * fail with -EFAULT.
+ */
+ if (written)
+ break;
+ ret = -EFAULT;
+ goto unlock_out;
+ }
+ }
+
+ /* The vcs_size might have changed while we slept to grab
+ * the user buffer, so recheck.
+ * Return data written up to now on failure.
+ */
+ size = vcs_size(inode);
+ if (pos >= size)
+ break;
+ if (this_round > size - pos)
+ this_round = size - pos;
+
+ /* OK, now actually push the write to the console
+ * under the lock using the local kernel buffer.
+ */
+
+ con_buf0 = con_buf;
+ orig_count = this_round;
+ maxcol = vc->vc_cols;
+ p = pos;
+ if (!attr) {
+ org0 = org = screen_pos(vc, p, viewed);
+ col = p % maxcol;
+ p += maxcol - col;
+
+ while (this_round > 0) {
+ unsigned char c = *con_buf0++;
+
+ this_round--;
+ vcs_scr_writew(vc,
+ (vcs_scr_readw(vc, org) & 0xff00) | c, org);
+ org++;
+ if (++col == maxcol) {
+ org = screen_pos(vc, p, viewed);
+ col = 0;
+ p += maxcol;
+ }
+ }
+ } else {
+ if (p < HEADER_SIZE) {
+ char header[HEADER_SIZE];
+
+ getconsxy(vc, header + 2);
+ while (p < HEADER_SIZE && this_round > 0) {
+ this_round--;
+ header[p++] = *con_buf0++;
+ }
+ if (!viewed)
+ putconsxy(vc, header + 2);
+ }
+ p -= HEADER_SIZE;
+ col = (p/2) % maxcol;
+ if (this_round > 0) {
+ org0 = org = screen_pos(vc, p/2, viewed);
+ if ((p & 1) && this_round > 0) {
+ char c;
+
+ this_round--;
+ c = *con_buf0++;
+#ifdef __BIG_ENDIAN
+ vcs_scr_writew(vc, c |
+ (vcs_scr_readw(vc, org) & 0xff00), org);
+#else
+ vcs_scr_writew(vc, (c << 8) |
+ (vcs_scr_readw(vc, org) & 0xff), org);
+#endif
+ org++;
+ p++;
+ if (++col == maxcol) {
+ org = screen_pos(vc, p/2, viewed);
+ col = 0;
+ }
+ }
+ p /= 2;
+ p += maxcol - col;
+ }
+ while (this_round > 1) {
+ unsigned short w;
+
+ w = get_unaligned(((unsigned short *)con_buf0));
+ vcs_scr_writew(vc, w, org++);
+ con_buf0 += 2;
+ this_round -= 2;
+ if (++col == maxcol) {
+ org = screen_pos(vc, p, viewed);
+ col = 0;
+ p += maxcol;
+ }
+ }
+ if (this_round > 0) {
+ unsigned char c;
+
+ c = *con_buf0++;
+#ifdef __BIG_ENDIAN
+ vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff) | (c << 8), org);
+#else
+ vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff00) | c, org);
+#endif
+ }
+ }
+ count -= orig_count;
+ written += orig_count;
+ buf += orig_count;
+ pos += orig_count;
+ if (org0)
+ update_region(vc, (unsigned long)(org0), org - org0);
+ }
+ *ppos += written;
+ ret = written;
+ if (written)
+ vcs_scr_updated(vc);
+
+unlock_out:
+ release_console_sem();
+
+ mutex_unlock(&con_buf_mtx);
+
+ return ret;
+}
+
+static unsigned int
+vcs_poll(struct file *file, poll_table *wait)
+{
+ struct vcs_poll_data *poll = vcs_poll_data_get(file);
+ int ret = 0;
+
+ if (poll) {
+ poll_wait(file, &poll->waitq, wait);
+ if (!poll->seen_last_update)
+ ret = POLLIN | POLLRDNORM;
+ }
+ return ret;
+}
+
+static int
+vcs_fasync(int fd, struct file *file, int on)
+{
+ struct vcs_poll_data *poll = file->private_data;
+
+ if (!poll) {
+ /* don't allocate anything if all we want is disable fasync */
+ if (!on)
+ return 0;
+ poll = vcs_poll_data_get(file);
+ if (!poll)
+ return -ENOMEM;
+ }
+
+ return fasync_helper(fd, file, on, &poll->fasync);
+}
+
+static int
+vcs_open(struct inode *inode, struct file *filp)
+{
+ unsigned int currcons = iminor(inode) & 127;
+ int ret = 0;
+
+ tty_lock();
+ if(currcons && !vc_cons_allocated(currcons-1))
+ ret = -ENXIO;
+ tty_unlock();
+ return ret;
+}
+
+static int vcs_release(struct inode *inode, struct file *file)
+{
+ struct vcs_poll_data *poll = file->private_data;
+
+ if (poll)
+ vcs_poll_data_free(poll);
+ return 0;
+}
+
+static const struct file_operations vcs_fops = {
+ .llseek = vcs_lseek,
+ .read = vcs_read,
+ .write = vcs_write,
+ .poll = vcs_poll,
+ .fasync = vcs_fasync,
+ .open = vcs_open,
+ .release = vcs_release,
+};
+
+static struct class *vc_class;
+
+void vcs_make_sysfs(int index)
+{
+ device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL,
+ "vcs%u", index + 1);
+ device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL,
+ "vcsa%u", index + 1);
+}
+
+void vcs_remove_sysfs(int index)
+{
+ device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1));
+ device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129));
+}
+
+int __init vcs_init(void)
+{
+ unsigned int i;
+
+ if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))
+ panic("unable to get major %d for vcs device", VCS_MAJOR);
+ vc_class = class_create(THIS_MODULE, "vc");
+
+ device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs");
+ device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa");
+ for (i = 0; i < MIN_NR_CONSOLES; i++)
+ vcs_make_sysfs(i);
+ return 0;
+}
--- /dev/null
+/*
+ * linux/drivers/char/vt.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>
+ *
+ * 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
+ *
+ * Removed console_lock, enabled interrupts across all console operations
+ * 13 March 2001, Andrew Morton
+ *
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/types.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/slab.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+#include <linux/smp_lock.h>
+#include <linux/tiocl.h>
+#include <linux/kbd_kern.h>
+#include <linux/consolemap.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/pm.h>
+#include <linux/font.h>
+#include <linux/bitops.h>
+#include <linux/notifier.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/kdb.h>
+#include <linux/ctype.h>
+
+#define MAX_NR_CON_DRIVER 16
+
+#define CON_DRIVER_FLAG_MODULE 1
+#define CON_DRIVER_FLAG_INIT 2
+#define CON_DRIVER_FLAG_ATTR 4
+
+struct con_driver {
+ const struct consw *con;
+ const char *desc;
+ struct device *dev;
+ int node;
+ int first;
+ int last;
+ int flag;
+};
+
+static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];
+const struct consw *conswitchp;
+
+/* 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)
+
+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);
+static int set_vesa_blanking(char __user *p);
+static void set_cursor(struct vc_data *vc);
+static void hide_cursor(struct vc_data *vc);
+static void console_callback(struct work_struct *ignored);
+static void blank_screen_t(unsigned long dummy);
+static void set_palette(struct vc_data *vc);
+
+static int printable; /* Is console ready for printing? */
+int default_utf8 = true;
+module_param(default_utf8, int, S_IRUGO | S_IWUSR);
+int global_cursor_default = -1;
+module_param(global_cursor_default, int, S_IRUGO | S_IWUSR);
+
+static int cur_default = CUR_DEFAULT;
+module_param(cur_default, int, S_IRUGO | S_IWUSR);
+
+/*
+ * 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;
+static int blankinterval = 10*60;
+core_param(consoleblank, blankinterval, int, 0444);
+
+static DECLARE_WORK(console_work, console_callback);
+
+/*
+ * fg_console is the current virtual console,
+ * last_console is the last used one,
+ * want_console is the console we want to switch to,
+ * saved_* variants are for save/restore around kernel debugger enter/leave
+ */
+int fg_console;
+int last_console;
+int want_console = -1;
+static int saved_fg_console;
+static int saved_last_console;
+static int saved_want_console;
+static int saved_vc_mode;
+static int saved_console_blanked;
+
+/*
+ * 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);
+
+static DEFINE_TIMER(console_timer, blank_screen_t, 0, 0);
+static int blank_state;
+static int blank_timer_expired;
+enum {
+ blank_off = 0,
+ blank_normal_wait,
+ blank_vesa_wait,
+};
+
+/*
+ * Notifier list for console events.
+ */
+static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);
+
+int register_vt_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&vt_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(register_vt_notifier);
+
+int unregister_vt_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&vt_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_vt_notifier);
+
+static void notify_write(struct vc_data *vc, unsigned int unicode)
+{
+ struct vt_notifier_param param = { .vc = vc, unicode = unicode };
+ atomic_notifier_call_chain(&vt_notifier_list, VT_WRITE, ¶m);
+}
+
+static void notify_update(struct vc_data *vc)
+{
+ struct vt_notifier_param param = { .vc = vc };
+ atomic_notifier_call_chain(&vt_notifier_list, VT_UPDATE, ¶m);
+}
+/*
+ * Low-Level Functions
+ */
+
+#define IS_FG(vc) ((vc)->vc_num == fg_console)
+
+#ifdef VT_BUF_VRAM_ONLY
+#define DO_UPDATE(vc) 0
+#else
+#define DO_UPDATE(vc) (CON_IS_VISIBLE(vc) && !console_blanked)
+#endif
+
+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;
+}
+
+/* Called from the keyboard irq path.. */
+static inline void scrolldelta(int lines)
+{
+ /* FIXME */
+ /* scrolldelta needs some kind of consistency lock, but the BKL was
+ and still is not protecting versus the scheduled back end */
+ scrollback_delta += lines;
+ schedule_console_callback();
+}
+
+void schedule_console_callback(void)
+{
+ schedule_work(&console_work);
+}
+
+static void scrup(struct vc_data *vc, unsigned int t, unsigned int b, int nr)
+{
+ unsigned short *d, *s;
+
+ if (t+nr >= b)
+ nr = b - t - 1;
+ if (b > vc->vc_rows || t >= b || nr < 1)
+ return;
+ if (CON_IS_VISIBLE(vc) && vc->vc_sw->con_scroll(vc, t, b, SM_UP, nr))
+ return;
+ d = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t);
+ s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * (t + nr));
+ scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row);
+ scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_video_erase_char,
+ vc->vc_size_row * nr);
+}
+
+static void scrdown(struct vc_data *vc, unsigned int t, unsigned int b, int nr)
+{
+ unsigned short *s;
+ unsigned int step;
+
+ if (t+nr >= b)
+ nr = b - t - 1;
+ if (b > vc->vc_rows || t >= b || nr < 1)
+ return;
+ if (CON_IS_VISIBLE(vc) && vc->vc_sw->con_scroll(vc, t, b, SM_DOWN, nr))
+ return;
+ s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t);
+ step = vc->vc_cols * nr;
+ scr_memmovew(s + step, s, (b - t - nr) * vc->vc_size_row);
+ scr_memsetw(s, vc->vc_video_erase_char, 2 * step);
+}
+
+static void do_update_region(struct vc_data *vc, unsigned long start, int count)
+{
+#ifndef VT_BUF_VRAM_ONLY
+ 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);
+ }
+ }
+#endif
+}
+
+void update_region(struct vc_data *vc, unsigned long start, int count)
+{
+ WARN_CONSOLE_UNLOCKED();
+
+ if (DO_UPDATE(vc)) {
+ hide_cursor(vc);
+ do_update_region(vc, start, count);
+ set_cursor(vc);
+ }
+}
+
+/* Structure of attributes is hardware-dependent */
+
+static u8 build_attr(struct vc_data *vc, u8 _color, u8 _intensity, u8 _blink,
+ u8 _underline, u8 _reverse, u8 _italic)
+{
+ if (vc->vc_sw->con_build_attr)
+ return vc->vc_sw->con_build_attr(vc, _color, _intensity,
+ _blink, _underline, _reverse, _italic);
+
+#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;
+ if (!vc->vc_can_do_color)
+ return _intensity |
+ (_italic ? 2 : 0) |
+ (_underline ? 4 : 0) |
+ (_reverse ? 8 : 0) |
+ (_blink ? 0x80 : 0);
+ if (_italic)
+ a = (a & 0xF0) | vc->vc_itcolor;
+ else if (_underline)
+ a = (a & 0xf0) | vc->vc_ulcolor;
+ else if (_intensity == 0)
+ a = (a & 0xf0) | vc->vc_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;
+ }
+#else
+ return 0;
+#endif
+}
+
+static void update_attr(struct vc_data *vc)
+{
+ vc->vc_attr = build_attr(vc, vc->vc_color, vc->vc_intensity,
+ vc->vc_blink, vc->vc_underline,
+ vc->vc_reverse ^ vc->vc_decscnm, vc->vc_italic);
+ vc->vc_video_erase_char = (build_attr(vc, vc->vc_color, 1, vc->vc_blink, 0, vc->vc_decscnm, 0) << 8) | ' ';
+}
+
+/* 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);
+ if (vc->vc_sw->con_invert_region)
+ vc->vc_sw->con_invert_region(vc, p, count);
+#ifndef VT_BUF_VRAM_ONLY
+ else {
+ u16 *q = p;
+ int cnt = count;
+ u16 a;
+
+ if (!vc->vc_can_do_color) {
+ while (cnt--) {
+ a = scr_readw(q);
+ a ^= 0x0800;
+ scr_writew(a, q);
+ q++;
+ }
+ } else if (vc->vc_hi_font_mask == 0x100) {
+ while (cnt--) {
+ a = scr_readw(q);
+ a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) | (((a) & 0x0e00) << 4);
+ scr_writew(a, q);
+ q++;
+ }
+ } else {
+ while (cnt--) {
+ a = scr_readw(q);
+ a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4);
+ scr_writew(a, q);
+ q++;
+ }
+ }
+ }
+#endif
+ if (DO_UPDATE(vc))
+ do_update_region(vc, (unsigned long) p, count);
+}
+
+/* used by selection: complement pointer position */
+void complement_pos(struct vc_data *vc, int offset)
+{
+ static int old_offset = -1;
+ static unsigned short old;
+ static unsigned short oldx, oldy;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ if (old_offset != -1 && old_offset >= 0 &&
+ old_offset < vc->vc_screenbuf_size) {
+ scr_writew(old, screenpos(vc, old_offset, 1));
+ if (DO_UPDATE(vc))
+ vc->vc_sw->con_putc(vc, old, oldy, oldx);
+ }
+
+ old_offset = offset;
+
+ if (offset != -1 && offset >= 0 &&
+ offset < vc->vc_screenbuf_size) {
+ unsigned short new;
+ unsigned short *p;
+ p = screenpos(vc, offset, 1);
+ old = scr_readw(p);
+ new = old ^ vc->vc_complement_mask;
+ scr_writew(new, p);
+ if (DO_UPDATE(vc)) {
+ oldx = (offset >> 1) % vc->vc_cols;
+ oldy = (offset >> 1) / vc->vc_cols;
+ vc->vc_sw->con_putc(vc, new, oldy, oldx);
+ }
+ }
+
+}
+
+static void insert_char(struct vc_data *vc, unsigned int nr)
+{
+ unsigned short *p, *q = (unsigned short *)vc->vc_pos;
+
+ p = q + vc->vc_cols - nr - vc->vc_x;
+ while (--p >= q)
+ scr_writew(scr_readw(p), p + nr);
+ scr_memsetw(q, vc->vc_video_erase_char, nr * 2);
+ vc->vc_need_wrap = 0;
+ if (DO_UPDATE(vc)) {
+ unsigned short oldattr = vc->vc_attr;
+ vc->vc_sw->con_bmove(vc, vc->vc_y, vc->vc_x, vc->vc_y, vc->vc_x + nr, 1,
+ vc->vc_cols - vc->vc_x - nr);
+ vc->vc_attr = vc->vc_video_erase_char >> 8;
+ while (nr--)
+ vc->vc_sw->con_putc(vc, vc->vc_video_erase_char, vc->vc_y, vc->vc_x + nr);
+ vc->vc_attr = oldattr;
+ }
+}
+
+static void delete_char(struct vc_data *vc, unsigned int nr)
+{
+ unsigned int i = vc->vc_x;
+ unsigned short *p = (unsigned short *)vc->vc_pos;
+
+ while (++i <= vc->vc_cols - nr) {
+ scr_writew(scr_readw(p+nr), p);
+ p++;
+ }
+ scr_memsetw(p, vc->vc_video_erase_char, nr * 2);
+ vc->vc_need_wrap = 0;
+ if (DO_UPDATE(vc)) {
+ unsigned short oldattr = vc->vc_attr;
+ vc->vc_sw->con_bmove(vc, vc->vc_y, vc->vc_x + nr, vc->vc_y, vc->vc_x, 1,
+ vc->vc_cols - vc->vc_x - nr);
+ vc->vc_attr = vc->vc_video_erase_char >> 8;
+ while (nr--)
+ vc->vc_sw->con_putc(vc, vc->vc_video_erase_char, vc->vc_y,
+ vc->vc_cols - 1 - nr);
+ vc->vc_attr = oldattr;
+ }
+}
+
+static int softcursor_original;
+
+static void add_softcursor(struct vc_data *vc)
+{
+ int i = scr_readw((u16 *) vc->vc_pos);
+ u32 type = vc->vc_cursor_type;
+
+ if (! (type & 0x10)) return;
+ if (softcursor_original != -1) return;
+ softcursor_original = i;
+ i |= ((type >> 8) & 0xff00 );
+ i ^= ((type) & 0xff00 );
+ if ((type & 0x20) && ((softcursor_original & 0x7000) == (i & 0x7000))) i ^= 0x7000;
+ if ((type & 0x40) && ((i & 0x700) == ((i & 0x7000) >> 4))) i ^= 0x0700;
+ scr_writew(i, (u16 *) vc->vc_pos);
+ if (DO_UPDATE(vc))
+ 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);
+ if (DO_UPDATE(vc))
+ 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)
+{
+ if (!IS_FG(vc) || console_blanked ||
+ vc->vc_mode == KD_GRAPHICS)
+ 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();
+
+ if (!CON_IS_VISIBLE(vc) ||
+ !vc->vc_sw->con_set_origin ||
+ !vc->vc_sw->con_set_origin(vc))
+ vc->vc_origin = (unsigned long)vc->vc_screenbuf;
+ vc->vc_visible_origin = vc->vc_origin;
+ vc->vc_scr_end = vc->vc_origin + vc->vc_screenbuf_size;
+ vc->vc_pos = vc->vc_origin + vc->vc_size_row * vc->vc_y + 2 * vc->vc_x;
+}
+
+static inline void save_screen(struct vc_data *vc)
+{
+ WARN_CONSOLE_UNLOCKED();
+
+ if (vc->vc_sw->con_save_screen)
+ vc->vc_sw->con_save_screen(vc);
+}
+
+/*
+ * Redrawing of screen
+ */
+
+static void clear_buffer_attributes(struct vc_data *vc)
+{
+ 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;
+ if (!CON_IS_VISIBLE(vc))
+ redraw = 1;
+ *vc->vc_display_fg = vc;
+ fg_console = vc->vc_num;
+ hide_cursor(old_vc);
+ if (!CON_IS_VISIBLE(old_vc)) {
+ save_screen(old_vc);
+ set_origin(old_vc);
+ }
+ } 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);
+ }
+
+ /* Forcibly update if we're panicing */
+ if ((update && vc->vc_mode != KD_GRAPHICS) ||
+ vt_force_oops_output(vc))
+ do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
+ }
+ set_cursor(vc);
+ if (is_switch) {
+ set_leds();
+ compute_shiftstate();
+ notify_update(vc);
+ }
+}
+
+/*
+ * 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;
+ vc->vc_uni_pagedir_loc = &vc->vc_uni_pagedir;
+ vc->vc_uni_pagedir = 0;
+ vc->vc_hi_font_mask = 0;
+ vc->vc_complement_mask = 0;
+ vc->vc_can_do_color = 0;
+ vc->vc_panic_force_write = false;
+ 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 */
+{
+ WARN_CONSOLE_UNLOCKED();
+
+ if (currcons >= MAX_NR_CONSOLES)
+ return -ENXIO;
+ if (!vc_cons[currcons].d) {
+ struct vc_data *vc;
+ struct vt_notifier_param param;
+
+ /* prevent users from taking too much memory */
+ if (currcons >= MAX_NR_USER_CONSOLES && !capable(CAP_SYS_RESOURCE))
+ 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 */
+ param.vc = vc = kzalloc(sizeof(struct vc_data), GFP_KERNEL);
+ if (!vc)
+ return -ENOMEM;
+ 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)
+ con_set_default_unimap(vc);
+ vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL);
+ if (!vc->vc_screenbuf) {
+ kfree(vc);
+ vc_cons[currcons].d = NULL;
+ return -ENOMEM;
+ }
+
+ /* 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, ¶m);
+ }
+ return 0;
+}
+
+static inline int resize_screen(struct vc_data *vc, int width, int height,
+ int user)
+{
+ /* Resizes the resolution of the display adapater */
+ int err = 0;
+
+ if (vc->vc_mode != KD_GRAPHICS && vc->vc_sw->con_resize)
+ err = vc->vc_sw->con_resize(vc, width, height, user);
+
+ 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)
+
+/**
+ * vc_do_resize - resizing method for the tty
+ * @tty: tty being resized
+ * @real_tty: real tty (different to tty if a pty/tty pair)
+ * @vc: virtual console private data
+ * @cols: columns
+ * @lines: lines
+ *
+ * Resize a virtual console, clipping according to the actual constraints.
+ * If the caller passes a tty structure then update the termios winsize
+ * information and perform any necessary signal handling.
+ *
+ * Caller must hold the console semaphore. Takes the termios mutex and
+ * ctrl_lock of the tty IFF a tty is passed.
+ */
+
+static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
+ unsigned int cols, unsigned int lines)
+{
+ unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0;
+ unsigned long end;
+ unsigned int old_cols, old_rows, old_row_size, old_screen_size;
+ unsigned int new_cols, new_rows, new_row_size, new_screen_size;
+ unsigned int user;
+ unsigned short *newscreen;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ if (!vc)
+ return -ENXIO;
+
+ user = vc->vc_resize_user;
+ vc->vc_resize_user = 0;
+
+ if (cols > VC_RESIZE_MAXCOL || lines > VC_RESIZE_MAXROW)
+ return -EINVAL;
+
+ new_cols = (cols ? cols : vc->vc_cols);
+ new_rows = (lines ? lines : vc->vc_rows);
+ new_row_size = new_cols << 1;
+ new_screen_size = new_row_size * new_rows;
+
+ if (new_cols == vc->vc_cols && new_rows == vc->vc_rows)
+ return 0;
+
+ newscreen = kmalloc(new_screen_size, GFP_USER);
+ if (!newscreen)
+ return -ENOMEM;
+
+ old_rows = vc->vc_rows;
+ old_cols = vc->vc_cols;
+ old_row_size = vc->vc_size_row;
+ old_screen_size = vc->vc_screenbuf_size;
+
+ err = resize_screen(vc, new_cols, new_rows, user);
+ 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;
+
+ 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;
+ }
+ }
+
+ end = old_origin + old_row_size * min(old_rows, new_rows);
+
+ update_attr(vc);
+
+ while (old_origin < end) {
+ scr_memcpyw((unsigned short *) new_origin,
+ (unsigned short *) old_origin, rlth);
+ if (rrem)
+ scr_memsetw((void *)(new_origin + rlth),
+ vc->vc_video_erase_char, rrem);
+ old_origin += old_row_size;
+ new_origin += new_row_size;
+ }
+ if (new_scr_end > new_origin)
+ scr_memsetw((void *)new_origin, vc->vc_video_erase_char,
+ new_scr_end - new_origin);
+ kfree(vc->vc_screenbuf);
+ vc->vc_screenbuf = newscreen;
+ vc->vc_screenbuf_size = new_screen_size;
+ set_origin(vc);
+
+ /* do part of a reset_terminal() */
+ vc->vc_top = 0;
+ vc->vc_bottom = vc->vc_rows;
+ gotoxy(vc, vc->vc_x, vc->vc_y);
+ save_cur(vc);
+
+ if (tty) {
+ /* Rewrite the requested winsize data with the actual
+ resulting sizes */
+ struct winsize ws;
+ memset(&ws, 0, sizeof(ws));
+ ws.ws_row = vc->vc_rows;
+ ws.ws_col = vc->vc_cols;
+ ws.ws_ypixel = vc->vc_scan_lines;
+ tty_do_resize(tty, &ws);
+ }
+
+ if (CON_IS_VISIBLE(vc))
+ update_screen(vc);
+ vt_event_post(VT_EVENT_RESIZE, vc->vc_num, vc->vc_num);
+ return err;
+}
+
+/**
+ * vc_resize - resize a VT
+ * @vc: virtual console
+ * @cols: columns
+ * @rows: rows
+ *
+ * Resize a virtual console as seen from the console end of things. We
+ * use the common vc_do_resize methods to update the structures. The
+ * caller must hold the console sem to protect console internals and
+ * vc->port.tty
+ */
+
+int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows)
+{
+ return vc_do_resize(vc->port.tty, vc, cols, rows);
+}
+
+/**
+ * vt_resize - resize a VT
+ * @tty: tty to resize
+ * @ws: winsize attributes
+ *
+ * Resize a virtual terminal. This is called by the tty layer as we
+ * register our own handler for resizing. The mutual helper does all
+ * the actual work.
+ *
+ * Takes the console sem and the called methods then take the tty
+ * termios_mutex and the tty ctrl_lock in that order.
+ */
+static int vt_resize(struct tty_struct *tty, struct winsize *ws)
+{
+ struct vc_data *vc = tty->driver_data;
+ int ret;
+
+ acquire_console_sem();
+ ret = vc_do_resize(tty, vc, ws->ws_col, ws->ws_row);
+ release_console_sem();
+ return ret;
+}
+
+void vc_deallocate(unsigned int currcons)
+{
+ WARN_CONSOLE_UNLOCKED();
+
+ if (vc_cons_allocated(currcons)) {
+ struct vc_data *vc = vc_cons[currcons].d;
+ struct vt_notifier_param param = { .vc = vc };
+
+ atomic_notifier_call_chain(&vt_notifier_list, VT_DEALLOCATE, ¶m);
+ vcs_remove_sysfs(currcons);
+ vc->vc_sw->con_deinit(vc);
+ put_pid(vc->vt_pid);
+ module_put(vc->vc_sw->owner);
+ kfree(vc->vc_screenbuf);
+ if (currcons >= MIN_NR_CONSOLES)
+ kfree(vc);
+ vc_cons[currcons].d = NULL;
+ }
+}
+
+/*
+ * VT102 emulator
+ */
+
+#define set_kbd(vc, x) set_vc_kbd_mode(kbd_table + (vc)->vc_num, (x))
+#define clr_kbd(vc, x) clr_vc_kbd_mode(kbd_table + (vc)->vc_num, (x))
+#define is_kbd(vc, x) vc_kbd_mode(kbd_table + (vc)->vc_num, (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};
+
+module_param_array(default_red, int, NULL, S_IRUGO | S_IWUSR);
+module_param_array(default_grn, int, NULL, S_IRUGO | S_IWUSR);
+module_param_array(default_blu, int, NULL, S_IRUGO | S_IWUSR);
+
+/*
+ * gotoxy() must verify all boundaries, because the arguments
+ * might also be negative. If the given position is out of
+ * 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);
+}
+
+void scrollback(struct vc_data *vc, int lines)
+{
+ if (!lines)
+ lines = vc->vc_rows / 2;
+ scrolldelta(-lines);
+}
+
+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)
+ scrup(vc, vc->vc_top, vc->vc_bottom, 1);
+ else if (vc->vc_y < vc->vc_rows - 1) {
+ vc->vc_y++;
+ vc->vc_pos += vc->vc_size_row;
+ }
+ vc->vc_need_wrap = 0;
+ notify_write(vc, '\n');
+}
+
+static void 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)
+ scrdown(vc, vc->vc_top, vc->vc_bottom, 1);
+ 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;
+ notify_write(vc, '\r');
+}
+
+static inline void bs(struct vc_data *vc)
+{
+ if (vc->vc_x) {
+ vc->vc_pos -= 2;
+ vc->vc_x--;
+ vc->vc_need_wrap = 0;
+ notify_write(vc, '\b');
+ }
+}
+
+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;
+ if (DO_UPDATE(vc)) {
+ /* do in two stages */
+ vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1,
+ vc->vc_cols - vc->vc_x);
+ vc->vc_sw->con_clear(vc, vc->vc_y + 1, 0,
+ vc->vc_rows - vc->vc_y - 1,
+ vc->vc_cols);
+ }
+ break;
+ case 1: /* erase from start to cursor */
+ count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1;
+ start = (unsigned short *)vc->vc_origin;
+ if (DO_UPDATE(vc)) {
+ /* do in two stages */
+ vc->vc_sw->con_clear(vc, 0, 0, vc->vc_y,
+ vc->vc_cols);
+ vc->vc_sw->con_clear(vc, vc->vc_y, 0, 1,
+ vc->vc_x + 1);
+ }
+ break;
+ case 2: /* erase whole display */
+ count = vc->vc_cols * vc->vc_rows;
+ start = (unsigned short *)vc->vc_origin;
+ if (DO_UPDATE(vc))
+ vc->vc_sw->con_clear(vc, 0, 0,
+ vc->vc_rows,
+ vc->vc_cols);
+ break;
+ default:
+ return;
+ }
+ scr_memsetw(start, vc->vc_video_erase_char, 2 * count);
+ 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;
+ if (DO_UPDATE(vc))
+ vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1,
+ vc->vc_cols - vc->vc_x);
+ 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;
+ if (DO_UPDATE(vc))
+ vc->vc_sw->con_clear(vc, vc->vc_y, 0, 1,
+ vc->vc_x + 1);
+ break;
+ case 2: /* erase whole line */
+ start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1));
+ count = vc->vc_cols;
+ if (DO_UPDATE(vc))
+ vc->vc_sw->con_clear(vc, vc->vc_y, 0, 1,
+ vc->vc_cols);
+ break;
+ default:
+ return;
+ }
+ scr_memsetw(start, vc->vc_video_erase_char, 2 * count);
+ vc->vc_need_wrap = 0;
+}
+
+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);
+ if (DO_UPDATE(vc))
+ 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;
+ vc->vc_italic = 0;
+ vc->vc_underline = 0;
+ vc->vc_reverse = 0;
+ vc->vc_blink = 0;
+ vc->vc_color = vc->vc_def_color;
+}
+
+/* console_sem is held */
+static void csi_m(struct vc_data *vc)
+{
+ int i;
+
+ for (i = 0; i <= vc->vc_npar; i++)
+ switch (vc->vc_par[i]) {
+ 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: /* ANSI X3.64-1979 (SCO-ish?)
+ * Enables underscore, white foreground
+ * with white underscore (Linux - use
+ * default foreground).
+ */
+ vc->vc_color = (vc->vc_def_color & 0x0f) | (vc->vc_color & 0xf0);
+ vc->vc_underline = 1;
+ break;
+ case 39: /* ANSI X3.64-1979 (SCO-ish?)
+ * Disable underline option.
+ * Reset colour to default? It did this
+ * before...
+ */
+ vc->vc_color = (vc->vc_def_color & 0x0f) | (vc->vc_color & 0xf0);
+ vc->vc_underline = 0;
+ break;
+ case 49:
+ vc->vc_color = (vc->vc_def_color & 0xf0) | (vc->vc_color & 0x0f);
+ break;
+ default:
+ 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;
+ }
+ update_attr(vc);
+}
+
+static void respond_string(const char *p, struct tty_struct *tty)
+{
+ while (*p) {
+ tty_insert_flip_char(tty, *p, 0);
+ p++;
+ }
+ con_schedule_flip(tty);
+}
+
+static void cursor_report(struct vc_data *vc, struct tty_struct *tty)
+{
+ char buf[40];
+
+ sprintf(buf, "\033[%d;%dR", vc->vc_y + (vc->vc_decom ? vc->vc_top + 1 : 1), vc->vc_x + 1);
+ respond_string(buf, tty);
+}
+
+static inline void status_report(struct tty_struct *tty)
+{
+ respond_string("\033[0n", tty); /* Terminal ok */
+}
+
+static inline void respond_ID(struct tty_struct * tty)
+{
+ respond_string(VT102ID, tty);
+}
+
+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 */
+int mouse_reporting(void)
+{
+ return vc_cons[fg_console].d->vc_report_mouse;
+}
+
+/* console_sem is held */
+static void set_mode(struct vc_data *vc, int on_off)
+{
+ int i;
+
+ for (i = 0; i <= vc->vc_npar; i++)
+ if (vc->vc_ques) {
+ switch(vc->vc_par[i]) { /* DEC private modes set/reset */
+ case 1: /* Cursor keys send ^[Ox/^[[x */
+ if (on_off)
+ set_kbd(vc, decckm);
+ else
+ clr_kbd(vc, decckm);
+ break;
+ case 3: /* 80/132 mode switch unimplemented */
+ vc->vc_deccolm = on_off;
+#if 0
+ vc_resize(deccolm ? 132 : 80, vc->vc_rows);
+ /* this alone does not suffice; some user mode
+ utility has to change the hardware regs */
+#endif
+ break;
+ case 5: /* Inverted screen on/off */
+ if (vc->vc_decscnm != on_off) {
+ vc->vc_decscnm = on_off;
+ invert_screen(vc, 0, vc->vc_screenbuf_size, 0);
+ update_attr(vc);
+ }
+ break;
+ case 6: /* Origin relative/absolute */
+ vc->vc_decom = on_off;
+ gotoxay(vc, 0, 0);
+ break;
+ case 7: /* Autowrap on/off */
+ vc->vc_decawm = on_off;
+ break;
+ case 8: /* Autorepeat on/off */
+ if (on_off)
+ set_kbd(vc, decarm);
+ else
+ clr_kbd(vc, decarm);
+ break;
+ case 9:
+ vc->vc_report_mouse = on_off ? 1 : 0;
+ break;
+ case 25: /* Cursor on/off */
+ vc->vc_deccm = on_off;
+ break;
+ case 1000:
+ vc->vc_report_mouse = on_off ? 2 : 0;
+ break;
+ }
+ } else {
+ switch(vc->vc_par[i]) { /* ANSI modes set/reset */
+ case 3: /* Monitor (display ctrls) */
+ vc->vc_disp_ctrl = on_off;
+ break;
+ case 4: /* Insert Mode on/off */
+ vc->vc_decim = on_off;
+ break;
+ case 20: /* Lf, Enter == CrLf/Lf */
+ if (on_off)
+ set_kbd(vc, lnm);
+ else
+ clr_kbd(vc, lnm);
+ break;
+ }
+ }
+}
+
+/* console_sem is held */
+static void setterm_command(struct vc_data *vc)
+{
+ switch(vc->vc_par[0]) {
+ case 1: /* set color for underline mode */
+ if (vc->vc_can_do_color &&
+ vc->vc_par[1] < 16) {
+ vc->vc_ulcolor = color_table[vc->vc_par[1]];
+ if (vc->vc_underline)
+ update_attr(vc);
+ }
+ break;
+ case 2: /* set color for half intensity mode */
+ if (vc->vc_can_do_color &&
+ vc->vc_par[1] < 16) {
+ vc->vc_halfcolor = color_table[vc->vc_par[1]];
+ if (vc->vc_intensity == 0)
+ update_attr(vc);
+ }
+ break;
+ case 8: /* store colors as defaults */
+ vc->vc_def_color = vc->vc_attr;
+ if (vc->vc_hi_font_mask == 0x100)
+ vc->vc_def_color >>= 1;
+ default_attr(vc);
+ update_attr(vc);
+ break;
+ case 9: /* set blanking interval */
+ blankinterval = ((vc->vc_par[1] < 60) ? vc->vc_par[1] : 60) * 60;
+ poke_blanked_console();
+ break;
+ case 10: /* set bell frequency in Hz */
+ if (vc->vc_npar >= 1)
+ vc->vc_bell_pitch = vc->vc_par[1];
+ else
+ vc->vc_bell_pitch = DEFAULT_BELL_PITCH;
+ break;
+ case 11: /* set bell duration in msec */
+ if (vc->vc_npar >= 1)
+ vc->vc_bell_duration = (vc->vc_par[1] < 2000) ?
+ vc->vc_par[1] * HZ / 1000 : 0;
+ else
+ vc->vc_bell_duration = DEFAULT_BELL_DURATION;
+ break;
+ case 12: /* bring specified console to the front */
+ if (vc->vc_par[1] >= 1 && vc_cons_allocated(vc->vc_par[1] - 1))
+ set_console(vc->vc_par[1] - 1);
+ break;
+ case 13: /* unblank the screen */
+ poke_blanked_console();
+ break;
+ case 14: /* set vesa powerdown interval */
+ vesa_off_interval = ((vc->vc_par[1] < 60) ? vc->vc_par[1] : 60) * 60 * HZ;
+ break;
+ case 15: /* activate the previous console */
+ set_console(last_console);
+ break;
+ }
+}
+
+/* console_sem is held */
+static void csi_at(struct vc_data *vc, unsigned int nr)
+{
+ if (nr > vc->vc_cols - vc->vc_x)
+ nr = vc->vc_cols - vc->vc_x;
+ else if (!nr)
+ nr = 1;
+ insert_char(vc, nr);
+}
+
+/* console_sem is held */
+static void csi_L(struct vc_data *vc, unsigned int nr)
+{
+ if (nr > vc->vc_rows - vc->vc_y)
+ nr = vc->vc_rows - vc->vc_y;
+ else if (!nr)
+ nr = 1;
+ scrdown(vc, vc->vc_y, vc->vc_bottom, nr);
+ vc->vc_need_wrap = 0;
+}
+
+/* console_sem is held */
+static void csi_P(struct vc_data *vc, unsigned int nr)
+{
+ if (nr > vc->vc_cols - vc->vc_x)
+ nr = vc->vc_cols - vc->vc_x;
+ else if (!nr)
+ nr = 1;
+ delete_char(vc, nr);
+}
+
+/* console_sem is held */
+static void csi_M(struct vc_data *vc, unsigned int nr)
+{
+ if (nr > vc->vc_rows - vc->vc_y)
+ nr = vc->vc_rows - vc->vc_y;
+ else if (!nr)
+ nr=1;
+ scrup(vc, vc->vc_y, vc->vc_bottom, nr);
+ vc->vc_need_wrap = 0;
+}
+
+/* console_sem is held (except via vc_init->reset_terminal */
+static void save_cur(struct vc_data *vc)
+{
+ vc->vc_saved_x = vc->vc_x;
+ vc->vc_saved_y = vc->vc_y;
+ vc->vc_s_intensity = vc->vc_intensity;
+ vc->vc_s_italic = vc->vc_italic;
+ vc->vc_s_underline = vc->vc_underline;
+ vc->vc_s_blink = vc->vc_blink;
+ vc->vc_s_reverse = vc->vc_reverse;
+ vc->vc_s_charset = vc->vc_charset;
+ vc->vc_s_color = vc->vc_color;
+ vc->vc_saved_G0 = vc->vc_G0_charset;
+ vc->vc_saved_G1 = vc->vc_G1_charset;
+}
+
+/* console_sem is held */
+static void restore_cur(struct vc_data *vc)
+{
+ gotoxy(vc, vc->vc_saved_x, vc->vc_saved_y);
+ vc->vc_intensity = vc->vc_s_intensity;
+ vc->vc_italic = vc->vc_s_italic;
+ vc->vc_underline = vc->vc_s_underline;
+ vc->vc_blink = vc->vc_s_blink;
+ vc->vc_reverse = vc->vc_s_reverse;
+ vc->vc_charset = vc->vc_s_charset;
+ vc->vc_color = vc->vc_s_color;
+ vc->vc_G0_charset = vc->vc_saved_G0;
+ vc->vc_G1_charset = vc->vc_saved_G1;
+ vc->vc_translate = set_translate(vc->vc_charset ? vc->vc_G1_charset : vc->vc_G0_charset, vc);
+ update_attr(vc);
+ vc->vc_need_wrap = 0;
+}
+
+enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
+ EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
+ ESpalette };
+
+/* console_sem is held (except via vc_init()) */
+static void reset_terminal(struct vc_data *vc, int do_clear)
+{
+ vc->vc_top = 0;
+ vc->vc_bottom = vc->vc_rows;
+ vc->vc_state = ESnormal;
+ vc->vc_ques = 0;
+ vc->vc_translate = set_translate(LAT1_MAP, vc);
+ vc->vc_G0_charset = LAT1_MAP;
+ vc->vc_G1_charset = GRAF_MAP;
+ vc->vc_charset = 0;
+ vc->vc_need_wrap = 0;
+ vc->vc_report_mouse = 0;
+ vc->vc_utf = default_utf8;
+ vc->vc_utf_count = 0;
+
+ vc->vc_disp_ctrl = 0;
+ vc->vc_toggle_meta = 0;
+
+ vc->vc_decscnm = 0;
+ vc->vc_decom = 0;
+ vc->vc_decawm = 1;
+ vc->vc_deccm = global_cursor_default;
+ vc->vc_decim = 0;
+
+ set_kbd(vc, decarm);
+ clr_kbd(vc, decckm);
+ clr_kbd(vc, kbdapplic);
+ clr_kbd(vc, lnm);
+ kbd_table[vc->vc_num].lockstate = 0;
+ kbd_table[vc->vc_num].slockstate = 0;
+ kbd_table[vc->vc_num].ledmode = LED_SHOW_FLAGS;
+ kbd_table[vc->vc_num].ledflagstate = kbd_table[vc->vc_num].default_ledflagstate;
+ /* do not do set_leds here because this causes an endless tasklet loop
+ when the keyboard hasn't been initialized yet */
+
+ vc->vc_cursor_type = cur_default;
+ vc->vc_complement_mask = vc->vc_s_complement_mask;
+
+ default_attr(vc);
+ update_attr(vc);
+
+ vc->vc_tab_stop[0] = 0x01010100;
+ vc->vc_tab_stop[1] =
+ vc->vc_tab_stop[2] =
+ vc->vc_tab_stop[3] =
+ vc->vc_tab_stop[4] =
+ vc->vc_tab_stop[5] =
+ vc->vc_tab_stop[6] =
+ vc->vc_tab_stop[7] = 0x01010101;
+
+ vc->vc_bell_pitch = DEFAULT_BELL_PITCH;
+ vc->vc_bell_duration = DEFAULT_BELL_DURATION;
+
+ gotoxy(vc, 0, 0);
+ save_cur(vc);
+ if (do_clear)
+ csi_J(vc, 2);
+}
+
+/* console_sem is held */
+static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
+{
+ /*
+ * Control characters can be used in the _middle_
+ * of an escape sequence.
+ */
+ switch (c) {
+ case 0:
+ return;
+ case 7:
+ if (vc->vc_bell_duration)
+ kd_mksound(vc->vc_bell_pitch, vc->vc_bell_duration);
+ return;
+ case 8:
+ bs(vc);
+ return;
+ case 9:
+ vc->vc_pos -= (vc->vc_x << 1);
+ while (vc->vc_x < vc->vc_cols - 1) {
+ vc->vc_x++;
+ if (vc->vc_tab_stop[vc->vc_x >> 5] & (1 << (vc->vc_x & 31)))
+ break;
+ }
+ vc->vc_pos += (vc->vc_x << 1);
+ notify_write(vc, '\t');
+ return;
+ case 10: case 11: case 12:
+ lf(vc);
+ if (!is_kbd(vc, lnm))
+ return;
+ case 13:
+ cr(vc);
+ return;
+ case 14:
+ vc->vc_charset = 1;
+ vc->vc_translate = set_translate(vc->vc_G1_charset, vc);
+ vc->vc_disp_ctrl = 1;
+ return;
+ case 15:
+ vc->vc_charset = 0;
+ vc->vc_translate = set_translate(vc->vc_G0_charset, vc);
+ vc->vc_disp_ctrl = 0;
+ return;
+ case 24: case 26:
+ vc->vc_state = ESnormal;
+ return;
+ case 27:
+ vc->vc_state = ESesc;
+ return;
+ case 127:
+ del(vc);
+ return;
+ case 128+27:
+ vc->vc_state = ESsquare;
+ return;
+ }
+ switch(vc->vc_state) {
+ case ESesc:
+ vc->vc_state = ESnormal;
+ switch (c) {
+ case '[':
+ vc->vc_state = ESsquare;
+ return;
+ case ']':
+ vc->vc_state = ESnonstd;
+ return;
+ case '%':
+ vc->vc_state = ESpercent;
+ return;
+ case 'E':
+ cr(vc);
+ lf(vc);
+ return;
+ case 'M':
+ ri(vc);
+ return;
+ case 'D':
+ lf(vc);
+ return;
+ case 'H':
+ vc->vc_tab_stop[vc->vc_x >> 5] |= (1 << (vc->vc_x & 31));
+ return;
+ case 'Z':
+ respond_ID(tty);
+ return;
+ case '7':
+ save_cur(vc);
+ return;
+ case '8':
+ restore_cur(vc);
+ return;
+ case '(':
+ vc->vc_state = ESsetG0;
+ return;
+ case ')':
+ vc->vc_state = ESsetG1;
+ return;
+ case '#':
+ vc->vc_state = EShash;
+ return;
+ case 'c':
+ reset_terminal(vc, 1);
+ return;
+ case '>': /* Numeric keypad */
+ clr_kbd(vc, kbdapplic);
+ return;
+ case '=': /* Appl. keypad */
+ set_kbd(vc, kbdapplic);
+ return;
+ }
+ return;
+ case ESnonstd:
+ if (c=='P') { /* palette escape sequence */
+ for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++)
+ vc->vc_par[vc->vc_npar] = 0;
+ vc->vc_npar = 0;
+ vc->vc_state = ESpalette;
+ return;
+ } else if (c=='R') { /* reset palette */
+ reset_palette(vc);
+ vc->vc_state = ESnormal;
+ } else
+ vc->vc_state = ESnormal;
+ return;
+ case ESpalette:
+ if (isxdigit(c)) {
+ vc->vc_par[vc->vc_npar++] = hex_to_bin(c);
+ if (vc->vc_npar == 7) {
+ int i = vc->vc_par[0] * 3, j = 1;
+ vc->vc_palette[i] = 16 * vc->vc_par[j++];
+ vc->vc_palette[i++] += vc->vc_par[j++];
+ vc->vc_palette[i] = 16 * vc->vc_par[j++];
+ vc->vc_palette[i++] += vc->vc_par[j++];
+ vc->vc_palette[i] = 16 * vc->vc_par[j++];
+ vc->vc_palette[i] += vc->vc_par[j];
+ set_palette(vc);
+ vc->vc_state = ESnormal;
+ }
+ } else
+ vc->vc_state = ESnormal;
+ return;
+ case ESsquare:
+ for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++)
+ vc->vc_par[vc->vc_npar] = 0;
+ vc->vc_npar = 0;
+ vc->vc_state = ESgetpars;
+ if (c == '[') { /* Function key */
+ vc->vc_state=ESfunckey;
+ return;
+ }
+ vc->vc_ques = (c == '?');
+ if (vc->vc_ques)
+ return;
+ case ESgetpars:
+ if (c == ';' && vc->vc_npar < NPAR - 1) {
+ vc->vc_npar++;
+ return;
+ } else if (c>='0' && c<='9') {
+ vc->vc_par[vc->vc_npar] *= 10;
+ vc->vc_par[vc->vc_npar] += c - '0';
+ return;
+ } else
+ vc->vc_state = ESgotpars;
+ case ESgotpars:
+ vc->vc_state = ESnormal;
+ switch(c) {
+ case 'h':
+ set_mode(vc, 1);
+ return;
+ case 'l':
+ set_mode(vc, 0);
+ return;
+ case 'c':
+ if (vc->vc_ques) {
+ if (vc->vc_par[0])
+ vc->vc_cursor_type = vc->vc_par[0] | (vc->vc_par[1] << 8) | (vc->vc_par[2] << 16);
+ else
+ vc->vc_cursor_type = cur_default;
+ return;
+ }
+ break;
+ case 'm':
+ if (vc->vc_ques) {
+ clear_selection();
+ if (vc->vc_par[0])
+ vc->vc_complement_mask = vc->vc_par[0] << 8 | vc->vc_par[1];
+ else
+ vc->vc_complement_mask = vc->vc_s_complement_mask;
+ return;
+ }
+ break;
+ case 'n':
+ if (!vc->vc_ques) {
+ if (vc->vc_par[0] == 5)
+ status_report(tty);
+ else if (vc->vc_par[0] == 6)
+ cursor_report(vc, tty);
+ }
+ return;
+ }
+ if (vc->vc_ques) {
+ vc->vc_ques = 0;
+ return;
+ }
+ switch(c) {
+ case 'G': case '`':
+ if (vc->vc_par[0])
+ vc->vc_par[0]--;
+ gotoxy(vc, vc->vc_par[0], vc->vc_y);
+ return;
+ case 'A':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, vc->vc_x, vc->vc_y - vc->vc_par[0]);
+ return;
+ case 'B': case 'e':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, vc->vc_x, vc->vc_y + vc->vc_par[0]);
+ return;
+ case 'C': case 'a':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, vc->vc_x + vc->vc_par[0], vc->vc_y);
+ return;
+ case 'D':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, vc->vc_x - vc->vc_par[0], vc->vc_y);
+ return;
+ case 'E':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, 0, vc->vc_y + vc->vc_par[0]);
+ return;
+ case 'F':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, 0, vc->vc_y - vc->vc_par[0]);
+ return;
+ case 'd':
+ if (vc->vc_par[0])
+ vc->vc_par[0]--;
+ gotoxay(vc, vc->vc_x ,vc->vc_par[0]);
+ return;
+ case 'H': case 'f':
+ if (vc->vc_par[0])
+ vc->vc_par[0]--;
+ if (vc->vc_par[1])
+ vc->vc_par[1]--;
+ gotoxay(vc, vc->vc_par[1], vc->vc_par[0]);
+ return;
+ case 'J':
+ csi_J(vc, vc->vc_par[0]);
+ return;
+ case 'K':
+ csi_K(vc, vc->vc_par[0]);
+ return;
+ case 'L':
+ csi_L(vc, vc->vc_par[0]);
+ return;
+ case 'M':
+ csi_M(vc, vc->vc_par[0]);
+ return;
+ case 'P':
+ csi_P(vc, vc->vc_par[0]);
+ return;
+ case 'c':
+ if (!vc->vc_par[0])
+ respond_ID(tty);
+ return;
+ case 'g':
+ if (!vc->vc_par[0])
+ vc->vc_tab_stop[vc->vc_x >> 5] &= ~(1 << (vc->vc_x & 31));
+ else if (vc->vc_par[0] == 3) {
+ vc->vc_tab_stop[0] =
+ vc->vc_tab_stop[1] =
+ vc->vc_tab_stop[2] =
+ vc->vc_tab_stop[3] =
+ vc->vc_tab_stop[4] =
+ vc->vc_tab_stop[5] =
+ vc->vc_tab_stop[6] =
+ vc->vc_tab_stop[7] = 0;
+ }
+ return;
+ case 'm':
+ csi_m(vc);
+ return;
+ case 'q': /* DECLL - but only 3 leds */
+ /* map 0,1,2,3 to 0,1,2,4 */
+ if (vc->vc_par[0] < 4)
+ setledstate(kbd_table + vc->vc_num,
+ (vc->vc_par[0] < 3) ? vc->vc_par[0] : 4);
+ return;
+ case 'r':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ if (!vc->vc_par[1])
+ vc->vc_par[1] = vc->vc_rows;
+ /* Minimum allowed region is 2 lines */
+ if (vc->vc_par[0] < vc->vc_par[1] &&
+ vc->vc_par[1] <= vc->vc_rows) {
+ vc->vc_top = vc->vc_par[0] - 1;
+ vc->vc_bottom = vc->vc_par[1];
+ gotoxay(vc, 0, 0);
+ }
+ return;
+ case 's':
+ save_cur(vc);
+ return;
+ case 'u':
+ restore_cur(vc);
+ return;
+ case 'X':
+ csi_X(vc, vc->vc_par[0]);
+ return;
+ case '@':
+ csi_at(vc, vc->vc_par[0]);
+ return;
+ case ']': /* setterm functions */
+ setterm_command(vc);
+ return;
+ }
+ return;
+ case ESpercent:
+ vc->vc_state = ESnormal;
+ switch (c) {
+ case '@': /* defined in ISO 2022 */
+ vc->vc_utf = 0;
+ return;
+ case 'G': /* prelim official escape code */
+ case '8': /* retained for compatibility */
+ vc->vc_utf = 1;
+ return;
+ }
+ return;
+ case ESfunckey:
+ vc->vc_state = ESnormal;
+ return;
+ case EShash:
+ vc->vc_state = ESnormal;
+ if (c == '8') {
+ /* DEC screen alignment test. kludge :-) */
+ vc->vc_video_erase_char =
+ (vc->vc_video_erase_char & 0xff00) | 'E';
+ csi_J(vc, 2);
+ vc->vc_video_erase_char =
+ (vc->vc_video_erase_char & 0xff00) | ' ';
+ do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
+ }
+ return;
+ case ESsetG0:
+ if (c == '0')
+ vc->vc_G0_charset = GRAF_MAP;
+ else if (c == 'B')
+ vc->vc_G0_charset = LAT1_MAP;
+ else if (c == 'U')
+ vc->vc_G0_charset = IBMPC_MAP;
+ else if (c == 'K')
+ vc->vc_G0_charset = USER_MAP;
+ if (vc->vc_charset == 0)
+ vc->vc_translate = set_translate(vc->vc_G0_charset, vc);
+ vc->vc_state = ESnormal;
+ return;
+ case ESsetG1:
+ if (c == '0')
+ vc->vc_G1_charset = GRAF_MAP;
+ else if (c == 'B')
+ vc->vc_G1_charset = LAT1_MAP;
+ else if (c == 'U')
+ vc->vc_G1_charset = IBMPC_MAP;
+ else if (c == 'K')
+ vc->vc_G1_charset = USER_MAP;
+ if (vc->vc_charset == 1)
+ vc->vc_translate = set_translate(vc->vc_G1_charset, vc);
+ vc->vc_state = ESnormal;
+ return;
+ default:
+ vc->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[CON_BUF_SIZE];
+DEFINE_MUTEX(con_buf_mtx);
+
+/* is_double_width() is based on the wcwidth() implementation by
+ * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
+ * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
+ */
+struct interval {
+ uint32_t first;
+ uint32_t last;
+};
+
+static int bisearch(uint32_t ucs, const struct interval *table, int max)
+{
+ int min = 0;
+ int mid;
+
+ if (ucs < table[0].first || ucs > table[max].last)
+ return 0;
+ while (max >= min) {
+ mid = (min + max) / 2;
+ if (ucs > table[mid].last)
+ min = mid + 1;
+ else if (ucs < table[mid].first)
+ max = mid - 1;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static int is_double_width(uint32_t ucs)
+{
+ static const struct interval double_width[] = {
+ { 0x1100, 0x115F }, { 0x2329, 0x232A }, { 0x2E80, 0x303E },
+ { 0x3040, 0xA4CF }, { 0xAC00, 0xD7A3 }, { 0xF900, 0xFAFF },
+ { 0xFE10, 0xFE19 }, { 0xFE30, 0xFE6F }, { 0xFF00, 0xFF60 },
+ { 0xFFE0, 0xFFE6 }, { 0x20000, 0x2FFFD }, { 0x30000, 0x3FFFD }
+ };
+ return bisearch(ucs, double_width, ARRAY_SIZE(double_width) - 1);
+}
+
+/* acquires console_sem */
+static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+#ifdef VT_BUF_VRAM_ONLY
+#define FLUSH do { } while(0);
+#else
+#define FLUSH if (draw_x >= 0) { \
+ vc->vc_sw->con_putcs(vc, (u16 *)draw_from, (u16 *)draw_to - (u16 *)draw_from, vc->vc_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 vc_data *vc;
+ unsigned char vc_attr;
+ struct vt_notifier_param param;
+ uint8_t rescan;
+ uint8_t inverse;
+ uint8_t width;
+ u16 himask, charmask;
+
+ if (in_interrupt())
+ return count;
+
+ might_sleep();
+
+ acquire_console_sem();
+ vc = tty->driver_data;
+ if (vc == NULL) {
+ printk(KERN_ERR "vt: argh, driver_data is NULL !\n");
+ release_console_sem();
+ return 0;
+ }
+
+ currcons = vc->vc_num;
+ if (!vc_cons_allocated(currcons)) {
+ /* could this happen? */
+ printk_once("con_write: tty %d not allocated\n", currcons+1);
+ release_console_sem();
+ return 0;
+ }
+
+ himask = vc->vc_hi_font_mask;
+ charmask = himask ? 0x1ff : 0xff;
+
+ /* undraw cursor first */
+ if (IS_FG(vc))
+ hide_cursor(vc);
+
+ param.vc = vc;
+
+ while (!tty->stopped && count) {
+ int orig = *buf;
+ c = orig;
+ buf++;
+ n++;
+ count--;
+ rescan = 0;
+ inverse = 0;
+ width = 1;
+
+ /* Do no translation at all in control states */
+ if (vc->vc_state != ESnormal) {
+ tc = c;
+ } else if (vc->vc_utf && !vc->vc_disp_ctrl) {
+ /* Combine UTF-8 into Unicode in vc_utf_char.
+ * vc_utf_count is the number of continuation bytes still
+ * expected to arrive.
+ * vc_npar is the number of continuation bytes arrived so
+ * far
+ */
+rescan_last_byte:
+ if ((c & 0xc0) == 0x80) {
+ /* Continuation byte received */
+ static const uint32_t utf8_length_changes[] = { 0x0000007f, 0x000007ff, 0x0000ffff, 0x001fffff, 0x03ffffff, 0x7fffffff };
+ if (vc->vc_utf_count) {
+ vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f);
+ vc->vc_npar++;
+ if (--vc->vc_utf_count) {
+ /* Still need some bytes */
+ continue;
+ }
+ /* Got a whole character */
+ c = vc->vc_utf_char;
+ /* Reject overlong sequences */
+ if (c <= utf8_length_changes[vc->vc_npar - 1] ||
+ c > utf8_length_changes[vc->vc_npar])
+ c = 0xfffd;
+ } else {
+ /* Unexpected continuation byte */
+ vc->vc_utf_count = 0;
+ c = 0xfffd;
+ }
+ } else {
+ /* Single ASCII byte or first byte of a sequence received */
+ if (vc->vc_utf_count) {
+ /* Continuation byte expected */
+ rescan = 1;
+ vc->vc_utf_count = 0;
+ c = 0xfffd;
+ } else if (c > 0x7f) {
+ /* First byte of a multibyte sequence received */
+ vc->vc_npar = 0;
+ if ((c & 0xe0) == 0xc0) {
+ vc->vc_utf_count = 1;
+ vc->vc_utf_char = (c & 0x1f);
+ } else if ((c & 0xf0) == 0xe0) {
+ vc->vc_utf_count = 2;
+ vc->vc_utf_char = (c & 0x0f);
+ } else if ((c & 0xf8) == 0xf0) {
+ vc->vc_utf_count = 3;
+ vc->vc_utf_char = (c & 0x07);
+ } else if ((c & 0xfc) == 0xf8) {
+ vc->vc_utf_count = 4;
+ vc->vc_utf_char = (c & 0x03);
+ } else if ((c & 0xfe) == 0xfc) {
+ vc->vc_utf_count = 5;
+ vc->vc_utf_char = (c & 0x01);
+ } else {
+ /* 254 and 255 are invalid */
+ c = 0xfffd;
+ }
+ if (vc->vc_utf_count) {
+ /* Still need some bytes */
+ continue;
+ }
+ }
+ /* Nothing to do if an ASCII byte was received */
+ }
+ /* End of UTF-8 decoding. */
+ /* c is the received character, or U+FFFD for invalid sequences. */
+ /* Replace invalid Unicode code points with U+FFFD too */
+ if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff)
+ c = 0xfffd;
+ tc = c;
+ } else { /* no utf or alternate charset mode */
+ tc = vc_translate(vc, c);
+ }
+
+ param.c = tc;
+ if (atomic_notifier_call_chain(&vt_notifier_list, VT_PREWRITE,
+ ¶m) == NOTIFY_STOP)
+ continue;
+
+ /* If the original code was a control character we
+ * only allow a glyph to be displayed if the code is
+ * not normally used (such as for cursor movement) or
+ * 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 ||
+ !(vc->vc_disp_ctrl ? (CTRL_ALWAYS >> c) & 1 :
+ vc->vc_utf || ((CTRL_ACTION >> c) & 1)))
+ && (c != 127 || vc->vc_disp_ctrl)
+ && (c != 128+27);
+
+ if (vc->vc_state == ESnormal && ok) {
+ if (vc->vc_utf && !vc->vc_disp_ctrl) {
+ if (is_double_width(c))
+ width = 2;
+ }
+ /* Now try to find out how to display it */
+ tc = conv_uni_to_pc(vc, tc);
+ if (tc & ~charmask) {
+ if (tc == -1 || tc == -2) {
+ continue; /* nothing to display */
+ }
+ /* Glyph not found */
+ if ((!(vc->vc_utf && !vc->vc_disp_ctrl) || c < 128) && !(c & ~charmask)) {
+ /* In legacy mode use the glyph we get by a 1:1 mapping.
+ This would make absolutely no sense with Unicode in mind,
+ but do this for ASCII characters since a font may lack
+ Unicode mapping info and we don't want to end up with
+ having question marks only. */
+ tc = c;
+ } else {
+ /* Display U+FFFD. If it's not found, display an inverse question mark. */
+ tc = conv_uni_to_pc(vc, 0xfffd);
+ if (tc < 0) {
+ inverse = 1;
+ tc = conv_uni_to_pc(vc, '?');
+ if (tc < 0) tc = '?';
+ }
+ }
+ }
+
+ if (!inverse) {
+ vc_attr = vc->vc_attr;
+ } else {
+ /* invert vc_attr */
+ if (!vc->vc_can_do_color) {
+ vc_attr = (vc->vc_attr) ^ 0x08;
+ } else if (vc->vc_hi_font_mask == 0x100) {
+ vc_attr = ((vc->vc_attr) & 0x11) | (((vc->vc_attr) & 0xe0) >> 4) | (((vc->vc_attr) & 0x0e) << 4);
+ } else {
+ vc_attr = ((vc->vc_attr) & 0x88) | (((vc->vc_attr) & 0x70) >> 4) | (((vc->vc_attr) & 0x07) << 4);
+ }
+ FLUSH
+ }
+
+ while (1) {
+ if (vc->vc_need_wrap || vc->vc_decim)
+ FLUSH
+ if (vc->vc_need_wrap) {
+ cr(vc);
+ lf(vc);
+ }
+ if (vc->vc_decim)
+ insert_char(vc, 1);
+ scr_writew(himask ?
+ ((vc_attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) :
+ (vc_attr << 8) + tc,
+ (u16 *) vc->vc_pos);
+ if (DO_UPDATE(vc) && draw_x < 0) {
+ draw_x = vc->vc_x;
+ draw_from = vc->vc_pos;
+ }
+ if (vc->vc_x == vc->vc_cols - 1) {
+ vc->vc_need_wrap = vc->vc_decawm;
+ draw_to = vc->vc_pos + 2;
+ } else {
+ vc->vc_x++;
+ draw_to = (vc->vc_pos += 2);
+ }
+
+ if (!--width) break;
+
+ tc = conv_uni_to_pc(vc, ' '); /* A space is printed in the second column */
+ if (tc < 0) tc = ' ';
+ }
+ notify_write(vc, c);
+
+ if (inverse) {
+ FLUSH
+ }
+
+ if (rescan) {
+ rescan = 0;
+ inverse = 0;
+ width = 1;
+ c = orig;
+ goto rescan_last_byte;
+ }
+ continue;
+ }
+ FLUSH
+ do_con_trol(tty, vc, orig);
+ }
+ FLUSH
+ console_conditional_schedule();
+ release_console_sem();
+ notify_update(vc);
+ return n;
+#undef FLUSH
+}
+
+/*
+ * This is the console switching callback.
+ *
+ * Doing console switching in a process context 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_sem.
+ */
+static void console_callback(struct work_struct *ignored)
+{
+ acquire_console_sem();
+
+ if (want_console >= 0) {
+ if (want_console != fg_console &&
+ vc_cons_allocated(want_console)) {
+ hide_cursor(vc_cons[fg_console].d);
+ change_console(vc_cons[want_console].d);
+ /* we only changed when the console had already
+ been allocated - a new console is not created
+ in an interrupt routine */
+ }
+ want_console = -1;
+ }
+ if (do_poke_blanked_console) { /* do not unblank for a LED change */
+ do_poke_blanked_console = 0;
+ poke_blanked_console();
+ }
+ if (scrollback_delta) {
+ struct vc_data *vc = vc_cons[fg_console].d;
+ clear_selection();
+ if (vc->vc_mode == KD_TEXT)
+ vc->vc_sw->con_scrolldelta(vc, scrollback_delta);
+ scrollback_delta = 0;
+ }
+ if (blank_timer_expired) {
+ do_blank_screen(0);
+ blank_timer_expired = 0;
+ }
+ notify_update(vc_cons[fg_console].d);
+
+ release_console_sem();
+}
+
+int set_console(int nr)
+{
+ struct vc_data *vc = vc_cons[fg_console].d;
+
+ if (!vc_cons_allocated(nr) || vt_dont_switch ||
+ (vc->vt_mode.mode == VT_AUTO && vc->vc_mode == KD_GRAPHICS)) {
+
+ /*
+ * Console switch will fail in console_callback() or
+ * change_console() so there is no point scheduling
+ * the callback
+ *
+ * Existing set_console() users don't check the return
+ * value so this shouldn't break anything
+ */
+ return -EINVAL;
+ }
+
+ want_console = nr;
+ schedule_console_callback();
+
+ return 0;
+}
+
+struct tty_driver *console_driver;
+
+#ifdef CONFIG_VT_CONSOLE
+
+/**
+ * vt_kmsg_redirect() - Sets/gets the kernel message console
+ * @new: The new virtual terminal number or -1 if the console should stay
+ * unchanged
+ *
+ * By default, the kernel messages are always printed on the current virtual
+ * console. However, the user may modify that default with the
+ * TIOCL_SETKMSGREDIRECT ioctl call.
+ *
+ * This function sets the kernel message console to be @new. It returns the old
+ * virtual console number. The virtual terminal number 0 (both as parameter and
+ * return value) means no redirection (i.e. always printed on the currently
+ * active console).
+ *
+ * The parameter -1 means that only the current console is returned, but the
+ * value is not modified. You may use the macro vt_get_kmsg_redirect() in that
+ * case to make the code more understandable.
+ *
+ * When the kernel is compiled without CONFIG_VT_CONSOLE, this function ignores
+ * the parameter and always returns 0.
+ */
+int vt_kmsg_redirect(int new)
+{
+ static int kmsg_con;
+
+ if (new != -1)
+ return xchg(&kmsg_con, new);
+ else
+ return kmsg_con;
+}
+
+/*
+ * Console on virtual terminal
+ *
+ * The console must be locked when we get here.
+ */
+
+static void vt_console_print(struct console *co, const char *b, unsigned count)
+{
+ struct vc_data *vc = vc_cons[fg_console].d;
+ unsigned char c;
+ static DEFINE_SPINLOCK(printing_lock);
+ const ushort *start;
+ ushort cnt = 0;
+ ushort myx;
+ int kmsg_console;
+
+ /* console busy or not yet initialized */
+ if (!printable)
+ return;
+ if (!spin_trylock(&printing_lock))
+ return;
+
+ kmsg_console = vt_get_kmsg_redirect();
+ if (kmsg_console && vc_cons_allocated(kmsg_console - 1))
+ vc = vc_cons[kmsg_console - 1].d;
+
+ /* read `x' only after setting currcons properly (otherwise
+ the `x' macro will read the x of the foreground console). */
+ myx = vc->vc_x;
+
+ if (!vc_cons_allocated(fg_console)) {
+ /* impossible */
+ /* printk("vt_console_print: tty %d not allocated ??\n", currcons+1); */
+ goto quit;
+ }
+
+ if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc))
+ goto quit;
+
+ /* undraw cursor first */
+ if (IS_FG(vc))
+ hide_cursor(vc);
+
+ start = (ushort *)vc->vc_pos;
+
+ /* Contrived structure to try to emulate original need_wrap behaviour
+ * Problems caused when we have need_wrap set on '\n' character */
+ while (count--) {
+ c = *b++;
+ if (c == 10 || c == 13 || c == 8 || vc->vc_need_wrap) {
+ if (cnt > 0) {
+ if (CON_IS_VISIBLE(vc))
+ vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, vc->vc_x);
+ vc->vc_x += cnt;
+ if (vc->vc_need_wrap)
+ vc->vc_x--;
+ cnt = 0;
+ }
+ if (c == 8) { /* backspace */
+ bs(vc);
+ start = (ushort *)vc->vc_pos;
+ myx = vc->vc_x;
+ continue;
+ }
+ if (c != 13)
+ lf(vc);
+ cr(vc);
+ start = (ushort *)vc->vc_pos;
+ myx = vc->vc_x;
+ if (c == 10 || c == 13)
+ continue;
+ }
+ scr_writew((vc->vc_attr << 8) + c, (unsigned short *)vc->vc_pos);
+ notify_write(vc, c);
+ cnt++;
+ if (myx == vc->vc_cols - 1) {
+ vc->vc_need_wrap = 1;
+ continue;
+ }
+ vc->vc_pos += 2;
+ myx++;
+ }
+ if (cnt > 0) {
+ if (CON_IS_VISIBLE(vc))
+ vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, vc->vc_x);
+ vc->vc_x += cnt;
+ if (vc->vc_x == vc->vc_cols) {
+ vc->vc_x--;
+ vc->vc_need_wrap = 1;
+ }
+ }
+ set_cursor(vc);
+ notify_update(vc);
+
+quit:
+ spin_unlock(&printing_lock);
+}
+
+static struct tty_driver *vt_console_device(struct console *c, int *index)
+{
+ *index = c->index ? c->index-1 : fg_console;
+ return console_driver;
+}
+
+static struct console vt_console_driver = {
+ .name = "tty",
+ .write = vt_console_print,
+ .device = vt_console_device,
+ .unblank = unblank_screen,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+#endif
+
+/*
+ * Handling of Linux-specific VC ioctls
+ */
+
+/*
+ * Generally a bit racy with respect to console_sem().
+ *
+ * There are some functions which don't need it.
+ *
+ * There are some functions which can sleep for arbitrary periods
+ * (paste_selection) but we don't need the lock there anyway.
+ *
+ * set_selection has locking, and definitely needs it
+ */
+
+int tioclinux(struct tty_struct *tty, unsigned long arg)
+{
+ char type, data;
+ char __user *p = (char __user *)arg;
+ int lines;
+ int ret;
+
+ if (current->signal->tty != tty && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (get_user(type, p))
+ return -EFAULT;
+ ret = 0;
+
+ switch (type)
+ {
+ case TIOCL_SETSEL:
+ acquire_console_sem();
+ ret = set_selection((struct tiocl_selection __user *)(p+1), tty);
+ release_console_sem();
+ break;
+ case TIOCL_PASTESEL:
+ ret = paste_selection(tty);
+ break;
+ case TIOCL_UNBLANKSCREEN:
+ acquire_console_sem();
+ unblank_screen();
+ release_console_sem();
+ break;
+ case TIOCL_SELLOADLUT:
+ ret = sel_loadlut(p);
+ break;
+ case TIOCL_GETSHIFTSTATE:
+
+ /*
+ * 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;
+ ret = __put_user(data, p);
+ break;
+ case TIOCL_GETMOUSEREPORTING:
+ data = mouse_reporting();
+ ret = __put_user(data, p);
+ break;
+ case TIOCL_SETVESABLANK:
+ ret = set_vesa_blanking(p);
+ break;
+ case TIOCL_GETKMSGREDIRECT:
+ data = vt_get_kmsg_redirect();
+ ret = __put_user(data, p);
+ break;
+ case TIOCL_SETKMSGREDIRECT:
+ if (!capable(CAP_SYS_ADMIN)) {
+ ret = -EPERM;
+ } else {
+ if (get_user(data, p+1))
+ ret = -EFAULT;
+ else
+ vt_kmsg_redirect(data);
+ }
+ break;
+ case TIOCL_GETFGCONSOLE:
+ ret = fg_console;
+ break;
+ case TIOCL_SCROLLCONSOLE:
+ if (get_user(lines, (s32 __user *)(p+4))) {
+ ret = -EFAULT;
+ } else {
+ scrollfront(vc_cons[fg_console].d, lines);
+ ret = 0;
+ }
+ break;
+ case TIOCL_BLANKSCREEN: /* until explicitly unblanked, not only poked */
+ acquire_console_sem();
+ ignore_poke = 1;
+ do_blank_screen(0);
+ release_console_sem();
+ break;
+ case TIOCL_BLANKEDSCREEN:
+ ret = console_blanked;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * /dev/ttyN handling
+ */
+
+static int con_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+ int retval;
+
+ retval = do_con_write(tty, buf, count);
+ con_flush_chars(tty);
+
+ return retval;
+}
+
+static int con_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ if (in_interrupt())
+ return 0; /* n_r3964 calls put_char() from interrupt context */
+ return do_con_write(tty, &ch, 1);
+}
+
+static int con_write_room(struct tty_struct *tty)
+{
+ if (tty->stopped)
+ return 0;
+ return 32768; /* No limit, really; we're not buffering */
+}
+
+static int con_chars_in_buffer(struct tty_struct *tty)
+{
+ 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...
+ */
+static void con_throttle(struct tty_struct *tty)
+{
+}
+
+static void con_unthrottle(struct tty_struct *tty)
+{
+ struct vc_data *vc = tty->driver_data;
+
+ wake_up_interruptible(&vc->paste_wait);
+}
+
+/*
+ * Turn the Scroll-Lock LED on when the tty is stopped
+ */
+static void con_stop(struct tty_struct *tty)
+{
+ int console_num;
+ if (!tty)
+ return;
+ console_num = tty->index;
+ if (!vc_cons_allocated(console_num))
+ return;
+ set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
+ set_leds();
+}
+
+/*
+ * Turn the Scroll-Lock LED off when the console is started
+ */
+static void con_start(struct tty_struct *tty)
+{
+ int console_num;
+ if (!tty)
+ return;
+ console_num = tty->index;
+ if (!vc_cons_allocated(console_num))
+ return;
+ clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
+ set_leds();
+}
+
+static void con_flush_chars(struct tty_struct *tty)
+{
+ struct vc_data *vc;
+
+ if (in_interrupt()) /* from flush_to_ldisc */
+ return;
+
+ /* if we race with con_close(), vt may be null */
+ acquire_console_sem();
+ vc = tty->driver_data;
+ if (vc)
+ set_cursor(vc);
+ release_console_sem();
+}
+
+/*
+ * Allocate the console screen memory.
+ */
+static int con_open(struct tty_struct *tty, struct file *filp)
+{
+ unsigned int currcons = tty->index;
+ int ret = 0;
+
+ acquire_console_sem();
+ if (tty->driver_data == NULL) {
+ ret = vc_allocate(currcons);
+ if (ret == 0) {
+ struct vc_data *vc = vc_cons[currcons].d;
+
+ /* Still being freed */
+ if (vc->port.tty) {
+ release_console_sem();
+ return -ERESTARTSYS;
+ }
+ tty->driver_data = vc;
+ vc->port.tty = tty;
+
+ if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
+ tty->winsize.ws_row = vc_cons[currcons].d->vc_rows;
+ tty->winsize.ws_col = vc_cons[currcons].d->vc_cols;
+ }
+ if (vc->vc_utf)
+ tty->termios->c_iflag |= IUTF8;
+ else
+ tty->termios->c_iflag &= ~IUTF8;
+ release_console_sem();
+ return ret;
+ }
+ }
+ release_console_sem();
+ return ret;
+}
+
+static void con_close(struct tty_struct *tty, struct file *filp)
+{
+ /* Nothing to do - we defer to shutdown */
+}
+
+static void con_shutdown(struct tty_struct *tty)
+{
+ struct vc_data *vc = tty->driver_data;
+ BUG_ON(vc == NULL);
+ acquire_console_sem();
+ vc->port.tty = NULL;
+ release_console_sem();
+ tty_shutdown(tty);
+}
+
+static int default_italic_color = 2; // green (ASCII)
+static int default_underline_color = 3; // cyan (ASCII)
+module_param_named(italic, default_italic_color, int, S_IRUGO | S_IWUSR);
+module_param_named(underline, default_underline_color, int, S_IRUGO | S_IWUSR);
+
+static void vc_init(struct vc_data *vc, unsigned int rows,
+ unsigned int cols, int do_clear)
+{
+ int j, k ;
+
+ vc->vc_cols = cols;
+ vc->vc_rows = rows;
+ vc->vc_size_row = cols << 1;
+ vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row;
+
+ set_origin(vc);
+ vc->vc_pos = vc->vc_origin;
+ reset_vc(vc);
+ for (j=k=0; j<16; j++) {
+ vc->vc_palette[k++] = default_red[j] ;
+ vc->vc_palette[k++] = default_grn[j] ;
+ vc->vc_palette[k++] = default_blu[j] ;
+ }
+ vc->vc_def_color = 0x07; /* white */
+ vc->vc_ulcolor = default_underline_color;
+ vc->vc_itcolor = default_italic_color;
+ vc->vc_halfcolor = 0x08; /* grey */
+ init_waitqueue_head(&vc->paste_wait);
+ reset_terminal(vc, do_clear);
+}
+
+/*
+ * This routine initializes console interrupts, and does nothing
+ * else. If you want the screen to clear, call tty_write with
+ * the appropriate escape-sequence.
+ */
+
+static int __init con_init(void)
+{
+ const char *display_desc = NULL;
+ struct vc_data *vc;
+ unsigned int currcons = 0, i;
+
+ acquire_console_sem();
+
+ if (conswitchp)
+ display_desc = conswitchp->con_startup();
+ if (!display_desc) {
+ fg_console = 0;
+ release_console_sem();
+ return 0;
+ }
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con_driver = ®istered_con_driver[i];
+
+ if (con_driver->con == NULL) {
+ con_driver->con = conswitchp;
+ con_driver->desc = display_desc;
+ con_driver->flag = CON_DRIVER_FLAG_INIT;
+ con_driver->first = 0;
+ con_driver->last = MAX_NR_CONSOLES - 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ con_driver_map[i] = conswitchp;
+
+ if (blankinterval) {
+ blank_state = blank_normal_wait;
+ mod_timer(&console_timer, jiffies + (blankinterval * HZ));
+ }
+
+ for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
+ vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT);
+ INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
+ tty_port_init(&vc->port);
+ visual_init(vc, currcons, 1);
+ vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
+ vc_init(vc, vc->vc_rows, vc->vc_cols,
+ currcons || !vc->vc_sw->con_save_screen);
+ }
+ currcons = fg_console = 0;
+ master_display_fg = vc = vc_cons[currcons].d;
+ set_origin(vc);
+ save_screen(vc);
+ gotoxy(vc, vc->vc_x, vc->vc_y);
+ csi_J(vc, 0);
+ update_screen(vc);
+ printk("Console: %s %s %dx%d",
+ vc->vc_can_do_color ? "colour" : "mono",
+ display_desc, vc->vc_cols, vc->vc_rows);
+ printable = 1;
+ printk("\n");
+
+ release_console_sem();
+
+#ifdef CONFIG_VT_CONSOLE
+ register_console(&vt_console_driver);
+#endif
+ return 0;
+}
+console_initcall(con_init);
+
+static const struct tty_operations con_ops = {
+ .open = con_open,
+ .close = con_close,
+ .write = con_write,
+ .write_room = con_write_room,
+ .put_char = con_put_char,
+ .flush_chars = con_flush_chars,
+ .chars_in_buffer = con_chars_in_buffer,
+ .ioctl = vt_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vt_compat_ioctl,
+#endif
+ .stop = con_stop,
+ .start = con_start,
+ .throttle = con_throttle,
+ .unthrottle = con_unthrottle,
+ .resize = vt_resize,
+ .shutdown = con_shutdown
+};
+
+static struct cdev vc0_cdev;
+
+int __init vty_init(const struct file_operations *console_fops)
+{
+ cdev_init(&vc0_cdev, console_fops);
+ if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
+ register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
+ panic("Couldn't register /dev/tty0 driver\n");
+ device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
+
+ vcs_init();
+
+ console_driver = alloc_tty_driver(MAX_NR_CONSOLES);
+ if (!console_driver)
+ panic("Couldn't allocate console driver\n");
+ console_driver->owner = THIS_MODULE;
+ console_driver->name = "tty";
+ console_driver->name_base = 1;
+ console_driver->major = TTY_MAJOR;
+ console_driver->minor_start = 1;
+ console_driver->type = TTY_DRIVER_TYPE_CONSOLE;
+ console_driver->init_termios = tty_std_termios;
+ if (default_utf8)
+ console_driver->init_termios.c_iflag |= IUTF8;
+ console_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
+ tty_set_operations(console_driver, &con_ops);
+ if (tty_register_driver(console_driver))
+ panic("Couldn't register console driver\n");
+ kbd_init();
+ console_map_init();
+#ifdef CONFIG_MDA_CONSOLE
+ mda_console_init();
+#endif
+ return 0;
+}
+
+#ifndef VT_SINGLE_DRIVER
+
+static struct class *vtconsole_class;
+
+static int bind_con_driver(const struct consw *csw, int first, int last,
+ int deflt)
+{
+ struct module *owner = csw->owner;
+ const char *desc = NULL;
+ struct con_driver *con_driver;
+ int i, j = -1, k = -1, retval = -ENODEV;
+
+ if (!try_module_get(owner))
+ return -ENODEV;
+
+ acquire_console_sem();
+
+ /* check if driver is registered */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = ®istered_con_driver[i];
+
+ if (con_driver->con == csw) {
+ desc = con_driver->desc;
+ retval = 0;
+ break;
+ }
+ }
+
+ if (retval)
+ goto err;
+
+ if (!(con_driver->flag & CON_DRIVER_FLAG_INIT)) {
+ csw->con_startup();
+ con_driver->flag |= CON_DRIVER_FLAG_INIT;
+ }
+
+ if (deflt) {
+ if (conswitchp)
+ module_put(conswitchp->owner);
+
+ __module_get(owner);
+ conswitchp = csw;
+ }
+
+ first = max(first, con_driver->first);
+ last = min(last, con_driver->last);
+
+ for (i = first; i <= last; i++) {
+ int old_was_color;
+ struct vc_data *vc = vc_cons[i].d;
+
+ if (con_driver_map[i])
+ module_put(con_driver_map[i]->owner);
+ __module_get(owner);
+ con_driver_map[i] = csw;
+
+ if (!vc || !vc->vc_sw)
+ continue;
+
+ j = i;
+
+ if (CON_IS_VISIBLE(vc)) {
+ k = i;
+ save_screen(vc);
+ }
+
+ old_was_color = vc->vc_can_do_color;
+ vc->vc_sw->con_deinit(vc);
+ vc->vc_origin = (unsigned long)vc->vc_screenbuf;
+ visual_init(vc, i, 0);
+ set_origin(vc);
+ update_attr(vc);
+
+ /* If the console changed between mono <-> color, then
+ * the attributes in the screenbuf will be wrong. The
+ * following resets all attributes to something sane.
+ */
+ if (old_was_color != vc->vc_can_do_color)
+ clear_buffer_attributes(vc);
+ }
+
+ printk("Console: switching ");
+ if (!deflt)
+ printk("consoles %d-%d ", first+1, last+1);
+ if (j >= 0) {
+ struct vc_data *vc = vc_cons[j].d;
+
+ printk("to %s %s %dx%d\n",
+ vc->vc_can_do_color ? "colour" : "mono",
+ desc, vc->vc_cols, vc->vc_rows);
+
+ if (k >= 0) {
+ vc = vc_cons[k].d;
+ update_screen(vc);
+ }
+ } else
+ printk("to %s\n", desc);
+
+ retval = 0;
+err:
+ release_console_sem();
+ module_put(owner);
+ return retval;
+};
+
+#ifdef CONFIG_VT_HW_CONSOLE_BINDING
+static int con_is_graphics(const struct consw *csw, int first, int last)
+{
+ int i, retval = 0;
+
+ for (i = first; i <= last; i++) {
+ struct vc_data *vc = vc_cons[i].d;
+
+ if (vc && vc->vc_mode == KD_GRAPHICS) {
+ retval = 1;
+ break;
+ }
+ }
+
+ return retval;
+}
+
+/**
+ * unbind_con_driver - unbind a console driver
+ * @csw: pointer to console driver to unregister
+ * @first: first in range of consoles that @csw should be unbound from
+ * @last: last in range of consoles that @csw should be unbound from
+ * @deflt: should next bound console driver be default after @csw is unbound?
+ *
+ * To unbind a driver from all possible consoles, pass 0 as @first and
+ * %MAX_NR_CONSOLES as @last.
+ *
+ * @deflt controls whether the console that ends up replacing @csw should be
+ * the default console.
+ *
+ * RETURNS:
+ * -ENODEV if @csw isn't a registered console driver or can't be unregistered
+ * or 0 on success.
+ */
+int unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
+{
+ struct module *owner = csw->owner;
+ const struct consw *defcsw = NULL;
+ struct con_driver *con_driver = NULL, *con_back = NULL;
+ int i, retval = -ENODEV;
+
+ if (!try_module_get(owner))
+ return -ENODEV;
+
+ acquire_console_sem();
+
+ /* check if driver is registered and if it is unbindable */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = ®istered_con_driver[i];
+
+ if (con_driver->con == csw &&
+ con_driver->flag & CON_DRIVER_FLAG_MODULE) {
+ retval = 0;
+ break;
+ }
+ }
+
+ if (retval) {
+ release_console_sem();
+ goto err;
+ }
+
+ retval = -ENODEV;
+
+ /* check if backup driver exists */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_back = ®istered_con_driver[i];
+
+ if (con_back->con &&
+ !(con_back->flag & CON_DRIVER_FLAG_MODULE)) {
+ defcsw = con_back->con;
+ retval = 0;
+ break;
+ }
+ }
+
+ if (retval) {
+ release_console_sem();
+ goto err;
+ }
+
+ if (!con_is_bound(csw)) {
+ release_console_sem();
+ goto err;
+ }
+
+ first = max(first, con_driver->first);
+ last = min(last, con_driver->last);
+
+ for (i = first; i <= last; i++) {
+ if (con_driver_map[i] == csw) {
+ module_put(csw->owner);
+ con_driver_map[i] = NULL;
+ }
+ }
+
+ if (!con_is_bound(defcsw)) {
+ const struct consw *defconsw = conswitchp;
+
+ defcsw->con_startup();
+ con_back->flag |= CON_DRIVER_FLAG_INIT;
+ /*
+ * vgacon may change the default driver to point
+ * to dummycon, we restore it here...
+ */
+ conswitchp = defconsw;
+ }
+
+ if (!con_is_bound(csw))
+ con_driver->flag &= ~CON_DRIVER_FLAG_INIT;
+
+ release_console_sem();
+ /* ignore return value, binding should not fail */
+ bind_con_driver(defcsw, first, last, deflt);
+err:
+ module_put(owner);
+ return retval;
+
+}
+EXPORT_SYMBOL(unbind_con_driver);
+
+static int vt_bind(struct con_driver *con)
+{
+ const struct consw *defcsw = NULL, *csw = NULL;
+ int i, more = 1, first = -1, last = -1, deflt = 0;
+
+ if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) ||
+ con_is_graphics(con->con, con->first, con->last))
+ goto err;
+
+ csw = con->con;
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con = ®istered_con_driver[i];
+
+ if (con->con && !(con->flag & CON_DRIVER_FLAG_MODULE)) {
+ defcsw = con->con;
+ break;
+ }
+ }
+
+ if (!defcsw)
+ goto err;
+
+ while (more) {
+ more = 0;
+
+ for (i = con->first; i <= con->last; i++) {
+ if (con_driver_map[i] == defcsw) {
+ if (first == -1)
+ first = i;
+ last = i;
+ more = 1;
+ } else if (first != -1)
+ break;
+ }
+
+ if (first == 0 && last == MAX_NR_CONSOLES -1)
+ deflt = 1;
+
+ if (first != -1)
+ bind_con_driver(csw, first, last, deflt);
+
+ first = -1;
+ last = -1;
+ deflt = 0;
+ }
+
+err:
+ return 0;
+}
+
+static int vt_unbind(struct con_driver *con)
+{
+ const struct consw *csw = NULL;
+ int i, more = 1, first = -1, last = -1, deflt = 0;
+
+ if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) ||
+ con_is_graphics(con->con, con->first, con->last))
+ goto err;
+
+ csw = con->con;
+
+ while (more) {
+ more = 0;
+
+ for (i = con->first; i <= con->last; i++) {
+ if (con_driver_map[i] == csw) {
+ if (first == -1)
+ first = i;
+ last = i;
+ more = 1;
+ } else if (first != -1)
+ break;
+ }
+
+ if (first == 0 && last == MAX_NR_CONSOLES -1)
+ deflt = 1;
+
+ if (first != -1)
+ unbind_con_driver(csw, first, last, deflt);
+
+ first = -1;
+ last = -1;
+ deflt = 0;
+ }
+
+err:
+ return 0;
+}
+#else
+static inline int vt_bind(struct con_driver *con)
+{
+ return 0;
+}
+static inline int vt_unbind(struct con_driver *con)
+{
+ return 0;
+}
+#endif /* CONFIG_VT_HW_CONSOLE_BINDING */
+
+static ssize_t store_bind(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct con_driver *con = dev_get_drvdata(dev);
+ int bind = simple_strtoul(buf, NULL, 0);
+
+ if (bind)
+ vt_bind(con);
+ else
+ vt_unbind(con);
+
+ return count;
+}
+
+static ssize_t show_bind(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct con_driver *con = dev_get_drvdata(dev);
+ int bind = con_is_bound(con->con);
+
+ return snprintf(buf, PAGE_SIZE, "%i\n", bind);
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct con_driver *con = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%s %s\n",
+ (con->flag & CON_DRIVER_FLAG_MODULE) ? "(M)" : "(S)",
+ con->desc);
+
+}
+
+static struct device_attribute device_attrs[] = {
+ __ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind),
+ __ATTR(name, S_IRUGO, show_name, NULL),
+};
+
+static int vtconsole_init_device(struct con_driver *con)
+{
+ int i;
+ int error = 0;
+
+ con->flag |= CON_DRIVER_FLAG_ATTR;
+ dev_set_drvdata(con->dev, con);
+ for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
+ error = device_create_file(con->dev, &device_attrs[i]);
+ if (error)
+ break;
+ }
+
+ if (error) {
+ while (--i >= 0)
+ device_remove_file(con->dev, &device_attrs[i]);
+ con->flag &= ~CON_DRIVER_FLAG_ATTR;
+ }
+
+ return error;
+}
+
+static void vtconsole_deinit_device(struct con_driver *con)
+{
+ int i;
+
+ if (con->flag & CON_DRIVER_FLAG_ATTR) {
+ for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
+ device_remove_file(con->dev, &device_attrs[i]);
+ con->flag &= ~CON_DRIVER_FLAG_ATTR;
+ }
+}
+
+/**
+ * con_is_bound - checks if driver is bound to the console
+ * @csw: console driver
+ *
+ * RETURNS: zero if unbound, nonzero if bound
+ *
+ * Drivers can call this and if zero, they should release
+ * all resources allocated on con_startup()
+ */
+int con_is_bound(const struct consw *csw)
+{
+ int i, bound = 0;
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ if (con_driver_map[i] == csw) {
+ bound = 1;
+ break;
+ }
+ }
+
+ return bound;
+}
+EXPORT_SYMBOL(con_is_bound);
+
+/**
+ * con_debug_enter - prepare the console for the kernel debugger
+ * @sw: console driver
+ *
+ * Called when the console is taken over by the kernel debugger, this
+ * function needs to save the current console state, then put the console
+ * into a state suitable for the kernel debugger.
+ *
+ * RETURNS:
+ * Zero on success, nonzero if a failure occurred when trying to prepare
+ * the console for the debugger.
+ */
+int con_debug_enter(struct vc_data *vc)
+{
+ int ret = 0;
+
+ saved_fg_console = fg_console;
+ saved_last_console = last_console;
+ saved_want_console = want_console;
+ saved_vc_mode = vc->vc_mode;
+ saved_console_blanked = console_blanked;
+ vc->vc_mode = KD_TEXT;
+ console_blanked = 0;
+ if (vc->vc_sw->con_debug_enter)
+ ret = vc->vc_sw->con_debug_enter(vc);
+#ifdef CONFIG_KGDB_KDB
+ /* Set the initial LINES variable if it is not already set */
+ if (vc->vc_rows < 999) {
+ int linecount;
+ char lns[4];
+ const char *setargs[3] = {
+ "set",
+ "LINES",
+ lns,
+ };
+ if (kdbgetintenv(setargs[0], &linecount)) {
+ snprintf(lns, 4, "%i", vc->vc_rows);
+ kdb_set(2, setargs);
+ }
+ }
+#endif /* CONFIG_KGDB_KDB */
+ return ret;
+}
+EXPORT_SYMBOL_GPL(con_debug_enter);
+
+/**
+ * con_debug_leave - restore console state
+ * @sw: console driver
+ *
+ * Restore the console state to what it was before the kernel debugger
+ * was invoked.
+ *
+ * RETURNS:
+ * Zero on success, nonzero if a failure occurred when trying to restore
+ * the console.
+ */
+int con_debug_leave(void)
+{
+ struct vc_data *vc;
+ int ret = 0;
+
+ fg_console = saved_fg_console;
+ last_console = saved_last_console;
+ want_console = saved_want_console;
+ console_blanked = saved_console_blanked;
+ vc_cons[fg_console].d->vc_mode = saved_vc_mode;
+
+ vc = vc_cons[fg_console].d;
+ if (vc->vc_sw->con_debug_leave)
+ ret = vc->vc_sw->con_debug_leave(vc);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(con_debug_leave);
+
+/**
+ * register_con_driver - register console driver to console layer
+ * @csw: console driver
+ * @first: the first console to take over, minimum value is 0
+ * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1
+ *
+ * DESCRIPTION: This function registers a console driver which can later
+ * bind to a range of consoles specified by @first and @last. It will
+ * also initialize the console driver by calling con_startup().
+ */
+int register_con_driver(const struct consw *csw, int first, int last)
+{
+ struct module *owner = csw->owner;
+ struct con_driver *con_driver;
+ const char *desc;
+ int i, retval = 0;
+
+ if (!try_module_get(owner))
+ return -ENODEV;
+
+ acquire_console_sem();
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = ®istered_con_driver[i];
+
+ /* already registered */
+ if (con_driver->con == csw)
+ retval = -EINVAL;
+ }
+
+ if (retval)
+ goto err;
+
+ desc = csw->con_startup();
+
+ if (!desc)
+ goto err;
+
+ retval = -EINVAL;
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = ®istered_con_driver[i];
+
+ if (con_driver->con == NULL) {
+ con_driver->con = csw;
+ con_driver->desc = desc;
+ con_driver->node = i;
+ con_driver->flag = CON_DRIVER_FLAG_MODULE |
+ CON_DRIVER_FLAG_INIT;
+ con_driver->first = first;
+ con_driver->last = last;
+ retval = 0;
+ break;
+ }
+ }
+
+ if (retval)
+ goto err;
+
+ con_driver->dev = device_create(vtconsole_class, NULL,
+ MKDEV(0, con_driver->node),
+ NULL, "vtcon%i",
+ con_driver->node);
+
+ if (IS_ERR(con_driver->dev)) {
+ printk(KERN_WARNING "Unable to create device for %s; "
+ "errno = %ld\n", con_driver->desc,
+ PTR_ERR(con_driver->dev));
+ con_driver->dev = NULL;
+ } else {
+ vtconsole_init_device(con_driver);
+ }
+
+err:
+ release_console_sem();
+ module_put(owner);
+ return retval;
+}
+EXPORT_SYMBOL(register_con_driver);
+
+/**
+ * unregister_con_driver - unregister console driver from console layer
+ * @csw: console driver
+ *
+ * DESCRIPTION: All drivers that registers to the console layer must
+ * call this function upon exit, or if the console driver is in a state
+ * where it won't be able to handle console services, such as the
+ * framebuffer console without loaded framebuffer drivers.
+ *
+ * The driver must unbind first prior to unregistration.
+ */
+int unregister_con_driver(const struct consw *csw)
+{
+ int i, retval = -ENODEV;
+
+ acquire_console_sem();
+
+ /* cannot unregister a bound driver */
+ if (con_is_bound(csw))
+ goto err;
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con_driver = ®istered_con_driver[i];
+
+ if (con_driver->con == csw &&
+ con_driver->flag & CON_DRIVER_FLAG_MODULE) {
+ vtconsole_deinit_device(con_driver);
+ device_destroy(vtconsole_class,
+ MKDEV(0, con_driver->node));
+ con_driver->con = NULL;
+ con_driver->desc = NULL;
+ con_driver->dev = NULL;
+ con_driver->node = 0;
+ con_driver->flag = 0;
+ con_driver->first = 0;
+ con_driver->last = 0;
+ retval = 0;
+ break;
+ }
+ }
+err:
+ release_console_sem();
+ return retval;
+}
+EXPORT_SYMBOL(unregister_con_driver);
+
+/*
+ * 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.
+ *
+ * take_over_console is basically a register followed by unbind
+ */
+int take_over_console(const struct consw *csw, int first, int last, int deflt)
+{
+ int err;
+
+ err = register_con_driver(csw, first, last);
+
+ if (!err)
+ bind_con_driver(csw, first, last, deflt);
+
+ return err;
+}
+
+/*
+ * give_up_console is a wrapper to unregister_con_driver. It will only
+ * work if driver is fully unbound.
+ */
+void give_up_console(const struct consw *csw)
+{
+ unregister_con_driver(csw);
+}
+
+static int __init vtconsole_class_init(void)
+{
+ int i;
+
+ vtconsole_class = class_create(THIS_MODULE, "vtconsole");
+ if (IS_ERR(vtconsole_class)) {
+ printk(KERN_WARNING "Unable to create vt console class; "
+ "errno = %ld\n", PTR_ERR(vtconsole_class));
+ vtconsole_class = NULL;
+ }
+
+ /* Add system drivers to sysfs */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con = ®istered_con_driver[i];
+
+ if (con->con && !con->dev) {
+ con->dev = device_create(vtconsole_class, NULL,
+ MKDEV(0, con->node),
+ NULL, "vtcon%i",
+ con->node);
+
+ if (IS_ERR(con->dev)) {
+ printk(KERN_WARNING "Unable to create "
+ "device for %s; errno = %ld\n",
+ con->desc, PTR_ERR(con->dev));
+ con->dev = NULL;
+ } else {
+ vtconsole_init_device(con);
+ }
+ }
+ }
+
+ return 0;
+}
+postcore_initcall(vtconsole_class_init);
+
+#endif
+
+/*
+ * Screen blanking
+ */
+
+static int set_vesa_blanking(char __user *p)
+{
+ unsigned int mode;
+
+ if (get_user(mode, p + 1))
+ return -EFAULT;
+
+ vesa_blank_mode = (mode < 4) ? mode : 0;
+ return 0;
+}
+
+void do_blank_screen(int entering_gfx)
+{
+ struct vc_data *vc = vc_cons[fg_console].d;
+ int i;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ if (console_blanked) {
+ if (blank_state == blank_vesa_wait) {
+ blank_state = blank_off;
+ vc->vc_sw->con_blank(vc, vesa_blank_mode + 1, 0);
+ }
+ return;
+ }
+
+ /* entering graphics mode? */
+ if (entering_gfx) {
+ hide_cursor(vc);
+ save_screen(vc);
+ vc->vc_sw->con_blank(vc, -1, 1);
+ console_blanked = fg_console + 1;
+ blank_state = blank_off;
+ set_origin(vc);
+ return;
+ }
+
+ if (blank_state != blank_normal_wait)
+ return;
+ blank_state = blank_off;
+
+ /* don't blank graphics */
+ if (vc->vc_mode != KD_TEXT) {
+ console_blanked = fg_console + 1;
+ return;
+ }
+
+ hide_cursor(vc);
+ del_timer_sync(&console_timer);
+ blank_timer_expired = 0;
+
+ save_screen(vc);
+ /* In case we need to reset origin, blanking hook returns 1 */
+ i = vc->vc_sw->con_blank(vc, vesa_off_interval ? 1 : (vesa_blank_mode + 1), 0);
+ console_blanked = fg_console + 1;
+ if (i)
+ set_origin(vc);
+
+ if (console_blank_hook && console_blank_hook(1))
+ return;
+
+ if (vesa_off_interval && vesa_blank_mode) {
+ blank_state = blank_vesa_wait;
+ mod_timer(&console_timer, jiffies + vesa_off_interval);
+ }
+ vt_event_post(VT_EVENT_BLANK, vc->vc_num, vc->vc_num);
+}
+EXPORT_SYMBOL(do_blank_screen);
+
+/*
+ * Called by timer as well as from vt_console_driver
+ */
+void do_unblank_screen(int leaving_gfx)
+{
+ struct vc_data *vc;
+
+ /* This should now always be called from a "sane" (read: can schedule)
+ * context for the sake of the low level drivers, except in the special
+ * case of oops_in_progress
+ */
+ if (!oops_in_progress)
+ might_sleep();
+
+ WARN_CONSOLE_UNLOCKED();
+
+ ignore_poke = 0;
+ if (!console_blanked)
+ return;
+ if (!vc_cons_allocated(fg_console)) {
+ /* impossible */
+ printk("unblank_screen: tty %d not allocated ??\n", fg_console+1);
+ return;
+ }
+ vc = vc_cons[fg_console].d;
+ /* Try to unblank in oops case too */
+ if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc))
+ return; /* but leave console_blanked != 0 */
+
+ if (blankinterval) {
+ mod_timer(&console_timer, jiffies + (blankinterval * HZ));
+ blank_state = blank_normal_wait;
+ }
+
+ console_blanked = 0;
+ if (vc->vc_sw->con_blank(vc, 0, leaving_gfx) || vt_force_oops_output(vc))
+ /* Low-level driver cannot restore -> do it ourselves */
+ update_screen(vc);
+ if (console_blank_hook)
+ console_blank_hook(0);
+ set_palette(vc);
+ set_cursor(vc);
+ vt_event_post(VT_EVENT_UNBLANK, vc->vc_num, vc->vc_num);
+}
+EXPORT_SYMBOL(do_unblank_screen);
+
+/*
+ * This is called by the outside world to cause a forced unblank, mostly for
+ * oopses. Currently, I just call do_unblank_screen(0), but we could eventually
+ * call it with 1 as an argument and so force a mode restore... that may kill
+ * X or at least garbage the screen but would also make the Oops visible...
+ */
+void unblank_screen(void)
+{
+ do_unblank_screen(0);
+}
+
+/*
+ * We defer the timer blanking to work queue so it can take the console mutex
+ * (console operations can still happen at irq time, but only from printk which
+ * has the console mutex. Not perfect yet, but better than no locking
+ */
+static void blank_screen_t(unsigned long dummy)
+{
+ if (unlikely(!keventd_up())) {
+ mod_timer(&console_timer, jiffies + (blankinterval * HZ));
+ return;
+ }
+ blank_timer_expired = 1;
+ schedule_work(&console_work);
+}
+
+void poke_blanked_console(void)
+{
+ WARN_CONSOLE_UNLOCKED();
+
+ /* Add this so we quickly catch whoever might call us in a non
+ * safe context. Nowadays, unblank_screen() isn't to be called in
+ * atomic contexts and is allowed to schedule (with the special case
+ * of oops_in_progress, but that isn't of any concern for this
+ * function. --BenH.
+ */
+ might_sleep();
+
+ /* This isn't perfectly race free, but a race here would be mostly harmless,
+ * at worse, we'll do a spurrious blank and it's unlikely
+ */
+ del_timer(&console_timer);
+ blank_timer_expired = 0;
+
+ if (ignore_poke || !vc_cons[fg_console].d || vc_cons[fg_console].d->vc_mode == KD_GRAPHICS)
+ return;
+ if (console_blanked)
+ unblank_screen();
+ else if (blankinterval) {
+ mod_timer(&console_timer, jiffies + (blankinterval * HZ));
+ blank_state = blank_normal_wait;
+ }
+}
+
+/*
+ * Palettes
+ */
+
+static void set_palette(struct vc_data *vc)
+{
+ WARN_CONSOLE_UNLOCKED();
+
+ if (vc->vc_mode != KD_GRAPHICS)
+ vc->vc_sw->con_set_palette(vc, color_table);
+}
+
+static int set_get_cmap(unsigned char __user *arg, int set)
+{
+ int i, j, k;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ for (i = 0; i < 16; i++)
+ if (set) {
+ get_user(default_red[i], arg++);
+ get_user(default_grn[i], arg++);
+ get_user(default_blu[i], arg++);
+ } else {
+ put_user(default_red[i], arg++);
+ put_user(default_grn[i], arg++);
+ put_user(default_blu[i], arg++);
+ }
+ if (set) {
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ if (vc_cons_allocated(i)) {
+ 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(vc_cons[i].d);
+ }
+ }
+ 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.
+ */
+
+int con_set_cmap(unsigned char __user *arg)
+{
+ int rc;
+
+ acquire_console_sem();
+ rc = set_get_cmap (arg,1);
+ release_console_sem();
+
+ return rc;
+}
+
+int con_get_cmap(unsigned char __user *arg)
+{
+ int rc;
+
+ acquire_console_sem();
+ rc = set_get_cmap (arg,0);
+ release_console_sem();
+
+ return rc;
+}
+
+void reset_palette(struct vc_data *vc)
+{
+ int j, k;
+ for (j=k=0; j<16; j++) {
+ vc->vc_palette[k++] = default_red[j];
+ vc->vc_palette[k++] = default_grn[j];
+ vc->vc_palette[k++] = default_blu[j];
+ }
+ set_palette(vc);
+}
+
+/*
+ * 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 won't have to convert the fontdata all the time.
+ * /Jes
+ */
+
+#define max_font_size 65536
+
+static int con_font_get(struct vc_data *vc, struct console_font_op *op)
+{
+ struct console_font font;
+ int rc = -EINVAL;
+ int c;
+
+ if (vc->vc_mode != KD_TEXT)
+ return -EINVAL;
+
+ if (op->data) {
+ font.data = kmalloc(max_font_size, GFP_KERNEL);
+ if (!font.data)
+ return -ENOMEM;
+ } else
+ font.data = NULL;
+
+ acquire_console_sem();
+ if (vc->vc_sw->con_font_get)
+ rc = vc->vc_sw->con_font_get(vc, &font);
+ else
+ rc = -ENOSYS;
+ release_console_sem();
+
+ if (rc)
+ goto out;
+
+ c = (font.width+7)/8 * 32 * font.charcount;
+
+ if (op->data && font.charcount > op->charcount)
+ rc = -ENOSPC;
+ if (!(op->flags & KD_FONT_FLAG_OLD)) {
+ if (font.width > op->width || font.height > op->height)
+ rc = -ENOSPC;
+ } else {
+ if (font.width != 8)
+ rc = -EIO;
+ else if ((op->height && font.height > op->height) ||
+ font.height > 32)
+ rc = -ENOSPC;
+ }
+ if (rc)
+ goto out;
+
+ op->height = font.height;
+ op->width = font.width;
+ op->charcount = font.charcount;
+
+ if (op->data && copy_to_user(op->data, font.data, c))
+ rc = -EFAULT;
+
+out:
+ kfree(font.data);
+ return rc;
+}
+
+static int con_font_set(struct vc_data *vc, struct console_font_op *op)
+{
+ struct console_font font;
+ int rc = -EINVAL;
+ int size;
+
+ if (vc->vc_mode != KD_TEXT)
+ return -EINVAL;
+ if (!op->data)
+ return -EINVAL;
+ if (op->charcount > 512)
+ return -EINVAL;
+ if (!op->height) { /* Need to guess font height [compat] */
+ int h, i;
+ u8 __user *charmap = op->data;
+ u8 tmp;
+
+ /* If from KDFONTOP ioctl, don't allow things which can be done in userland,
+ so that we can get rid of this soon */
+ if (!(op->flags & KD_FONT_FLAG_OLD))
+ return -EINVAL;
+ for (h = 32; h > 0; h--)
+ for (i = 0; i < op->charcount; i++) {
+ if (get_user(tmp, &charmap[32*i+h-1]))
+ return -EFAULT;
+ if (tmp)
+ goto nonzero;
+ }
+ return -EINVAL;
+ nonzero:
+ op->height = h;
+ }
+ if (op->width <= 0 || op->width > 32 || op->height > 32)
+ return -EINVAL;
+ size = (op->width+7)/8 * 32 * op->charcount;
+ if (size > max_font_size)
+ return -ENOSPC;
+ font.charcount = op->charcount;
+ font.height = op->height;
+ font.width = op->width;
+ font.data = memdup_user(op->data, size);
+ if (IS_ERR(font.data))
+ return PTR_ERR(font.data);
+ acquire_console_sem();
+ if (vc->vc_sw->con_font_set)
+ rc = vc->vc_sw->con_font_set(vc, &font, op->flags);
+ else
+ rc = -ENOSYS;
+ release_console_sem();
+ kfree(font.data);
+ return rc;
+}
+
+static int con_font_default(struct vc_data *vc, struct console_font_op *op)
+{
+ struct console_font font = {.width = op->width, .height = op->height};
+ char name[MAX_FONT_NAME];
+ char *s = name;
+ int rc;
+
+ if (vc->vc_mode != KD_TEXT)
+ return -EINVAL;
+
+ if (!op->data)
+ s = NULL;
+ else if (strncpy_from_user(name, op->data, MAX_FONT_NAME - 1) < 0)
+ return -EFAULT;
+ else
+ name[MAX_FONT_NAME - 1] = 0;
+
+ acquire_console_sem();
+ if (vc->vc_sw->con_font_default)
+ rc = vc->vc_sw->con_font_default(vc, &font, s);
+ else
+ rc = -ENOSYS;
+ release_console_sem();
+ if (!rc) {
+ op->width = font.width;
+ op->height = font.height;
+ }
+ return rc;
+}
+
+static int con_font_copy(struct vc_data *vc, struct console_font_op *op)
+{
+ int con = op->height;
+ int rc;
+
+ if (vc->vc_mode != KD_TEXT)
+ return -EINVAL;
+
+ acquire_console_sem();
+ if (!vc->vc_sw->con_font_copy)
+ rc = -ENOSYS;
+ else if (con < 0 || !vc_cons_allocated(con))
+ rc = -ENOTTY;
+ else if (con == vc->vc_num) /* nothing to do */
+ rc = 0;
+ else
+ rc = vc->vc_sw->con_font_copy(vc, con);
+ release_console_sem();
+ return rc;
+}
+
+int con_font_op(struct vc_data *vc, struct console_font_op *op)
+{
+ switch (op->op) {
+ case KD_FONT_OP_SET:
+ return con_font_set(vc, op);
+ case KD_FONT_OP_GET:
+ return con_font_get(vc, op);
+ case KD_FONT_OP_SET_DEFAULT:
+ return con_font_default(vc, op);
+ case KD_FONT_OP_COPY:
+ return con_font_copy(vc, op);
+ }
+ return -ENOSYS;
+}
+
+/*
+ * Interface exported to selection and vcs.
+ */
+
+/* used by selection */
+u16 screen_glyph(struct vc_data *vc, int offset)
+{
+ u16 w = scr_readw(screenpos(vc, offset, 1));
+ u16 c = w & 0xff;
+
+ if (w & vc->vc_hi_font_mask)
+ c |= 0x100;
+ return c;
+}
+EXPORT_SYMBOL_GPL(screen_glyph);
+
+/* used by vcs - note the word offset */
+unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed)
+{
+ return screenpos(vc, 2 * w_offset, viewed);
+}
+
+void getconsxy(struct vc_data *vc, unsigned char *p)
+{
+ p[0] = vc->vc_x;
+ p[1] = vc->vc_y;
+}
+
+void putconsxy(struct vc_data *vc, unsigned char *p)
+{
+ hide_cursor(vc);
+ gotoxy(vc, p[0], p[1]);
+ set_cursor(vc);
+}
+
+u16 vcs_scr_readw(struct vc_data *vc, const u16 *org)
+{
+ if ((unsigned long)org == vc->vc_pos && softcursor_original != -1)
+ return softcursor_original;
+ return scr_readw(org);
+}
+
+void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org)
+{
+ scr_writew(val, org);
+ if ((unsigned long)org == vc->vc_pos) {
+ softcursor_original = -1;
+ add_softcursor(vc);
+ }
+}
+
+void vcs_scr_updated(struct vc_data *vc)
+{
+ notify_update(vc);
+}
+
+/*
+ * Visible symbols for modules
+ */
+
+EXPORT_SYMBOL(color_table);
+EXPORT_SYMBOL(default_red);
+EXPORT_SYMBOL(default_grn);
+EXPORT_SYMBOL(default_blu);
+EXPORT_SYMBOL(update_region);
+EXPORT_SYMBOL(redraw_screen);
+EXPORT_SYMBOL(vc_resize);
+EXPORT_SYMBOL(fg_console);
+EXPORT_SYMBOL(console_blank_hook);
+EXPORT_SYMBOL(console_blanked);
+EXPORT_SYMBOL(vc_cons);
+EXPORT_SYMBOL(global_cursor_default);
+#ifndef VT_SINGLE_DRIVER
+EXPORT_SYMBOL(take_over_console);
+EXPORT_SYMBOL(give_up_console);
+#endif
--- /dev/null
+/*
+ * linux/drivers/char/vt_ioctl.c
+ *
+ * Copyright (C) 1992 obz under the linux copyright
+ *
+ * Dynamic diacritical handling - aeb@cwi.nl - Dec 1993
+ * Dynamic keymap and string allocation - aeb@cwi.nl - May 1994
+ * Restrict VT switching via ioctl() - grif@cs.ucr.edu - Dec 1995
+ * Some code moved for less code duplication - Andi Kleen - Mar 1997
+ * Check put/get_user, cleanups - acme@conectiva.com.br - Jun 2001
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/compat.h>
+#include <linux/module.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/console.h>
+#include <linux/consolemap.h>
+#include <linux/signal.h>
+#include <linux/smp_lock.h>
+#include <linux/timex.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/kbd_diacr.h>
+#include <linux/selection.h>
+
+char vt_dont_switch;
+extern struct tty_driver *console_driver;
+
+#define VT_IS_IN_USE(i) (console_driver->ttys[i] && console_driver->ttys[i]->count)
+#define VT_BUSY(i) (VT_IS_IN_USE(i) || i == fg_console || vc_cons[i].d == sel_cons)
+
+/*
+ * Console (vt and kd) routines, as defined by USL SVR4 manual, and by
+ * experimentation and study of X386 SYSV handling.
+ *
+ * One point of difference: SYSV vt's are /dev/vtX, which X >= 0, and
+ * /dev/console is a separate ttyp. Under Linux, /dev/tty0 is /dev/console,
+ * and the vc start at /dev/ttyX, X >= 1. We maintain that here, so we will
+ * always treat our set of vt as numbered 1..MAX_NR_CONSOLES (corresponding to
+ * ttys 0..MAX_NR_CONSOLES-1). Explicitly naming VT 0 is illegal, but using
+ * /dev/tty0 (fg_console) as a target is legal, since an implicit aliasing
+ * to the current console is done by the main ioctl code.
+ */
+
+#ifdef CONFIG_X86
+#include <linux/syscalls.h>
+#endif
+
+static void complete_change_console(struct vc_data *vc);
+
+/*
+ * User space VT_EVENT handlers
+ */
+
+struct vt_event_wait {
+ struct list_head list;
+ struct vt_event event;
+ int done;
+};
+
+static LIST_HEAD(vt_events);
+static DEFINE_SPINLOCK(vt_event_lock);
+static DECLARE_WAIT_QUEUE_HEAD(vt_event_waitqueue);
+
+/**
+ * vt_event_post
+ * @event: the event that occurred
+ * @old: old console
+ * @new: new console
+ *
+ * Post an VT event to interested VT handlers
+ */
+
+void vt_event_post(unsigned int event, unsigned int old, unsigned int new)
+{
+ struct list_head *pos, *head;
+ unsigned long flags;
+ int wake = 0;
+
+ spin_lock_irqsave(&vt_event_lock, flags);
+ head = &vt_events;
+
+ list_for_each(pos, head) {
+ struct vt_event_wait *ve = list_entry(pos,
+ struct vt_event_wait, list);
+ if (!(ve->event.event & event))
+ continue;
+ ve->event.event = event;
+ /* kernel view is consoles 0..n-1, user space view is
+ console 1..n with 0 meaning current, so we must bias */
+ ve->event.oldev = old + 1;
+ ve->event.newev = new + 1;
+ wake = 1;
+ ve->done = 1;
+ }
+ spin_unlock_irqrestore(&vt_event_lock, flags);
+ if (wake)
+ wake_up_interruptible(&vt_event_waitqueue);
+}
+
+/**
+ * vt_event_wait - wait for an event
+ * @vw: our event
+ *
+ * Waits for an event to occur which completes our vt_event_wait
+ * structure. On return the structure has wv->done set to 1 for success
+ * or 0 if some event such as a signal ended the wait.
+ */
+
+static void vt_event_wait(struct vt_event_wait *vw)
+{
+ unsigned long flags;
+ /* Prepare the event */
+ INIT_LIST_HEAD(&vw->list);
+ vw->done = 0;
+ /* Queue our event */
+ spin_lock_irqsave(&vt_event_lock, flags);
+ list_add(&vw->list, &vt_events);
+ spin_unlock_irqrestore(&vt_event_lock, flags);
+ /* Wait for it to pass */
+ wait_event_interruptible_tty(vt_event_waitqueue, vw->done);
+ /* Dequeue it */
+ spin_lock_irqsave(&vt_event_lock, flags);
+ list_del(&vw->list);
+ spin_unlock_irqrestore(&vt_event_lock, flags);
+}
+
+/**
+ * vt_event_wait_ioctl - event ioctl handler
+ * @arg: argument to ioctl
+ *
+ * Implement the VT_WAITEVENT ioctl using the VT event interface
+ */
+
+static int vt_event_wait_ioctl(struct vt_event __user *event)
+{
+ struct vt_event_wait vw;
+
+ if (copy_from_user(&vw.event, event, sizeof(struct vt_event)))
+ return -EFAULT;
+ /* Highest supported event for now */
+ if (vw.event.event & ~VT_MAX_EVENT)
+ return -EINVAL;
+
+ vt_event_wait(&vw);
+ /* If it occurred report it */
+ if (vw.done) {
+ if (copy_to_user(event, &vw.event, sizeof(struct vt_event)))
+ return -EFAULT;
+ return 0;
+ }
+ return -EINTR;
+}
+
+/**
+ * vt_waitactive - active console wait
+ * @event: event code
+ * @n: new console
+ *
+ * Helper for event waits. Used to implement the legacy
+ * event waiting ioctls in terms of events
+ */
+
+int vt_waitactive(int n)
+{
+ struct vt_event_wait vw;
+ do {
+ if (n == fg_console + 1)
+ break;
+ vw.event.event = VT_EVENT_SWITCH;
+ vt_event_wait(&vw);
+ if (vw.done == 0)
+ return -EINTR;
+ } while (vw.event.newev != n);
+ return 0;
+}
+
+/*
+ * these are the valid i/o ports we're allowed to change. they map all the
+ * video ports
+ */
+#define GPFIRST 0x3b4
+#define GPLAST 0x3df
+#define GPNUM (GPLAST - GPFIRST + 1)
+
+#define i (tmp.kb_index)
+#define s (tmp.kb_table)
+#define v (tmp.kb_value)
+static inline int
+do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, struct kbd_struct *kbd)
+{
+ struct kbentry tmp;
+ ushort *key_map, val, ov;
+
+ if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
+ return -EFAULT;
+
+ if (!capable(CAP_SYS_TTY_CONFIG))
+ perm = 0;
+
+ switch (cmd) {
+ case KDGKBENT:
+ key_map = key_maps[s];
+ if (key_map) {
+ val = U(key_map[i]);
+ if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
+ val = K_HOLE;
+ } else
+ val = (i ? K_HOLE : K_NOSUCHMAP);
+ return put_user(val, &user_kbe->kb_value);
+ case KDSKBENT:
+ if (!perm)
+ return -EPERM;
+ if (!i && v == K_NOSUCHMAP) {
+ /* deallocate map */
+ key_map = key_maps[s];
+ if (s && key_map) {
+ key_maps[s] = NULL;
+ if (key_map[0] == U(K_ALLOCATED)) {
+ kfree(key_map);
+ keymap_count--;
+ }
+ }
+ break;
+ }
+
+ if (KTYP(v) < NR_TYPES) {
+ if (KVAL(v) > max_vals[KTYP(v)])
+ return -EINVAL;
+ } else
+ if (kbd->kbdmode != VC_UNICODE)
+ return -EINVAL;
+
+ /* ++Geert: non-PC keyboards may generate keycode zero */
+#if !defined(__mc68000__) && !defined(__powerpc__)
+ /* assignment to entry 0 only tests validity of args */
+ if (!i)
+ break;
+#endif
+
+ if (!(key_map = key_maps[s])) {
+ int j;
+
+ if (keymap_count >= MAX_NR_OF_USER_KEYMAPS &&
+ !capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ key_map = kmalloc(sizeof(plain_map),
+ GFP_KERNEL);
+ if (!key_map)
+ return -ENOMEM;
+ key_maps[s] = key_map;
+ key_map[0] = U(K_ALLOCATED);
+ for (j = 1; j < NR_KEYS; j++)
+ key_map[j] = U(K_HOLE);
+ keymap_count++;
+ }
+ ov = U(key_map[i]);
+ if (v == ov)
+ break; /* nothing to do */
+ /*
+ * Attention Key.
+ */
+ if (((ov == K_SAK) || (v == K_SAK)) && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ key_map[i] = U(v);
+ if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
+ compute_shiftstate();
+ break;
+ }
+ return 0;
+}
+#undef i
+#undef s
+#undef v
+
+static inline int
+do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, int perm)
+{
+ struct kbkeycode tmp;
+ int kc = 0;
+
+ if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode)))
+ return -EFAULT;
+ switch (cmd) {
+ case KDGETKEYCODE:
+ kc = getkeycode(tmp.scancode);
+ if (kc >= 0)
+ kc = put_user(kc, &user_kbkc->keycode);
+ break;
+ case KDSETKEYCODE:
+ if (!perm)
+ return -EPERM;
+ kc = setkeycode(tmp.scancode, tmp.keycode);
+ break;
+ }
+ return kc;
+}
+
+static inline int
+do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
+{
+ struct kbsentry *kbs;
+ char *p;
+ u_char *q;
+ u_char __user *up;
+ int sz;
+ int delta;
+ char *first_free, *fj, *fnw;
+ int i, j, k;
+ int ret;
+
+ if (!capable(CAP_SYS_TTY_CONFIG))
+ perm = 0;
+
+ kbs = kmalloc(sizeof(*kbs), GFP_KERNEL);
+ if (!kbs) {
+ ret = -ENOMEM;
+ goto reterr;
+ }
+
+ /* we mostly copy too much here (512bytes), but who cares ;) */
+ if (copy_from_user(kbs, user_kdgkb, sizeof(struct kbsentry))) {
+ ret = -EFAULT;
+ goto reterr;
+ }
+ kbs->kb_string[sizeof(kbs->kb_string)-1] = '\0';
+ i = kbs->kb_func;
+
+ switch (cmd) {
+ case KDGKBSENT:
+ sz = sizeof(kbs->kb_string) - 1; /* sz should have been
+ a struct member */
+ up = user_kdgkb->kb_string;
+ p = func_table[i];
+ if(p)
+ for ( ; *p && sz; p++, sz--)
+ if (put_user(*p, up++)) {
+ ret = -EFAULT;
+ goto reterr;
+ }
+ if (put_user('\0', up)) {
+ ret = -EFAULT;
+ goto reterr;
+ }
+ kfree(kbs);
+ return ((p && *p) ? -EOVERFLOW : 0);
+ case KDSKBSENT:
+ if (!perm) {
+ ret = -EPERM;
+ goto reterr;
+ }
+
+ q = func_table[i];
+ first_free = funcbufptr + (funcbufsize - funcbufleft);
+ for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++)
+ ;
+ if (j < MAX_NR_FUNC)
+ fj = func_table[j];
+ else
+ fj = first_free;
+
+ delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string);
+ if (delta <= funcbufleft) { /* it fits in current buf */
+ if (j < MAX_NR_FUNC) {
+ memmove(fj + delta, fj, first_free - fj);
+ for (k = j; k < MAX_NR_FUNC; k++)
+ if (func_table[k])
+ func_table[k] += delta;
+ }
+ if (!q)
+ func_table[i] = fj;
+ funcbufleft -= delta;
+ } else { /* allocate a larger buffer */
+ sz = 256;
+ while (sz < funcbufsize - funcbufleft + delta)
+ sz <<= 1;
+ fnw = kmalloc(sz, GFP_KERNEL);
+ if(!fnw) {
+ ret = -ENOMEM;
+ goto reterr;
+ }
+
+ if (!q)
+ func_table[i] = fj;
+ if (fj > funcbufptr)
+ memmove(fnw, funcbufptr, fj - funcbufptr);
+ for (k = 0; k < j; k++)
+ if (func_table[k])
+ func_table[k] = fnw + (func_table[k] - funcbufptr);
+
+ if (first_free > fj) {
+ memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
+ for (k = j; k < MAX_NR_FUNC; k++)
+ if (func_table[k])
+ func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
+ }
+ if (funcbufptr != func_buf)
+ kfree(funcbufptr);
+ funcbufptr = fnw;
+ funcbufleft = funcbufleft - delta + sz - funcbufsize;
+ funcbufsize = sz;
+ }
+ strcpy(func_table[i], kbs->kb_string);
+ break;
+ }
+ ret = 0;
+reterr:
+ kfree(kbs);
+ return ret;
+}
+
+static inline int
+do_fontx_ioctl(int cmd, struct consolefontdesc __user *user_cfd, int perm, struct console_font_op *op)
+{
+ struct consolefontdesc cfdarg;
+ int i;
+
+ if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case PIO_FONTX:
+ if (!perm)
+ return -EPERM;
+ op->op = KD_FONT_OP_SET;
+ op->flags = KD_FONT_FLAG_OLD;
+ op->width = 8;
+ op->height = cfdarg.charheight;
+ op->charcount = cfdarg.charcount;
+ op->data = cfdarg.chardata;
+ return con_font_op(vc_cons[fg_console].d, op);
+ case GIO_FONTX: {
+ op->op = KD_FONT_OP_GET;
+ op->flags = KD_FONT_FLAG_OLD;
+ op->width = 8;
+ op->height = cfdarg.charheight;
+ op->charcount = cfdarg.charcount;
+ op->data = cfdarg.chardata;
+ i = con_font_op(vc_cons[fg_console].d, op);
+ if (i)
+ return i;
+ cfdarg.charheight = op->height;
+ cfdarg.charcount = op->charcount;
+ if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc)))
+ return -EFAULT;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static inline int
+do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_data *vc)
+{
+ struct unimapdesc tmp;
+
+ if (copy_from_user(&tmp, user_ud, sizeof tmp))
+ return -EFAULT;
+ if (tmp.entries)
+ if (!access_ok(VERIFY_WRITE, tmp.entries,
+ tmp.entry_ct*sizeof(struct unipair)))
+ return -EFAULT;
+ switch (cmd) {
+ case PIO_UNIMAP:
+ if (!perm)
+ return -EPERM;
+ return con_set_unimap(vc, tmp.entry_ct, tmp.entries);
+ case GIO_UNIMAP:
+ if (!perm && fg_console != vc->vc_num)
+ return -EPERM;
+ return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp.entries);
+ }
+ return 0;
+}
+
+
+
+/*
+ * We handle the console-specific ioctl's here. We allow the
+ * capability to modify any console, not just the fg_console.
+ */
+int vt_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vc_data *vc = tty->driver_data;
+ struct console_font_op op; /* used in multiple places here */
+ struct kbd_struct * kbd;
+ unsigned int console;
+ unsigned char ucval;
+ unsigned int uival;
+ void __user *up = (void __user *)arg;
+ int i, perm;
+ int ret = 0;
+
+ console = vc->vc_num;
+
+ tty_lock();
+
+ if (!vc_cons_allocated(console)) { /* impossible? */
+ ret = -ENOIOCTLCMD;
+ goto out;
+ }
+
+
+ /*
+ * To have permissions to do most of the vt ioctls, we either have
+ * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.
+ */
+ perm = 0;
+ if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG))
+ perm = 1;
+
+ kbd = kbd_table + console;
+ switch (cmd) {
+ case TIOCLINUX:
+ ret = tioclinux(tty, arg);
+ break;
+ case KIOCSOUND:
+ if (!perm)
+ goto eperm;
+ /*
+ * The use of PIT_TICK_RATE is historic, it used to be
+ * the platform-dependent CLOCK_TICK_RATE between 2.6.12
+ * and 2.6.36, which was a minor but unfortunate ABI
+ * change.
+ */
+ if (arg)
+ arg = PIT_TICK_RATE / arg;
+ kd_mksound(arg, 0);
+ break;
+
+ case KDMKTONE:
+ if (!perm)
+ goto eperm;
+ {
+ unsigned int ticks, count;
+
+ /*
+ * Generate the tone for the appropriate number of ticks.
+ * If the time is zero, turn off sound ourselves.
+ */
+ ticks = HZ * ((arg >> 16) & 0xffff) / 1000;
+ count = ticks ? (arg & 0xffff) : 0;
+ if (count)
+ count = PIT_TICK_RATE / count;
+ kd_mksound(count, ticks);
+ break;
+ }
+
+ case KDGKBTYPE:
+ /*
+ * this is naive.
+ */
+ ucval = KB_101;
+ goto setchar;
+
+ /*
+ * These cannot be implemented on any machine that implements
+ * ioperm() in user level (such as Alpha PCs) or not at all.
+ *
+ * XXX: you should never use these, just call ioperm directly..
+ */
+#ifdef CONFIG_X86
+ case KDADDIO:
+ case KDDELIO:
+ /*
+ * KDADDIO and KDDELIO may be able to add ports beyond what
+ * we reject here, but to be safe...
+ */
+ if (arg < GPFIRST || arg > GPLAST) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = sys_ioperm(arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0;
+ break;
+
+ case KDENABIO:
+ case KDDISABIO:
+ ret = sys_ioperm(GPFIRST, GPNUM,
+ (cmd == KDENABIO)) ? -ENXIO : 0;
+ break;
+#endif
+
+ /* Linux m68k/i386 interface for setting the keyboard delay/repeat rate */
+
+ case KDKBDREP:
+ {
+ struct kbd_repeat kbrep;
+
+ if (!capable(CAP_SYS_TTY_CONFIG))
+ goto eperm;
+
+ if (copy_from_user(&kbrep, up, sizeof(struct kbd_repeat))) {
+ ret = -EFAULT;
+ break;
+ }
+ ret = kbd_rate(&kbrep);
+ if (ret)
+ break;
+ if (copy_to_user(up, &kbrep, sizeof(struct kbd_repeat)))
+ ret = -EFAULT;
+ break;
+ }
+
+ case KDSETMODE:
+ /*
+ * currently, setting the mode from KD_TEXT to KD_GRAPHICS
+ * doesn't do a whole lot. i'm not sure if it should do any
+ * restoration of modes or what...
+ *
+ * XXX It should at least call into the driver, fbdev's definitely
+ * need to restore their engine state. --BenH
+ */
+ if (!perm)
+ goto eperm;
+ switch (arg) {
+ case KD_GRAPHICS:
+ break;
+ case KD_TEXT0:
+ case KD_TEXT1:
+ arg = KD_TEXT;
+ case KD_TEXT:
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ if (vc->vc_mode == (unsigned char) arg)
+ break;
+ vc->vc_mode = (unsigned char) arg;
+ if (console != fg_console)
+ break;
+ /*
+ * explicitly blank/unblank the screen if switching modes
+ */
+ acquire_console_sem();
+ if (arg == KD_TEXT)
+ do_unblank_screen(1);
+ else
+ do_blank_screen(1);
+ release_console_sem();
+ break;
+
+ case KDGETMODE:
+ uival = vc->vc_mode;
+ goto setint;
+
+ case KDMAPDISP:
+ case KDUNMAPDISP:
+ /*
+ * these work like a combination of mmap and KDENABIO.
+ * this could be easily finished.
+ */
+ ret = -EINVAL;
+ break;
+
+ case KDSKBMODE:
+ if (!perm)
+ goto eperm;
+ switch(arg) {
+ case K_RAW:
+ kbd->kbdmode = VC_RAW;
+ break;
+ case K_MEDIUMRAW:
+ kbd->kbdmode = VC_MEDIUMRAW;
+ break;
+ case K_XLATE:
+ kbd->kbdmode = VC_XLATE;
+ compute_shiftstate();
+ break;
+ case K_UNICODE:
+ kbd->kbdmode = VC_UNICODE;
+ compute_shiftstate();
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ tty_ldisc_flush(tty);
+ break;
+
+ case KDGKBMODE:
+ uival = ((kbd->kbdmode == VC_RAW) ? K_RAW :
+ (kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW :
+ (kbd->kbdmode == VC_UNICODE) ? K_UNICODE :
+ K_XLATE);
+ goto setint;
+
+ /* this could be folded into KDSKBMODE, but for compatibility
+ reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */
+ case KDSKBMETA:
+ switch(arg) {
+ case K_METABIT:
+ clr_vc_kbd_mode(kbd, VC_META);
+ break;
+ case K_ESCPREFIX:
+ set_vc_kbd_mode(kbd, VC_META);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+
+ case KDGKBMETA:
+ uival = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT);
+ setint:
+ ret = put_user(uival, (int __user *)arg);
+ break;
+
+ case KDGETKEYCODE:
+ case KDSETKEYCODE:
+ if(!capable(CAP_SYS_TTY_CONFIG))
+ perm = 0;
+ ret = do_kbkeycode_ioctl(cmd, up, perm);
+ break;
+
+ case KDGKBENT:
+ case KDSKBENT:
+ ret = do_kdsk_ioctl(cmd, up, perm, kbd);
+ break;
+
+ case KDGKBSENT:
+ case KDSKBSENT:
+ ret = do_kdgkb_ioctl(cmd, up, perm);
+ break;
+
+ case KDGKBDIACR:
+ {
+ struct kbdiacrs __user *a = up;
+ struct kbdiacr diacr;
+ int i;
+
+ if (put_user(accent_table_size, &a->kb_cnt)) {
+ ret = -EFAULT;
+ break;
+ }
+ for (i = 0; i < accent_table_size; i++) {
+ diacr.diacr = conv_uni_to_8bit(accent_table[i].diacr);
+ diacr.base = conv_uni_to_8bit(accent_table[i].base);
+ diacr.result = conv_uni_to_8bit(accent_table[i].result);
+ if (copy_to_user(a->kbdiacr + i, &diacr, sizeof(struct kbdiacr))) {
+ ret = -EFAULT;
+ break;
+ }
+ }
+ break;
+ }
+ case KDGKBDIACRUC:
+ {
+ struct kbdiacrsuc __user *a = up;
+
+ if (put_user(accent_table_size, &a->kb_cnt))
+ ret = -EFAULT;
+ else if (copy_to_user(a->kbdiacruc, accent_table,
+ accent_table_size*sizeof(struct kbdiacruc)))
+ ret = -EFAULT;
+ break;
+ }
+
+ case KDSKBDIACR:
+ {
+ struct kbdiacrs __user *a = up;
+ struct kbdiacr diacr;
+ unsigned int ct;
+ int i;
+
+ if (!perm)
+ goto eperm;
+ if (get_user(ct,&a->kb_cnt)) {
+ ret = -EFAULT;
+ break;
+ }
+ if (ct >= MAX_DIACR) {
+ ret = -EINVAL;
+ break;
+ }
+ accent_table_size = ct;
+ for (i = 0; i < ct; i++) {
+ if (copy_from_user(&diacr, a->kbdiacr + i, sizeof(struct kbdiacr))) {
+ ret = -EFAULT;
+ break;
+ }
+ accent_table[i].diacr = conv_8bit_to_uni(diacr.diacr);
+ accent_table[i].base = conv_8bit_to_uni(diacr.base);
+ accent_table[i].result = conv_8bit_to_uni(diacr.result);
+ }
+ break;
+ }
+
+ case KDSKBDIACRUC:
+ {
+ struct kbdiacrsuc __user *a = up;
+ unsigned int ct;
+
+ if (!perm)
+ goto eperm;
+ if (get_user(ct,&a->kb_cnt)) {
+ ret = -EFAULT;
+ break;
+ }
+ if (ct >= MAX_DIACR) {
+ ret = -EINVAL;
+ break;
+ }
+ accent_table_size = ct;
+ if (copy_from_user(accent_table, a->kbdiacruc, ct*sizeof(struct kbdiacruc)))
+ ret = -EFAULT;
+ break;
+ }
+
+ /* the ioctls below read/set the flags usually shown in the leds */
+ /* don't use them - they will go away without warning */
+ case KDGKBLED:
+ ucval = kbd->ledflagstate | (kbd->default_ledflagstate << 4);
+ goto setchar;
+
+ case KDSKBLED:
+ if (!perm)
+ goto eperm;
+ if (arg & ~0x77) {
+ ret = -EINVAL;
+ break;
+ }
+ kbd->ledflagstate = (arg & 7);
+ kbd->default_ledflagstate = ((arg >> 4) & 7);
+ set_leds();
+ break;
+
+ /* the ioctls below only set the lights, not the functions */
+ /* for those, see KDGKBLED and KDSKBLED above */
+ case KDGETLED:
+ ucval = getledstate();
+ setchar:
+ ret = put_user(ucval, (char __user *)arg);
+ break;
+
+ case KDSETLED:
+ if (!perm)
+ goto eperm;
+ setledstate(kbd, arg);
+ break;
+
+ /*
+ * A process can indicate its willingness to accept signals
+ * generated by pressing an appropriate key combination.
+ * Thus, one can have a daemon that e.g. spawns a new console
+ * upon a keypress and then changes to it.
+ * See also the kbrequest field of inittab(5).
+ */
+ case KDSIGACCEPT:
+ {
+ if (!perm || !capable(CAP_KILL))
+ goto eperm;
+ if (!valid_signal(arg) || arg < 1 || arg == SIGKILL)
+ ret = -EINVAL;
+ else {
+ spin_lock_irq(&vt_spawn_con.lock);
+ put_pid(vt_spawn_con.pid);
+ vt_spawn_con.pid = get_pid(task_pid(current));
+ vt_spawn_con.sig = arg;
+ spin_unlock_irq(&vt_spawn_con.lock);
+ }
+ break;
+ }
+
+ case VT_SETMODE:
+ {
+ struct vt_mode tmp;
+
+ if (!perm)
+ goto eperm;
+ if (copy_from_user(&tmp, up, sizeof(struct vt_mode))) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS) {
+ ret = -EINVAL;
+ goto out;
+ }
+ acquire_console_sem();
+ vc->vt_mode = tmp;
+ /* the frsig is ignored, so we set it to 0 */
+ vc->vt_mode.frsig = 0;
+ put_pid(vc->vt_pid);
+ vc->vt_pid = get_pid(task_pid(current));
+ /* no switch is required -- saw@shade.msu.ru */
+ vc->vt_newvt = -1;
+ release_console_sem();
+ break;
+ }
+
+ case VT_GETMODE:
+ {
+ struct vt_mode tmp;
+ int rc;
+
+ acquire_console_sem();
+ memcpy(&tmp, &vc->vt_mode, sizeof(struct vt_mode));
+ release_console_sem();
+
+ rc = copy_to_user(up, &tmp, sizeof(struct vt_mode));
+ if (rc)
+ ret = -EFAULT;
+ break;
+ }
+
+ /*
+ * Returns global vt state. Note that VT 0 is always open, since
+ * it's an alias for the current VT, and people can't use it here.
+ * We cannot return state for more than 16 VTs, since v_state is short.
+ */
+ case VT_GETSTATE:
+ {
+ struct vt_stat __user *vtstat = up;
+ unsigned short state, mask;
+
+ if (put_user(fg_console + 1, &vtstat->v_active))
+ ret = -EFAULT;
+ else {
+ state = 1; /* /dev/tty0 is always open */
+ for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask;
+ ++i, mask <<= 1)
+ if (VT_IS_IN_USE(i))
+ state |= mask;
+ ret = put_user(state, &vtstat->v_state);
+ }
+ break;
+ }
+
+ /*
+ * Returns the first available (non-opened) console.
+ */
+ case VT_OPENQRY:
+ for (i = 0; i < MAX_NR_CONSOLES; ++i)
+ if (! VT_IS_IN_USE(i))
+ break;
+ uival = i < MAX_NR_CONSOLES ? (i+1) : -1;
+ goto setint;
+
+ /*
+ * ioctl(fd, VT_ACTIVATE, num) will cause us to switch to vt # num,
+ * with num >= 1 (switches to vt 0, our console, are not allowed, just
+ * to preserve sanity).
+ */
+ case VT_ACTIVATE:
+ if (!perm)
+ goto eperm;
+ if (arg == 0 || arg > MAX_NR_CONSOLES)
+ ret = -ENXIO;
+ else {
+ arg--;
+ acquire_console_sem();
+ ret = vc_allocate(arg);
+ release_console_sem();
+ if (ret)
+ break;
+ set_console(arg);
+ }
+ break;
+
+ case VT_SETACTIVATE:
+ {
+ struct vt_setactivate vsa;
+
+ if (!perm)
+ goto eperm;
+
+ if (copy_from_user(&vsa, (struct vt_setactivate __user *)arg,
+ sizeof(struct vt_setactivate))) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (vsa.console == 0 || vsa.console > MAX_NR_CONSOLES)
+ ret = -ENXIO;
+ else {
+ vsa.console--;
+ acquire_console_sem();
+ ret = vc_allocate(vsa.console);
+ if (ret == 0) {
+ struct vc_data *nvc;
+ /* This is safe providing we don't drop the
+ console sem between vc_allocate and
+ finishing referencing nvc */
+ nvc = vc_cons[vsa.console].d;
+ nvc->vt_mode = vsa.mode;
+ nvc->vt_mode.frsig = 0;
+ put_pid(nvc->vt_pid);
+ nvc->vt_pid = get_pid(task_pid(current));
+ }
+ release_console_sem();
+ if (ret)
+ break;
+ /* Commence switch and lock */
+ set_console(arg);
+ }
+ }
+
+ /*
+ * wait until the specified VT has been activated
+ */
+ case VT_WAITACTIVE:
+ if (!perm)
+ goto eperm;
+ if (arg == 0 || arg > MAX_NR_CONSOLES)
+ ret = -ENXIO;
+ else
+ ret = vt_waitactive(arg);
+ break;
+
+ /*
+ * If a vt is under process control, the kernel will not switch to it
+ * immediately, but postpone the operation until the process calls this
+ * ioctl, allowing the switch to complete.
+ *
+ * According to the X sources this is the behavior:
+ * 0: pending switch-from not OK
+ * 1: pending switch-from OK
+ * 2: completed switch-to OK
+ */
+ case VT_RELDISP:
+ if (!perm)
+ goto eperm;
+
+ if (vc->vt_mode.mode != VT_PROCESS) {
+ ret = -EINVAL;
+ break;
+ }
+ /*
+ * Switching-from response
+ */
+ acquire_console_sem();
+ if (vc->vt_newvt >= 0) {
+ if (arg == 0)
+ /*
+ * Switch disallowed, so forget we were trying
+ * to do it.
+ */
+ vc->vt_newvt = -1;
+
+ else {
+ /*
+ * The current vt has been released, so
+ * complete the switch.
+ */
+ int newvt;
+ newvt = vc->vt_newvt;
+ vc->vt_newvt = -1;
+ ret = vc_allocate(newvt);
+ if (ret) {
+ release_console_sem();
+ break;
+ }
+ /*
+ * When we actually do the console switch,
+ * make sure we are atomic with respect to
+ * other console switches..
+ */
+ complete_change_console(vc_cons[newvt].d);
+ }
+ } else {
+ /*
+ * Switched-to response
+ */
+ /*
+ * If it's just an ACK, ignore it
+ */
+ if (arg != VT_ACKACQ)
+ ret = -EINVAL;
+ }
+ release_console_sem();
+ break;
+
+ /*
+ * Disallocate memory associated to VT (but leave VT1)
+ */
+ case VT_DISALLOCATE:
+ if (arg > MAX_NR_CONSOLES) {
+ ret = -ENXIO;
+ break;
+ }
+ if (arg == 0) {
+ /* deallocate all unused consoles, but leave 0 */
+ acquire_console_sem();
+ for (i=1; i<MAX_NR_CONSOLES; i++)
+ if (! VT_BUSY(i))
+ vc_deallocate(i);
+ release_console_sem();
+ } else {
+ /* deallocate a single console, if possible */
+ arg--;
+ if (VT_BUSY(arg))
+ ret = -EBUSY;
+ else if (arg) { /* leave 0 */
+ acquire_console_sem();
+ vc_deallocate(arg);
+ release_console_sem();
+ }
+ }
+ break;
+
+ case VT_RESIZE:
+ {
+ struct vt_sizes __user *vtsizes = up;
+ struct vc_data *vc;
+
+ ushort ll,cc;
+ if (!perm)
+ goto eperm;
+ if (get_user(ll, &vtsizes->v_rows) ||
+ get_user(cc, &vtsizes->v_cols))
+ ret = -EFAULT;
+ else {
+ acquire_console_sem();
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ vc = vc_cons[i].d;
+
+ if (vc) {
+ vc->vc_resize_user = 1;
+ vc_resize(vc_cons[i].d, cc, ll);
+ }
+ }
+ release_console_sem();
+ }
+ break;
+ }
+
+ case VT_RESIZEX:
+ {
+ struct vt_consize __user *vtconsize = up;
+ ushort ll,cc,vlin,clin,vcol,ccol;
+ if (!perm)
+ goto eperm;
+ if (!access_ok(VERIFY_READ, vtconsize,
+ sizeof(struct vt_consize))) {
+ ret = -EFAULT;
+ break;
+ }
+ /* FIXME: Should check the copies properly */
+ __get_user(ll, &vtconsize->v_rows);
+ __get_user(cc, &vtconsize->v_cols);
+ __get_user(vlin, &vtconsize->v_vlin);
+ __get_user(clin, &vtconsize->v_clin);
+ __get_user(vcol, &vtconsize->v_vcol);
+ __get_user(ccol, &vtconsize->v_ccol);
+ vlin = vlin ? vlin : vc->vc_scan_lines;
+ if (clin) {
+ if (ll) {
+ if (ll != vlin/clin) {
+ /* Parameters don't add up */
+ ret = -EINVAL;
+ break;
+ }
+ } else
+ ll = vlin/clin;
+ }
+ if (vcol && ccol) {
+ if (cc) {
+ if (cc != vcol/ccol) {
+ ret = -EINVAL;
+ break;
+ }
+ } else
+ cc = vcol/ccol;
+ }
+
+ if (clin > 32) {
+ ret = -EINVAL;
+ break;
+ }
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ if (!vc_cons[i].d)
+ continue;
+ acquire_console_sem();
+ if (vlin)
+ vc_cons[i].d->vc_scan_lines = vlin;
+ if (clin)
+ vc_cons[i].d->vc_font.height = clin;
+ vc_cons[i].d->vc_resize_user = 1;
+ vc_resize(vc_cons[i].d, cc, ll);
+ release_console_sem();
+ }
+ break;
+ }
+
+ case PIO_FONT: {
+ if (!perm)
+ goto eperm;
+ op.op = KD_FONT_OP_SET;
+ op.flags = KD_FONT_FLAG_OLD | KD_FONT_FLAG_DONT_RECALC; /* Compatibility */
+ op.width = 8;
+ op.height = 0;
+ op.charcount = 256;
+ op.data = up;
+ ret = con_font_op(vc_cons[fg_console].d, &op);
+ break;
+ }
+
+ case GIO_FONT: {
+ op.op = KD_FONT_OP_GET;
+ op.flags = KD_FONT_FLAG_OLD;
+ op.width = 8;
+ op.height = 32;
+ op.charcount = 256;
+ op.data = up;
+ ret = con_font_op(vc_cons[fg_console].d, &op);
+ break;
+ }
+
+ case PIO_CMAP:
+ if (!perm)
+ ret = -EPERM;
+ else
+ ret = con_set_cmap(up);
+ break;
+
+ case GIO_CMAP:
+ ret = con_get_cmap(up);
+ break;
+
+ case PIO_FONTX:
+ case GIO_FONTX:
+ ret = do_fontx_ioctl(cmd, up, perm, &op);
+ break;
+
+ case PIO_FONTRESET:
+ {
+ if (!perm)
+ goto eperm;
+
+#ifdef BROKEN_GRAPHICS_PROGRAMS
+ /* With BROKEN_GRAPHICS_PROGRAMS defined, the default
+ font is not saved. */
+ ret = -ENOSYS;
+ break;
+#else
+ {
+ op.op = KD_FONT_OP_SET_DEFAULT;
+ op.data = NULL;
+ ret = con_font_op(vc_cons[fg_console].d, &op);
+ if (ret)
+ break;
+ con_set_default_unimap(vc_cons[fg_console].d);
+ break;
+ }
+#endif
+ }
+
+ case KDFONTOP: {
+ if (copy_from_user(&op, up, sizeof(op))) {
+ ret = -EFAULT;
+ break;
+ }
+ if (!perm && op.op != KD_FONT_OP_GET)
+ goto eperm;
+ ret = con_font_op(vc, &op);
+ if (ret)
+ break;
+ if (copy_to_user(up, &op, sizeof(op)))
+ ret = -EFAULT;
+ break;
+ }
+
+ case PIO_SCRNMAP:
+ if (!perm)
+ ret = -EPERM;
+ else
+ ret = con_set_trans_old(up);
+ break;
+
+ case GIO_SCRNMAP:
+ ret = con_get_trans_old(up);
+ break;
+
+ case PIO_UNISCRNMAP:
+ if (!perm)
+ ret = -EPERM;
+ else
+ ret = con_set_trans_new(up);
+ break;
+
+ case GIO_UNISCRNMAP:
+ ret = con_get_trans_new(up);
+ break;
+
+ case PIO_UNIMAPCLR:
+ { struct unimapinit ui;
+ if (!perm)
+ goto eperm;
+ ret = copy_from_user(&ui, up, sizeof(struct unimapinit));
+ if (ret)
+ ret = -EFAULT;
+ else
+ con_clear_unimap(vc, &ui);
+ break;
+ }
+
+ case PIO_UNIMAP:
+ case GIO_UNIMAP:
+ ret = do_unimap_ioctl(cmd, up, perm, vc);
+ break;
+
+ case VT_LOCKSWITCH:
+ if (!capable(CAP_SYS_TTY_CONFIG))
+ goto eperm;
+ vt_dont_switch = 1;
+ break;
+ case VT_UNLOCKSWITCH:
+ if (!capable(CAP_SYS_TTY_CONFIG))
+ goto eperm;
+ vt_dont_switch = 0;
+ break;
+ case VT_GETHIFONTMASK:
+ ret = put_user(vc->vc_hi_font_mask,
+ (unsigned short __user *)arg);
+ break;
+ case VT_WAITEVENT:
+ ret = vt_event_wait_ioctl((struct vt_event __user *)arg);
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ }
+out:
+ tty_unlock();
+ return ret;
+eperm:
+ ret = -EPERM;
+ goto out;
+}
+
+void reset_vc(struct vc_data *vc)
+{
+ vc->vc_mode = KD_TEXT;
+ kbd_table[vc->vc_num].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
+ vc->vt_mode.mode = VT_AUTO;
+ vc->vt_mode.waitv = 0;
+ vc->vt_mode.relsig = 0;
+ vc->vt_mode.acqsig = 0;
+ vc->vt_mode.frsig = 0;
+ put_pid(vc->vt_pid);
+ vc->vt_pid = NULL;
+ vc->vt_newvt = -1;
+ if (!in_interrupt()) /* Via keyboard.c:SAK() - akpm */
+ reset_palette(vc);
+}
+
+void vc_SAK(struct work_struct *work)
+{
+ struct vc *vc_con =
+ container_of(work, struct vc, SAK_work);
+ struct vc_data *vc;
+ struct tty_struct *tty;
+
+ acquire_console_sem();
+ vc = vc_con->d;
+ if (vc) {
+ tty = vc->port.tty;
+ /*
+ * SAK should also work in all raw modes and reset
+ * them properly.
+ */
+ if (tty)
+ __do_SAK(tty);
+ reset_vc(vc);
+ }
+ release_console_sem();
+}
+
+#ifdef CONFIG_COMPAT
+
+struct compat_consolefontdesc {
+ unsigned short charcount; /* characters in font (256 or 512) */
+ unsigned short charheight; /* scan lines per character (1-32) */
+ compat_caddr_t chardata; /* font data in expanded form */
+};
+
+static inline int
+compat_fontx_ioctl(int cmd, struct compat_consolefontdesc __user *user_cfd,
+ int perm, struct console_font_op *op)
+{
+ struct compat_consolefontdesc cfdarg;
+ int i;
+
+ if (copy_from_user(&cfdarg, user_cfd, sizeof(struct compat_consolefontdesc)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case PIO_FONTX:
+ if (!perm)
+ return -EPERM;
+ op->op = KD_FONT_OP_SET;
+ op->flags = KD_FONT_FLAG_OLD;
+ op->width = 8;
+ op->height = cfdarg.charheight;
+ op->charcount = cfdarg.charcount;
+ op->data = compat_ptr(cfdarg.chardata);
+ return con_font_op(vc_cons[fg_console].d, op);
+ case GIO_FONTX:
+ op->op = KD_FONT_OP_GET;
+ op->flags = KD_FONT_FLAG_OLD;
+ op->width = 8;
+ op->height = cfdarg.charheight;
+ op->charcount = cfdarg.charcount;
+ op->data = compat_ptr(cfdarg.chardata);
+ i = con_font_op(vc_cons[fg_console].d, op);
+ if (i)
+ return i;
+ cfdarg.charheight = op->height;
+ cfdarg.charcount = op->charcount;
+ if (copy_to_user(user_cfd, &cfdarg, sizeof(struct compat_consolefontdesc)))
+ return -EFAULT;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+struct compat_console_font_op {
+ compat_uint_t op; /* operation code KD_FONT_OP_* */
+ compat_uint_t flags; /* KD_FONT_FLAG_* */
+ compat_uint_t width, height; /* font size */
+ compat_uint_t charcount;
+ compat_caddr_t data; /* font data with height fixed to 32 */
+};
+
+static inline int
+compat_kdfontop_ioctl(struct compat_console_font_op __user *fontop,
+ int perm, struct console_font_op *op, struct vc_data *vc)
+{
+ int i;
+
+ if (copy_from_user(op, fontop, sizeof(struct compat_console_font_op)))
+ return -EFAULT;
+ if (!perm && op->op != KD_FONT_OP_GET)
+ return -EPERM;
+ op->data = compat_ptr(((struct compat_console_font_op *)op)->data);
+ op->flags |= KD_FONT_FLAG_OLD;
+ i = con_font_op(vc, op);
+ if (i)
+ return i;
+ ((struct compat_console_font_op *)op)->data = (unsigned long)op->data;
+ if (copy_to_user(fontop, op, sizeof(struct compat_console_font_op)))
+ return -EFAULT;
+ return 0;
+}
+
+struct compat_unimapdesc {
+ unsigned short entry_ct;
+ compat_caddr_t entries;
+};
+
+static inline int
+compat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud,
+ int perm, struct vc_data *vc)
+{
+ struct compat_unimapdesc tmp;
+ struct unipair __user *tmp_entries;
+
+ if (copy_from_user(&tmp, user_ud, sizeof tmp))
+ return -EFAULT;
+ tmp_entries = compat_ptr(tmp.entries);
+ if (tmp_entries)
+ if (!access_ok(VERIFY_WRITE, tmp_entries,
+ tmp.entry_ct*sizeof(struct unipair)))
+ return -EFAULT;
+ switch (cmd) {
+ case PIO_UNIMAP:
+ if (!perm)
+ return -EPERM;
+ return con_set_unimap(vc, tmp.entry_ct, tmp_entries);
+ case GIO_UNIMAP:
+ if (!perm && fg_console != vc->vc_num)
+ return -EPERM;
+ return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp_entries);
+ }
+ return 0;
+}
+
+long vt_compat_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vc_data *vc = tty->driver_data;
+ struct console_font_op op; /* used in multiple places here */
+ struct kbd_struct *kbd;
+ unsigned int console;
+ void __user *up = (void __user *)arg;
+ int perm;
+ int ret = 0;
+
+ console = vc->vc_num;
+
+ tty_lock();
+
+ if (!vc_cons_allocated(console)) { /* impossible? */
+ ret = -ENOIOCTLCMD;
+ goto out;
+ }
+
+ /*
+ * To have permissions to do most of the vt ioctls, we either have
+ * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.
+ */
+ perm = 0;
+ if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG))
+ perm = 1;
+
+ kbd = kbd_table + console;
+ switch (cmd) {
+ /*
+ * these need special handlers for incompatible data structures
+ */
+ case PIO_FONTX:
+ case GIO_FONTX:
+ ret = compat_fontx_ioctl(cmd, up, perm, &op);
+ break;
+
+ case KDFONTOP:
+ ret = compat_kdfontop_ioctl(up, perm, &op, vc);
+ break;
+
+ case PIO_UNIMAP:
+ case GIO_UNIMAP:
+ ret = compat_unimap_ioctl(cmd, up, perm, vc);
+ break;
+
+ /*
+ * all these treat 'arg' as an integer
+ */
+ case KIOCSOUND:
+ case KDMKTONE:
+#ifdef CONFIG_X86
+ case KDADDIO:
+ case KDDELIO:
+#endif
+ case KDSETMODE:
+ case KDMAPDISP:
+ case KDUNMAPDISP:
+ case KDSKBMODE:
+ case KDSKBMETA:
+ case KDSKBLED:
+ case KDSETLED:
+ case KDSIGACCEPT:
+ case VT_ACTIVATE:
+ case VT_WAITACTIVE:
+ case VT_RELDISP:
+ case VT_DISALLOCATE:
+ case VT_RESIZE:
+ case VT_RESIZEX:
+ goto fallback;
+
+ /*
+ * the rest has a compatible data structure behind arg,
+ * but we have to convert it to a proper 64 bit pointer.
+ */
+ default:
+ arg = (unsigned long)compat_ptr(arg);
+ goto fallback;
+ }
+out:
+ tty_unlock();
+ return ret;
+
+fallback:
+ tty_unlock();
+ return vt_ioctl(tty, file, cmd, arg);
+}
+
+
+#endif /* CONFIG_COMPAT */
+
+
+/*
+ * Performs the back end of a vt switch. Called under the console
+ * semaphore.
+ */
+static void complete_change_console(struct vc_data *vc)
+{
+ unsigned char old_vc_mode;
+ int old = fg_console;
+
+ last_console = fg_console;
+
+ /*
+ * If we're switching, we could be going from KD_GRAPHICS to
+ * KD_TEXT mode or vice versa, which means we need to blank or
+ * unblank the screen later.
+ */
+ old_vc_mode = vc_cons[fg_console].d->vc_mode;
+ switch_screen(vc);
+
+ /*
+ * This can't appear below a successful kill_pid(). If it did,
+ * then the *blank_screen operation could occur while X, having
+ * received acqsig, is waking up on another processor. This
+ * condition can lead to overlapping accesses to the VGA range
+ * and the framebuffer (causing system lockups).
+ *
+ * To account for this we duplicate this code below only if the
+ * controlling process is gone and we've called reset_vc.
+ */
+ if (old_vc_mode != vc->vc_mode) {
+ if (vc->vc_mode == KD_TEXT)
+ do_unblank_screen(1);
+ else
+ do_blank_screen(1);
+ }
+
+ /*
+ * If this new console is under process control, send it a signal
+ * telling it that it has acquired. Also check if it has died and
+ * clean up (similar to logic employed in change_console())
+ */
+ if (vc->vt_mode.mode == VT_PROCESS) {
+ /*
+ * Send the signal as privileged - kill_pid() will
+ * tell us if the process has gone or something else
+ * is awry
+ */
+ if (kill_pid(vc->vt_pid, vc->vt_mode.acqsig, 1) != 0) {
+ /*
+ * The controlling process has died, so we revert back to
+ * normal operation. In this case, we'll also change back
+ * to KD_TEXT mode. I'm not sure if this is strictly correct
+ * but it saves the agony when the X server dies and the screen
+ * remains blanked due to KD_GRAPHICS! It would be nice to do
+ * this outside of VT_PROCESS but there is no single process
+ * to account for and tracking tty count may be undesirable.
+ */
+ reset_vc(vc);
+
+ if (old_vc_mode != vc->vc_mode) {
+ if (vc->vc_mode == KD_TEXT)
+ do_unblank_screen(1);
+ else
+ do_blank_screen(1);
+ }
+ }
+ }
+
+ /*
+ * Wake anyone waiting for their VT to activate
+ */
+ vt_event_post(VT_EVENT_SWITCH, old, vc->vc_num);
+ return;
+}
+
+/*
+ * Performs the front-end of a vt switch
+ */
+void change_console(struct vc_data *new_vc)
+{
+ struct vc_data *vc;
+
+ if (!new_vc || new_vc->vc_num == fg_console || vt_dont_switch)
+ return;
+
+ /*
+ * If this vt is in process mode, then we need to handshake with
+ * that process before switching. Essentially, we store where that
+ * vt wants to switch to and wait for it to tell us when it's done
+ * (via VT_RELDISP ioctl).
+ *
+ * We also check to see if the controlling process still exists.
+ * If it doesn't, we reset this vt to auto mode and continue.
+ * This is a cheap way to track process control. The worst thing
+ * that can happen is: we send a signal to a process, it dies, and
+ * the switch gets "lost" waiting for a response; hopefully, the
+ * user will try again, we'll detect the process is gone (unless
+ * the user waits just the right amount of time :-) and revert the
+ * vt to auto control.
+ */
+ vc = vc_cons[fg_console].d;
+ if (vc->vt_mode.mode == VT_PROCESS) {
+ /*
+ * Send the signal as privileged - kill_pid() will
+ * tell us if the process has gone or something else
+ * is awry.
+ *
+ * We need to set vt_newvt *before* sending the signal or we
+ * have a race.
+ */
+ vc->vt_newvt = new_vc->vc_num;
+ if (kill_pid(vc->vt_pid, vc->vt_mode.relsig, 1) == 0) {
+ /*
+ * It worked. Mark the vt to switch to and
+ * return. The process needs to send us a
+ * VT_RELDISP ioctl to complete the switch.
+ */
+ return;
+ }
+
+ /*
+ * The controlling process has died, so we revert back to
+ * normal operation. In this case, we'll also change back
+ * to KD_TEXT mode. I'm not sure if this is strictly correct
+ * but it saves the agony when the X server dies and the screen
+ * remains blanked due to KD_GRAPHICS! It would be nice to do
+ * this outside of VT_PROCESS but there is no single process
+ * to account for and tracking tty count may be undesirable.
+ */
+ reset_vc(vc);
+
+ /*
+ * Fall through to normal (VT_AUTO) handling of the switch...
+ */
+ }
+
+ /*
+ * Ignore all switches in KD_GRAPHICS+VT_AUTO mode
+ */
+ if (vc->vc_mode == KD_GRAPHICS)
+ return;
+
+ complete_change_console(new_vc);
+}
+
+/* Perform a kernel triggered VT switch for suspend/resume */
+
+static int disable_vt_switch;
+
+int vt_move_to_console(unsigned int vt, int alloc)
+{
+ int prev;
+
+ acquire_console_sem();
+ /* Graphics mode - up to X */
+ if (disable_vt_switch) {
+ release_console_sem();
+ return 0;
+ }
+ prev = fg_console;
+
+ if (alloc && vc_allocate(vt)) {
+ /* we can't have a free VC for now. Too bad,
+ * we don't want to mess the screen for now. */
+ release_console_sem();
+ return -ENOSPC;
+ }
+
+ if (set_console(vt)) {
+ /*
+ * We're unable to switch to the SUSPEND_CONSOLE.
+ * Let the calling function know so it can decide
+ * what to do.
+ */
+ release_console_sem();
+ return -EIO;
+ }
+ release_console_sem();
+ tty_lock();
+ if (vt_waitactive(vt + 1)) {
+ pr_debug("Suspend: Can't switch VCs.");
+ tty_unlock();
+ return -EINTR;
+ }
+ tty_unlock();
+ return prev;
+}
+
+/*
+ * Normally during a suspend, we allocate a new console and switch to it.
+ * When we resume, we switch back to the original console. This switch
+ * can be slow, so on systems where the framebuffer can handle restoration
+ * of video registers anyways, there's little point in doing the console
+ * switch. This function allows you to disable it by passing it '0'.
+ */
+void pm_set_vt_switch(int do_switch)
+{
+ acquire_console_sem();
+ disable_vt_switch = !do_switch;
+ release_console_sem();
+}
+EXPORT_SYMBOL(pm_set_vt_switch);