From: Greg Kroah-Hartman Date: Thu, 4 Nov 2010 19:50:47 +0000 (-0700) Subject: TTY: create drivers/tty/vt and move the vt code there X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=60d4ae8d436b8be6a8aedb63440203d5395e9f53;p=GitHub%2FLineageOS%2FG12%2Fandroid_kernel_amlogic_linux-4.9.git TTY: create drivers/tty/vt and move the vt code there The vt and other related code is moved into the drivers/tty/vt directory. Acked-by: Arnd Bergmann Cc: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/char/Makefile b/drivers/char/Makefile index f308494bfc90..ba53ec956c95 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -2,18 +2,10 @@ # 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 @@ -106,28 +98,3 @@ obj-$(CONFIG_RAMOOPS) += ramoops.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 diff --git a/drivers/char/consolemap.c b/drivers/char/consolemap.c deleted file mode 100644 index 45d3e80156d4..000000000000 --- a/drivers/char/consolemap.c +++ /dev/null @@ -1,745 +0,0 @@ -/* - * 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 , July 1998 - * - * Fix bug in inverse translation. Stanislav Voronyi , Dec 1998 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 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); diff --git a/drivers/char/cp437.uni b/drivers/char/cp437.uni deleted file mode 100644 index bc6163484f62..000000000000 --- a/drivers/char/cp437.uni +++ /dev/null @@ -1,291 +0,0 @@ -# -# 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 diff --git a/drivers/char/defkeymap.c_shipped b/drivers/char/defkeymap.c_shipped deleted file mode 100644 index d2208dfe3f67..000000000000 --- a/drivers/char/defkeymap.c_shipped +++ /dev/null @@ -1,262 +0,0 @@ -/* Do not edit this file! It was automatically generated by */ -/* loadkeys --mktable defkeymap.map > defkeymap.c */ - -#include -#include -#include - -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; diff --git a/drivers/char/defkeymap.map b/drivers/char/defkeymap.map deleted file mode 100644 index 50b30cace261..000000000000 --- a/drivers/char/defkeymap.map +++ /dev/null @@ -1,357 +0,0 @@ -# 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 'ÿ' diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c deleted file mode 100644 index e95d7876ca6b..000000000000 --- a/drivers/char/keyboard.c +++ /dev/null @@ -1,1454 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -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; -} diff --git a/drivers/char/selection.c b/drivers/char/selection.c deleted file mode 100644 index ebae344ce910..000000000000 --- a/drivers/char/selection.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -/* Don't take this from : 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; -} diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c deleted file mode 100644 index 273ab44cc91d..000000000000 --- a/drivers/char/vc_screen.c +++ /dev/null @@ -1,644 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#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; -} diff --git a/drivers/char/vt.c b/drivers/char/vt.c deleted file mode 100644 index a8ec48ed14d9..000000000000 --- a/drivers/char/vt.c +++ /dev/null @@ -1,4209 +0,0 @@ -/* - * 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, - * - * - * Rudimentary ISO 10646/Unicode/UTF-8 character set support by - * Markus Kuhn, . - * - * 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 - * - * - * User-defined bell sound, new setterm control sequences and printk - * redirection by Martin Mares 19-Nov-95 - * - * APM screenblank bug fixed Takashi Manabe - * - * Merge with the abstract console driver by Geert Uytterhoeven - * , Jan 1997. - * - * Original m68k console driver modifications by - * - * - Arno Griffioen - * - David Carter - * - * 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 for more information). - * - * Support for changeable cursor shape - * by Pavel Machek , August 1997 - * - * Ported to i386 and con_scrolldelta fixed - * by Emmanuel Marty , April 1998 - * - * Resurrected character buffers in videoram plus lots of other trickery - * by Martin Mares , 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 , Aug 2006 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c deleted file mode 100644 index 6b68a0fb4611..000000000000 --- a/drivers/char/vt_ioctl.c +++ /dev/null @@ -1,1788 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -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 -#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; iv_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); diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 7f63b3315e82..c43ef48b1a0f 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o obj-$(CONFIG_N_HDLC) += n_hdlc.o obj-$(CONFIG_N_GSM) += n_gsm.o obj-$(CONFIG_R3964) += n_r3964.o + +obj-y += vt/ diff --git a/drivers/tty/vt/Makefile b/drivers/tty/vt/Makefile new file mode 100644 index 000000000000..14a51c9960df --- /dev/null +++ b/drivers/tty/vt/Makefile @@ -0,0 +1,34 @@ +# +# 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 diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c new file mode 100644 index 000000000000..45d3e80156d4 --- /dev/null +++ b/drivers/tty/vt/consolemap.c @@ -0,0 +1,745 @@ +/* + * 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 , July 1998 + * + * Fix bug in inverse translation. Stanislav Voronyi , Dec 1998 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 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); diff --git a/drivers/tty/vt/cp437.uni b/drivers/tty/vt/cp437.uni new file mode 100644 index 000000000000..bc6163484f62 --- /dev/null +++ b/drivers/tty/vt/cp437.uni @@ -0,0 +1,291 @@ +# +# 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 diff --git a/drivers/tty/vt/defkeymap.c_shipped b/drivers/tty/vt/defkeymap.c_shipped new file mode 100644 index 000000000000..d2208dfe3f67 --- /dev/null +++ b/drivers/tty/vt/defkeymap.c_shipped @@ -0,0 +1,262 @@ +/* Do not edit this file! It was automatically generated by */ +/* loadkeys --mktable defkeymap.map > defkeymap.c */ + +#include +#include +#include + +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; diff --git a/drivers/tty/vt/defkeymap.map b/drivers/tty/vt/defkeymap.map new file mode 100644 index 000000000000..50b30cace261 --- /dev/null +++ b/drivers/tty/vt/defkeymap.map @@ -0,0 +1,357 @@ +# 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 'ÿ' diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c new file mode 100644 index 000000000000..e95d7876ca6b --- /dev/null +++ b/drivers/tty/vt/keyboard.c @@ -0,0 +1,1454 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c new file mode 100644 index 000000000000..ebae344ce910 --- /dev/null +++ b/drivers/tty/vt/selection.c @@ -0,0 +1,348 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Don't take this from : 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; +} diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c new file mode 100644 index 000000000000..273ab44cc91d --- /dev/null +++ b/drivers/tty/vt/vc_screen.c @@ -0,0 +1,644 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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; +} diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c new file mode 100644 index 000000000000..a8ec48ed14d9 --- /dev/null +++ b/drivers/tty/vt/vt.c @@ -0,0 +1,4209 @@ +/* + * 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, + * + * + * Rudimentary ISO 10646/Unicode/UTF-8 character set support by + * Markus Kuhn, . + * + * 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 + * + * + * User-defined bell sound, new setterm control sequences and printk + * redirection by Martin Mares 19-Nov-95 + * + * APM screenblank bug fixed Takashi Manabe + * + * Merge with the abstract console driver by Geert Uytterhoeven + * , Jan 1997. + * + * Original m68k console driver modifications by + * + * - Arno Griffioen + * - David Carter + * + * 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 for more information). + * + * Support for changeable cursor shape + * by Pavel Machek , August 1997 + * + * Ported to i386 and con_scrolldelta fixed + * by Emmanuel Marty , April 1998 + * + * Resurrected character buffers in videoram plus lots of other trickery + * by Martin Mares , 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 , Aug 2006 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c new file mode 100644 index 000000000000..6b68a0fb4611 --- /dev/null +++ b/drivers/tty/vt/vt_ioctl.c @@ -0,0 +1,1788 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +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 +#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; iv_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);